Вы находитесь на странице: 1из 8


How to drive the reset pin of the interface from the sequence rather than from the top testbench module, as in case of traditional testbenches? Does it provide any advantage over the traditional way of driving reset? Driving the reset pin forms the fundamental part of any testbench. It basically marks the start of the traffic phase in the given setup. The following code snippets illustrates about driving the reset pin.
// Defines an interface that provides access to a reset signal. // This interface can be used to write sequences to drive the reset logic. interface reset_if(); logic reset; logic clk; modport reset_modport (input clk, output reset); endinterface //Top-level testbench //TB Interface instance to provide access to the reset signal //Set the reset interface on the sequencer module test_top; . . . bit SystemClock; . . . reset_if reset_if(); assign reset_if.clk = SystemClock; initial uvm_config_db#(virtual reset_if.reset_modport)::set(uvm_root::get(), "uvm_test_top.env.sequencer", "reset_mp", reset_if.reset_modport); endmodule //Example UVM sequence for driving reset pin on the interface class simple_reset_sequence extends uvm_sequence; `uvm_object_utils(simple_reset_sequence) . . . virtual task body(); p_sequencer.reset_mp.reset <= 1'b1; repeat(10) @(posedge p_sequencer.reset_mp.clk); p_sequencer.reset_mp.reset <= 1'b0; repeat(10) @(posedge p_sequencer.reset_mp.clk); p_sequencer.reset_mp.reset <= 1'b1; endtask: body endclass

The advantages of driving the reset pin from the sequence over the testbench: 1. You can provide configurable delay assertion or deassertion of reset pin

depending on the requirement. 2. If on-the-fly reset needs to be tested, the testbench code remains unchanged. 3. You can use the phase jumps to reset and create various interesting scenarios.

How to implement the watchdog for verification environment?

In verification environments, you often need to wait for the signal to have specific value and then continue with the execution of procedural code. initial begin ... wait (my_ifc.sig == 1); $display(...); ... end However, you should always check if the signal eventually gets its expected value. Different methodologies (vmm/ovm/uvm) suggest different ways to implement this checker, but in this article, the focus is on methodology independent solution. Generally, you need to start with the following two threads: 1. To wait until the signal gets its desired value 2. To terminate the waiting process after a specified delay If the first thread is completed and the signal receives its desired value, you can terminate the second thread and stop waiting for timeout. Alternatively, if timeout is reached, you need to stop waiting for the signal to get its desired value. This algorithm can be easily implemented with fork-join_any block initial begin ... // watchdog implementation starts here fork : my_watchdog begin // start thread of waiting for (my_ifc.sig == 1); wait (my_ifc.sig == 1); end begin // start thread of timeout wait #(watchdog_delay); $display("error:..."); end join_any disable my_watchdog; // report accordingly

if (status == good) // report good status ... else // report bad status ... // watchdog implementation ends here ... end In the above example, fork-join_any block and disable my_watchdog statements are used. Both of them are required, fork-join_any starts separate threads for each begin-end block inside of it. fork-join_any unblocks the execution of its scope after any (first) thread within it is finished. However, other threads continue their execution. Thus, you need to disable the running thread inside the fork-join_any after any other internal thread is finished. There is also another method to kill threads started within fork-join_xxx. fork begin ... end begin ... end join_any disable fork; However, disable fork; statement kills all the fork blocks (and their internal threads) in its scope. If you decide to use disable fork, you need to make sure to enclose your fork-join_any block within its own scope formed by begin-end. `define watchdog(SIG, VALUE, DELAY) \ begin \ fork \ begin \ wait (SIG === VALUE); \ end \ begin \ #(DELAY); \ $display(`"error: watchdog timer expired waiting for SIG to get VALUE`"); \ end \ join_any \ disable fork; \ end

What information should I know about driver-sequencer connection in UVM?

In UVM, it is mandatory to connect the "seq_item_port" provided inside the uvm_driver to a sequencer's export("seq_item_export"). If you try to access "get_next_item" of the port without the mandatory connection, you get a null access error from the uvm base code as the port is trying to access the export which is not connected. To avoid such errors and debug effort, add the below check in the driver (extended from uvm_driver) after the connect_phase. if (seq_item_port.size()==0) `uvm_fatal("run_phase","seq_item_port unconnected") else seq_item_port.get_next_item(req); .... Here, the check is present in the run_phase. There are "Connection Error" checks in the UVM port base class for the "seq_item_port" to check for minimum and maximum number of exports to which it is connected. Since seq_item_port is configured to connect to minimum zero and maximum one export, it does not flag any error when the port is not connected to any export.

