1.定义transaction
`ifndef APB_RW_SV
`define APB_RW_SV
import uvm_pkg::*;
//apb_rw sequence item derived from base uvm_sequence_item
class apb_rw extends uvm_sequence_item;
//typedef for READ/Write transaction type
typedef enum {READ, WRITE} kind_e;
rand bit [31:0] addr; //Address
rand logic [31:0] data; //Data - For write or read response
rand kind_e apb_cmd; //command type
//Register with factory for dynamic creation
`uvm_object_utils(apb_rw)
function new (string name = "apb_rw");
super.new(name);
endfunction
function string convert2string();
return $psprintf("kind=%s addr=%0h data=%0h ",apb_cmd,addr,data);
endfunction
endclass: apb_rw
`endif
2.定义sequence
`ifndef APB_SEQUENCES_SV
`define APB_SEQUENCES_SV
//------------------------
//Base APB sequence derived from uvm_sequence and parameterized with sequence item of type apb_rw
//------------------------
class apb_base_seq extends uvm_sequence#(apb_rw);
`uvm_object_utils(apb_base_seq)
function new(string name ="");
super.new(name);
endfunction
//Main Body method that gets executed once sequence is started
task body();//在body中启动item
apb_rw rw_trans;
//Create 10 random APB read/write transaction and send to driver
repeat(10) begin
rw_trans = apb_rw::type_id::create(.name("rw_trans"),.contxt(get_full_name()));
start_item(rw_trans);
assert (rw_trans.randomize());
finish_item(rw_trans);
end
endtask
endclass
`endif
3.定义sequencer
class apb_sequencer extends uvm_sequencer #(apb_rw);
`uvm_component_utils(apb_sequencer)
function new(input string name, uvm_component parent=null);
super.new(name, parent);
endfunction : new
endclass : apb_sequencer
4.定义接口
`ifndef APB_IF_SV
`define APB_IF_SV
interface apb_if(input bit pclk);
wire [31:0] paddr;
wire psel;
wire penable;
wire pwrite;
wire [31:0] prdata;
wire [31:0] pwdata;
//Master Clocking block - used for Drivers
clocking master_cb @(posedge pclk);//master接口
output paddr, psel, penable, pwrite, pwdata;
input prdata;
endclocking: master_cb
//Slave Clocking Block - used for any Slave BFMs
clocking slave_cb @(posedge pclk);//slave接口
input paddr, psel, penable, pwrite, pwdata;
output prdata;
endclocking: slave_cb
//Monitor Clocking block - For sampling by monitor components
clocking monitor_cb @(posedge pclk);
input paddr, psel, penable, pwrite, prdata, pwdata;
endclocking: monitor_cb
modport master(clocking master_cb);
modport slave(clocking slave_cb);
modport passive(clocking monitor_cb);
endinterface: apb_if
`endif
5.定义driver
`ifndef APB_DRV_SEQ_MON_SV
`define APB_DRV_SEQ_MON_SV
typedef apb_config;
typedef apb_agent;
//---------------------------------------------
// APB master driver Class
//---------------------------------------------
class apb_master_drv extends uvm_driver#(apb_rw);
`uvm_component_utils(apb_master_drv)
virtual apb_if vif;
apb_config cfg;
function new(string name,uvm_component parent = null);
super.new(name,parent);
endfunction
//Build Phase
//Get the virtual interface handle form the agent (parent ) or from config_db
function void build_phase(uvm_phase phase);
apb_agent agent;
super.build_phase(phase);
if ($cast(agent, get_parent()) && agent != null) begin
vif = agent.vif;
end
else begin
if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif)) begin
`uvm_fatal("APB/DRV/NOVIF", "No virtual interface specified for this driver instance")
end
end
endfunction
//Run Phase
//Implement the Driver -Sequencer API to get an item
//Based on if it is Read/Write - drive on APB interface the corresponding pins
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
this.vif.master_cb.psel <= '0;
this.vif.master_cb.penable <= '0;
forever begin
apb_rw tr;
@ (this.vif.master_cb);
//First get an item from sequencer
seq_item_port.get_next_item(tr);//获取下一个item
@ (this.vif.master_cb);
uvm_report_info("APB_DRIVER ", $psprintf("Got Transaction %s",tr.convert2string()));
//Decode the APB Command and call either the read/write function
case (tr.apb_cmd)
apb_rw::READ: drive_read(tr.addr, tr.data);
apb_rw::WRITE: drive_write(tr.addr, tr.data);
endcase
//Handshake DONE back to sequencer
seq_item_port.item_done();
end
endtask: run_phase
virtual protected task drive_read(input bit [31:0] addr,
output logic [31:0] data);
this.vif.master_cb.paddr <= addr;
this.vif.master_cb.pwrite <= '0;
this.vif.master_cb.psel <= '1;
@ (this.vif.master_cb);
this.vif.master_cb.penable <= '1;
@ (this.vif.master_cb);
data = this.vif.master_cb.prdata;
this.vif.master_cb.psel <= '0;
this.vif.master_cb.penable <= '0;
endtask: drive_read
virtual protected task drive_write(input bit [31:0] addr,
input bit [31:0] data);
this.vif.master_cb.paddr <= addr;
this.vif.master_cb.pwdata <= data;
this.vif.master_cb.pwrite <= '1;
this.vif.master_cb.psel <= '1;
@ (this.vif.master_cb);
this.vif.master_cb.penable <= '1;
@ (this.vif.master_cb);
this.vif.master_cb.psel <= '0;
this.vif.master_cb.penable <= '0;
endtask: drive_write
endclass: apb_master_drv
6.定义monitor
class apb_monitor extends uvm_monitor;
virtual apb_if.passive vif;
//Analysis port -parameterized to apb_rw transaction
///Monitor writes transaction objects to this port once detected on interface
uvm_analysis_port#(apb_rw) ap;
//config class handle
apb_config cfg;
`uvm_component_utils(apb_monitor)
function new(string name, uvm_component parent = null);
super.new(name, parent);
ap = new("ap", this);
endfunction: new
//Build Phase - Get handle to virtual if from agent/config_db
virtual function void build_phase(uvm_phase phase);
apb_agent agent;
if ($cast(agent, get_parent()) && agent != null) begin
vif = agent.vif;
end
else begin
virtual apb_if tmp;
if (!uvm_config_db#(virtual apb_if)::get(this, "", "apb_if", tmp)) begin
`uvm_fatal("APB/MON/NOVIF", "No virtual interface specified for this monitor instance")
end
vif = tmp;
end
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
forever begin
apb_rw tr;
// Wait for a SETUP cycle
do begin
@ (this.vif.monitor_cb);
end
while (this.vif.monitor_cb.psel !== 1'b1 ||
this.vif.monitor_cb.penable !== 1'b0);
//create a transaction object
tr = apb_rw::type_id::create("tr", this);
//populate fields based on values seen on interface
tr.apb_cmd = (this.vif.monitor_cb.pwrite) ? apb_rw::WRITE : apb_rw::READ;
tr.addr = this.vif.monitor_cb.paddr;
@ (this.vif.monitor_cb);
if (this.vif.monitor_cb.penable !== 1'b1) begin
`uvm_error("APB", "APB protocol violation: SETUP cycle not followed by ENABLE cycle");
end
tr.data = (tr.apb_cmd == apb_rw::READ) ? this.vif.monitor_cb.prdata : '0;
tr.data = (tr.apb_cmd == apb_rw::WRITE) ? this.vif.monitor_cb.pwdata : '0;
uvm_report_info("APB_MONITOR", $psprintf("Got Transaction %s",tr.convert2string()));
//Write to analysis port
ap.write(tr);
end
endtask: run_phase
endclass: apb_monitor
`endif
7.定义agent
class apb_agent extends uvm_agent;
//Agent will have the sequencer, driver and monitor components for the APB interface
apb_sequencer sqr;
apb_master_drv drv;
apb_monitor mon;
virtual apb_if vif;
`uvm_component_utils_begin(apb_agent)
`uvm_field_object(sqr, UVM_ALL_ON)
`uvm_field_object(drv, UVM_ALL_ON)
`uvm_field_object(mon, UVM_ALL_ON)
`uvm_component_utils_end
function new(string name, uvm_component parent = null);
super.new(name, parent);
endfunction
//Build phase of agent - construct sequencer, driver and monitor
//get handle to virtual interface from env (parent) config_db
//and pass handle down to srq/driver/monitor
virtual function void build_phase(uvm_phase phase);
sqr = apb_sequencer::type_id::create("sqr", this);
drv = apb_master_drv::type_id::create("drv", this);
mon = apb_monitor::type_id::create("mon", this);
if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif)) begin
`uvm_fatal("APB/AGT/NOVIF", "No virtual interface specified for this agent instance")
end
uvm_config_db#(virtual apb_if)::set( this, "sqr", "vif", vif);
uvm_config_db#(virtual apb_if)::set( this, "drv", "vif", vif);
uvm_config_db#(virtual apb_if)::set( this, "mon", "vif", vif);
endfunction: build_phase
//Connect - driver and sequencer port to export
virtual function void connect_phase(uvm_phase phase);
drv.seq_item_port.connect(sqr.seq_item_export);
uvm_report_info("apb_agent::", "connect_phase, Connected driver to sequencer");
endfunction
endclass: apb_agent
8.定义env
class apb_env extends uvm_env;
`uvm_component_utils(apb_env);
//ENV class will have agent as its sub component
apb_agent agt;
//virtual interface for APB interface
virtual apb_if vif;
function new(string name, uvm_component parent = null);
super.new(name, parent);
endfunction
//Build phase - Construct agent and get virtual interface handle from test and pass it down to agent
function void build_phase(uvm_phase phase);
agt = apb_agent::type_id::create("agt", this);
if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif)) begin
`uvm_fatal("APB/AGT/NOVIF", "No virtual interface specified for this env instance")
end
uvm_config_db#(virtual apb_if)::set( this, "agt", "vif", vif);
endfunction: build_phase
endclass : apb_env
9.定义test
class apb_base_test extends uvm_test;
//Register with factory
`uvm_component_utils(apb_base_test);
apb_env env;
apb_config cfg;
virtual apb_if vif;
function new(string name = "apb_base_test", uvm_component parent = null);
super.new(name, parent);
endfunction
//Build phase - Construct the cfg and env class using factory
//Get the virtual interface handle from Test and then set it config db for the env component
function void build_phase(uvm_phase phase);
cfg = apb_config::type_id::create("cfg", this);
env = apb_env::type_id::create("env", this);
//
if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif)) begin
`uvm_fatal("APB/DRV/NOVIF", "No virtual interface specified for this test instance")
end
uvm_config_db#(virtual apb_if)::set( this, "env", "vif", vif);
endfunction
//Run phase - Create an abp_sequence and start it on the apb_sequencer
task run_phase( uvm_phase phase );
apb_base_seq apb_seq;
apb_seq = apb_base_seq::type_id::create("apb_seq");
phase.raise_objection( this, "Starting apb_base_seqin main phase" );
$display("%t Starting sequence apb_seq run_phase",$time);
apb_seq.start(env.agt.sqr);
#100ns;
phase.drop_objection( this , "Finished apb_seq in main phase" );
endtask: run_phase
endclass
`endif
10.顶层模块
import uvm_pkg::*;
`include "uvm_macros.svh"
//Include all files
`include "apb_if.svh"
`include "apb_rw.svh"
`include "apb_driver_seq_mon.svh"
`include "apb_agent_env_config.svh"
`include "apb_sequences.svh"
`include "apb_test.svh"
//--------------------------------------------------------
//Top level module that instantiates just a physical apb interface
//No real DUT or APB slave as of now
//--------------------------------------------------------
module test;
logic pclk;
logic [31:0] paddr;
logic psel;
logic penable;
logic pwrite;
logic [31:0] prdata;
logic [31:0] pwdata;
initial begin
pclk=0;
end
//Generate a clock
always begin
#10 pclk = ~pclk;
end
//Instantiate a physical interface for APB interface
apb_if apb_if(.pclk(pclk));
initial begin
//Pass this physical interface to test top (which will further pass it down to env->agent->drv/sqr/mon
uvm_config_db#(virtual apb_if)::set( null, "uvm_test_top", "vif", apb_if);
//Call the test - but passing run_test argument as test class name
//Another option is to not pass any test argument and use +UVM_TEST on command line to sepecify which test to run
run_test("apb_base_test");
end
endmodule