UVM verification environment universal template 2.0
UVM verification environment starting point for use in verification of any RTL Digital Design Under Test (DUT)
Introduction 2
UVM verification environment architecture 2
DUT 3
Interface 4
Sequence item class templates evolution 4
Sequence item class template 1.0 ( an OBJECT class) 4
Sequence item ( or transaction ) class template 2.0 ( an OBJECT class, file: sequence_item.sv ) 5
Sequence class template evolution 7
Base sequence class template 1.0 ( an OBJECT class, file: sequence.sv ) 7
Sequence class template 2.0 ( an OBJECT class, file: sequence.sv ) 9
Sequencer class template evolution 12
Sequencer class template 1.0 ( once more Component class ) 12
Sequencer class template 2.0 ( once more a Component class: sequencer.sv ) 13
Driver class template evolution 16
Driver class template 1.0 ( one more Component class ) 16
Driver class template 2.0 ( one more Component class: file driver.sv ) 18
Monitor class template evolution 22
Monitor class template 1.0 ( one more Component class ) 22
Monitor class template 2.0 ( one more Component class: file monitor.sv ) 23
Scoreboard template ( A Component class file : scoreboard.sv ) 28
Agent class template evolution 32
Agent class template 1.0 ( once more Component class ) 32
Agent class template 2.0 ( once more a Component class: file agent.sv ) 33
Environment class template evolution 36
Environment class template 1.0 36
Environment class template 2.0 ( A Component class: file env.sv) 38
UVM Test Component template evolution 42
UVM Test Component template 1.0 42
UVM Test Component template 2.0 44
testbench.sv (top testbench module ) 49
Compilation 53
Introduction
In this blog post there is a source code of all UVM verification environment files to use, as a starting point, in verification of any RTL Digital Design Under Test (DUT). The assembly of files of the UVM verification template compiles OK.
To achieve the successful compilation of the UVM verification environment universal template files, in this case, a Scorebord.sv file is not present ( empty file) and a dummy DUT ( SystemVerilog RTL module and its corresponding SystemVerilog interface) is included.
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 previously showed block diagram
DUT
module SimpleDUT ( input clk, input reset, input [3:0] din_i, output [3:0] dout_o ); // Logic for the DUT assign dout_o = din_i; endmodule |
Interface
interface SimpleDUT_interface(input logic clock ); logic reset; logic [3:0] din_i, dout_o; endinterface: SimpleDUT_interface |
Sequence item class templates evolution
Sequence item class template 1.0 ( an OBJECT class)
The sequence item class is an object class and not a component class so I won't copy
everything from the previous component class templates.
//Object class class SimpleDUT_sequence_item extends uvm_sequence_item; // just like the component class this class will also need a macro but it will be UVM object utils `uvm_object_utils(SimpleDUT_sequence_item)
// Instantiation logic/wires/registers same as used in interface go here // so in other words: straight copy from interface // We are going to use this to generate random values about DUT input port values on one hand and to store the output result on the other hand. // So to do that what we are going to do is declare these signals as DUT input port values Rand signals
//-------------------------------------------------------- //Instantiation //-------------------------------------------------------- rand logic [3:0] din_i; logic [3:0] dout_o;
// let's see how Constructor for an object class will be defined: // In object class Constructor we just pass the name which can be done like this //-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_sequence_item"); super.new(name);
endfunction: new
endclass: SimpleDUT_sequence_item |
Sequence item ( or transaction ) class template 2.0 ( an OBJECT class, file: sequence_item.sv )
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. For our dummy DUT there is only one data transfer possible for applied input data din_i, output data dout_o = din_i. In general we could expect that DUT also has Power On Reset signal so we will keep transfer of RST_N in the transaction too, although RST_N is not connected in our dummy DUT.
//Object class class SimpleDUT_sequence_item extends uvm_sequence_item; // just like the component class this class will also need a macro but it will be UVM object utils `uvm_object_utils(SimpleDUT_sequence_item)
// Instantiation logic/wires/registers same as used in interface go here // so in other words: straight copy from interface // We are going to use this to generate random values about DUT input port values on one hand and to store the output result on the other hand. // So to do that what we are going to do is declare these signals as DUT input port values Rand signals
//-------------------------------------------------------- //Instantiation //-------------------------------------------------------- rand logic reset; rand logic [3:0] din_i; logic [3:0] dout_o;
// 1. change of SimpleDUT_sequence_item template 1.0: let's put some default constraints that we want in our sequence item
//-------------------------------------------------------- //Default Constraints //-------------------------------------------------------- constraint input1_c { din_i inside {[0:15]};}
// let's see how Constructor for an object class will be defined: // In object class Constructor we just pass the name which can be done like this //-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_sequence_item"); super.new(name);
endfunction: new
endclass: SimpleDUT_sequence_item
|
Sequence class template evolution
Base sequence class template 1.0 ( an OBJECT class, file: sequence.sv )
class SimpleDUT_base_sequence extends uvm_sequence; `uvm_object_utils(SimpleDUT_base_sequence)
//-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name= "SimpleDUT_base_sequence"); super.new(name); `uvm_info("BASE_SEQ", "Inside Constructor!", UVM_HIGH) endfunction
// in sequence class we need to define // body describing what is going to happen when we call this sequence
//-------------------------------------------------------- //Body Task //-------------------------------------------------------- task body(); `uvm_info("BASE_SEQ", "Inside body task!", UVM_HIGH)
endtask: body endclass: SimpleDUT_base_sequence
class SimpleDUT_test_sequence extends SimpleDUT_base_sequence; `uvm_object_utils(SimpleDUT_test_sequence) SimpleDUT_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) endtask: body endclass: SimpleDUT_test_sequence |
Sequence class template 2.0 ( 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 to be sent to the driver. This is a job for the sequence. Sequences are an ordered collection of transactions, they shape transactions to our needs and generate as many as we want.
class SimpleDUT_base_sequence extends uvm_sequence; `uvm_object_utils(SimpleDUT_base_sequence) //1. Change based on Base sequence item class template 1.0: // In a sequence class we need to start a basic sequence. // This means that the sequence class will start or generate a sequence item class packets and then send them to the driver. // In order to do that we need to instantiate a sequence item class over here. // let's call it reset_pk SimpleDUT_sequence_item reset_pkt;
//-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name= "SimpleDUT_base_sequence"); super.new(name); `uvm_info("BASE_SEQ", "Inside Constructor!", UVM_HIGH) endfunction
// in sequence class we need to define // body describing what is going to happen when we call this sequence
//-------------------------------------------------------- //Body Task //-------------------------------------------------------- task body(); `uvm_info("BASE_SEQ", "Inside body task!", UVM_HIGH) // 2. Change based on Base sequence item class template 1.0: // Further we need to send the reset_pkt to the driver // First step is to create the packet. for this so we say reset_pkt = SimpleDUT_sequence_item ...., // and then we call the "create" method of type_id create. // Finally we say that a name of our packet is "reset_pkt"( you can give any name here) reset_pkt = SimpleDUT_sequence_item::type_id::create("reset_pkt");
// 3. Change based on Base sequence item class template 1.0: // Next we need to start_item and finish_item. // Here we want to randomize that package although with "reset" constraint to be only one value: 1. // As a result this sequence will just reset the DUT ( assumption here is that "reset" is active high. start_item(reset_pkt); // Since constraint on the "reset" tigh reset to 1 // this sequence will just reset the duty that we have ( the reset is active high )
reset_pkt.randomize() with {reset==1;}; finish_item(reset_pkt); endtask: body endclass: SimpleDUT_base_sequence |
// 4. Change based on Base sequence item class template 1.0: // we can also create another sequence which is not reset sequence class SimpleDUT_test_sequence extends SimpleDUT_base_sequence; `uvm_object_utils(SimpleDUT_test_sequence) SimpleDUT_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)
// 5. Change based on Base sequence item class template: // In this test sequence the randomized "reset" is equal to zero because now with this sequence we don't want uh to reset anything. This time we want to drive some values to the input of DUT. item = SimpleDUT_sequence_item::type_id::create("item"); start_item(item); item.randomize() with {reset==0;}; finish_item(item);
endtask: body endclass: SimpleDUT_test_sequence |
Sequencer class template evolution
Sequencer class template 1.0 ( once more Component class )
Note #1: Same as driver class, the sequencer class is a parameterized class so we need to pass the name of the sequence item class that we are going to use
Note# 2: also in the sequencer we don't need the “run” phase so I'll just comment out that part
// Note #1: Same as driver class, the sequencer class is a parameterized class so we need to pass the name of the sequence item class that we are going to use // we haven't created the sequence item class but I know the name of it will be SimpleDUT_sequence_item)
// class SimpleDUT_sequencer extends uvm_sequencer; class SimpleDUT_sequencer extends uvm_sequencer#(SimpleDUT_sequence_item); `uvm_component_utils(SimpleDUT_sequencer)
//-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_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
// Note# 2: also in the sequencer we don't need the Run phase so I'll just comment out that part /* //-------------------------------------------------------- //Run Phase //-------------------------------------------------------- task run_phase (uvm_phase phase); super.run_phase(phase); `uvm_info("SEQUENCER_CLASS", "Run Phase!", UVM_HIGH)
// Logic
endtask: run_phase */
endclass: SimpleDUT_sequencer |
Sequencer class template 2.0 ( once more a Component class: 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_sequence_item) SimpleDUT_sequencer; |
Note #1: Same as driver class, the sequencer class is a parameterized class so we need to pass the name of the sequence item class that we are going to use
Note# 2: also in the sequencer we don't need the “run” phase so I'll just comment out that part
// Note #1: Same as driver class, the sequencer class is a parameterized class so we need to pass the name of the sequence item class that we are going to use // The name of sequence item class already created is SimpleDUT_sequence_item)
class SimpleDUT_sequencer extends uvm_sequencer#(SimpleDUT_sequence_item); `uvm_component_utils(SimpleDUT_sequencer)
//-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_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_sequencer |
Driver class template evolution
Driver class template 1.0 ( one more Component class )
Note: driver class is a parameterized class so we need to pass the name of the sequence item class that we are going to use
The driver knows what type of sequence item that we are going to drive on the sequencer and that is why we pass this as a parameter
// Note: driver class is a parameterized class so we need to pass the name of the sequence item class that we are going to use // we haven't created the sequence item class but I know the name of it will be SimpleDUT_sequence_item)
class SimpleDUT_driver extends uvm_driver#(SimpleDUT_sequence_item); `uvm_component_utils(SimpleDUT_driver)
//-------------------------------------------------------- //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) 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
endtask: run_phase
endclass: SimpleDUT_driver |
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.
Note: driver class is a parameterized class so we need to pass the name of the sequence item ( in this case already defined: SimpleDUT_sequence_item class)
e.g. class SimpleDUT_driver extends uvm_driver#(SimpleDUT_sequence_item);
The driver knows what type of sequence item that we are going to drive on the sequencer and that is why we pass this as a parameter
// Note: driver class is a parameterized class so we need to pass the name of the sequence item class that we are going to use ( in this case our previously defined sequence item class is SimpleDUT_sequence_item) class SimpleDUT_driver extends uvm_driver#(SimpleDUT_sequence_item); `uvm_component_utils(SimpleDUT_driver)
// 1. driver class template ( 1.0) change: We need to have access to the DUT interface ( here it has the name: "vif") // from the uvm_config_db. // Also we want to copy or use a name that we // can be used throughout the driver for that class. // We need to use "vif" so we do need a handle for this virtual interface // so we do need to declare it in the class somewhere here. // Finally what we declare is a virtual SimpleDUT_interface and the handle would be // "vif". Now we know that this is virtual interface inside the driver virtual SimpleDUT_interface vif;
//3. driver class template (1.0) change : // Here we are moving on to update of the Run phase of the driver // which is the most important phase in the driver. // But first: the thing we need to do first is to declare the handle to the item that we are going // to use in the driver, and that is SimpleDUT_sequence_item 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)
//2. driver class template (1.0) change :In the top testbench module we just set the interface now we need to get the interface so we can get that // interface in driver class and monitor class
// we can get that interface in any function that we want to but ideal way is to get it in the build phase // The line: uvm_config_db #(virtual SimpleDUT_interface)::get(this, "*", "vif", vif) represents the usage of the uvm_config_db class and its get method in the context of the UVM (Universal Verification Methodology) framework in SystemVerilog.The get method is used to retrieve a configuration setting from the database. 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)
//4. driver class template (1.0) change : // what does a driver do? A driver drives the logic that we want to use to simulate the DUT. // let's just drive the inputs to the interface with this handle (vif) // What we want to do is: forever run this driver task until the // simulation ends. In other words, forever keep driving these transactions. // And what do we want to drive? We want to drive this packet which is called "item". // Logic forever begin // Create "item" item = SimpleDUT_sequence_item::type_id::create("item"); // then we use the ports that we need to declare in the agent and we will call it seq_item_port) // we just use this port inside the driver to get the sequences from the sequencer and to do that we use // this method: get_next_item
seq_item_port.get_next_item(item);
//5. driver class template (1.0) change : // Logic: Our code of the driver here // In order to write the driver, it's easier to implement the code directly as a normal testbench and observe its behavior through waveforms. // Further we will reuse the normal testbench code to implement here in the run phase and lets assume that our driver code will be captured in task "drive" taking an argument: the transaction "item". // We can just create a task that can drive the items although it's not necessary to create create this // task you can just easily write down here whatever the logic is. // On the other hand writing a task is an // easy way so that in case you need to change something in driver class or // drive method; this way it does not affect the Run phase, and the Run phase is the most important phase // in driver class.
drive(item);
//6. driver class template (1.0) change : // after doing or driving something we can just call seq_item_port.item_done() // indicating that we are done driving the item and we are ready to move on to get next item seq_item_port.item_done(); end
endtask: run_phase
//7. driver class template(1.0) change : // Here is task "drive" and it also using the item object that is passed by reference // We want to drive the packet transaction on the interface // but we want to do it only on the positive edge of the clock. // Where is that clock, where can we access that clock ? // We can access that clock from virtual interface handle that we already have // and we use non-blocking assignments although it's not necessary though. //-------------------------------------------------------- //[Method] Drive //-------------------------------------------------------- task drive(SimpleDUT_sequence_item item); // e.g. for our "dummy" DUT // @(posedge vif.clock); // vif.reset <= item.reset; // vif.din_i <= item.din_i; endtask: drive
endclass: SimpleDUT_driver |
Monitor class template evolution
Monitor class template 1.0 ( one more Component class )
class SimpleDUT_monitor extends uvm_monitor; `uvm_component_utils(SimpleDUT_monitor)
//-------------------------------------------------------- //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) 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
endtask: run_phase
endclass: SimpleDUT_monitor |
Monitor class template 2.0 ( one more Component class: file monitor.sv )
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)
//1. monitor class template (1.0) change : As in the driver already explained, the monitor also need access to DUT interface virtual SimpleDUT_interface vif;
// //3. monitor class template(1.0) change : In the monitor class we are sampling from the interface // also we will need a name of the handle to the item class that we are going to use in the monitor. SimpleDUT_sequence_item item;
// 6. monitor class template change (1.0) : One thing we need to see is how are we going to connect the scoreboard and // monitor so it is going to be done just like how we connected the driver and sequencer
// Note* a friendly reminder from agent.sv how we connected the driver and sequence: // drv.seq_item_port.connect(seqr.seq_item_export);
// driver and sequencer had these ports ( e.g. seq_item_port.connect) by default but monitor and // scoreboard does not have them so we need to create those ports with uvm_analysis_port.
// syntax is something like this: uvm_analysis_port // and then we pass the parameter to say what kind of sequence item we are going to drive through these ports. // Basically we say that we are going to drive packet of type SimpleDUT_sequence_item and // then we can name the port as "monitor_port" // and then we also need to create monitor_port for example in build phase // we need to pass two arguments first is the name of the port and second is the parent 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)
//2. monitor class template (1.0) change : As in the driver already explained, the monitor also need access to DUT interface if(!(uvm_config_db #(virtual SimpleDUT_interface)::get(this, "*", "vif", vif))) begin `uvm_error("MONITOR_CLASS", "Failed to get VIF from config DB!") end
// 7. monitor class template change : // We need to create monitor_port for example in build phase // we need to pass two arguments first is the name of the port and second is the parent 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) //4. monitor class template(1.0) change : // What we want to do is: forever run this until the // simulation ends in other words forever keep monitoring these transactions. // We want to sample both the input and the output. // Here I am using blocking because it is a general practice to use non-blocking for driver and blocking for // monitor class but if you use otherwise that is also completely fine. // Logic forever begin // 5. monitor class template(1.0) change : creation of a transaction // to store sampled data ( e.g. on each clock tick) and the transaction is passed to scoreboard 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 // // @(posedge vif.clock); // item.dout_o = vif.dout_o; // 8. monitor class template change: send item to scoreboard through the port: use method "write" // to write "item" to scoreboard using port "monitor_port" // we also need to implement this "write" method inside the scoreboard @(posedge vif.clock); monitor_port.write(item); end
endtask: run_phase
endclass: SimpleDUT_monitor |
Scoreboard template ( A Component class file : scoreboard.sv )
class SimpleDUT_scoreboard extends uvm_test; `uvm_component_utils(SimpleDUT_scoreboard) //1. Change to UVM scoreboard component class template 1.0: // Similar to the monitor class, we need to do something similar for the scoreboard port. // Now the caveat is that driver will be sending packets, so in drivers case the port will be a transmitter port (uses uvm_analysis_port) // Scoreboard will need a receiver port so Scoreboard port will be a receiver port ( uses: uvm_analysis_imp ). // uvm_analysis_imp has two parameters: one is the sequence item, and here we are going to use // SimpleDUT_sequence_item and the other is the name of the scoreboard (SimpleDUT_scoreboard). // Finally we can call this port as scoreboard_port. uvm_analysis_imp #(SimpleDUT_sequence_item, SimpleDUT_scoreboard) scoreboard_port;
//4. Change to UVM scoreboard component class template 1.0: // let's say monitor sends or writes // this item in the port and scoreboard will receive it so what we need to do is // make a database or store that item or packets let's say we want to run // the sequence 100 times and we want to save all those 100 transactions so we // can use a data structure to save all the packets. Easiest data structure // is the queue that we can use here so that it becomes a sort of a fifo. // As a conclusion here we are going to create a queue of type // sequence item and we can call this queue: transactions. // transactions: This is the name given to the array of SimpleDUT_sequence_item objects // It is a variable name that can be used to access individual elements within the array. // The [$] syntax allows the array size to be determined dynamically at runtime, as opposed to a fixed size declared at compile-time. 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)
//2. Change to UVM scoreboard component class template // scoreboard_port we need to construct/create in for example build phase 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
// 3. Change to UVM scoreboard component class template // We also need to implement the "write" method ( from monitor class line: monitor_port.write(item);): // this is the most important part of the scoreboard: write method. // The write method determines what is going to happen when monitor sends the item in the // scoreboard. // We are implementing this method here and that's why the name over // here is: uvm_analysis_imp #((SimpleDUT_sequence_item, SimpleDUT_scoreboard) scoreboard_port; uvm_analysis_imp stands for implementation). // The port name is UVM analysis implementation (uvm_analysis_imp) because we are // implementing this write method in this class. // We also need to pass "item" as a parameter. //-------------------------------------------------------- //Write Method //-------------------------------------------------------- function void write(SimpleDUT_sequence_item item); // Make a database or store that item or packets. // use this transactions.push_back what do we want to push is this item so // one by one whenever this method is called. We are just pushing this item into "transactions" // array transactions.push_back(item);
// in the Run phase we will do something about those transactions maybe compare or // something like that
endfunction: write
//-------------------------------------------------------- //Run Phase //-------------------------------------------------------- task run_phase (uvm_phase phase); super.run_phase(phase); `uvm_info("SCB_CLASS", "Run Phase!", UVM_HIGH)
//5. Change to UVM scoreboard component class template // In the Run phase like in other run phases we just want to keep on running this forever until the // simulation ends forever begin /* // get the packet // generate expected value // compare it with actual value // score the transactions accordingly */ SimpleDUT_sequence_item curr_trans;
// we need to wait for until the transaction queue size becomes greater than 0 // we don't want to run this task of popping up the data from fifo if we have not received any transactions yet. wait((transactions.size() != 0));
// copy/ pop it from the fifo "transactions" only one transaction at a time and then we play with that in the // forever loop curr_trans = transactions.pop_front();
//"compare" is task that take as argument that one piece of data we just popped from the fifo compare(curr_trans); end
endtask: run_phase
//6. Change to UVM scoreboard component class template // Adding compare task: //-------------------------------------------------------- //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 |
Agent class template evolution
Agent class template 1.0 ( once more Component class )
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 )
class SimpleDUT_agent extends uvm_agent; `uvm_component_utils(SimpleDUT_agent)
//-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_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) endfunction: build_phase
//-------------------------------------------------------- //Connect Phase //-------------------------------------------------------- function void connect_phase(uvm_phase phase); super.connect_phase(phase); `uvm_info("AGENT_CLASS", "Connect Phase!", UVM_HIGH)
endfunction: connect_phase
//-------------------------------------------------------- //Run Phase //-------------------------------------------------------- task run_phase (uvm_phase phase); super.run_phase(phase); `uvm_info("AGENT_CLASS", "Run Phase!", UVM_HIGH)
// Logic
endtask: run_phase
endclass: SimpleDUT_agent |
Agent class template 2.0 ( 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 )
class SimpleDUT_agent extends uvm_agent; `uvm_component_utils(SimpleDUT_agent)
// according to our block diagram the agent will contain driver, monitor and sequencer so we need to write the code accordingly
SimpleDUT_driver drv; SimpleDUT_monitor mon; SimpleDUT_sequencer seqr; SimpleDUT_sequence_item item; //-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_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)
// we also need to create the driver, monitor and sequencer inside the build method drv = SimpleDUT_driver::type_id::create("drv", this); mon = SimpleDUT_monitor::type_id::create("mon", this); seqr = SimpleDUT_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)
// In the connect phase we need to connect the sequence item ports of driver and sequencer. This is required // to exchange the transactions. // These are the ports ( e.g. seq_item_port) that are by default defined inside the driver and // sequencer class so this is how we just connect them. 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) // What we want to do is: forever run this until the // simulation ends in other words forever keep monitoring these transactions. // forever begin item = SimpleDUT_sequence_item::type_id::create("item"); // Logic of monitoring DUT and storing results in "item" // to write "item" to scoreboard using port "monitor_port" // we also need to implement this "write" method inside the scoreboard mon.monitor_port.write(item); // end endtask: run_phase
endclass: SimpleDUT_agent |
Environment class template evolution
Environment class template 1.0
Let's create the environment class template which will be same as the test class template ( and in the same fashion all other necessary COMPONENT classes templates )
class SimpleDUT_env extends uvm_test; `uvm_component_utils(SimpleDUT_env)
//-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_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) endfunction: build_phase
//-------------------------------------------------------- //Connect Phase //-------------------------------------------------------- function void connect_phase(uvm_phase phase); super.connect_phase(phase); `uvm_info("ENV_CLASS", "Connect Phase!", UVM_HIGH)
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_env |
Environment class template 2.0 ( A Component class: file env.sv)
Let's create the environment class template which will be same as the test class template ( and in the same fashion all other necessary COMPONENT classes templates )
class SimpleDUT_env extends uvm_test; `uvm_component_utils(SimpleDUT_env) // according to our block diagram the environment class will contain agent, then agent will contain driver monitor and sequencer so we need to write the code accordingly
// Here in environment class, let's instantiate a SimpleDUT_agent object named env and initialize it using the create method, passing a unique name and the current environment class as the parent. This allows the environment class to interact with and control the instantiated during test execution.
// 1. change of environment class based on environment class template: so let's instantiate the // agent class inside the environment (SimpleDUT_agent agnt)
// This is just the instantiation // This line declares a variable named env of type alu_agent. This variable will be used to hold an instance of the SimpleDUT__agent class, which represents the agent in the environment.
SimpleDUT_agent agnt;
// 4. change of environment class based on environment class template: // In order to connect monitor and scoreboard we will always go to a higher component then these two. // Since agent and scoreboard lie on the same hierarchy while driver and monitor and sequencer lie inside the agent so now I need to go up a hierarchy above agent and scoreboard which is the environment. // In the environment we can connect those ports in the connect phase of environment: // To connect the scoreboard later we need to instantiate it here SimpleDUT_scoreboard scb;
//-------------------------------------------------------- //Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_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)
// 2. change of environment class based on environment class template: and we also need to create an agent inside the build method( agnt = SimpleDUT__agent::type_id::create("agnt", this);) // This line creates an instance of the agent_env class using the create method // this is a component class we need to pass two parameters: one is the "agnt" a name itself // so whatever we want the agnt to be called to be known as in the testbench and // you also need to pass "this" as a parameter this will specify who is the parent of agent // so it's just like SimpleDUT_env contains the agent class
// SimpleDUT_agent::type_id is used to obtain the type identifier of the SimpleDUT__env class. The type identifier is a unique identifier generated by the UVM framework for each class. It allows for dynamic object creation and type checking.
// The create method takes two arguments: // "agnt": This is the desired name for the created object. It is passed as a string.this: This is a reference to the current object or component. In this case, it refers to the test class where this code is executed. // The this pointer is used to specify the parent of the created object, which helps establish the hierarchical relationship between components in the UVM environment. agnt = SimpleDUT_agent::type_id::create("agnt", this); // 5. change of environment class based on environment class template: // same as 2. Change, do the same for scoreboard scb = SimpleDUT_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)
// 3. change of environment class based on environment class template: // Since we don't have the monitor handle over here in env.sv // right we need to access it through agent: agnt.mon.monitor_port.connect // and this is how we connect monitor port and scoreboard port // our monitor will monitor the items uh or sample the items and then send items to // scoreboard through the port and scoreboard will do something with it // To connect the scoreboard here, we need to instantiate it before 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_env |
UVM Test Component template evolution
UVM Test Component template 1.0
// let's create a new class in a file named test.sv // we can start with naming our class SimpleDUT_test. // SimpleDUT_test extends uvm_test class
//COMPONENT class class SimpleDUT_test extends uvm_test; // first thing we need to do in a class // is declare the necessary macros that will help it // register everything with the UVM classes // so we need to call this macro called `uvm_component_utils // and we need to pass the same name of the class that we are going to use // and that is SimpleDUT_test
`uvm_component_utils(SimpleDUT_test)
// second thing we need to do is write a Constructor // start with: function new(); // endfunction // inside brackets of new() two parameters: first string name is equal to the // name of the class( SimpleDUT_test) and then we also need to write // uvm_component parent
// Finally basic constructor for UVM classes is: // function new(string name = "SimpleDUT_test", uvm_component parent); // super.new(name, parent); // endfunction: new
// also keep some display statement so that it becomes easier to debug // we will use the default `uvm_info macro that UVM already has // it has like three parameters first is the tag of the comment that we want to give( we can just give a // TEST_CLASS and then we can also use a // just some comment that we want to pass when inside this Constructor // ( "Inside Constructor!") // and then verbosity level for example UVM_HIGH
//-------------------------------------------------------- // Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_test", uvm_component parent); super.new(name, parent); `uvm_info("TEST_CLASS", "Inside Constructor!", UVM_HIGH) endfunction: new
//and now we need to write the build phase so // we do that by using function void
//-------------------------------------------------------- // Build Phase //-------------------------------------------------------- function void build_phase(uvm_phase phase); super.build_phase(phase); `uvm_info("TEST_CLASS", "Build Phase!", UVM_HIGH) endfunction: build_phase
// Next is connect phase which is same similar to build phase // but we'll just change the syntax
//-------------------------------------------------------- //Connect Phase //-------------------------------------------------------- function void connect_phase(uvm_phase phase); super.connect_phase(phase); `uvm_info("TEST_CLASS", "Connect Phase!", UVM_HIGH)
endfunction: connect_phase
// we also need to write a run phase. There are a lot of phases in a UVM but // these three are the main phases with which // our SimpleDUT could be verified easily
// all the other phases are functions while only the run phase is a task because // run phase can consume time
//-------------------------------------------------------- //Run Phase //-------------------------------------------------------- task run_phase (uvm_phase phase); super.run_phase(phase); `uvm_info("TEST_CLASS", "Run Phase!", UVM_HIGH) endtask: run_phase
endclass: SimpleDUT_test |
UVM Test Component template 2.0
it will have two purposes:
class SimpleDUT_test extends uvm_test; `uvm_component_utils(SimpleDUT_test)
// according to our block diagram the test component // will contain environment class environment class will contain agent agent will contain driver Monitor and // sequencer so we need to write the code accordingly
// Here in test class let's instantiate an SimpleDUT_env object named env and initialize it using the create method, passing a unique name and the current test class as the parent. This allows the test class to interact with and control the instantiated environment during test execution.
// 1. change of test class based on test class template: so let's instantiate the // environment class inside the test SimpleDUT_env env)
// This is just the instantiation // This line declares a variable named env of type SimpleDUT__env. This variable will be used to hold an instance of the SimpleDUT__env class, which represents the test environment. SimpleDUT_env env;
// 4. change of test class based on test class template: in order to start our sequences in the run phase // we need the sequences // instances i so we have two sequences that we created one is the // base sequence and another one is the test sequence so let's just create handles // Instantiation two sequences SimpleDUT_base_sequence reset_seq; SimpleDUT_test_sequence test_seq;
//-------------------------------------------------------- // Constructor //-------------------------------------------------------- function new(string name = "SimpleDUT_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)
// 2. change of test class based on test class template: and we also need to create env inside the build method( env = SimpleDUT_env::type_id::create("env", this);) // This line creates an instance of the SimpleDUT_env class using the create method // this is a component class we need to pass two parameters: one is the "env" a name itself // so whatever we want the env to be called to be known as in the testbench and // you also need to pass "this" as a parameter this will specify who is the parent of environment // so it's just like SimpleDUT_test contains the environment class
// SimpleDUT_env::type_id is used to obtain the type identifier of the SimpleDUT_env class. The type identifier is a unique identifier generated by the UVM framework for each class. It allows for dynamic object creation and type checking.
// The create method takes two arguments: // "env": This is the desired name for the created object. It is passed as a string.this: This is a reference to the current object or component. In this case, it refers to the test class where this code is executed. // The this pointer is used to specify the parent of the created object, which helps establish the hierarchical relationship between components in the UVM environment.
env = SimpleDUT_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) // 3. change of test class based on test class template: run phase of test class // will start the sequences that it wants to run // so to do that one important thing test class we need to do in test class is // we need to say that hey I'm going to run my test // we use the phase.raise_objection // and we also need to after we are done // with the test we also need to phase.drop_objection so that the test can end phase.raise_objection(this); // 5. change of test class based on test class template: When starting my reset sequence on sequencer first we // specify a path of that sequencer is something like env agent sequencer // ( env.agnt.seqr) because our sequence is lying inside the agent which is lying inside the environment. // So we specify that path over here so what this will do is to start this reset sequence on this // sequencer which is lying inside this agent which is in turn lying inside this environment because there can be // multiple environments and multiple agents and also multiple sequencers so we are specifying which sequencer we want to run our // sequence on
//reset_seq // Constructing the reset sequence reset_seq = SimpleDUT_base_sequence::type_id::create("reset_seq"); // Starting the reset sequence on the sequencer on path env.agnt.seqr reset_seq.start(env.agnt.seqr); #10; // e.g. repeat test sequence 100 times // repeat(100) begin //test_seq // Constructing the test sequence test_seq = SimpleDUT_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); #10; // end phase.drop_objection(this);
endtask: run_phase
endclass: SimpleDUT_test |
testbench.sv (top testbench module )
`timescale 1ns/1ns
// This is where the basic UVM necessary classes are included that we need to get started with our top module. // The first thing, whenever you start with a UVM test bench, you need to define some UVM based packages so we need to import them here using this basic syntax import uvm_pkg::*;
// we also need to include some UVM macros `include "uvm_macros.svh"
//-------------------------------------------------------- //Include Files //-------------------------------------------------------- // The way we include the classes in the top testbench is that the order of the included classes is very important. // The first class that you are going to use should be the first one included and so on. // e.g. that is the reason why we start with sequence item class etc.etc.
`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 clock; SimpleDUT_interface intf(.clock(clock)); SimpleDUT dut( .clk(intf.clock), .reset(intf.reset), .din_i(intf.din_i), .dout_o(intf.dout_o) );
//-------------------------------------------------------- //Interface Setting //-------------------------------------------------------- initial begin // In SystemVerilog, the uvm_config_db is a utility provided by the Universal Verification Methodology (UVM) library. It allows you to set configuration values in a centralized database that can be accessed by different components of your testbench // uvm_config_db is the class that provides the configuration database functionality in UVM // In the given example, the uvm_config_db::set call sets the configuration value "vif" with the value intf (an instance of SimpleDUT_interface) in the global context for all instances in the hierarchy. This allows other components of the testbench to access and use this configuration value when needed. /* #(virtual SimpleDUT_interface): This is a type parameter passed to the uvm_config_db class, specifying the data type of the configuration value. In this case, it is a virtual interface type called SimpleDUT_interface. set: This is a static method of the uvm_config_db class used to set a configuration value in the database. null: This represents the context in which the configuration value is being set. In this case, it is set to null, indicating a global context. "*": This is the hierarchical path pattern specifying the scope at which the configuration value is applicable. In this case, "*" indicates that the configuration applies to all instances in the hierarchy. "vif": This is the name of the configuration field being set. It is a user-defined identifier that represents a specific configuration parameter or object in the database. intf: This is the value being set for the configuration field "vif". It represents an instance of the SimpleDUT_interface virtual interface. */
uvm_config_db #(virtual SimpleDUT_interface)::set(null, "*", "vif", intf ); //-- Refer: https://www.synopsys.com/content/dam/synopsys/services/whitepapers/hierarchical-testbench-configuration-using-uvm.pdf end
// The last thing to add in our top testbench is: start the test and that is done by calling run test task in. // The top module should take care of our test so when you reach this stage.
//Start The Test //-------------------------------------------------------- initial begin // This line executes a UVM built-in task run_test(). The purpose of this task is to start the specified test from the UVM test library. In this case, it starts the test named "SimpleDUT_test". run_test("SimpleDUT_test"); end //-------------------------------------------------------- //Clock Generation //-------------------------------------------------------- // Since the clock for DUT is generated from the top testbench module , let's generate the clock here initial begin clock = 0; #5; forever begin clock = ~clock; #2; end end //-------------------------------------------------------- //Maximum Simulation Time //-------------------------------------------------------- // let's also keep a safety check which terminates our simulation if a given number of clock Cycles X exceeds so we will keep it to maybe 5000ns initial begin #5000; $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 |
Compilation
[2023-07-21 08:52:49 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 -- Fri Jul 21 04:52:50 2023
Parsing design file 'design.sv'
Parsing design file 'testbench.sv'
Note-[SV-LCM-PPWI] Package previously wildcard imported
testbench.sv, 5
$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
recompiling module SimpleDUT_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 _416_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.754 seconds to compile + .447 seconds to elab + .568 seconds to link
Chronologic VCS simulator copyright 1991-2021
Contains Synopsys proprietary information.
Compiler version S-2021.09; Runtime version S-2021.09; Jul 21 04:53 2023
UVM_INFO /apps/vcsmx/vcs/S-2021.09//etc/uvm-1.2/src/base/uvm_root.svh(402) @ 0: reporter [UVM/RELNOTES]
----------------------------------------------------------------
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
----------------------------------------------------------------
*********** IMPORTANT RELEASE NOTES ************
You are using a version of the UVM library that has been compiled
with `UVM_NO_DEPRECATED undefined.
See http://www.eda.org/svdb/view.php?id=3313 for more details.
You are using a version of the UVM library that has been compiled
with `UVM_OBJECT_DO_NOT_NEED_CONSTRUCTOR undefined.
See http://www.eda.org/svdb/view.php?id=3770 for more details.
(Specify +UVM_NO_RELNOTES to turn off this notice)
UVM_INFO test.sv(31) @ 0: uvm_test_top [TEST_CLASS] Inside Constructor!
UVM_INFO @ 0: reporter [RNTST] Running test SimpleDUT_test...
UVM_INFO test.sv(39) @ 0: uvm_test_top [TEST_CLASS] Build Phase!
UVM_INFO env.sv(30) @ 0: uvm_test_top.env [ENV_CLASS] Inside Constructor!
UVM_INFO env.sv(39) @ 0: uvm_test_top.env [ENV_CLASS] Build Phase!
UVM_INFO agent.sv(17) @ 0: uvm_test_top.env.agnt [AGENT_CLASS] Inside Constructor!
UVM_INFO scoreboard.sv(31) @ 0: uvm_test_top.env.scb [SCB_CLASS] Inside Constructor!
UVM_INFO agent.sv(27) @ 0: uvm_test_top.env.agnt [AGENT_CLASS] Build Phase!
UVM_INFO driver.sv(29) @ 0: uvm_test_top.env.agnt.drv [DRIVER_CLASS] Inside Constructor!
UVM_INFO monitor.sv(35) @ 0: uvm_test_top.env.agnt.mon [MONITOR_CLASS] Inside Constructor!
UVM_INFO sequencer.sv(13) @ 0: uvm_test_top.env.agnt.seqr [SEQUENCER_CLASS] Inside Constructor!
UVM_INFO driver.sv(39) @ 0: uvm_test_top.env.agnt.drv [DRIVER_CLASS] Build Phase!
UVM_INFO monitor.sv(45) @ 0: uvm_test_top.env.agnt.mon [MONITOR_CLASS] Build Phase!
UVM_INFO sequencer.sv(23) @ 0: uvm_test_top.env.agnt.seqr [SEQUENCER_CLASS] Build Phase!
UVM_INFO scoreboard.sv(41) @ 0: uvm_test_top.env.scb [SCB_CLASS] Build Phase!
UVM_INFO driver.sv(58) @ 0: uvm_test_top.env.agnt.drv [DRIVER_CLASS] Connect Phase!
UVM_INFO monitor.sv(65) @ 0: uvm_test_top.env.agnt.mon [MONITOR_CLASS] Connect Phase!
UVM_INFO sequencer.sv(33) @ 0: uvm_test_top.env.agnt.seqr [SEQUENCER_CLASS] Connect Phase!
UVM_INFO agent.sv(42) @ 0: uvm_test_top.env.agnt [AGENT_CLASS] Connect Phase!
UVM_INFO scoreboard.sv(55) @ 0: uvm_test_top.env.scb [SCB_CLASS] Connect Phase!
UVM_INFO env.sv(66) @ 0: uvm_test_top.env [ENV_CLASS] Connect Phase!
UVM_INFO test.sv(64) @ 0: uvm_test_top [TEST_CLASS] Connect Phase!
UVM_INFO driver.sv(68) @ 0: uvm_test_top.env.agnt.drv [DRIVER_CLASS] Run Phase!
UVM_INFO monitor.sv(75) @ 0: uvm_test_top.env.agnt.mon [MONITOR_CLASS] Run Phase!
UVM_INFO agent.sv(59) @ 0: uvm_test_top.env.agnt [AGENT_CLASS] Run Phase!
UVM_INFO scoreboard.sv(91) @ 0: uvm_test_top.env.scb [SCB_CLASS] Run Phase!
UVM_INFO scoreboard.sv(136) @ 0: uvm_test_top.env.scb [COMPARE] Transaction Passed! ACT= x, EXP= x
UVM_INFO env.sv(86) @ 0: uvm_test_top.env [ENV_CLASS] Run Phase!
UVM_INFO test.sv(72) @ 0: uvm_test_top [TEST_CLASS] Run Phase!
UVM_INFO sequence.sv(17) @ 0: reporter@@reset_seq [BASE_SEQ] Inside Constructor!
UVM_INFO sequence.sv(27) @ 0: uvm_test_top.env.agnt.seqr@@reset_seq [BASE_SEQ] Inside body task!
UVM_INFO scoreboard.sv(136) @ 5: uvm_test_top.env.scb [COMPARE] Transaction Passed! ACT= x, EXP= x
UVM_INFO scoreboard.sv(136) @ 9: uvm_test_top.env.scb [COMPARE] Transaction Passed! ACT= x, EXP= x
UVM_INFO sequence.sv(17) @ 10: reporter@@test_seq [BASE_SEQ] Inside Constructor!
UVM_INFO sequence.sv(62) @ 10: reporter@@test_seq [TEST_SEQ] Inside Constructor!
UVM_INFO sequence.sv(70) @ 10: uvm_test_top.env.agnt.seqr@@test_seq [TEST_SEQ] Inside body task!
UVM_INFO scoreboard.sv(136) @ 13: uvm_test_top.env.scb [COMPARE] Transaction Passed! ACT= x, EXP= x
UVM_INFO scoreboard.sv(136) @ 17: uvm_test_top.env.scb [COMPARE] Transaction Passed! ACT= x, EXP= x
UVM_INFO /apps/vcsmx/vcs/S-2021.09//etc/uvm-1.2/src/base/uvm_objection.svh(1276) @ 20: 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) @ 20: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
** Report counts by severity
UVM_INFO : 40
UVM_WARNING : 0
UVM_ERROR : 0
UVM_FATAL : 0
** Report counts by id
[AGENT_CLASS] 4
[BASE_SEQ] 3
[COMPARE] 5
[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] 2
[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 20
V C S S i m u l a t i o n R e p o r t
Time: 20 ns
CPU Time: 0.640 seconds; Data structure size: 0.3Mb
Fri Jul 21 04:53:02 2023
© 2023 ASIC Stoic. All rights reserved.