How to implement dynamic/soft reset in UVM?

Sometimes dynamic/soft reset is requred in normal simulation flow. UVM offers a mechanisam to make the run-time phasing jump to a specified phase, that is the jump() task of uvm_phase. For the the jump() task of uvm_phase, if the destination phase of jump is within the current phase schedule, a simple local jump takes place. If the jump-to phase is outside of the current schedule then the jump affects other schedules which share the phase. You just need to call phase.jump(uvm_reset_phase::get()) in main_phase to implement dynamic reset. Once phase.jump(uvm_reset_phase::get()) is called in main_phase in any object, and then the phase jump to reset_phase.

Note that all the components objects of the same uvm_domain will also jump to the reset_phase after main_phase finishes, but the compnents that do not belong to this domain will not jump to the reset_phase and keep their normal flow. The following example demonstrates how you can achieve the dynamic resets in UVM. In this example top_obj, a_obj and b_obj are in the same uvm_domain. ///////////// //Source code ///////////// module test; import uvm_pkg::*; class A extends uvm_component; function new (string name, uvm_component parent); super.new(name,parent); endfunction endclass class B extends uvm_component; function new (string name, uvm_component parent); super.new(name,parent); endfunction endclass class top extends uvm_env; bit first_reset = 1'b1; A a_obj; B b_obj; function new (string name, uvm_component parent); super.new(name,parent); endfunction function void build_phase(uvm_phase phase); $display("%0t: %0s: build", $time, get_full_name()); a_obj = new("a_obj", this); b_obj = new("b_obj", this); endfunction function void end_of_elaboration_phase(uvm_phase phase); $display("%0t: %0s: end_of_elaboration", $time, get_full_name()); endfunction function void start_of_simulation_phase(uvm_phase phase); $display("%0t: %0s: start_of_simulation", $time, get_full_name()); endfunction task reset_phase(uvm_phase phase);

phase.raise_objection(this); $display("%0t: %0s: reset phase", $time, get_full_name()); phase.drop_objection(this); endtask task configure_phase(uvm_phase phase); phase.raise_objection(this); $display("%0t: %0s: configure phase", $time, get_full_name()); phase.drop_objection(this); endtask task main_phase(uvm_phase phase); phase.raise_objection(this); $display("%0t: %0s: start main phase", $time, get_full_name()); if (first_reset) begin first_reset = 0; phase.jump(uvm_reset_phase::get()); //<-- calls jump() task end $display("%0t: %0s: end main phase", $time, get_full_name()); phase.drop_objection(this); endtask task shutdown_phase(uvm_phase phase); $display("%0t: %0s: shutdown phase", $time, get_full_name()); endtask function void extract_phase(uvm_phase phase); $display("%0t: %0s: extract", $time, get_full_name()); endfunction function void check_phase(uvm_phase phase); $display("%0t: %0s: check", $time, get_full_name()); endfunction function void report_phase(uvm_phase phase); $display("%0t: %0s: report", $time, get_full_name()); endfunction function void final_phase(uvm_phase phase); $display("%0t: %0s: final", $time, get_full_name()); endfunction endclass top top_obj = new("top", null); initial begin run_test(); end endmodule

///////////// //Simulation result ///////////// UVM_INFO @ 0: reporter [RNTST] Running test ... 0: top: build 0: top: end_of_elaboration 0: top: start_of_simulation 0: top: reset phase 0: top: configure phase 0: top: start main phase UVM_INFO /global/apps2/vcs_2011.03/etc/uvm/base/uvm_phases.svh(2189) @ 0: reporter [PH_JUMP] phase main (schedule uvm_sched, domain uvm) is jumping to phase reset UVM_WARNING @ 0: main [OBJTN_CLEAR] Object 'uvm_top' cleared objection counts for main 0: top: end main phase 0: top: reset phase //<-- all the components phase jump to reset phase and run again 0: top: configure phase 0: top: start main phase 0: top: end main phase 0: top: shutdown phase 0: top: extract 0: top: check 0: top: report 0: top: final