操作VCS:make elab 、 make run GUI=1 TEST=___________ &
questasim:pwd当前路径 、 cd到sim目录下、 do rkv_i2c_sim.do进行编译和仿真
APB master侧:
apb_transfer: extends uvm_sequence_item;
两个自定义枚举:表示读写 apb_trans_kind、表示OKERROR状态apb_trans_status;
定义随机32位addr、32位data、trans_kind、trans_status、软约束为1的idle_cycles;
注册 域的自动化;
`ifndef LVC_APB_TRANSFER_SV
`define LVC_APB_TRANSFER_SV
//------------------------------------------------------------------------------
//
// transfer enums, parameters, and events
//
//------------------------------------------------------------------------------
typedef enum {IDLE, WRITE, READ } lvc_apb_trans_kind;
typedef enum {OK, ERROR} lvc_apb_trans_status;
//------------------------------------------------------------------------------
//
// CLASS: lvc_apb_transfer
//
//------------------------------------------------------------------------------
class lvc_apb_transfer extends uvm_sequence_item;
// USER: Add transaction fields
rand bit [31:0] addr;
rand bit [31:0] data;
rand lvc_apb_trans_kind trans_kind;
rand lvc_apb_trans_status trans_status;
rand int idle_cycles;
constraint cstr{
soft idle_cycles == 1;
};
// USER: Add constraint blocks
`uvm_object_utils_begin(lvc_apb_transfer)
`uvm_field_enum (lvc_apb_trans_kind, trans_kind, UVM_ALL_ON)
// USER: Register fields here
`uvm_field_int (addr, UVM_ALL_ON)
`uvm_field_int (data, UVM_ALL_ON)
`uvm_field_int (idle_cycles, UVM_ALL_ON)
`uvm_object_utils_end
// new - constructor
function new (string name = "lvc_apb_transfer_inst");
super.new(name);
endfunction : new
endclass : lvc_apb_transfer
`endif // LVC_APB_TRANSFER_SV
apb_master_driver.svh头文件:参数化类extends uvm_driver #(lvc_apb_transfer);
例化config;注册;virtual vif;
声明函数new (string name, uvm_component parent); 任务run()、get_and_drive()、drive_transfer(lvc_apb_transfer t)、reset_listener()、do_idle()、do_write(lvc_apb_transfer t)、do_read(lvc_apb_transfer t);
`ifndef LVC_APB_MASTER_DRIVER_SVH
`define LVC_APB_MASTER_DRIVER_SVH
class lvc_apb_master_driver extends uvm_driver #(lvc_apb_transfer);
//
//
// Public interface (Component users may manipulate these fields/methods)
//
//
lvc_apb_config cfg;
// USER: Add your fields here
// This macro performs UVM object creation, type control manipulation, and
// factory registration
`uvm_component_utils_begin(lvc_apb_master_driver)
// USER: Register fields here
`uvm_component_utils_end
// new - constructor
extern function new (string name, uvm_component parent);
// uvm run phase
extern virtual task run();
//
//
// Implementation (private) interface
//
//
// The virtual interface used to drive and view HDL signals.
virtual lvc_apb_if vif;
// This is the method that is responsible for getting sequence transactions
// and driving the transaction into the DUT
extern virtual protected task get_and_drive();
// This method drives a sequence trasnaction onto the interface
extern virtual protected task drive_transfer(lvc_apb_transfer t);
// This method reset interface signals
extern virtual protected task reset_listener();
// This method that is responsible for sending an idle cycle to the DUT
extern protected task do_idle();
// This method that is to trigger write transaction
extern protected task do_write(lvc_apb_transfer t);
// This method that is to trigger read transaction
extern protected task do_read(lvc_apb_transfer t);
endclass : lvc_apb_master_driver
`endif // LVC_APB_MASTER_DRIVER_SVH
apb_master_driver.sv:
实现头文件中各函数任务;run()→get_and_drive()→get_next_item、drive_transfer(req)中判断trans_kind读写(如下),克隆、id、itemdone→reset_listener()将paddr pwrite psel penable pwdata置0;
写do_write(t):第一拍给paddr、pwrite=1、psel=1、penable=0,t.data给pwdata;第二拍penable=1传输数据;10ps后等待接口上pready等于1后,1ps判断接口上pslverr如果为1,状态转为ERROR,不为1转为OK;
读do_read(t):第一拍给paddr、pwrite=0、psel=1、penable=0;第二拍penable=1传输数据;10ps后等待接口上pready等于1后,1ps判断接口上pslverr如果为1,状态转为ERROR,不为1转为OK;并将vif上prdata给t.data;
`ifndef LVC_APB_MASTER_DRIVER_SV
`define LVC_APB_MASTER_DRIVER_SV
function lvc_apb_master_driver::new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
task lvc_apb_master_driver::run();
fork
get_and_drive();
reset_listener();
join_none
endtask : run
task lvc_apb_master_driver::get_and_drive();
forever begin
seq_item_port.get_next_item(req);
`uvm_info(get_type_name(), "sequencer got next item", UVM_HIGH)
drive_transfer(req);
void'($cast(rsp, req.clone()));
rsp.set_sequence_id(req.get_sequence_id());
rsp.set_transaction_id(req.get_transaction_id());
seq_item_port.item_done(rsp);
`uvm_info(get_type_name(), "sequencer item_done_triggered", UVM_HIGH)
end
endtask : get_and_drive
task lvc_apb_master_driver::drive_transfer (lvc_apb_transfer t);
`uvm_info(get_type_name(), "drive_transfer", UVM_HIGH)
case(t.trans_kind)
IDLE : this.do_idle();
WRITE : this.do_write(t);
READ : this.do_read(t);
default : `uvm_error("ERRTYPE", "unrecognized transaction type")
endcase
endtask : drive_transfer
task lvc_apb_master_driver::do_write(lvc_apb_transfer t);
`uvm_info(get_type_name(), "do_write ...", UVM_HIGH)
@(vif.cb_mst);
vif.cb_mst.paddr <= t.addr;
vif.cb_mst.pwrite <= 1;
vif.cb_mst.psel <= 1;
vif.cb_mst.penable <= 0;
vif.cb_mst.pwdata <= t.data;
@(vif.cb_mst);
vif.cb_mst.penable <= 1;
#10ps;
wait(vif.pready === 1);
#1ps;
if(vif.pslverr === 1) begin
t.trans_status = ERROR;
if(cfg.master_pslverr_status_severity == UVM_ERROR)
`uvm_error(get_type_name(), "PSLVERR asserted!")
else
`uvm_warning(get_type_name(), "PSLVERR asserted!")
end
else begin
t.trans_status = OK;
end
repeat(t.idle_cycles) this.do_idle();
endtask: do_write
task lvc_apb_master_driver::do_read(lvc_apb_transfer t);
`uvm_info(get_type_name(), "do_write ...", UVM_HIGH)
@(vif.cb_mst);
vif.cb_mst.paddr <= t.addr;
vif.cb_mst.pwrite <= 0;
vif.cb_mst.psel <= 1;
vif.cb_mst.penable <= 0;
@(vif.cb_mst);
vif.cb_mst.penable <= 1;
#10ps;
wait(vif.pready === 1);
#1ps;
if(vif.pslverr === 1) begin
t.trans_status = ERROR;
if(cfg.master_pslverr_status_severity == UVM_ERROR)
`uvm_error(get_type_name(), "PSLVERR asserted!")
else
`uvm_warning(get_type_name(), "PSLVERR asserted!")
end
else begin
t.trans_status = OK;
end
t.data = vif.prdata;
repeat(t.idle_cycles) this.do_idle();
endtask: do_read
task lvc_apb_master_driver::do_idle();
`uvm_info(get_type_name(), "do_idle ...", UVM_HIGH)
@(vif.cb_mst);
vif.cb_mst.psel <= 0;
vif.cb_mst.penable <= 0;
vif.cb_mst.pwdata <= 0;
endtask:do_idle
task lvc_apb_master_driver::reset_listener();
`uvm_info(get_type_name(), "reset_listener ...", UVM_HIGH)
fork
forever begin
@(negedge vif.rstn); // ASYNC reset
vif.paddr <= 0;
vif.pwrite <= 0;
vif.psel <= 0;
vif.penable <= 0;
vif.pwdata <= 0;
end
join_none
endtask
`endif // LVC_APB_MASTER_DRIVER_SV
apb_master_monitor.svh头文件:extends uvm_monitor;
例化config;virtual vif;item_collected_port;定义两个变量checks_enable(控制是否检查)、coverage_enable(控制是否更新覆盖率)默认打开;注册,两个变量域的自动化;
`ifndef LVC_APB_MASTER_MONITOR_SVH
`define LVC_APB_MASTER_MONITOR_SVH
class lvc_apb_master_monitor extends uvm_monitor;
//
//
// Public interface (Component users may manipulate these fields/methods)
//
//
lvc_apb_config cfg;
// This field controls if this monitor has its checkers enabled
// (by default checkers are on)
bit checks_enable = 1;
// This field controls if this monitor has its coverage enabled
// (by default coverage is on)
bit coverage_enable = 1;
// This property is the virtual interface needed for this component to drive
// and view HDL signals
virtual lvc_apb_if vif;
// USER: Add your fields here
// The following is the analysis port that allows this monitor's transaction
// information to be sent to other verification componets such as
// scoreboards
uvm_analysis_port #(lvc_apb_transfer) item_collected_port;
// This macro performs UVM object creation, type control manipulation, and
// factory registration
`uvm_component_utils_begin(lvc_apb_master_monitor)
`uvm_field_int(checks_enable, UVM_ALL_ON)
`uvm_field_int(coverage_enable, UVM_ALL_ON)
// USER: Register fields here
`uvm_component_utils_end
// new - constructor
extern function new(string name, uvm_component parent=null);
// uvm run phase
extern virtual task run();
// Events needed to trigger covergroups
event lvc_apb_master_cov_transaction;
// Transfer collected covergroup
covergroup lvc_apb_master_cov_trans @lvc_apb_master_cov_transaction;
// USER implemented coverpoints
endgroup : lvc_apb_master_cov_trans
//
//
// Implementation (private) interface
//
//
//This is the transaction being collected by this monitor
protected lvc_apb_transfer trans_collected;
// This method is responsible for collecting transactions, checking,
// and updating coverage
extern virtual protected task monitor_transactions();
// This is the methods that collects transactions
extern virtual protected task collect_transfer();
// This is the method that performs checks on a transaction
extern protected function void perform_transfer_checks();
// This is the method that updates coverage based on a transaction
extern protected function void perform_transfer_coverage();
endclass : lvc_apb_master_monitor
`endif // LVC_APB_MASTER_MONITOR_SVH
apb_master_monitor.sv:
实现头文件中各函数任务:new item_collected_port;run()→monitor_transactions()→collect_transfer()中在cb_mon时钟上升沿等psel=1penable=0时,工厂创建一个trans_collected后,判断读写(如下)→perform_transfer_checks(目前没用)→perform_transfer_coverage(目前没用);
写操作:等一上升沿并且接口上pready=1(说明写操作成功传输、mon需要监测);接口上传输paddr、pwdata、WRITE、OKERROR状态到trans_collected;
读操作:等一上升沿并且接口上pready=1(说明读操作成功传输、mon需要监测);接口上传输paddr、prdata、READ、OKERROR状态到trans_collected;
`ifndef LVC_APB_MASTER_MONITOR_SV
`define LVC_APB_MASTER_MONITOR_SV
function lvc_apb_master_monitor::new(string name, uvm_component parent=null);
super.new(name, parent);
item_collected_port = new("item_collected_port",this);
endfunction:new
task lvc_apb_master_monitor::monitor_transactions();
forever begin
// Extract data from interface into transaction
collect_transfer();
// Check transaction
if (checks_enable)
perform_transfer_checks();
// Update coverage
if (coverage_enable)
perform_transfer_coverage();
// Publish to subscribers
item_collected_port.write(trans_collected);
end
endtask // monitor_transactions
task lvc_apb_master_monitor::run();
fork
monitor_transactions();
join_none
endtask // run
task lvc_apb_master_monitor::collect_transfer();
// Advance clock
@(vif.cb_mon iff (vif.cb_mon.psel === 1'b1 && vif.cb_mon.penable === 1'b0));
trans_collected = lvc_apb_transfer::type_id::create("trans_collected");
case(vif.cb_mon.pwrite)
1'b1 : begin
@(vif.cb_mon iff vif.cb_mon.pready === 1'b1);
trans_collected.addr = vif.cb_mon.paddr;
trans_collected.data = vif.cb_mon.pwdata;
trans_collected.trans_kind = WRITE;
trans_collected.trans_status = vif.cb_mon.pslverr === 1'b0 ? OK : ERROR;
end
1'b0 : begin
@(vif.cb_mon iff vif.cb_mon.pready === 1'b1);
trans_collected.addr = vif.cb_mon.paddr;
trans_collected.data = vif.cb_mon.prdata;
trans_collected.trans_kind = READ;
trans_collected.trans_status = vif.cb_mon.pslverr === 1'b0 ? OK : ERROR;
end
default : `uvm_error(get_type_name(), "ERROR pwrite signal value")
endcase
endtask: collect_transfer
// perform_transfer_checks
function void lvc_apb_master_monitor::perform_transfer_checks();
// USER: do some checks on the transfer here
endfunction : perform_transfer_checks
// perform_transfer_coverage
function void lvc_apb_master_monitor::perform_transfer_coverage();
// USER: coverage implementation
-> lvc_apb_master_cov_transaction;
endfunction : perform_transfer_coverage
`endif // LVC_APB_MASTER_MONITOR_SV
apb_master_sequencer.svh头文件:参数类 extends uvm_sequencer #(lvc_apb_transfer);
例化config;注册;virtual vif; 声明new();
`ifndef LVC_APB_MASTER_SEQUENCER_SVH
`define LVC_APB_MASTER_SEQUENCER_SVH
class lvc_apb_master_sequencer extends uvm_sequencer #(lvc_apb_transfer);
//
//
// Public interface (Component users may manipulate these fields/methods)
//
//
lvc_apb_config cfg;
// Provide implementations of virtual methods such as get_type_name and create
`uvm_component_utils_begin(lvc_apb_master_sequencer)
// USER: Register fields
`uvm_component_utils_end
// new - constructor
extern function new (string name, uvm_component parent);
//
//
// Implementation (private) interface
//
//
// The virtual interface used to drive and view HDL signals.
virtual lvc_apb_if vif;
endclass : lvc_apb_master_sequencer
`endif // LVC_APB_MASTER_SEQUENCER_SVH
apb_master_sequencer.sv: 实现new
`ifndef LVC_APB_MASTER_SEQUENCER_SV
`define LVC_APB_MASTER_SEQUENCER_SV
function lvc_apb_master_sequencer::new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
`endif // LVC_APB_MASTER_SEQUENCER_SV
apb_master_agent.svh头文件:extends uvm_agent;
例化config;例化drv、sqr、mon;注册;virtual vif;
声明函数new()、build()、connect()、assign_vi(分配virtual vif);
`ifndef LVC_APB_MASTER_AGENT_SVH
`define LVC_APB_MASTER_AGENT_SVH
class lvc_apb_master_agent extends uvm_agent;
//
//
// Public interface (Component users may manipulate these fields/methods)
//
//
lvc_apb_config cfg;
// The following are the verification components that make up
// this agent
lvc_apb_master_driver driver;
lvc_apb_master_sequencer sequencer;
lvc_apb_master_monitor monitor;
virtual lvc_apb_if vif;
// USER: Add your fields here
// This macro performs UVM object creation, type control manipulation, and
// factory registration
`uvm_component_utils_begin(lvc_apb_master_agent)
// USER: Register your fields here
`uvm_component_utils_end
// new - constructor
extern function new (string name, uvm_component parent);
// uvm build phase
extern function void build();
// uvm connection phase
extern function void connect();
// This method assigns the virtual interfaces to the agent's children
extern function void assign_vi(virtual lvc_apb_if vif);
//
//
// Implementation (private) interface
//
//
endclass : lvc_apb_master_agent
`endif // LVC_APB_MASTER_AGENT_SVH
apb_master_agent.sv:new();
build()中get config、virtual vif 并 工厂创建mon、cfg传递给mon;判断cfg中is_active是否为1,创建seq、drv并传递cfg;
connect()中调用assign_vi函数传递vif;判断is_active为1,将drv port和seq export连接;
assign_vi函数,vif连接mon的vif;判断is_active为1,vif连接seq和drv的vif;
`ifndef LVC_APB_MASTER_AGENT_SV
`define LVC_APB_MASTER_AGENT_SV
function lvc_apb_master_agent::new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new
function void lvc_apb_master_agent::build();
super.build();
// get config
if( !uvm_config_db#(lvc_apb_config)::get(this,"","cfg", cfg)) begin
`uvm_warning("GETCFG","cannot get config object from config DB")
cfg = lvc_apb_config::type_id::create("cfg");
end
// get virtual interface
if( !uvm_config_db#(virtual lvc_apb_if)::get(this,"","vif", vif)) begin
`uvm_fatal("GETVIF","cannot get vif handle from config DB")
end
monitor = lvc_apb_master_monitor::type_id::create("monitor",this);
monitor.cfg = cfg;
if(cfg.is_active == UVM_ACTIVE) begin
sequencer = lvc_apb_master_sequencer::type_id::create("sequencer",this);
sequencer.cfg = cfg;
driver = lvc_apb_master_driver::type_id::create("driver",this);
driver.cfg = cfg;
end
endfunction : build
function void lvc_apb_master_agent::connect();
assign_vi(vif);
if(is_active == UVM_ACTIVE) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
end
endfunction : connect
function void lvc_apb_master_agent::assign_vi(virtual lvc_apb_if vif);
monitor.vif = vif;
if (is_active == UVM_ACTIVE) begin
sequencer.vif = vif;
driver.vif = vif;
end
endfunction : assign_vi
`endif // LVC_APB_MASTER_AGENT_SV