UVVM Counter Testbench Example - EDA Playground
Warning! This exercise has been opened in another tab; autosave has been disabled. Close this tab or refresh to reactivate.

 Languages & Libraries

 Tools & Simulators

 Examples

206


842
1
--========================================================================================================================
2
-- Copyright (c) 2017 by Bitvis AS.  All rights reserved.
3
-- You should have received a copy of the license file containing the MIT License (see LICENSE.TXT), if not,
4
-- contact Bitvis AS <support@bitvis.no>.
5
--
6
-- UVVM AND ANY PART THEREOF ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
7
-- WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
8
-- OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
9
-- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH UVVM OR THE USE OR OTHER DEALINGS IN UVVM.
10
--========================================================================================================================
11
12
------------------------------------------------------------------------------------------
13
-- Description   : See library quick reference (under 'doc') and README-file(s)
14
------------------------------------------------------------------------------------------
15
16
library ieee;
17
use ieee.std_logic_1164.all;
18
use ieee.numeric_std.all;
19
20
library uvvm_util;
21
context uvvm_util.uvvm_util_context;
22
23
library uvvm_vvc_framework;
24
use uvvm_vvc_framework.ti_vvc_framework_support_pkg.all;
25
26
use work.td_cmd_queue_pkg.all;
27
use work.td_result_queue_pkg.all;
28
use work.vvc_cmd_pkg.all;
29
use work.vvc_methods_pkg.all;
30
use work.td_vvc_framework_common_methods_pkg.all;
31
use work.td_target_support_pkg.all;
32
33
--=================================================================================================
34
--=================================================================================================
35
--=================================================================================================
36
package td_vvc_entity_support_pkg is
37
38
  type t_vvc_labels is record
39
    scope        : string(1 to C_LOG_SCOPE_WIDTH);
40
    vvc_name     : string(1 to C_LOG_SCOPE_WIDTH-2);
41
    instance_idx : natural;
42
    channel      : t_channel;
43
  end record;
44
45
  -------------------------------------------
46
  -- assign_vvc_labels
47
  -------------------------------------------
48
  -- This function puts common VVC labels into a record - to reduce the number of procedure parameters
49
  function assign_vvc_labels(
50
    scope        : string;
51
    vvc_name     : string;
52
    instance_idx : integer;
53
    channel      : t_channel
54
    ) return t_vvc_labels;
55
56
57
  -------------------------------------------
58
  -- format_msg
59
  -------------------------------------------
60
  -- Generates a sting containing the command message and index
61
  -- - Format: Message [index]
62
  impure function format_msg(
63
      command    : t_vvc_cmd_record
64
    ) return string;
65
66
67
  -------------------------------------------
68
  -- vvc_constructor
69
  -------------------------------------------
70
  -- Procedure used as concurrent process in the VVCs
71
  -- - Sets up the vvc_config, command queue and result_queue
72
  -- - Verifies that UVVM has been initialized
73
  procedure vvc_constructor(
74
    constant scope                                 : in string;
75
    constant instance_idx                          : in natural;
76
    variable vvc_config                            : inout t_vvc_config;
77
    variable command_queue                         : inout work.td_cmd_queue_pkg.t_generic_queue;
78
    variable result_queue                          : inout work.td_result_queue_pkg.t_generic_queue;
79
    constant bfm_config                            : in t_bfm_config;
80
    constant cmd_queue_count_max                   : in natural;
81
    constant cmd_queue_count_threshold             : in natural;
82
    constant cmd_queue_count_threshold_severity    : in t_alert_level;
83
    constant result_queue_count_max                : in natural;
84
    constant result_queue_count_threshold          : in natural;
85
    constant result_queue_count_threshold_severity : in t_alert_level
86
  );
87
88
  -------------------------------------------
89
  -- initialize_interpreter
90
  -------------------------------------------
91
  -- Initialises the VVC interpreter
92
  -- - Clears terminate_current_cmd.set to '0'
93
  procedure initialize_interpreter (
94
    signal terminate_current_cmd      : out t_flag_record;
95
    signal global_awaiting_completion : out std_logic_vector(C_MAX_NUM_SEQUENCERS-1 downto 0)
96
    );
97
98
99
  -------------------------------------------
100
  -- await_cmd_from_sequencer
101
  -------------------------------------------
102
  -- Waits for a command from the central sequencer. Continues on matching VVC, Instance, Name and Channel (unless channel = NA)
103
  -- - Log at start using ID_CMD_INTERPRETER_WAIT and at the end using ID_CMD_INTERPRETER
104
  procedure await_cmd_from_sequencer(
105
    constant vvc_labels        : in t_vvc_labels;
106
    constant vvc_config        : in t_vvc_config;
107
    signal VVCT                : in t_vvc_target_record;
108
    signal VVC_BROADCAST       : inout std_logic;
109
    signal global_vvc_busy : inout std_logic;
110
    signal vvc_ack             : out std_logic;
111
    constant shared_vvc_cmd    : in t_vvc_cmd_record;
112
    variable output_vvc_cmd    : out t_vvc_cmd_record
113
    );
114
115
116
  -------------------------------------------
117
  -- put_command_on_queue
118
  -------------------------------------------
119
  -- Puts the received command (by Interpreter) on the VVC queue (for later retrieval by Executor)
120
  procedure put_command_on_queue(
121
    constant command             : in t_vvc_cmd_record;
122
    variable command_queue       : inout work.td_cmd_queue_pkg.t_generic_queue;
123
    variable vvc_status          : inout t_vvc_status;
124
    signal   queue_is_increasing : out   boolean
125
    );
126
127
128
  -------------------------------------------
129
  -- interpreter_await_completion
130
  -------------------------------------------
131
  -- Immediate command: await_completion (in interpreter)
132
  -- - Command description in Quick reference for UVVM common methods
