UVM Testbench Architecture Example 1671715841
UVM Testbench Architecture Example 1671715841
UVM Testbench Architecture Example 1671715841
UVM TESTBECNH
EXAMPLE CODE
UVM TestBench architecture
TestBench Components/Objects
Sequence item
fields required to generate the stimulus are declared in the sequence_item.
sequence_item can be used as a placeholder for the activity monitored by the
monitor on DUT signals.
1/19
class mem_seq_item extends uvm_sequence_item;
//Utility macro
`uvm_object_utils(mem_seq_item)
//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction
endclass
//Utility macro
`uvm_object_utils(mem_seq_item)
//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction
endclass
//Utility macro
`uvm_object_utils(mem_seq_item)
//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction
endclass
4. In order to use the uvm_object methods ( copy, compare, pack, unpack, record, print,
and etc ),
all the fields are registered to uvm_field_* macros.
2/19
class mem_seq_item extends uvm_sequence_item;
//data and control fields
rand bit [3:0] addr;
rand bit wr_en;
rand bit rd_en;
rand bit [7:0] wdata;
bit [7:0] rdata;
//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction
endclass
5. Either write or read operation will be performed at once, so the constraint is added to
generate wr_en and rd_en.
//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction
endclass
3/19
class mem_seq_item extends uvm_sequence_item;
//data and control fields
rand bit [3:0] addr;
rand bit wr_en;
rand bit rd_en;
rand bit [7:0] wdata;
bit [7:0] rdata;
//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction
endclass
Sequence
Sequence generates the stimulus and sends to driver via
sequencer. An agent can have any number of sequences.
`uvm_sequence_utils(mem_sequence,mem_sequencer)
//Constructor
function new(string name = "mem_sequence");
super.new(name);
endfunction
endclass
2. Logic to generate and send the sequence_item is added inside the body() method
4/19
class mem_sequence extends uvm_sequence#(mem_seq_item);
`uvm_sequence_utils(mem_sequence,mem_sequencer)
//Constructor
function new(string name = "mem_sequence");
super.new(name);
endfunction
req = mem_seq_item::type_id::create("req");
wait_for_grant();
req.randomize();
send_request(req);
wait_for_item_done();
endtask
endclass
write sequence
`uvm_object_utils(mem_wr_seq)
//Constructor
function new(string name = "mem_wr_seq");
super.new(name);
endfunction
endclass
read sequence
`uvm_object_utils(mem_rd_seq)
//Constructor
function new(string name = "mem_rd_seq");
super.new(name);
endfunction
endclass
5/19
Sequencer
Sequencer is written by extending uvm_sequencer, there is no extra logic required to
be added in the sequencer.
`uvm_component_utils(mem_sequencer)
//constructor
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction
endclass
Driver
driver receives the stimulus from sequence via sequencer and drives on interface signals.
1. driver is written by extending the uvm_driver
class mem_driver extends uvm_driver #(mem_seq_item);
`uvm_component_utils(mem_driver)
// Constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
endclass : mem_driver
// Virtual Interface
virtual mem_if vif;
5. Add driving logic. get the seq_item and drive to DUT signals,
6/19
// run phase
virtual task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
......
.. driving logic ..
......
seq_item_port.item_done();
end
endtask : run_phase
7/19
class mem_driver extends uvm_driver #(mem_seq_item);
// Virtual Interface
virtual mem_if vif;
`uvm_component_utils(mem_driver)
// Constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
// run phase
virtual task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
//respond_to_transfer(req);
drive();
seq_item_port.item_done();
end
endtask : run_phase
// drive
virtual task drive();
req.print();
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;
@(posedge vif.DRIVER.clk);
`DRIV_IF.addr <= req.addr;
if(req.wr_en) begin
`DRIV_IF.wr_en <= req.wr_en;
`DRIV_IF.wdata <= req.wdata;
//$display("\tADDR = %0h \tWDATA = %0h",req.addr,trans.wdata);
@(posedge vif.DRIVER.clk);
end
if(req.rd_en) begin
`DRIV_IF.rd_en <= req.rd_en;
@(posedge vif.DRIVER.clk);
`DRIV_IF.rd_en <= 0;
@(posedge vif.DRIVER.clk);
req.rdata = `DRIV_IF.rdata;
// $display("\tADDR = %0h \tRDATA = %0h",trans.addr,`DRIV_IF.rdata);
end
$display("-----------------------------------------");
endtask : drive
endclass : mem_driver
8/19
Monitor
Monitor samples the DUT signals through the virtual interface and converts the
signal level activity to the transaction level.
`uvm_component_utils(mem_monitor)
// new - constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
endclass : mem_monitor
// Virtual Interface
virtual mem_if vif;
5. Declare seq_item handle, Used as a place holder for sampled signal activity,
mem_seq_item trans_collected;
9/19
// run phase
virtual task run_phase(uvm_phase phase);
forever begin
//sampling logic
@(posedge vif.MONITOR.clk);
wait(vif.monitor_cb.wr_en || vif.monitor_cb.rd_en);
trans_collected.addr = vif.monitor_cb.addr;
if(vif.monitor_cb.wr_en) begin
trans_collected.wr_en = vif.monitor_cb.wr_en;
trans_collected.wdata = vif.monitor_cb.wdata;
trans_collected.rd_en = 0;
@(posedge vif.MONITOR.clk);
end
if(vif.monitor_cb.rd_en) begin
trans_collected.rd_en = vif.monitor_cb.rd_en;
trans_collected.wr_en = 0;
@(posedge vif.MONITOR.clk);
@(posedge vif.MONITOR.clk);
trans_collected.rdata = vif.monitor_cb.rdata;
end
end
endtask : run_phase
7. After sampling, by using the write method send the sampled transaction packet to
the scoreboard,
item_collected_port.write(trans_collected);
10/19
class mem_monitor extends uvm_monitor;
// Virtual Interface
virtual mem_if vif;
`uvm_component_utils(mem_monitor)
// new - constructor
function new (string name, uvm_component parent);
super.new(name, parent);
trans_collected = new();
item_collected_port = new("item_collected_port", this);
endfunction : new
// run phase
virtual task run_phase(uvm_phase phase);
forever begin
@(posedge vif.MONITOR.clk);
wait(vif.monitor_cb.wr_en || vif.monitor_cb.rd_en);
trans_collected.addr = vif.monitor_cb.addr;
if(vif.monitor_cb.wr_en) begin
trans_collected.wr_en = vif.monitor_cb.wr_en;
trans_collected.wdata = vif.monitor_cb.wdata;
trans_collected.rd_en = 0;
@(posedge vif.MONITOR.clk);
end
if(vif.monitor_cb.rd_en) begin
trans_collected.rd_en = vif.monitor_cb.rd_en;
trans_collected.wr_en = 0;
@(posedge vif.MONITOR.clk);
@(posedge vif.MONITOR.clk);
trans_collected.rdata = vif.monitor_cb.rdata;
end
item_collected_port.write(trans_collected);
end
endtask : run_phase
endclass : mem_monitor
Agent
An agent is a container class contains a driver, a sequencer, and a monitor.
11/19
1. agent is written by extending the uvm_agent,
// constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
endclass : mem_agent
driver and sequencer will be created only for the active agent.
// build_phase
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// connect_phase
function void connect_phase(uvm_phase phase);
if(get_is_active() == UVM_ACTIVE) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
end
endfunction : connect_phase
12/19
class mem_agent extends uvm_agent;
//declaring agent components
mem_driver driver;
mem_sequencer sequencer;
mem_monitor monitor;
// constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
// build_phase
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// connect_phase
function void connect_phase(uvm_phase phase);
if(get_is_active() == UVM_ACTIVE) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
end
endfunction : connect_phase
endclass : mem_agent
Scoreboard
scoreboard receives the transaction from the monitor and compares it with the
reference values.
`uvm_component_utils(mem_scoreboard)
// new - constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
endclass : mem_scoreboard
2. Declare and Create TLM Analysis port, ( to receive transaction pkt from Monitor),
13/19
//Declaring port
uvm_analysis_imp#(mem_seq_item, mem_scoreboard) item_collected_export;
//creating port
item_collected_export = new("item_collected_export", this);
monitor.item_collected_port.connect(scoreboard.item_collected_export);
4. write method of the scoreboard will receive the transaction packet from the monitor,
on calling write method from the monitor
// run phase
virtual task run_phase(uvm_phase phase);
--- comparision logic ---
endtask : run_phase
14/19
class mem_scoreboard extends uvm_scoreboard;
`uvm_component_utils(mem_scoreboard)
uvm_analysis_imp#(mem_seq_item, mem_scoreboard) item_collected_export;
// new - constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
// write
virtual function void write(mem_seq_item pkt);
$display("SCB:: Pkt recived");
pkt.print();
endfunction : write
// run phase
virtual task run_phase(uvm_phase phase);
--- comparision logic ---
endtask : run_phase
endclass : mem_scoreboard
Environment/env
The environment is the container class, It contains one or more agents, as well as
other components such as the scoreboard, top-level monitor, and checker.
`uvm_component_utils(mem_model_env)
// new - constructor
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new
endclass : mem_model_env
mem_agent mem_agnt;
mem_scoreboard mem_scb;
15/19
3. Connecting monitor port to scoreboard port,
mem_agnt.monitor.item_collected_port.connect(mem_scb.item_collected_export);
//---------------------------------------
// agent and scoreboard instance
//---------------------------------------
mem_agent mem_agnt;
mem_scoreboard mem_scb;
`uvm_component_utils(mem_model_env)
//---------------------------------------
// constructor
//---------------------------------------
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new
//---------------------------------------
// build_phase - crate the components
//---------------------------------------
function void build_phase(uvm_phase phase);
super.build_phase(phase);
//---------------------------------------
// connect_phase - connecting monitor and scoreboard port
//---------------------------------------
function void connect_phase(uvm_phase phase);
mem_agnt.monitor.item_collected_port.connect(mem_scb.item_collected_export);
endfunction : connect_phase
endclass : mem_model_env
Test
The test defines the test scenario for the testbench.
16/19
class mem_model_test extends uvm_test;
`uvm_component_utils(mem_model_test)
endclass : mem_model_test
mem_model_env env;
mem_sequence seq;
env = mem_model_env::type_id::create("env",this);
seq = mem_sequence::type_id::create("seq");
4. Start sequence,
seq.start(env.mem_agnt.sequencer);
`uvm_component_utils(mem_model_test)
mem_model_env env;
mem_sequence seq;
endclass : mem_model_test
TestBench_Top
17/19
TestBench top is the module, it connects the DUT and Verification environment
components.
Testbench_top contains,
DUT instance
interface instance
run_test() method
virtual interface set config_db
clock and reset generation logic
wave dump logic
run test
18/19
module tbench_top;
//clock generation
always #5 clk = ~clk;
//reset Generation
initial begin
reset = 1;
#5 reset =0;
end
initial begin
run_test();
end
endmodule
Edit and Execute the Memory Model UVM TestBench code in EDA Playground.
https://www.edaplayground.com/x/5r89
19/19