An use case of UVM verification environment universal template 2.0
UVM verification environment universal template 2.0 used in building verification of a DUT that features a handshake-controlled input and output data transfers
Introduction
In this blog post I would like to show a use case of UVM verification environment universal template 2.0 ( as described in one of the previous blog posts: https://asicstoic.blogspot.com/2023/07/uvm-verification-environment-universal.html) on a DUT verified using non UVM verification environment described also in one of the previous blog posts: https://asicstoic.blogspot.com/2023/07/uvm-like-non-uvm-verification.html.
The goal of this blog post is to show how seamlessly the non UVM verification environment , especially its driver, monitor and scoreboard part can be transferred to the UVM verification environment using the UVM verification environment universal template 2.0.
UVM verification environment architecture
For UVM verification of any DUT what we need to think about is an architecture so our architecture will contain files organized in a System Verilog UVM hierarchy as described in the previously shown block diagram.
DUT input and output data exchange controls using handshake interfaces is based on Producer/Consumer exchange of data. The same DUT and the handshake mechanism were already described in one of the previous blog posts: https://asicstoic.blogspot.com/2023/06/verilog-rtl-verification-cocotb-based.html
DUT
module SimpleDUT_hs ( input CLK, input RST_N,
input en_din_i, output logic rdy_din_o, input [3:0] din_i,
input en_dout_i, output logic rdy_dout_o, output logic[3:0] dout_o );
// Define the states using localparam localparam START_ST = 1'b0; localparam WR_ST = 1'b1;
logic next_rdy_dout;
logic[3:0] next_dout ;
logic state, next_state ;
always @(posedge CLK or negedge RST_N) begin if (!RST_N) begin state <= START_ST; rdy_dout_o <= 1'b0 ; dout_o <= 4'd0 ; end else begin state <= next_state ; rdy_dout_o <= next_rdy_dout ; dout_o <= next_dout ; end end
always @(*) begin next_state = state ; next_rdy_dout = rdy_dout_o; next_dout = dout_o;
rdy_din_o = 1'b0; case(state) //////////////////////////// START_ST: begin if(en_din_i) begin next_dout = din_i; rdy_din_o = 1'b1;
next_rdy_dout = 1'b1;
next_state = WR_ST; end end
/////////////////////////// WR_ST: begin case({en_din_i, en_dout_i}) 2'b00, 2'b10: next_state = state ; 2'b01: begin next_rdy_dout = 1'b0; next_state = START_ST; end 2'b11: begin next_dout = din_i; rdy_din_o = 1'b1; end endcase
end endcase end
endmodule |
Interface
The name of the file is: interface.sv .
interface intf(input logic CLK); //declaring the signals logic RST_N; logic en_din_i; logic rdy_din_o; logic [3:0] din_i;
logic en_dout_i; logic rdy_dout_o; logic [3:0] dout_o ;
// misc. used in driver and present here for // waveform observation purposes integer counter, state;
endinterface |
Sequence item ( or transaction ) class template 2.0
The first step in verifying a RTL design is defining what kind of data should be sent to the DUT.
The concept of transaction means the smallest data transfers that can be executed in a verification model.
//Object class class SimpleDUT_sequence_item extends uvm_sequence_item; `uvm_object_utils(SimpleDUT_sequence_item)
//-------------------------------------------------------- //Instantiation //-------------------------------------------------------- // e.g. // rand logic reset; // rand logic [3:0] din_i; // logic [3:0] dout_o;
//-------------------------------------------------------- //Default Constraints //-------------------------------------------------------- // e.g. // constraint input1_c { din_i inside {[0:15]};}
//-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_sequence_item"); super.new(name);
endfunction: new
endclass: SimpleDUT_sequence_item
|
Sequence item ( or transaction ) class: a final solution
class SimpleDUT_hs_sequence_item extends uvm_sequence_item; `uvm_object_utils(SimpleDUT_hs_sequence_item)
//1.change in relation to the correspondent 2.0 template: 1 Instantiation logic/wires/registers same as used in interface go here again and it is again straight copy from interface // Again we are going to use this to generate random values about DUT input port values on one hand and to store the output results on the other hand. This time we are going to store the values of all handshake signals too.
//-------------------------------------------------------- //Instantiation //-------------------------------------------------------- rand logic [3:0] din_i; logic [3:0] dout_o; rand logic RST_N;
logic en_din_i; logic rdy_din_o;
logic en_dout_i; logic rdy_dout_o;
//-------------------------------------------------------- //Default Constraints //-------------------------------------------------------- // 2.change in relation to the correspondent 2.0 template: // adding DUT function appropriate constraints of randomized input port values constraint input1_c { din_i inside {[0:15]};} constraint input2 { RST_N inside {[0:1]};} //-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_hs_sequence_item"); super.new(name);
endfunction: new // 3.change in relation to the correspondent 2.0 template: adding a display method to transaction for capturing in log file generated/used transaction signals // Adding display() method to display Transaction properties. function void display(string name); $display("-------------------------"); $display("- %s ",name); $display("-------------------------"); $display("- en_din_i = %0d, rdy_din_o = %0d, din_i = %0d", en_din_i, rdy_din_o, din_i ); $display("- en_dout_i = %0d, rdy_dout_o = %0d, dout_o = %0d", en_dout_i,rdy_dout_o, dout_o); $display("-------------------------"); endfunction
endclass: SimpleDUT_hs_sequence_item |
Sequence class template 2.0 same as a final solution ( an OBJECT class, file: sequence.sv )
Now that we have a transaction, the next step is to create a sequence.
After a basic transaction has been specified, the verification environment will need to generate a collection of them and get them ready for sending to the driver. This is a job for the sequence. Sequence is an ordered collection of transactions. Also the sequence shapes transactions to our needs and generates as many transactions as needed.
class SimpleDUT_hs_base_sequence extends uvm_sequence; `uvm_object_utils(SimpleDUT_hs_base_sequence) SimpleDUT_hs_sequence_item reset_pkt;
//-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name= "SimpleDUT_hs_base_sequence"); super.new(name); `uvm_info("BASE_SEQ", "Inside Constructor!", UVM_HIGH) endfunction
//-------------------------------------------------------- //Body Task //-------------------------------------------------------- task body(); `uvm_info("BASE_SEQ", "Inside body task!", UVM_HIGH) reset_pkt = SimpleDUT_hs_sequence_item::type_id::create("reset_pkt"); start_item(reset_pkt); reset_pkt.randomize() with {RST_N==0;}; finish_item(reset_pkt); endtask: body endclass: SimpleDUT_hs_base_sequence
class SimpleDUT_hs_test_sequence extends SimpleDUT_hs_base_sequence; `uvm_object_utils(SimpleDUT_hs_test_sequence) SimpleDUT_hs_sequence_item item; //-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name= "SimpleDUT_test_sequence"); super.new(name); `uvm_info("TEST_SEQ", "Inside Constructor!", UVM_HIGH) endfunction //-------------------------------------------------------- //Body Task //-------------------------------------------------------- task body(); `uvm_info("TEST_SEQ", "Inside body task!", UVM_HIGH) item = SimpleDUT_hs_sequence_item::type_id::create("item"); start_item(item); item.randomize() with {RST_N==1;}; finish_item(item); endtask: body endclass: SimpleDUT_hs_test_sequence |
Sequencer class template 2.0 same as a final solution ( once more a Component class and file name is sequencer.sv )
The sequencer is responsible for sending the sequences to the driver.
The easiest way to include the sequencer in UVM testbench/infrastructure template is:
typedef uvm_sequencer#(SimpleDUT_hs_sequence_item) SimpleDUT_hs_sequencer; |
Note*: In the sequencer we don't need the “run” phase
class SimpleDUT_hs_sequencer extends uvm_sequencer#(SimpleDUT_hs_sequence_item); `uvm_component_utils(SimpleDUT_hs_sequencer)
//-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_hs_sequencer", uvm_component parent); super.new(name, parent); `uvm_info("SEQUENCER_CLASS", "Inside Constructor!", UVM_HIGH)
endfunction: new
//-------------------------------------------------------- //Build Phase //-------------------------------------------------------- function void build_phase(uvm_phase phase); super.build_phase(phase); `uvm_info("SEQUENCER_CLASS", "Build Phase!", UVM_HIGH) endfunction: build_phase
//-------------------------------------------------------- //Connect Phase //-------------------------------------------------------- function void connect_phase(uvm_phase phase); super.connect_phase(phase); `uvm_info("SEQUENCER_CLASS", "Connect Phase!", UVM_HIGH)
endfunction: connect_phase
endclass: SimpleDUT_hs_sequencer |
Driver class template 2.0 ( one more Component class: file driver.sv )
The driver is a block whose role is to interact with the DUT. The driver pulls transactions from the sequencer and sends them repetitively to the signal-level interface.
The driver’s functionality should only be limited to send the necessary data to the DUT.
class SimpleDUT_driver extends uvm_driver#(SimpleDUT_sequence_item); `uvm_component_utils(SimpleDUT_driver)
virtual SimpleDUT_interface vif;
SimpleDUT_sequence_item item;
//-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_driver", uvm_component parent); super.new(name, parent); `uvm_info("DRIVER_CLASS", "Inside Constructor!", UVM_HIGH)
endfunction: new
//-------------------------------------------------------- //Build Phase //-------------------------------------------------------- function void build_phase(uvm_phase phase); super.build_phase(phase); `uvm_info("DRIVER_CLASS", "Build Phase!", UVM_HIGH)
if(!(uvm_config_db #(virtual SimpleDUT_interface)::get(this, "*", "vif", vif))) begin `uvm_error("DRIVER_CLASS", "Failed to get VIF from config DB!") end endfunction: build_phase
//-------------------------------------------------------- //Connect Phase //-------------------------------------------------------- function void connect_phase(uvm_phase phase); super.connect_phase(phase); `uvm_info("DRIVER_CLASS", "Connect Phase!", UVM_HIGH)
endfunction: connect_phase
//-------------------------------------------------------- //Run Phase //-------------------------------------------------------- task run_phase (uvm_phase phase); super.run_phase(phase); `uvm_info("DRIVER_CLASS", "Run Phase!", UVM_HIGH)
// Logic drive();
endtask: run_phase
//-------------------------------------------------------- //[Method] Drive //-------------------------------------------------------- task drive(); // e.g. for our "dummy" DUT // forever begin
// Create "item" // item = SimpleDUT_sequence_item::type_id::create("item"); // seq_item_port.get_next_item(item);
// @(posedge vif.clock); // vif.reset <= item.reset; // vif.din_i <= item.din_i;
// seq_item_port.item_done();
// end endtask: drive
endclass: SimpleDUT_driver |
Driver class template final solution ( file driver.sv )
class SimpleDUT_hs_driver extends uvm_driver#(SimpleDUT_hs_sequence_item); `uvm_component_utils(SimpleDUT_hs_driver) virtual SimpleDUT_hs_interface vif;
SimpleDUT_hs_sequence_item item; //-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_hs_driver", uvm_component parent); super.new(name, parent); `uvm_info("DRIVER_CLASS", "Inside Constructor!", UVM_HIGH)
endfunction: new
//-------------------------------------------------------- //Build Phase //-------------------------------------------------------- function void build_phase(uvm_phase phase); super.build_phase(phase); `uvm_info("DRIVER_CLASS", "Build Phase!", UVM_HIGH)
if(!(uvm_config_db #(virtual SimpleDUT_hs_interface)::get(this, "*", "vif", vif))) begin `uvm_error("DRIVER_CLASS", "Failed to get VIF from config DB!") end endfunction: build_phase
//-------------------------------------------------------- //Connect Phase //-------------------------------------------------------- function void connect_phase(uvm_phase phase); super.connect_phase(phase); `uvm_info("DRIVER_CLASS", "Connect Phase!", UVM_HIGH)
endfunction: connect_phase
//-------------------------------------------------------- //Run Phase //-------------------------------------------------------- task run_phase (uvm_phase phase); super.run_phase(phase); `uvm_info("DRIVER_CLASS", "Run Phase!", UVM_HIGH) drive(); endtask: run_phase
//-------------------------------------------------------- //[Method] Drive //-------------------------------------------------------- // 1. Change in relation to the corresponding template virtual task drive(); SimpleDUT_hs_sequence_item item; integer j, i; integer num_iterations = $urandom_range(1, 21); integer num_iterations2 = $urandom_range(1, 21); begin vif.en_din_i = 0; vif.en_dout_i = 0;
vif.counter = 0; vif.state = 0; forever begin // Handshake control inputs reset: if there is still output data not consumed new data is not "produced" because DUT depth of the data pipeline is 1. If there is no output data to be "consumed" both output handshake control data ports are 0 if(vif.rdy_dout_o) vif.en_din_i = 1'b0; else vif.en_dout_i = 1'b0; // if "counter flag is 0: pick up new transaction data, and apply its // reset to DUT interface if(vif.counter==0) begin item = SimpleDUT_hs_sequence_item::type_id::create("item"); seq_item_port.get_next_item(item); vif.RST_N <= item.RST_N; end // new transaction data is available and its reset par is applied to DUT // now wait for one clock cycle .... @(negedge vif.CLK); // if reset is applied to DUT, do nothing just go back // and pick up a new transaction data if(!item.RST_N ) begin vif.en_dout_i = 0; vif.en_din_i = 0; seq_item_port.item_done(); End
if(vif.counter==0 && item.RST_N ) begin // DUT is not under reset so here there is a start of driving the current transaction to DUT interface vif.en_dout_i = 0; vif.en_din_i = 0; // after a random number of clock cycles ( in the range of 1 to 20) // "produce" input data to DUT num_iterations = $urandom_range(1, 21); for (j = 0; j < num_iterations; j++) begin @(negedge vif.CLK); end @(posedge vif.CLK); vif.din_i = item.din_i; vif.en_din_i = 1; vif.state = 1; end case(vif.state) 1: begin vif.counter = vif.counter + 1; if(vif.counter==2) vif.state = 2; end 2: begin if(vif.rdy_dout_o) begin // here DUT output data is ready to be "consumed" // so after a random number of clock cycles ( in the range of 1 to 20) // the DUT output data is indeed "consumed" vif.en_din_i = 1'b0; num_iterations2 = $urandom_range(1, 21); for (i = 0; i < num_iterations2; i++) begin @(negedge vif.CLK); end vif.en_dout_i = 1; vif.counter = vif.counter + 1; if(vif.counter==3) vif.state = 3; end else begin vif.en_dout_i = 0 ; end end 3: begin
if(vif.rdy_dout_o) vif.en_din_i = 1'b0; else begin // After the DUT data is "consumed" pick - up next transaction data // and repeat the whole process again. vif.en_dout_i = 1'b0; vif.counter = 0; vif.state = 0; seq_item_port.item_done(); end end endcase end end endtask: drive
endclass: SimpleDUT_hs_driver |
Monitor class template 2.0
The monitor is a self-contained model that observes the communication of the DUT with the testbench. At most, it should observe the outputs of the design and, in case of not respecting the protocol’s rules, the monitor must return an error. The monitor is a passive component, it doesn’t drive any signals into the DUT.
class SimpleDUT_monitor extends uvm_monitor; `uvm_component_utils(SimpleDUT_monitor)
virtual SimpleDUT_interface vif;
SimpleDUT_sequence_item item;
uvm_analysis_port #(SimpleDUT_sequence_item) monitor_port;
//-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_monitor", uvm_component parent); super.new(name, parent); `uvm_info("MONITOR_CLASS", "Inside Constructor!", UVM_HIGH) endfunction: new
//-------------------------------------------------------- //Build Phase //-------------------------------------------------------- function void build_phase(uvm_phase phase); super.build_phase(phase); `uvm_info("MONITOR_CLASS", "Build Phase!", UVM_HIGH)
if(!(uvm_config_db #(virtual SimpleDUT_interface)::get(this, "*", "vif", vif))) begin `uvm_error("MONITOR_CLASS", "Failed to get VIF from config DB!") end monitor_port = new("monitor_port", this); endfunction: build_phase
//-------------------------------------------------------- //Connect Phase //-------------------------------------------------------- function void connect_phase(uvm_phase phase); super.connect_phase(phase); `uvm_info("MONITOR_CLASS", "Connect Phase!", UVM_HIGH) endfunction: connect_phase
//-------------------------------------------------------- //Run Phase //-------------------------------------------------------- task run_phase (uvm_phase phase); super.run_phase(phase); `uvm_info("MONITOR_CLASS", "Run Phase!", UVM_HIGH) forever begin item = SimpleDUT_sequence_item::type_id::create("item"); // e.g. monitoring logic for our dummy DUT // wait(!vif.reset); //sample inputs // @(posedge vif.clock); // item.din_i = vif.din_i; //sample output // item.dout_o = vif.dout_o; // @(posedge vif.clock);
monitor_port.write(item); end
endtask: run_phase
endclass: SimpleDUT_monitor |
Monitor class final solution (file monitor.sv)
class SimpleDUT_hs_monitor extends uvm_monitor; `uvm_component_utils(SimpleDUT_hs_monitor)
virtual SimpleDUT_hs_interface vif;
SimpleDUT_hs_sequence_item item;
uvm_analysis_port #(SimpleDUT_hs_sequence_item) monitor_port;
// 1. change : creating mailbox handle mailbox mon2scb; //-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_hs_monitor", uvm_component parent); super.new(name, parent); `uvm_info("MONITOR_CLASS", "Inside Constructor!", UVM_HIGH)
// 2. change: getting the mailbox handles from environment this.mon2scb = mon2scb;
endfunction: new
//-------------------------------------------------------- //Build Phase //-------------------------------------------------------- function void build_phase(uvm_phase phase); super.build_phase(phase); `uvm_info("MONITOR_CLASS", "Build Phase!", UVM_HIGH)
if(!(uvm_config_db #(virtual SimpleDUT_hs_interface)::get(this, "*", "vif", vif))) begin `uvm_error("MONITOR_CLASS", "Failed to get VIF from config DB!") end
monitor_port = new("monitor_port", this); endfunction: build_phase
//-------------------------------------------------------- //Connect Phase //-------------------------------------------------------- function void connect_phase(uvm_phase phase); super.connect_phase(phase); `uvm_info("MONITOR_CLASS", "Connect Phase!", UVM_HIGH)
endfunction: connect_phase
//-------------------------------------------------------- //Run Phase //-------------------------------------------------------- task run_phase (uvm_phase phase); super.run_phase(phase); `uvm_info("MONITOR_CLASS", "Run Phase!", UVM_HIGH) // Logic // 3. change to the corresponding template: monitoring will take place only // if DUT is not under reset wait(!vif.RST_N); wait(vif.RST_N);
forever begin item = SimpleDUT_hs_sequence_item::type_id::create("item");
// 4. change to the corresponding template @(negedge vif.CLK); fork begin // pick-up DUT input "produced" data at appropriate handshake control time wait(vif.rdy_dout_o && vif.en_dout_i ); item.dout_o = vif.dout_o; end begin // pick-up DUT output "consumed" data at appropriate handshake control time wait(vif.en_din_i && vif.rdy_din_o ); item.din_i = vif.din_i; end join @( negedge vif.CLK); // DUT input data and a corresponding output data are passed to scoreboard // for verification self-checking monitor_port.write(item); end
endtask: run_phase
endclass: SimpleDUT_hs_monitor |
Scoreboard template
class SimpleDUT_scoreboard extends uvm_test; `uvm_component_utils(SimpleDUT_scoreboard) uvm_analysis_imp #(SimpleDUT_sequence_item, SimpleDUT_scoreboard) scoreboard_port;
SimpleDUT_sequence_item transactions[$];
//-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_scoreboard", uvm_component parent); super.new(name, parent); `uvm_info("SCB_CLASS", "Inside Constructor!", UVM_HIGH)
endfunction: new
//-------------------------------------------------------- //Build Phase //-------------------------------------------------------- function void build_phase(uvm_phase phase); super.build_phase(phase); `uvm_info("SCB_CLASS", "Build Phase!", UVM_HIGH)
scoreboard_port = new("scoreboard_port", this); endfunction: build_phase
//-------------------------------------------------------- //Connect Phase //-------------------------------------------------------- function void connect_phase(uvm_phase phase); super.connect_phase(phase); `uvm_info("SCB_CLASS", "Connect Phase!", UVM_HIGH)
endfunction: connect_phase
//-------------------------------------------------------- //Write Method //-------------------------------------------------------- function void write(SimpleDUT_sequence_item item);
transactions.push_back(item); endfunction: write
//-------------------------------------------------------- //Run Phase //-------------------------------------------------------- task run_phase (uvm_phase phase); super.run_phase(phase); `uvm_info("SCB_CLASS", "Run Phase!", UVM_HIGH)
forever begin /* // get the packet // generate expected value // compare it with actual value // score the transactions accordingly */ SimpleDUT_sequence_item curr_trans;
wait((transactions.size() != 0));
curr_trans = transactions.pop_front();
compare(curr_trans); end
endtask: run_phase
//-------------------------------------------------------- //Compare : Generate Expected Result and Compare with Actual //-------------------------------------------------------- task compare(SimpleDUT_sequence_item curr_trans); logic [3:0] expected; logic [3:0] actual; expected = curr_trans.din_i; actual = curr_trans.dout_o; if(actual != expected) begin `uvm_error("COMPARE", $sformatf("Transaction failed! ACT=%d, EXP=%d", actual, expected))end else begin `uvm_info("COMPARE", $sformatf("Transaction Passed! ACT=%d, EXP=%d", actual, expected), UVM_LOW) end endtask: compare
endclass: SimpleDUT_scoreboard
|
Scoreboard class final solution (file : scoreboard.sv)
class SimpleDUT_hs_scoreboard extends uvm_test; `uvm_component_utils(SimpleDUT_hs_scoreboard)
uvm_analysis_imp #(SimpleDUT_hs_sequence_item, SimpleDUT_hs_scoreboard) scoreboard_port; // 1. change of the corresponding template: adding access to interface to save // handshake control ports to a transaction data virtual SimpleDUT_hs_interface vif;
SimpleDUT_hs_sequence_item transactions[$];
// 2. change of the corresponding template: count the number of transactions int no_transactions;
//-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_hs_scoreboard", uvm_component parent); super.new(name, parent); `uvm_info("SCB_CLASS", "Inside Constructor!", UVM_HIGH)
endfunction: new
//-------------------------------------------------------- //Build Phase //-------------------------------------------------------- function void build_phase(uvm_phase phase); super.build_phase(phase); `uvm_info("SCB_CLASS", "Build Phase!", UVM_HIGH)
scoreboard_port = new("scoreboard_port", this);
// 3. change of a corresponding template: if(!(uvm_config_db #(virtual SimpleDUT_hs_interface)::get(this, "*", "vif", vif))) begin `uvm_error("SCB_CLASS", "Failed to get VIF from config DB!") end
endfunction: build_phase
//-------------------------------------------------------- //Connect Phase //-------------------------------------------------------- function void connect_phase(uvm_phase phase); super.connect_phase(phase); `uvm_info("SCB_CLASS", "Connect Phase!", UVM_HIGH)
endfunction: connect_phase
//-------------------------------------------------------- //Write Method //-------------------------------------------------------- function void write(SimpleDUT_hs_sequence_item item); // 4. change of a corresponding template: adding handshake control signals to a transaction data item.en_din_i = vif.en_din_i; item.rdy_din_o = vif.rdy_din_o; item.en_dout_i = vif.en_dout_i; item.rdy_dout_o = vif.rdy_dout_o;
transactions.push_back(item); item.display("[ Scoreboard ]"); endfunction: write
//-------------------------------------------------------- //Run Phase //-------------------------------------------------------- task run_phase (uvm_phase phase); super.run_phase(phase); `uvm_info("SCB_CLASS", "Run Phase!", UVM_HIGH) forever begin /* // get the packet // generate expected value // compare it with actual value // score the transactions accordingly */ SimpleDUT_hs_sequence_item curr_trans;
wait((transactions.size() != 0)); curr_trans = transactions.pop_front();
compare(curr_trans); end
endtask: run_phase
//-------------------------------------------------------- //Compare : Generate Expected Result and Compare with Actual //-------------------------------------------------------- task compare(SimpleDUT_hs_sequence_item curr_trans); logic [3:0] expected; logic [3:0] actual; expected = curr_trans.din_i; actual = curr_trans.dout_o; if(actual != expected) begin `uvm_error("COMPARE", $sformatf("Transaction failed! ACT=%d, EXP=%d", actual, expected)) end else begin `uvm_info("COMPARE", $sformatf("Transaction Passed! ACT=%d, EXP=%d", actual, expected), UVM_LOW) end no_transactions++; endtask: compare
endclass: SimpleDUT_hs_scoreboard
|
Agent class template 2.0 same as a final solution ( once more a Component class: file agent.sv )
Let's create the agent class template which will be same as the environment and test class templates ( and in the same fashion all other necessary COMPONENT classes )
The agent class template will have three instance class templates: driver sequencer and monitor.
class SimpleDUT_hs_agent extends uvm_agent; `uvm_component_utils(SimpleDUT_hs_agent)
SimpleDUT_hs_driver drv; SimpleDUT_hs_monitor mon; SimpleDUT_hs_sequencer seqr; SimpleDUT_hs_sequence_item item; //-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_hs_agent", uvm_component parent); super.new(name, parent); `uvm_info("AGENT_CLASS", "Inside Constructor!", UVM_HIGH)
endfunction: new
//-------------------------------------------------------- //Build Phase //-------------------------------------------------------- function void build_phase(uvm_phase phase); super.build_phase(phase); `uvm_info("AGENT_CLASS", "Build Phase!", UVM_HIGH)
drv = SimpleDUT_hs_driver::type_id::create("drv", this); mon = SimpleDUT_hs_monitor::type_id::create("mon", this); seqr = SimpleDUT_hs_sequencer::type_id::create("seqr", this); endfunction: build_phase
//-------------------------------------------------------- //Connect Phase //-------------------------------------------------------- function void connect_phase(uvm_phase phase); super.connect_phase(phase); `uvm_info("AGENT_CLASS", "Connect Phase!", UVM_HIGH)
drv.seq_item_port.connect(seqr.seq_item_export); endfunction: connect_phase
//-------------------------------------------------------- //Run Phase //-------------------------------------------------------- task run_phase (uvm_phase phase); super.run_phase(phase); `uvm_info("AGENT_CLASS", "Run Phase!", UVM_HIGH) item = SimpleDUT_hs_sequence_item::type_id::create("item"); mon.monitor_port.write(item); endtask: run_phase
endclass: SimpleDUT_hs_agent |
Environment class template 2.0 same as a final solution ( A Component class: file env.sv)
As a friendly reminder the environment class template will have instances of agent and scoreboard classes template instances as it is planned in our UVM verification infrastructure architecture.
class SimpleDUT_hs_env extends uvm_test; `uvm_component_utils(SimpleDUT_hs_env)
SimpleDUT_hs_agent agnt;
SimpleDUT_hs_scoreboard scb; //-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_hs_env", uvm_component parent); super.new(name, parent); `uvm_info("ENV_CLASS", "Inside Constructor!", UVM_HIGH)
endfunction: new
//-------------------------------------------------------- //Build Phase //-------------------------------------------------------- function void build_phase(uvm_phase phase); super.build_phase(phase); `uvm_info("ENV_CLASS", "Build Phase!", UVM_HIGH)
agnt = SimpleDUT_hs_agent::type_id::create("agnt", this); scb = SimpleDUT_hs_scoreboard::type_id::create("scb", this); endfunction: build_phase
//-------------------------------------------------------- //Connect Phase //-------------------------------------------------------- function void connect_phase(uvm_phase phase); super.connect_phase(phase); `uvm_info("ENV_CLASS", "Connect Phase!", UVM_HIGH) agnt.mon.monitor_port.connect(scb.scoreboard_port);
endfunction: connect_phase
//-------------------------------------------------------- //Run Phase //-------------------------------------------------------- task run_phase (uvm_phase phase); super.run_phase(phase); `uvm_info("ENV_CLASS", "Run Phase!", UVM_HIGH)
// Logic
endtask: run_phase
endclass: SimpleDUT_hs_env |
UVM Test Component template 2.0 same as final solution
It will have two purposes:
class SimpleDUT_hs_test extends uvm_test; `uvm_component_utils(SimpleDUT_hs_test) SimpleDUT_hs_env env;
// Instantiation two sequences SimpleDUT_hs_base_sequence reset_seq; SimpleDUT_hs_test_sequence test_seq;
//-------------------------------------------------------- // Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_hs_test", uvm_component parent); super.new(name, parent); `uvm_info("TEST_CLASS", "Inside Constructor!", UVM_HIGH) endfunction: new //-------------------------------------------------------- // Build Phase //-------------------------------------------------------- function void build_phase(uvm_phase phase); super.build_phase(phase); `uvm_info("TEST_CLASS", "Build Phase!", UVM_HIGH)
env = SimpleDUT_hs_env::type_id::create("env", this);
endfunction: build_phase
//-------------------------------------------------------- //Connect Phase //-------------------------------------------------------- function void connect_phase(uvm_phase phase); super.connect_phase(phase); `uvm_info("TEST_CLASS", "Connect Phase!", UVM_HIGH)
endfunction: connect_phase //-------------------------------------------------------- //Run Phase //-------------------------------------------------------- task run_phase (uvm_phase phase); super.run_phase(phase); `uvm_info("TEST_CLASS", "Run Phase!", UVM_HIGH) phase.raise_objection(this);
// Constructing the reset sequence reset_seq = SimpleDUT_hs_base_sequence::type_id::create("reset_seq"); reset_seq.start(env.agnt.seqr); // e.g. repeat test sequence 100 times repeat(100) begin //test_seq // Constructing the test sequence test_seq = SimpleDUT_hs_test_sequence::type_id::create("test_seq"); // Starting the test sequence on the sequencer on path env.agnt.seqr test_seq.start(env.agnt.seqr); end phase.drop_objection(this);
endtask: run_phase
endclass: SimpleDUT_hs_test |
testbench.sv (top testbench module )
`timescale 1ns/1ns
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "interface.sv" `include "sequence_item.sv" `include "sequence.sv" `include "sequencer.sv" `include "driver.sv" `include "monitor.sv" `include "agent.sv" `include "scoreboard.sv" `include "env.sv" `include "test.sv"
module top;
// the clock for DUT is generated from the top testbench module so let's declare a signal clock here logic CLK; SimpleDUT_hs_interface intf(.CLK(CLK)); SimpleDUT_hs dut( .CLK(intf.CLK), .RST_N(intf.RST_N), .din_i(intf.din_i), .dout_o(intf.dout_o), .en_din_i(intf.en_din_i), .rdy_din_o(intf.rdy_din_o), .en_dout_i(intf.en_dout_i), .rdy_dout_o(intf.rdy_dout_o)); //-------------------------------------------------------- //Interface Setting //-------------------------------------------------------- initial begin uvm_config_db #(virtual SimpleDUT_hs_interface)::set(null, "*", "vif", intf ); end
//Start The Test //-------------------------------------------------------- initial begin run_test("SimpleDUT_hs_test"); end //-------------------------------------------------------- //Clock Generation //-------------------------------------------------------- initial begin CLK = 0; #5; forever begin CLK = ~CLK; #2; end end //-------------------------------------------------------- //Maximum Simulation Time //-------------------------------------------------------- // initial begin // #1000; // $display("Sorry! Ran out of clock cycles!"); // $finish(); // end //-------------------------------------------------------- //Generate Waveforms //-------------------------------------------------------- // if you want to see the waveforms you can also use the dump file command to tell the simulator to dump variable values so that // you can inspect the waveforms after verification/simulation
initial begin $dumpfile("d.vcd"); $dumpvars(); end endmodule: top |
Waveforms
Compilation/Verification log
[2023-07-26 07:25:46 UTC] vcs -licqueue '-timescale=1ns/1ns' '+vcs+flush+all' '+warn=all' '-sverilog' +incdir+$UVM_HOME/src $UVM_HOME/src/uvm.sv $UVM_HOME/src/dpi/uvm_dpi.cc -CFLAGS -DVCS design.sv testbench.sv && ./simv +vcs+lic+wait '+UVM_VERBOSITY=UVM_HIGH'
Chronologic VCS (TM)
Version S-2021.09 -- Wed Jul 26 03:25:47 2023
Copyright (c) 1991 - 2021 Synopsys, Inc.
Parsing design file 'design.sv'
Parsing design file 'testbench.sv'
Note-[SV-LCM-PPWI] Package previously wildcard imported
testbench.sv, 3
$unit
Package 'uvm_pkg' already wildcard imported.
Ignoring uvm_pkg::*
See the SystemVerilog LRM(1800-2005), section 19.2.1.
Parsing included file '/apps/vcsmx/vcs/S-2021.09//etc/uvm-1.2/src/uvm_macros.svh'.
Back to file 'testbench.sv'.
Parsing included file 'interface.sv'.
Back to file 'testbench.sv'.
Parsing included file 'sequence_item.sv'.
Back to file 'testbench.sv'.
Parsing included file 'sequence.sv'.
Back to file 'testbench.sv'.
Parsing included file 'sequencer.sv'.
Back to file 'testbench.sv'.
Parsing included file 'driver.sv'.
Back to file 'testbench.sv'.
Parsing included file 'monitor.sv'.
Back to file 'testbench.sv'.
Parsing included file 'agent.sv'.
Back to file 'testbench.sv'.
Parsing included file 'scoreboard.sv'.
Back to file 'testbench.sv'.
Parsing included file 'env.sv'.
Back to file 'testbench.sv'.
Parsing included file 'test.sv'.
Back to file 'testbench.sv'.
Top Level Modules:
top
TimeScale is 1 ns / 1 ns
Starting vcs inline pass...
6 modules and 0 UDP read.
recompiling package vcs_paramclassrepository
recompiling package _vcs_DPI_package
recompiling package uvm_pkg
recompiling module SimpleDUT_hs
recompiling module SimpleDUT_hs_interface
recompiling module top
All of 6 modules done
rm -f _cuarc*.so _csrc*.so pre_vcsobj_*.so share_vcsobj_*.so
g++ -w -pipe -m32 -DVCS -O -I/apps/vcsmx/vcs/S-2021.09/include -c /apps/vcsmx/vcs/S-2021.09//etc/uvm-1.2/src/dpi/uvm_dpi.cc
gcc -w -pipe -m32 -DVCS -O -I/apps/vcsmx/vcs/S-2021.09/include -c -o uM9F1_0x2aB.o uM9F1_0x2aB.c
if [ -x ../simv ]; then chmod a-x ../simv; fi
g++ -o ../simv -m32 -m32 -rdynamic -Wl,-rpath='$ORIGIN'/simv.daidir -Wl,-rpath=./simv.daidir -Wl,-rpath=/apps/vcsmx/vcs/S-2021.09/linux/lib -L/apps/vcsmx/vcs/S-2021.09/linux/lib -Wl,-rpath-link=./ -Wl,--no-as-needed uvm_dpi.o objs/amcQw_d.o _415_archive_1.so SIM_l.o uM9F1_0x2aB.o rmapats_mop.o rmapats.o rmar.o rmar_nd.o rmar_llvm_0_1.o rmar_llvm_0_0.o -lvirsim -lerrorinf -lsnpsmalloc -lvfs -lvcsnew -lsimprofile -luclinative /apps/vcsmx/vcs/S-2021.09/linux/lib/vcs_tls.o -Wl,-whole-archive -lvcsucli -Wl,-no-whole-archive ./../simv.daidir/vc_hdrs.o /apps/vcsmx/vcs/S-2021.09/linux/lib/vcs_save_restore_new.o /apps/vcsmx/vcs/S-2021.09/linux/lib/ctype-stubs_32.a -ldl -lc -lm -lpthread -ldl
../simv up to date
CPU time: 9.779 seconds to compile + .459 seconds to elab + .578 seconds to link
----------------------------------------------------------------
UVM-1.2.Synopsys
(C) 2007-2014 Mentor Graphics Corporation
(C) 2007-2014 Cadence Design Systems, Inc.
(C) 2006-2014 Synopsys, Inc.
(C) 2011-2013 Cypress Semiconductor Corp.
(C) 2013-2014 NVIDIA Corporation
----------------------------------------------------------------
UVM_INFO test.sv(15) @ 0: uvm_test_top [TEST_CLASS] Inside Constructor!
UVM_INFO @ 0: reporter [RNTST] Running test SimpleDUT_hs_test...
UVM_INFO test.sv(23) @ 0: uvm_test_top [TEST_CLASS] Build Phase!
UVM_INFO env.sv(13) @ 0: uvm_test_top.env [ENV_CLASS] Inside Constructor!
UVM_INFO env.sv(22) @ 0: uvm_test_top.env [ENV_CLASS] Build Phase!
UVM_INFO agent.sv(15) @ 0: uvm_test_top.env.agnt [AGENT_CLASS] Inside Constructor!
UVM_INFO scoreboard.sv(18) @ 0: uvm_test_top.env.scb [SCB_CLASS] Inside Constructor!
UVM_INFO agent.sv(25) @ 0: uvm_test_top.env.agnt [AGENT_CLASS] Build Phase!
UVM_INFO driver.sv(11) @ 0: uvm_test_top.env.agnt.drv [DRIVER_CLASS] Inside Constructor!
UVM_INFO monitor.sv(17) @ 0: uvm_test_top.env.agnt.mon [MONITOR_CLASS] Inside Constructor!
UVM_INFO sequencer.sv(10) @ 0: uvm_test_top.env.agnt.seqr [SEQUENCER_CLASS] Inside Constructor!
UVM_INFO driver.sv(21) @ 0: uvm_test_top.env.agnt.drv [DRIVER_CLASS] Build Phase!
UVM_INFO monitor.sv(30) @ 0: uvm_test_top.env.agnt.mon [MONITOR_CLASS] Build Phase!
UVM_INFO sequencer.sv(20) @ 0: uvm_test_top.env.agnt.seqr [SEQUENCER_CLASS] Build Phase!
UVM_INFO scoreboard.sv(28) @ 0: uvm_test_top.env.scb [SCB_CLASS] Build Phase!
UVM_INFO driver.sv(35) @ 0: uvm_test_top.env.agnt.drv [DRIVER_CLASS] Connect Phase!
UVM_INFO monitor.sv(46) @ 0: uvm_test_top.env.agnt.mon [MONITOR_CLASS] Connect Phase!
UVM_INFO sequencer.sv(30) @ 0: uvm_test_top.env.agnt.seqr [SEQUENCER_CLASS] Connect Phase!
UVM_INFO agent.sv(40) @ 0: uvm_test_top.env.agnt [AGENT_CLASS] Connect Phase!
UVM_INFO scoreboard.sv(44) @ 0: uvm_test_top.env.scb [SCB_CLASS] Connect Phase!
UVM_INFO env.sv(35) @ 0: uvm_test_top.env [ENV_CLASS] Connect Phase!
UVM_INFO test.sv(36) @ 0: uvm_test_top [TEST_CLASS] Connect Phase!
UVM_INFO driver.sv(45) @ 0: uvm_test_top.env.agnt.drv [DRIVER_CLASS] Run Phase!
UVM_INFO monitor.sv(56) @ 0: uvm_test_top.env.agnt.mon [MONITOR_CLASS] Run Phase!
UVM_INFO agent.sv(53) @ 0: uvm_test_top.env.agnt [AGENT_CLASS] Run Phase!
-------------------------
- [ Scoreboard ]
-------------------------
- en_din_i = 0, rdy_din_o = x, din_i = x
- en_dout_i = 0, rdy_dout_o = x, dout_o = x
-------------------------
UVM_INFO scoreboard.sv(68) @ 0: uvm_test_top.env.scb [SCB_CLASS] Run Phase!
UVM_INFO scoreboard.sv(101) @ 0: uvm_test_top.env.scb [COMPARE] Transaction Passed! ACT= x, EXP= x
UVM_INFO env.sv(48) @ 0: uvm_test_top.env [ENV_CLASS] Run Phase!
UVM_INFO test.sv(44) @ 0: uvm_test_top [TEST_CLASS] Run Phase!
UVM_INFO sequence.sv(15) @ 0: reporter@@reset_seq [BASE_SEQ] Inside Constructor!
UVM_INFO sequence.sv(25) @ 0: uvm_test_top.env.agnt.seqr@@reset_seq [BASE_SEQ] Inside body task!
UVM_INFO sequence.sv(15) @ 7: reporter@@test_seq [BASE_SEQ] Inside Constructor!
UVM_INFO sequence.sv(69) @ 7: reporter@@test_seq [TEST_SEQ] Inside Constructor!
UVM_INFO sequence.sv(77) @ 7: uvm_test_top.env.agnt.seqr@@test_seq [TEST_SEQ] Inside body task!
-------------------------
- [ Scoreboard ]
-------------------------
- en_din_i = 0, rdy_din_o = 0, din_i = 15
- en_dout_i = 0, rdy_dout_o = 0, dout_o = 15
-------------------------
UVM_INFO scoreboard.sv(101) @ 135: uvm_test_top.env.scb [COMPARE] Transaction Passed! ACT=15, EXP=15
UVM_INFO sequence.sv(15) @ 135: reporter@@test_seq [BASE_SEQ] Inside Constructor!
UVM_INFO sequence.sv(69) @ 135: reporter@@test_seq [TEST_SEQ] Inside Constructor!
UVM_INFO sequence.sv(77) @ 135: uvm_test_top.env.agnt.seqr@@test_seq [TEST_SEQ] Inside body task!
-------------------------
- [ Scoreboard ]
-------------------------
- en_din_i = 0, rdy_din_o = 0, din_i = 5
- en_dout_i = 0, rdy_dout_o = 0, dout_o = 5
-------------------------
UVM_INFO scoreboard.sv(101) @ 187: uvm_test_top.env.scb [COMPARE] Transaction Passed! ACT= 5, EXP= 5
UVM_INFO sequence.sv(15) @ 187: reporter@@test_seq [BASE_SEQ] Inside Constructor!
UVM_INFO sequence.sv(69) @ 187: reporter@@test_seq [TEST_SEQ] Inside Constructor!
UVM_INFO sequence.sv(77) @ 187: uvm_test_top.env.agnt.seqr@@test_seq [TEST_SEQ] Inside body task!
ETC. ETC.
-------------
UVM_INFO scoreboard.sv(101) @ 10635: uvm_test_top.env.scb [COMPARE] Transaction Passed! ACT= 6, EXP= 6
UVM_INFO sequence.sv(15) @ 10635: reporter@@test_seq [BASE_SEQ] Inside Constructor!
UVM_INFO sequence.sv(69) @ 10635: reporter@@test_seq [TEST_SEQ] Inside Constructor!
UVM_INFO sequence.sv(77) @ 10635: uvm_test_top.env.agnt.seqr@@test_seq [TEST_SEQ] Inside body task!
-------------------------
- [ Scoreboard ]
-------------------------
- en_din_i = 0, rdy_din_o = 0, din_i = 13
- en_dout_i = 0, rdy_dout_o = 0, dout_o = 13
-------------------------
UVM_INFO scoreboard.sv(101) @ 10747: uvm_test_top.env.scb [COMPARE] Transaction Passed! ACT=13, EXP=13
UVM_INFO /apps/vcsmx/vcs/S-2021.09//etc/uvm-1.2/src/base/uvm_objection.svh(1276) @ 10747: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /apps/vcsmx/vcs/S-2021.09//etc/uvm-1.2/src/base/uvm_report_server.svh(904) @ 10747: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
** Report counts by severity
UVM_INFO : 433
UVM_WARNING : 0
UVM_ERROR : 0
UVM_FATAL : 0
** Report counts by id
[AGENT_CLASS] 4
[BASE_SEQ] 102
[COMPARE] 101
[DRIVER_CLASS] 4
[ENV_CLASS] 4
[MONITOR_CLASS] 4
[RNTST] 1
[SCB_CLASS] 4
[SEQUENCER_CLASS] 3
[TEST_CLASS] 4
[TEST_DONE] 1
[TEST_SEQ] 200
[UVM/RELNOTES] 1
$finish called from file "/apps/vcsmx/vcs/S-2021.09//etc/uvm-1.2/src/base/uvm_root.svh", line 527.
$finish at simulation time 10747
V C S S i m u l a t i o n R e p o r t
Time: 10747 ns
CPU Time: 0.660 seconds; Data structure size: 0.3Mb
Wed Jul 26 03:25:59 2023
© 2023 ASIC Stoic. All rights reserved.