133
  -- - Will wait until given command(s) is completed by the excutor (if not already completed)
134
  -- - Log using ID_IMMEDIATE_CMD when await completed
135
  -- - Log using ID_IMMEDIATE_CMD_WAIT if waiting is actually needed
136
  procedure interpreter_await_completion(
137
    constant command                              : in t_vvc_cmd_record;
138
    variable command_queue                        : inout work.td_cmd_queue_pkg.t_generic_queue;
139
    constant vvc_config                           : in t_vvc_config;
140
    signal executor_is_busy                       : in boolean;
141
    constant vvc_labels                           : in t_vvc_labels;
142
    signal last_cmd_idx_executed                  : in natural;
143
    constant await_completion_pending_msg_id      : in t_msg_id := ID_IMMEDIATE_CMD_WAIT;
144
    constant await_completion_finished_msg_id     : in t_msg_id := ID_IMMEDIATE_CMD
145
    );
146
147
  -------------------------------------------
148
  -- interpreter_await_any_completion
149
  -------------------------------------------
150
  -- Immediate command: await_any_completion() (in interpreter)
151
  -- - This procedure is called by the interpreter if sequencer calls await_any_completion()
152
  --    - It waits for the first of the following :
153
  --      'await_completion' of this VVC, or
154
  --      until global_awaiting_completion(idx) /= '1' (any of the other involved VVCs completed).
155
  -- - Refer to description in Quick reference for UVVM common methods
156
  -- - Log using ID_IMMEDIATE_CMD when the wait completed
157
  -- - Log using ID_IMMEDIATE_CMD_WAIT if waiting is actually needed
158
  procedure interpreter_await_any_completion(
159
    constant command                              : in t_vvc_cmd_record;
160
    variable command_queue                        : inout work.td_cmd_queue_pkg.t_generic_queue;
161
    constant vvc_config                           : in t_vvc_config;
162
    signal executor_is_busy                       : in boolean;
163
    constant vvc_labels                           : in t_vvc_labels;
164
    signal last_cmd_idx_executed                  : in natural;
165
    signal global_awaiting_completion             : inout std_logic_vector; -- Handshake with other VVCs performing await_any_completion
166
    constant await_completion_pending_msg_id      : in t_msg_id := ID_IMMEDIATE_CMD_WAIT;
167
    constant await_completion_finished_msg_id     : in t_msg_id := ID_IMMEDIATE_CMD
168
    );
169
170
  -------------------------------------------
171
  -- interpreter_flush_command_queue
172
  -------------------------------------------
173
  -- Immediate command: flush_command_queue (in interpreter)
174
  -- - Command description in Quick reference for UVVM common methods
175
  -- - Log using ID_IMMEDIATE_CMD
176
  procedure interpreter_flush_command_queue(
177
    constant command            : in t_vvc_cmd_record;
178
    variable command_queue      : inout work.td_cmd_queue_pkg.t_generic_queue;
179
    constant vvc_config         : in t_vvc_config;
180
    variable vvc_status         : inout t_vvc_status;
181
    constant vvc_labels         : in t_vvc_labels
182
    );
183
184
185
  -------------------------------------------
186
  -- interpreter_terminate_current_command
187
  -------------------------------------------
188
  -- Immediate command: terminate_current_command (in interpreter)
189
  -- - Command description in Quick reference for UVVM common methods
190
  -- - Log using ID_IMMEDIATE_CMD
191
  procedure interpreter_terminate_current_command(
192
    constant command              : in t_vvc_cmd_record;
193
    constant vvc_config           : in t_vvc_config;
194
    constant vvc_labels           : in t_vvc_labels;
195
    signal terminate_current_cmd  : inout t_flag_record
196
    );
197
198
199
  -------------------------------------------
200
  -- interpreter_fetch_result
201
  -------------------------------------------
202
  -- Immediate command: interpreter_fetch_result (in interpreter)
203
  -- - Command description in Quick reference for UVVM common methods
204
  -- - Log using ID_IMMEDIATE_CMD
205
  -- t_vvc_response is specific to each VVC,
206
  -- so the BFM can return any type which is then transported from the VVC to the sequencer via a fetch_result() call
207
  procedure interpreter_fetch_result(
208
    variable result_queue           : inout work.td_result_queue_pkg.t_generic_queue;
209
    constant command                : in t_vvc_cmd_record;
210
    constant vvc_config             : in t_vvc_config;
211
    constant vvc_labels             : in t_vvc_labels;
212
    constant last_cmd_idx_executed  : in natural;
213
    variable shared_vvc_response    : inout work.vvc_cmd_pkg.t_vvc_response
214
    );
215
216
  -------------------------------------------
217
  -- initialize_executor
218
  -------------------------------------------
219
  -- Initialises the VVC executor
220
  -- - Resets terminate_current_cmd.reset flag
221
  procedure initialize_executor (
222
    signal terminate_current_cmd  : inout t_flag_record
223
    );
224
225
226
  -------------------------------------------
227
  -- fetch_command_and_prepare_executor
228
  -------------------------------------------
229
  -- Fetches a command from the queue (waits until available if needed)
230
  -- - Log command using ID_CMD_EXECUTOR
231
  -- - Log using ID_CMD_EXECUTOR_WAIT if queue is empty
232
  -- - Sets relevant flags
233
  procedure fetch_command_and_prepare_executor(
234
    variable command              : inout t_vvc_cmd_record;
235
    variable command_queue        : inout work.td_cmd_queue_pkg.t_generic_queue;
236
    constant vvc_config           : in t_vvc_config;
237
    variable vvc_status           : inout t_vvc_status;
238
    signal queue_is_increasing    : in boolean;
239
    signal executor_is_busy       : inout boolean;
240
    constant vvc_labels           : in t_vvc_labels
241
    );
242
243
244
  -------------------------------------------
245
  -- store_result
246
  -------------------------------------------
247
  -- Store result from BFM in the VVC's result_queue
248
  -- The result_queue is used to store a generic type that is returned from
249
  -- a read/expect BFM procedure.
250
  -- It can be fetched later using fetch_result() to return it from the VVC to sequencer
251
  procedure store_result(
252
    variable result_queue  : inout work.td_result_queue_pkg.t_generic_queue;
253
    constant cmd_idx       : in natural;
254
    constant result          : in t_vvc_result
255
    );
256
257
258
  -------------------------------------------
259
  -- insert_inter_bfm_delay_if_requested
260
  -------------------------------------------
261
  -- Inserts delay of either START2START or FINISH2START in time, given that
262
  -- - vvc_config inter-bfm delay type is not set to NO_DELAY
263
  -- - command_is_bfm_access is set to true
264
  -- - Both timestamps are not set to 0 ns.
265
  -- A log message with ID ID_CMD_EXECUTOR is issued when the delay begins and
266
  -- when it has finished delaying.
267
  procedure insert_inter_bfm_delay_if_requested(
268
    constant vvc_config                           : in t_vvc_config;
269
    constant command_is_bfm_access                : in boolean;
270
    constant timestamp_start_of_last_bfm_access   : in time;
271
    constant timestamp_end_of_last_bfm_access     : in time;
272
    constant scope                                : in string := C_SCOPE
273
  );
274
275
276
  function broadcast_cmd_to_shared_cmd (
277
    constant broadcast_cmd : t_broadcastable_cmd
278
  ) return t_operation;
279
280
  function get_command_type_from_operation (
281
    constant broadcast_cmd : t_broadcastable_cmd
282
  ) return t_immediate_or_queued;
283
284
  procedure populate_shared_vvc_cmd_with_broadcast (
285
    variable output_vvc_cmd   : out t_vvc_cmd_record
286
  );
287
288
end package td_vvc_entity_support_pkg;
289
290
291
292
package body td_vvc_entity_support_pkg is
293
294
295
  function assign_vvc_labels(
296
    scope        : string;
297
    vvc_name     : string;
298
    instance_idx : integer;
299
    channel      : t_channel
300
    ) return t_vvc_labels is
301
    variable vvc_labels : t_vvc_labels;
302
  begin
303
    vvc_labels.scope        := pad_string(scope, NUL, vvc_labels.scope'length);
304
    vvc_labels.vvc_name     := pad_string(vvc_name, NUL, vvc_labels.vvc_name'length);
305
    vvc_labels.instance_idx := instance_idx;
306
    vvc_labels.channel      := channel;
307
    return vvc_labels;
308
  end;
309
310
311
  impure function format_msg(
312
      command    : t_vvc_cmd_record
313
    ) return string is
314
  begin
315
    return add_msg_delimiter(to_string(command.msg)) & " " & format_command_idx(command);
316
  end;
317
318
  procedure vvc_constructor(
319
    constant scope                                 : in string;
320
    constant instance_idx                          : in natural;
321
    variable vvc_config                            : inout t_vvc_config;
322
    variable command_queue                         : inout work.td_cmd_queue_pkg.t_generic_queue;
323
    variable result_queue                          : inout work.td_result_queue_pkg.t_generic_queue;
324
    constant bfm_config                            : in t_bfm_config;
325
    constant cmd_queue_count_max                   : in natural;
326
    constant cmd_queue_count_threshold             : in natural;
327
    constant cmd_queue_count_threshold_severity    : in t_alert_level;
328
    constant result_queue_count_max                : in natural;
329
    constant result_queue_count_threshold          : in natural;
330
    constant result_queue_count_threshold_severity : in t_alert_level
331
  ) is
332
    variable v_delta_cycle_counter : natural := 0;
333
  begin
334
    check_value(instance_idx <= C_MAX_VVC_INSTANCE_NUM, TB_FAILURE, "Generic VVC Instance index =" & to_string(instance_idx) &
335
                " cannot exceed C_MAX_VVC_INSTANCE_NUM in UVVM adaptations = " & to_string(C_MAX_VVC_INSTANCE_NUM), C_SCOPE, ID_NEVER);
336
    vvc_config.bfm_config :=  bfm_config;
337
    vvc_config.cmd_queue_count_max := cmd_queue_count_max;
338
    vvc_config.cmd_queue_count_threshold := cmd_queue_count_threshold;
339
    vvc_config.cmd_queue_count_threshold_severity := cmd_queue_count_threshold_severity;
340
    vvc_config.result_queue_count_max := result_queue_count_max;
341
    vvc_config.result_queue_count_threshold := result_queue_count_threshold;
342
    vvc_config.result_queue_count_threshold_severity := result_queue_count_threshold_severity;
343
344
    log(ID_CONSTRUCTOR, "VVC instantiated.", scope, vvc_config.msg_id_panel);
345
    command_queue.set_scope(scope & ":cmd_queue");
346
    command_queue.set_queue_count_max(cmd_queue_count_max);
347
    command_queue.set_queue_count_threshold(cmd_queue_count_threshold);
348
    command_queue.set_queue_count_threshold_severity(cmd_queue_count_threshold_severity);
349
    log(ID_CONSTRUCTOR_SUB, "Command queue instantiated with size " & to_string(command_queue.get_queue_count_max(VOID)), command_queue.get_scope(VOID), vvc_config.msg_id_panel);
350
351
    result_queue.set_scope(scope & ":result_queue");
352
    result_queue.set_queue_count_max(result_queue_count_max);
353
    result_queue.set_queue_count_threshold(result_queue_count_threshold);
354
    result_queue.set_queue_count_threshold_severity(result_queue_count_threshold_severity);
355
    log(ID_CONSTRUCTOR_SUB, "Result queue instantiated with size " & to_string(result_queue.get_queue_count_max(VOID)), result_queue.get_scope(VOID), vvc_config.msg_id_panel);
356
357
    if shared_uvvm_state /= PHASE_A then
358
      loop
359
        wait for 0 ns;
360
        v_delta_cycle_counter := v_delta_cycle_counter + 1;
361
        exit when shared_uvvm_state = PHASE_A;
362
        check_value((shared_uvvm_state /= IDLE), TB_FAILURE, "UVVM will not work without intitalize_uvvm instantiated as a concurrent procedure in the test harness", scope);
363
      end loop;
364
    end if;
365
366
    wait;  -- show message only once per VVC instance
367
  end procedure;
368
369
  procedure initialize_interpreter (
370
    signal terminate_current_cmd      : out t_flag_record;
371
    signal global_awaiting_completion : out std_logic_vector(C_MAX_NUM_SEQUENCERS-1 downto 0)
372
    ) is
373
  begin
374
    terminate_current_cmd  <= (set => '0', reset => 'Z', is_active => 'Z');  -- Initialise to avoid undefineds. This process is driving param 1 only.
375
    wait for 0 ns;  -- delay by 1 delta cycle to allow constructor to finish first
376
377
    global_awaiting_completion <= (others => 'Z'); -- Avoid driving until the VVC is involved in await_any_completion()
378
   end procedure;
379
380
  function broadcast_cmd_to_shared_cmd (
381
    constant broadcast_cmd : t_broadcastable_cmd
382
  ) return t_operation is
383
  begin
384
    case broadcast_cmd is
385
      when AWAIT_COMPLETION          => return AWAIT_COMPLETION;
386
      when ENABLE_LOG_MSG            => return ENABLE_LOG_MSG;
387
      when DISABLE_LOG_MSG           => return DISABLE_LOG_MSG;
388
      when FLUSH_COMMAND_QUEUE       => return FLUSH_COMMAND_QUEUE;
389
      when INSERT_DELAY              => return INSERT_DELAY;
390
      when TERMINATE_CURRENT_COMMAND => return TERMINATE_CURRENT_COMMAND;
391
      when others                    => return NO_OPERATION;
392
    end case;
393
  end function;
394
395
396
  function get_command_type_from_operation (
397
    constant broadcast_cmd : t_broadcastable_cmd
398
  ) return t_immediate_or_queued is
399
  begin
400
    case broadcast_cmd is
401
      when AWAIT_COMPLETION           => return IMMEDIATE;
402
      when ENABLE_LOG_MSG             => return IMMEDIATE;
403
      when DISABLE_LOG_MSG            => return IMMEDIATE;
404
      when FLUSH_COMMAND_QUEUE        => return IMMEDIATE;
405
      when TERMINATE_CURRENT_COMMAND  => return IMMEDIATE;
406
      when INSERT_DELAY               => return QUEUED;
407
      when others                     => return NO_command_type;
408
    end case;
409
  end function;
410
411
412
  procedure populate_shared_vvc_cmd_with_broadcast (
413
    variable output_vvc_cmd   : out t_vvc_cmd_record
414
  ) is
415
  begin
416
417
    -- Increment the shared command index. This is normally done in the CDM, but for broadcast commands it is done by the VVC itself.
418
    check_value((shared_uvvm_state /= IDLE), TB_FAILURE, "UVVM will not work without uvvm_vvc_framework.ti_uvvm_engine instantiated in the test harness", C_SCOPE, ID_NEVER);
419
    await_semaphore_in_delta_cycles(protected_broadcast_semaphore);
420
    shared_cmd_idx := shared_cmd_idx + 1;
421
422
    -- Populate the shared VVC command record
423
    output_vvc_cmd.operation    := broadcast_cmd_to_shared_cmd(shared_vvc_broadcast_cmd.operation);
424
    output_vvc_cmd.msg_id       := shared_vvc_broadcast_cmd.msg_id;
425
    output_vvc_cmd.msg          := shared_vvc_broadcast_cmd.msg;
426
    output_vvc_cmd.quietness    := shared_vvc_broadcast_cmd.quietness;
427
    output_vvc_cmd.delay        := shared_vvc_broadcast_cmd.delay;
428
    output_vvc_cmd.timeout      := shared_vvc_broadcast_cmd.timeout;
429
    output_vvc_cmd.gen_integer_array(0)  := shared_vvc_broadcast_cmd.gen_integer;
430
    output_vvc_cmd.proc_call    := shared_vvc_broadcast_cmd.proc_call;
431
    output_vvc_cmd.cmd_idx      := shared_cmd_idx;
432
    output_vvc_cmd.command_type := get_command_type_from_operation(shared_vvc_broadcast_cmd.operation);
433
434
    if global_show_msg_for_uvvm_cmd then
435
      log(ID_UVVM_SEND_CMD, to_string(shared_vvc_cmd.proc_call) & ": " & add_msg_delimiter(to_string(shared_vvc_cmd.msg)) & "."
436
          & format_command_idx(shared_cmd_idx), C_SCOPE);
437
    else
438
      log(ID_UVVM_SEND_CMD, to_string(shared_vvc_cmd.proc_call)
439
          & format_command_idx(shared_cmd_idx), C_SCOPE);
440
    end if;
441
    release_semaphore(protected_broadcast_semaphore);
442
443
  end procedure;
444
445
  procedure await_cmd_from_sequencer(
446
    constant vvc_labels        : in t_vvc_labels;
447
    constant vvc_config        : in t_vvc_config;
448
    signal VVCT                : in t_vvc_target_record;
449
    signal VVC_BROADCAST       : inout std_logic;
450
    signal global_vvc_busy : inout std_logic;
451
    signal vvc_ack             : out std_logic;
452
    constant shared_vvc_cmd    : in t_vvc_cmd_record;
453
    variable output_vvc_cmd    : out t_vvc_cmd_record
454
    ) is
455
    variable v_was_broadcast : boolean := false;
456
  begin
457
    vvc_ack <= 'Z';  -- Do not contribute to the acknowledge unless selected
458
    -- Wait for a new command
459
    log(ID_CMD_INTERPRETER_WAIT, "Interpreter: Waiting for command", to_string(vvc_labels.scope) , vvc_config.msg_id_panel);
460
461
    loop
462
      VVC_BROADCAST <= 'Z';
463
      global_vvc_busy <= 'L';
464
      wait until (VVCT.trigger = '1' or VVC_BROADCAST = '1');
465
      if VVC_BROADCAST'event and VVC_BROADCAST = '1' then
466
        v_was_broadcast := true;
467
        VVC_BROADCAST <= '1';
468
        populate_shared_vvc_cmd_with_broadcast(output_vvc_cmd);
469
      else
470
        -- set VVC_BROADCAST to 0 to force a broadcast to wait for that VVC
471
        VVC_BROADCAST <= '0';
472
        global_vvc_busy   <= '1';
473
        -- copy shared_vvc_cmd to release the semaphore
474
        output_vvc_cmd := shared_vvc_cmd;
475
      end if;
476
477
      -- Check that the channel is valid
478
      if (not v_was_broadcast) then
479
        if (VVCT.vvc_instance_idx = vvc_labels.instance_idx and
480
            VVCT.vvc_name(1 to valid_length(vvc_labels.vvc_name)) = vvc_labels.vvc_name(1 to valid_length(vvc_labels.vvc_name))) then
481
          if ((VVCT.vvc_channel = NA and vvc_labels.channel /= NA) or
482
             (vvc_labels.channel = NA and (VVCT.vvc_channel /= NA and VVCT.vvc_channel /= ALL_CHANNELS))) then
483
              tb_warning(to_string(output_vvc_cmd.proc_call) & " Channel "& to_string(VVCT.vvc_channel) & " not supported on this VVC " & format_command_idx(output_vvc_cmd), to_string(vvc_labels.scope));
484
              -- only release semaphore and stay in loop forcing a timeout too
485
              release_semaphore(protected_semaphore);
486
          end if;
487
        end if;
488
      end if;
489
490
      exit when (v_was_broadcast or                                                                                                     -- Broadcast, or
491
                (((VVCT.vvc_instance_idx = vvc_labels.instance_idx) or (VVCT.vvc_instance_idx = ALL_INSTANCES)) and              -- Index is correct or broadcast index
492
                ((VVCT.vvc_channel = ALL_CHANNELS) or (VVCT.vvc_channel = vvc_labels.channel)) and                                      -- Channel is correct or broadcast channel
493
                VVCT.vvc_name(1 to valid_length(vvc_labels.vvc_name)) = vvc_labels.vvc_name(1 to valid_length(vvc_labels.vvc_name))));  -- Name is correct
494
    end loop;
495
    if ((VVCT.vvc_instance_idx = ALL_INSTANCES) or (VVCT.vvc_channel = ALL_CHANNELS) ) then
496
      -- in case of a multicast block the global acknowledge until all vvc receiving the message processed it
497
      vvc_ack <= '0';
498
    end if;
499
500
    wait for 0 ns;
501
    if not v_was_broadcast then
502
      -- release the semaphore if it was not a broadcast
503
      release_semaphore(protected_semaphore);
504
    end if;
505
506
    log(ID_CMD_INTERPRETER, to_string(output_vvc_cmd.proc_call) & ". Command received " & format_command_idx(output_vvc_cmd), vvc_labels.scope, vvc_config.msg_id_panel);    -- Get and ack the new command
507
  end procedure;
508
509
510
  procedure put_command_on_queue(
511
    constant command              : in t_vvc_cmd_record;
512
    variable command_queue        : inout work.td_cmd_queue_pkg.t_generic_queue;
513
    variable vvc_status           : inout t_vvc_status;
514
    signal   queue_is_increasing  : out   boolean
515
    ) is
516
  begin
517
    command_queue.put(command); 
518
    vvc_status.pending_cmd_cnt := command_queue.get_count(VOID);
519
    queue_is_increasing <= true;
520
    wait for 0 ns;
521
    queue_is_increasing <= false;
522
  end procedure;
523
524
525
  procedure interpreter_await_completion(
526
    constant command              : in t_vvc_cmd_record;
527
    variable command_queue        : inout work.td_cmd_queue_pkg.t_generic_queue;
528
    constant vvc_config           : in t_vvc_config;
529
    signal executor_is_busy       : in boolean;
530
    constant vvc_labels           : in t_vvc_labels;
531
    signal last_cmd_idx_executed  : in natural;
532
    constant await_completion_pending_msg_id      : in t_msg_id := ID_IMMEDIATE_CMD_WAIT;
533
    constant await_completion_finished_msg_id     : in t_msg_id := ID_IMMEDIATE_CMD
534
    ) is
535
536
    alias wanted_idx              : integer is command.gen_integer_array(0); -- generic integer used as wanted command idx to wait for
537
538
  begin
539
    if wanted_idx = -1 then
540
      -- await completion of all commands
541
      if not command_queue.is_empty(VOID) or executor_is_busy then
542
        log(await_completion_pending_msg_id, "await_completion() - Pending completion " & to_string(command.msg) & " " & format_command_idx(command), to_string(vvc_labels.scope), vvc_config.msg_id_panel);    -- Get and ack the new command
543
        loop
544
          if command.timeout = 0 ns then
545
            wait until executor_is_busy = false;
546
          else
547
            wait until (executor_is_busy = false) for command.timeout;
548
          end if;
549
          if command_queue.is_empty(VOID) or not executor_is_busy'event then
550
            exit;
551
          end if;
552
        end loop;
553
      end if;
554
      log(await_completion_finished_msg_id, "await_completion()  => Finished. " & to_string(command.msg) & " " & format_command_idx(command), to_string(vvc_labels.scope), vvc_config.msg_id_panel);    -- Get and ack the new command
555
556
    else -- await specific instruction
557
      if last_cmd_idx_executed < wanted_idx then
558
        log(await_completion_pending_msg_id, "await_completion(" & to_string(wanted_idx) & ") - Pending selected " & to_string(command.msg) & " "  & format_command_idx(command), to_string(vvc_labels.scope), vvc_config.msg_id_panel);    -- Get and ack the new command
559
        loop
560
          if command.timeout = 0 ns then
561
            wait until executor_is_busy = false;
562
          else
563
            wait until executor_is_busy = false for command.timeout;
564
          end if;
565
          if last_cmd_idx_executed >= wanted_idx  or not executor_is_busy'event then
566
            exit;
567
          end if;
568
        end loop;
569
      end if;
570
      log(await_completion_finished_msg_id, "await_completion(" & to_string(wanted_idx) & ") => Finished. " & to_string(command.msg) & " " &  format_command_idx(command), to_string(vvc_labels.scope), vvc_config.msg_id_panel); -- Get & ack the new command
571
    end if;
572
  end procedure;
573
574
  ------------------------------------------------------------------------------------------
575
  -- Wait for any of the following :
576
  --   await_completion of this VVC, or
577
  --   until global_awaiting_completion /= '1' (any of the other involved VVCs completed).
578
  ------------------------------------------------------------------------------------------
579
  procedure interpreter_await_any_completion(
580
    constant command                              : in t_vvc_cmd_record;
581
    variable command_queue                        : inout work.td_cmd_queue_pkg.t_generic_queue;
582
    constant vvc_config                           : in t_vvc_config;
583
    signal executor_is_busy                       : in boolean;
584
    constant vvc_labels                           : in t_vvc_labels;
585
    signal last_cmd_idx_executed                  : in natural;
586
    signal global_awaiting_completion             : inout std_logic_vector; -- Handshake from other VVCs performing await_any_completion
587
    constant await_completion_pending_msg_id      : in t_msg_id := ID_IMMEDIATE_CMD_WAIT;
588
    constant await_completion_finished_msg_id     : in t_msg_id := ID_IMMEDIATE_CMD
589
    ) is
590
591
    alias wanted_idx              : integer is command.gen_integer_array(0); -- generic integer used as wanted command idx to wait for
592
    alias awaiting_completion_idx : integer is command.gen_integer_array(1); -- generic integer used as awaiting_completion_idx
593
594
    variable v_done               : boolean := false; -- Whether we're done waiting
595
596
    -----------------------------------------------
597
    -- Local function
598
    -- Return whether if this VVC has completed
599
    -----------------------------------------------
600
    impure function this_vvc_completed (
601
      dummy : t_void
602
    ) return boolean is
603
    begin
604
      if wanted_idx = -1 then
605
        -- All commands must be completed (i.e. not just a selected command index)
606
        return (executor_is_busy = false and command_queue.is_empty(VOID));
607
      else
608
        -- await a SPECIFIC command in this VVC
609
        return (last_cmd_idx_executed >= wanted_idx);
610
      end if;
611
    end;
612
613
  begin
614
615
    --
616
    -- Signal that this VVC is participating in the await_any_completion group by driving global_awaiting_completion
617
    --
618
    if not command.gen_boolean then -- NOT_LAST
619
      -- If this is a NOT_LAST call: Wait for delta cycles until the LAST call sets it to '1',
620
      -- then set it to '1' here.
621
      -- Purpose of waiting : synchronize the LAST VVC with all the NOT_LAST VVCs, so that it doesn't have to wait a magic number of delta cycles
622
      while global_awaiting_completion(awaiting_completion_idx) = 'Z' loop
623
        wait for 0 ns;
624
      end loop;
625
      global_awaiting_completion(awaiting_completion_idx) <= '1';
626
    else
627
      -- If this is a LAST call: Set to '1' for at least one delta cycle, so that all NOT_LAST calls detects it.
628
      global_awaiting_completion(awaiting_completion_idx) <= '1';
629
      wait for 0 ns;
630
    end if;
631
632
    -- This VVC already completed?
633
    if this_vvc_completed(VOID) then
634
      v_done := true;
635
    end if;
636
637
    -- Any of the other involved VVCs already completed?
638
    if (global_awaiting_completion(awaiting_completion_idx) = 'X' or global_awaiting_completion(awaiting_completion_idx) = '0') then
639
      v_done := true;
640
    end if;
641
642
    if not v_done then
643
      -- Start waiting for the first of this VVC or other VVC
644
      log(await_completion_pending_msg_id, to_string(command.proc_call) & " - Pending completion " & to_string(command.msg) & " " & format_command_idx(command), to_string(vvc_labels.scope), vvc_config.msg_id_panel);
645
646
      loop
647
        wait until ((executor_is_busy = false) or (global_awaiting_completion(awaiting_completion_idx) /= '1')) for command.timeout;
648
649
        if this_vvc_completed(VOID) or                   -- This VVC is done
650
           global_awaiting_completion(awaiting_completion_idx) = '0' or   -- All other involved VVCs are done
651
           global_awaiting_completion(awaiting_completion_idx) = 'X' then -- Some other involved VVCs are done
652
          exit;
653
        end if;
654
655
656
        if not ((executor_is_busy'event) or (global_awaiting_completion(awaiting_completion_idx) /= '1')) then  -- Indicates timeout
657
          -- When NOT_LAST (command.gen_boolean = false): Timeout must be reported here instead of in send_command_to_vvc()
658
          -- becuase the command is always acknowledged immediately by the VVC to allow the sequencer to continue
659
          if not command.gen_boolean then
660
            tb_error("Timeout during " & to_string(command.proc_call) & "=> " & format_msg(command), to_string(vvc_labels.scope));
661
          end if;
662
663
          exit;
664
        end if;
665
666
      end loop;
667
    end if;
668
669
      global_awaiting_completion(awaiting_completion_idx) <= '0'; -- Signal that we're done waiting
670
671
      -- Handshake : Wait until every involved VVC notice the value is 'X' or '0', and all agree to being done ('0')
672
      if global_awaiting_completion(awaiting_completion_idx) /= '0' then
673
        wait until (global_awaiting_completion(awaiting_completion_idx) = '0');
674
      end if;
675
676
      global_awaiting_completion(awaiting_completion_idx) <= 'Z'; -- Idle
677
678
      log(await_completion_finished_msg_id, to_string(command.proc_call) & "=> Finished. " & format_msg(command), to_string(vvc_labels.scope), vvc_config.msg_id_panel); -- Get & ack the new command
679
680
  end procedure;
681
682
  procedure interpreter_flush_command_queue(
683
    constant command            : in t_vvc_cmd_record;
684
    variable command_queue      : inout work.td_cmd_queue_pkg.t_generic_queue;
685
    constant vvc_config         : in t_vvc_config;
686
    variable vvc_status         : inout t_vvc_status;
687
    constant vvc_labels         : in t_vvc_labels
688
    ) is
689
  begin
690
    log(ID_IMMEDIATE_CMD, "Flushing command queue (" & to_string(shared_vvc_cmd.gen_integer_array(0)) & ") " & format_command_idx(shared_vvc_cmd), to_string(vvc_labels.scope), vvc_config.msg_id_panel);
691
    command_queue.flush(VOID);
692
    vvc_status.pending_cmd_cnt := command_queue.get_count(VOID);
693
  end;
694
695
696
  procedure interpreter_terminate_current_command(
697
    constant command              : in t_vvc_cmd_record;
698
    constant vvc_config           : in t_vvc_config;
699
    constant vvc_labels           : in t_vvc_labels;
700
    signal terminate_current_cmd  : inout t_flag_record
701
    ) is
702
  begin
703
    log(ID_IMMEDIATE_CMD, "Terminating command in executor", to_string(vvc_labels.scope), vvc_config.msg_id_panel);
704
    set_flag(terminate_current_cmd);
705
  end procedure;
706
707
  procedure interpreter_fetch_result(
708
    variable result_queue           : inout work.td_result_queue_pkg.t_generic_queue;
709
    constant command                : in t_vvc_cmd_record;
710
    constant vvc_config             : in t_vvc_config;
711
    constant vvc_labels             : in t_vvc_labels;
712
    constant last_cmd_idx_executed  : in natural;
713
    variable shared_vvc_response    : inout work.vvc_cmd_pkg.t_vvc_response
714
    ) is
715
    variable v_current_element    : work.vvc_cmd_pkg.t_vvc_result_queue_element;
716
    variable v_local_result_queue : work.td_result_queue_pkg.t_generic_queue;
717
  begin
718
    v_local_result_queue.set_scope(to_string(vvc_labels.scope));
719
720
    shared_vvc_response.fetch_is_accepted := false;  -- default
721
    if last_cmd_idx_executed < command.gen_integer_array(0) then
722
      tb_warning(to_string(command.proc_call) & ". Requested result is not yet available. " & format_command_idx(command), to_string(vvc_labels.scope));
723
    else
724
      -- Search for the command idx among the elements of the queue.
725
      -- Easiest method of doing this is to pop elements, and pushing them again
726
      -- if the cmd idx does not match. Not very efficient, but an OK initial implementation.
727
728
      -- Pop the element. Compare cmd idx. If it does not match, push to local result queue.
729
      -- If an index matches, set shared_vvc_response.result. (Don't push element back to result queue)
730
      while result_queue.get_count(VOID) > 0 loop
731
        v_current_element := result_queue.get(VOID); 
732
        if v_current_element.cmd_idx = command.gen_integer_array(0) then
733
          shared_vvc_response.fetch_is_accepted := true;
734
          shared_vvc_response.result              := v_current_element.result;
735
          log(ID_IMMEDIATE_CMD, to_string(command.proc_call) & " Requested result is found" & ". " & to_string(command.msg) & " " & format_command_idx(command), to_string(vvc_labels.scope), vvc_config.msg_id_panel); -- Get and ack the new command
736
          exit;
737
        else
738
          -- No match for element: put in local result queue
739
          v_local_result_queue.put(v_current_element); 
740
        end if;
741
      end loop;
742
743
      -- Pop each element of local result queue and push to result queue.
744
      -- This is to clear the local result queue and restore the result
745
      -- queue to its original state (except that the matched element is not put back).
746
      while v_local_result_queue.get_count(VOID) > 0 loop
747
        result_queue.put(v_local_result_queue.get(VOID));
748
      end loop;
749
750
      if  not shared_vvc_response.fetch_is_accepted then
751
        tb_warning(to_string(command.proc_call) & ". Requested result was not found. Given command index is not available in this VVC. " & format_command_idx(command), to_string(vvc_labels.scope));
752
      end if;
753
    end if;
754
  end procedure;
755
756
757
  procedure initialize_executor (
758
    signal terminate_current_cmd  : inout t_flag_record
759
    ) is
760
  begin
761
    reset_flag(terminate_current_cmd);
762
    wait for 0 ns;  -- delay by 1 delta cycle to allow constructor to finish first
763
   end procedure;
764
765
766
  procedure fetch_command_and_prepare_executor(
767
    variable command              : inout t_vvc_cmd_record;
768
    variable command_queue        : inout work.td_cmd_queue_pkg.t_generic_queue;
769
    constant vvc_config           : in t_vvc_config;
770
    variable vvc_status           : inout t_vvc_status;
771
    signal   queue_is_increasing  : in boolean;
772
    signal executor_is_busy       : inout boolean;
773
    constant vvc_labels           : in t_vvc_labels
774
    ) is
775
  begin
776
    executor_is_busy  <= false;
777
    wait for 0 ns;  -- to allow delta updates in other processes.
778
    if command_queue.is_empty(VOID) then
779
      log(ID_CMD_EXECUTOR_WAIT, "Executor: Waiting for command", to_string(vvc_labels.scope), vvc_config.msg_id_panel);
780
      wait until queue_is_increasing;
781
    end if;
782
783
    -- Queue is now not empty
784
    executor_is_busy  <= true;
785
    wait until executor_is_busy;
786
    vvc_status.previous_cmd_idx := command.cmd_idx;
787
    command := command_queue.get(VOID); 
788
    log(ID_CMD_EXECUTOR, to_string(command.proc_call) & " - Will be executed " & format_command_idx(command), to_string(vvc_labels.scope), vvc_config.msg_id_panel);    -- Get and ack the new command
789
    vvc_status.pending_cmd_cnt := command_queue.get_count(VOID);
790
    vvc_status.current_cmd_idx := command.cmd_idx;
791
  end procedure;
792
793
  -- The result_queue is used so that whatever type defined in the VVC can be stored,
794
  -- and later fetched with fetch_result()
795
  procedure store_result(
796
    variable result_queue  : inout work.td_result_queue_pkg.t_generic_queue;
797
    constant cmd_idx       : in natural;
798
    constant result          : in t_vvc_result
799
    ) is
800
    variable v_result_queue_element : t_vvc_result_queue_element;
801
  begin
802
    v_result_queue_element.cmd_idx := cmd_idx;
803
    v_result_queue_element.result := result;
804
    result_queue.put(v_result_queue_element); 
805
  end procedure;
806
807
  procedure insert_inter_bfm_delay_if_requested(
808
    constant vvc_config                           : in t_vvc_config;
809
    constant command_is_bfm_access                : in boolean;
810
    constant timestamp_start_of_last_bfm_access   : in time;
811
    constant timestamp_end_of_last_bfm_access     : in time;
812
    constant scope                                : in string := C_SCOPE
813
  ) is
814
  begin
815
    -- If both timestamps are at 0 ns we interpret this as the first BFM access, hence no delay shall be applied.
816
    if ((vvc_config.inter_bfm_delay.delay_type /= NO_DELAY) and
817
         command_is_bfm_access and
818
         not ((timestamp_start_of_last_bfm_access = 0 ns) and (timestamp_end_of_last_bfm_access = 0 ns))) then
819
      case vvc_config.inter_bfm_delay.delay_type is
820
        when TIME_FINISH2START =>
821
          if now < (timestamp_end_of_last_bfm_access + vvc_config.inter_bfm_delay.delay_in_time) then
822
            log(ID_INSERTED_DELAY, "Delaying BFM access until time " & to_string(timestamp_end_of_last_bfm_access + vvc_config.inter_bfm_delay.delay_in_time)
823
                & ".", scope, vvc_config.msg_id_panel);
824
            wait for (timestamp_end_of_last_bfm_access + vvc_config.inter_bfm_delay.delay_in_time - now);
825
          end if;
826
        when TIME_START2START =>
827
          if now < (timestamp_start_of_last_bfm_access + vvc_config.inter_bfm_delay.delay_in_time) then
828
            log(ID_INSERTED_DELAY, "Delaying BFM access until time " & to_string(timestamp_start_of_last_bfm_access + vvc_config.inter_bfm_delay.delay_in_time)
829
                & ".", scope, vvc_config.msg_id_panel);
830
            wait for (timestamp_start_of_last_bfm_access + vvc_config.inter_bfm_delay.delay_in_time - now);
831
          end if;
832
        when others =>
833
          tb_error("Delay type " & to_upper(to_string(vvc_config.inter_bfm_delay.delay_type)) & " not supported for this VVC.", C_SCOPE);
834
      end case;
835
      log(ID_INSERTED_DELAY, "Finished delaying BFM access", scope, vvc_config.msg_id_panel);
836
    end if;
837
  end procedure;
838
839
end package body td_vvc_entity_support_pkg;
840
841
842
34
 
1
library IEEE;
2
use IEEE.Std_logic_1164.all;
3
use IEEE.Numeric_std.all;
4
5
entity Counter is
6
  port (Clock, Reset, Enable, Load, UpDn: in Std_logic;
7
        Data: in Std_logic_vector(7 downto 0);
8
        Q:   out Std_logic_vector(7 downto 0));
9
end;
10
11
architecture RTL of Counter is
12
  signal Cnt: Unsigned(7 downto 0);
13
begin
14
  process (Clock, Reset)
15
  begin
16
    if Reset = '1' then
17
      Cnt <= (others => '0');
18
    elsif Rising_edge(Clock) then
19
      if Enable = '1' then
20
        if Load = '1' then
21
          Cnt <= Unsigned(Data);
22
        elsif UpDn = '1' then
23
          Cnt <= Cnt + 1;
24
        else
25
          Cnt <= Cnt - 1;
26
        end if; 
27
      end if;
28
    end if;
29
  end process;
30
  
31
  Q <= Std_logic_vector(Cnt);
32
33
end;
34
3146 views and 2 likes     
 
This example shows a simple UVVM testbench being used to test a simple counter design.

This example shows a simple UVVM testbench being used to test a simple counter design.

1150:0