目录
1.1实现apb_master_driver的drive_transfer()
1.3编写apb_master_monitor中的collect_transfer()
1.2编写apb_master_sequence中的apb_master_write_seq::body()任务
1.4编写apb_slave_driver的drive_response()
这里涉及的是VIP的开发和MCDF用APB改造。
MCDF改造
所有的接口都发生了改变,对应在硬件中都做了修改。
APB接口
特点是一总(AHB或者AXI)多从,1总通过APB桥发放到后面的多从slave;2个clk完成1次W/R操作,用状态机表示为idle->setup->enable
验证环境的改造
由于接口几乎全部被更新,所以mcdf这一层的接口和tb的接口也需要做相应的更改。
这里似乎没有涉及到env的问题,等下看看具体代码。
实战任务
直接看到APB部分的文件夹,可以看到文件数量大大增加,采取了`include的方式将文件分开了。
apb_pkg只有简单的几行代码,信息都包含在别的文件中。
`ifndef APB_PKG_SV
`define APB_PKG_SV
package apb_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "apb.svh"
endpackage : apb_pkg
`endif // `ifndef APB_PKG_SV
apb_pkg include了apb.svh,而后者include了主从的各个组件的sv和svh文件。
`ifndef APB_SVH
`define APB_SVH
`include "apb_transfer.sv"
`include "apb_config.sv"
`include "apb_master_driver.svh"
`include "apb_master_monitor.svh"
`include "apb_master_sequencer.svh"
`include "apb_master_agent.svh"
`include "apb_slave_driver.svh"
`include "apb_slave_monitor.svh"
`include "apb_slave_sequencer.svh"
`include "apb_slave_agent.svh"
`include "apb_master_driver.sv"
`include "apb_master_monitor.sv"
`include "apb_master_sequencer.sv"
`include "apb_master_agent.sv"
`include "apb_master_seq_lib.sv"
`include "apb_slave_driver.sv"
`include "apb_slave_monitor.sv"
`include "apb_slave_sequencer.sv"
`include "apb_slave_agent.sv"
`include "apb_slave_seq_lib.sv"
`endif // `ifndef APB_SVH
在apb_tb中,include了test.svh和if.sv,要跑起仿真只需要编译pkg和tb即可(其他文件都被include了,其实也会被编译到)。tb只做了接口的配置
`timescale 1ps/1ps
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "apb_tests.svh"
`include "apb_if.sv"
module apb_tb;
bit clk, rstn;
initial begin
fork
begin
forever #5ns clk = !clk;
end
begin
#100ns;
rstn <= 1'b1;
#100ns;
rstn <= 1'b0;
#100ns;
rstn <= 1'b1;
end
join_none
end
apb_if intf(clk, rstn);
initial begin
uvm_config_db#(virtual apb_if)::set(uvm_root::get(), "uvm_test_top.env.mst", "vif", intf);
uvm_config_db#(virtual apb_if)::set(uvm_root::get(), "uvm_test_top.env.slv", "vif", intf);
run_test("apb_single_transaction_test");
end
endmodule
在apb_if中,包括了接口+时钟块,覆盖组covergroup。接口信号按照时序图给出,时钟块分为master和slave,信号的方向互补,monitor的时钟块所有信号都是input。
这里又遇到覆盖率收集的问题,复习一下。定义覆盖组后再initial块中例化各个覆盖组。
https://mp.csdn.net/mp_blog/creation/editor/125731520
`ifndef APB_IF_SV
`define APB_IF_SV
interface apb_if (input clk, input rstn);
logic [31:0] paddr;
logic pwrite;
logic psel;
logic penable;
logic [31:0] pwdata;
logic [31:0] prdata;
// Control flags
bit has_checks = 1;
bit has_coverage = 1;
// Actual Signals
// USER: Add interface signals
clocking cb_mst @(posedge clk);
// USER: Add clocking block detail
default input #1ps output #1ps;
output paddr, pwrite, psel, penable, pwdata;
input prdata;
endclocking : cb_mst
clocking cb_slv @(posedge clk);
// USER: Add clocking block detail
default input #1ps output #1ps;
input paddr, pwrite, psel, penable, pwdata;
output prdata;
endclocking : cb_slv
clocking cb_mon @(posedge clk);
// USER: Add clocking block detail
default input #1ps output #1ps;
input paddr, pwrite, psel, penable, pwdata, prdata;
endclocking : cb_mon
// Coverage and assertions to be implemented here.
// USER: Add assertions/coverage here
// APB command covergroup
covergroup cg_apb_command @(posedge clk iff rstn);
pwrite: coverpoint pwrite{
type_option.weight = 0;
bins write = {1};
bins read = {0};
}
psel : coverpoint psel{
type_option.weight = 0;
bins sel = {1};
bins unsel = {0};
}
cmd : cross pwrite, psel{
bins cmd_write = binsof(psel.sel) && binsof(pwrite.write);
bins cmd_read = binsof(psel.sel) && binsof(pwrite.read);
bins cmd_idle = binsof(psel.unsel);
}
endgroup
// APB transaction timing group
covergroup cg_apb_trans_timing_group @(posedge clk iff rstn);
psel: coverpoint psel{
bins single = (0 => 1 => 1 => 0);
bins burst_2 = (0 => 1 [*4] => 0);
bins burst_4 = (0 => 1 [*8] => 0);
bins burst_8 = (0 => 1 [*16] => 0);
bins burst_16 = (0 => 1 [*32] => 0);
bins burst_32 = (0 => 1 [*64] => 0);
}
penable: coverpoint penable {
bins single = (0 => 1 => 0 [*2:10] => 1);
bins burst = (0 => 1 => 0 => 1);
}
endgroup
// APB write & read order group
covergroup cg_apb_write_read_order_group @(posedge clk iff (rstn && penable));
write_read_order: coverpoint pwrite{
bins write_write = (1 => 1);
bins write_read = (1 => 0);
bins read_write = (0 => 1);
bins read_read = (0 => 0);
}
endgroup
initial begin
automatic cg_apb_command cg0 = new();
automatic cg_apb_trans_timing_group cg1 = new();
automatic cg_apb_write_read_order_group cg2 = new();
end
endinterface : apb_if
`endif // APB_IF_SV
关于test文件,画了个框图如下:
接下来再逐个看各个组件
apb_config
这是等下用来配置agent是active还是passive用的object
class apb_config extends uvm_object;
`uvm_object_utils(apb_config)
// USER to specify the config items
uvm_active_passive_enum is_active = UVM_ACTIVE;
function new (string name = "apb_config");
super.new(name);
endfunction : new
endclass
`endif // APB_CONFIG_SV
apb_master_agent
由于使用了头文件,这里的方法名都要加上apb_master_agent::
agent做了三件事:build()例化对象,connect()连接driver和sequencer(TLM通信,两个组件自带的seq_item_PORT)
assign_vi配置接口
//头文件
`ifndef APB_MASTER_AGENT_SVH
`define APB_MASTER_AGENT_SVH
class apb_master_agent extends uvm_agent;
apb_config cfg;
apb_master_driver driver;
apb_master_sequencer sequencer;
apb_master_monitor monitor;
virtual apb_if vif;
`uvm_component_utils_begin(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 apb_if vif);
endclass : apb_master_agent
`endif // APB_MASTER_AGENT_SVH
//sv文件
`ifndef APB_MASTER_AGENT_SV
`define APB_MASTER_AGENT_SV
//由于使用了头文件,这里的方法名都要加上apb_master_agent::
function apb_master_agent::new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new
function void apb_master_agent::build();
super.build();
// get config
if( !uvm_config_db#(apb_config)::get(this,"","cfg", cfg)) begin
`uvm_warning("GETCFG","cannot get config object from config DB")
cfg = apb_config::type_id::create("cfg");
end
// get virtual interface
if( !uvm_config_db#(virtual apb_if)::get(this,"","vif", vif)) begin
`uvm_fatal("GETVIF","cannot get vif handle from config DB")
end
monitor = apb_master_monitor::type_id::create("monitor",this);
monitor.cfg = cfg;
if(cfg.is_active == UVM_ACTIVE) begin
sequencer = apb_master_sequencer::type_id::create("sequencer",this);
sequencer.cfg = cfg;
driver = apb_master_driver::type_id::create("driver",this);
driver.cfg = cfg;
end
endfunction : build
function void 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 apb_master_agent::assign_vi(virtual apb_if vif);
monitor.vif = vif;
if (is_active == UVM_ACTIVE) begin
sequencer.vif = vif;
driver.vif = vif;
end
endfunction : assign_vi
`endif // APB_MASTER_AGENT_SV
apb_master_driver
`ifndef APB_MASTER_DRIVER_SVH
`define APB_MASTER_DRIVER_SVH
class apb_master_driver extends uvm_driver #(apb_transfer);
apb_config cfg;
`uvm_component_utils_begin(apb_master_driver)
`uvm_component_utils_end
extern function new (string name, uvm_component parent);
extern virtual task run();
virtual apb_if vif;
extern virtual protected task get_and_drive();
extern virtual protected task drive_transfer(apb_transfer t);
extern virtual protected task reset_listener();
extern protected task do_idle();
extern protected task do_write(apb_transfer t);
extern protected task do_read(apb_transfer t);
endclass : apb_master_driver
`endif // APB_MASTER_DRIVER_SVH
driver的run_phase同时运行两个线程:get and drive和复位。
而1.1就是要编写drive这个方法,对应APB的idle和读写,分别对应三个方法。在三个方法中根据时序图编写,addr和write在idle时保持不变(为了省电),时序图和代码如下。
`ifndef APB_MASTER_DRIVER_SV
`define APB_MASTER_DRIVER_SV
function apb_master_driver::new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
task apb_master_driver::run();
fork
get_and_drive();
reset_listener();
join_none
endtask : run
task 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());
seq_item_port.item_done(rsp);
`uvm_info(get_type_name(), "sequencer item_done_triggered", UVM_HIGH)
end
endtask : get_and_drive
task apb_master_driver::drive_transfer (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 apb_master_driver::do_write(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;
repeat(t.idle_cycles) this.do_idle();
endtask: do_write
task apb_master_driver::do_read(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;
#100ps;//为了采样,人为添加一个延迟,避免detal cycle
t.data = vif.prdata;
repeat(t.idle_cycles) this.do_idle();
endtask: do_read
task apb_master_driver::do_idle();
`uvm_info(get_type_name(), "do_idle ...", UVM_HIGH)
@(vif.cb_mst);
//vif.cb_mst.paddr <= 0;
//vif.cb_mst.pwrite <= 0;
vif.cb_mst.psel <= 0;
vif.cb_mst.penable <= 0;
vif.cb_mst.pwdata <= 0;
endtask:do_idle
task 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 // APB_MASTER_DRIVER_SV
1.1实现apb_master_driver的drive_transfer()
task apb_master_driver::drive_transfer (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
//其中apb_transfer如下,就是个item
`ifndef APB_TRANSFER_SV
`define APB_TRANSFER_SV
typedef enum {IDLE, WRITE, READ } apb_trans_kind;
class apb_transfer extends uvm_sequence_item;
rand bit [31:0] addr;
rand bit [31:0] data;
rand apb_trans_kind trans_kind;
rand int idle_cycles;
constraint cstr{
soft idle_cycles == 1; };
`uvm_object_utils_begin(apb_transfer)
`uvm_field_enum (apb_trans_kind, trans_kind, UVM_ALL_ON)
`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
function new (string name = "apb_transfer_inst");
super.new(name);
endfunction : new
endclass : apb_transfer
`endif // APB_TRANSFER_SV
再来看monitor
apb_master_monitor
TLM通信端口是uvm_analysis_port
`ifndef APB_MASTER_MONITOR_SVH
`define APB_MASTER_MONITOR_SVH
class apb_master_monitor extends uvm_monitor;
apb_config cfg;
bit checks_enable = 1;
bit coverage_enable = 1;
virtual apb_if vif;
uvm_analysis_port #(apb_transfer) item_collected_port;
`uvm_component_utils_begin(apb_master_monitor)
`uvm_field_int(checks_enable, UVM_ALL_ON)
`uvm_field_int(coverage_enable, UVM_ALL_ON)
`uvm_component_utils_end
extern function new(string name, uvm_component parent=null);
extern function void build();
extern virtual task run();
event apb_master_cov_transaction;
covergroup apb_master_cov_trans @apb_master_cov_transaction;
endgroup : apb_master_cov_trans
protected apb_transfer trans_collected;
extern virtual protected task monitor_transactions();
extern virtual protected task collect_transfer();
extern protected function void perform_transfer_checks();
extern protected function void perform_transfer_coverage();
endclass : apb_master_monitor
`endif // APB_MASTER_MONITOR_SVH
`ifndef APB_MASTER_MONITOR_SV
`define APB_MASTER_MONITOR_SV
function apb_master_monitor::new(string name, uvm_component parent=null);
super.new(name, parent);
item_collected_port = new("item_collected_port",this);
trans_collected = new();
endfunction:new
// build
function void apb_master_monitor::build();
super.build();
endfunction : build
task 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 apb_master_monitor::run();
fork
monitor_transactions();
join_none
endtask // run
task apb_master_monitor::collect_transfer();
apb_transfer t;
// Advance clock
@(vif.cb_mon);
if(vif.cb_slv.psel === 1'b1 && vif.cb_slv.penable === 1'b0) begin
t = apb_transfer::type_id::create("t");
case(vif.cb_slv.pwrite)
1'b1 : begin
@(vif.cb_mon);//在第二个周期
t.addr = vif.cb_mon.paddr;
t.data = vif.cb_mon.pwdata;
t.trans_kind = WRITE;
end
1'b0 : begin
@(vif.cb_mon);
t.addr = vif.cb_mon.paddr;
t.data = vif.cb_mon.prdata;
t.trans_kind = READ;
end
default : `uvm_error(get_type_name(), "ERROR pwrite signal value")
endcase
item_collected_port.write(t);
end
endtask: collect_transfer
// perform_transfer_checks
function void apb_master_monitor::perform_transfer_checks();
// USER: do some checks on the transfer here
endfunction : perform_transfer_checks
// perform_transfer_coverage
function void apb_master_monitor::perform_transfer_coverage();
// USER: coverage implementation
-> apb_master_cov_transaction;
endfunction : perform_transfer_coverage
`endif // APB_MASTER_MONITOR_SV
1.3编写apb_master_monitor中的collect_transfer()
用于从接口获取item过来。
在setup阶段判断是读还是写,注意!要多用一次@(vif.cb_mon),APB在第二个周期完成读写操作
然后把item通过analysis port的write函数写入item
task apb_master_monitor::collect_transfer();
apb_transfer t;
// Advance clock
@(vif.cb_mon);
if(vif.cb_slv.psel === 1'b1 && vif.cb_slv.penable === 1'b0) begin
t = apb_transfer::type_id::create("t");
case(vif.cb_slv.pwrite)
1'b1 : begin
@(vif.cb_mon);//在第二个周期
t.addr = vif.cb_mon.paddr;
t.data = vif.cb_mon.pwdata;
t.trans_kind = WRITE;
end
1'b0 : begin
@(vif.cb_mon);
t.addr = vif.cb_mon.paddr;
t.data = vif.cb_mon.prdata;
t.trans_kind = READ;
end
default : `uvm_error(get_type_name(), "ERROR pwrite signal value")
endcase
item_collected_port.write(t);
end
endtask: collect_transfer
apb_master_sequence_Lib
包含base seq和各个seq,包括写1个,读1个,写完读1个,连续写,连续读,每次读写都需要一次`uvm_do_with,读完要把总线设为idle
1.2编写apb_master_sequence中的apb_master_write_seq::body()任务
base sequence只注册和例化,等待具体的子类来扩展。
一共有5个seq,如上所示。
single_write的body,发送一个req过去后获取一个rsp
virtual task body();
`uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
`uvm_do_with(req, {trans_kind == WRITE; addr == local::addr; data == local::data;})
get_response(rsp);
`uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
endtask: body
single_read的body,发送req过去后获取rsp并将rsp的data给到data,完成读出
virtual task body();
`uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
`uvm_do_with(req, {trans_kind == READ; addr == local::addr;})
get_response(rsp);
data = rsp.data;
`uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
endtask: body
write_read的body,写的时候要限定类型和idle_cycle,拿到rsp后读,读只限定类型和地址即可
virtual task body();
`uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
`uvm_do_with(req, {trans_kind == WRITE;
addr == local::addr;
data == local::data;
idle_cycles == local::idle_cycles;
})
get_response(rsp);
`uvm_do_with(req, {trans_kind == READ; addr == local::addr;})
get_response(rsp);
data = rsp.data;
`uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
endtask: body
burst_write的body,写完拿到rsp后要将类型置为IDLE
不明白为什么地址要addr == local::addr + (i<<2);
virtual task body();
`uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
foreach(data[i]) begin
`uvm_do_with(req, {trans_kind == WRITE;
addr == local::addr + (i<<2);
data == local::data[i];
idle_cycles == 0;
})
get_response(rsp);
end
`uvm_do_with(req, {trans_kind == IDLE;})
get_response(rsp);
`uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
endtask: body
burst_read的body同理。
virtual task body();
`uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
foreach(data[i]) begin
`uvm_do_with(req, {trans_kind == READ;
addr == local::addr + (i<<2);
idle_cycles == 0;
})
get_response(rsp);
data[i] = rsp.data;
end
`uvm_do_with(req, {trans_kind == IDLE;})
get_response(rsp);
`uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
endtask: body
apb_master_sequencer
只有注册和new函数例化
slave端
agent是一样的
apb_slave_driver
由于要接收master端的item,这里有个关联数组mem。
之前的mem出现在test的base_test_sequence中,用来存放读写数据。
与master相对应的是drive req,这里是drive_response,这也作为一个并行线程(master是两个并行线程)
复位时要清空mem的数据,因为这边是从端
`ifndef APB_SLAVE_DRIVER_SV
`define APB_SLAVE_DRIVER_SV
function apb_slave_driver::new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
task apb_slave_driver::run();
fork
get_and_drive();
reset_listener();
drive_response();
join_none
endtask : run
task apb_slave_driver::get_and_drive();
forever begin
seq_item_port.get_next_item(req);
`uvm_info(get_type_name(), "sequencer got next item", UVM_HIGH)
void'($cast(rsp, req.clone()));
rsp.set_sequence_id(req.get_sequence_id());
seq_item_port.item_done(rsp);
`uvm_info(get_type_name(), "sequencer item_done_triggered", UVM_HIGH)
end
endtask : get_and_drive
task apb_slave_driver::drive_response();
`uvm_info(get_type_name(), "drive_response", UVM_HIGH)
forever begin
@(vif.cb_slv);
if(vif.cb_slv.psel === 1'b1 && vif.cb_slv.penable === 1'b0) begin
case(vif.cb_slv.pwrite)
1'b1 : this.do_write();
1'b0 : this.do_read();
default : `uvm_error(get_type_name(), "ERROR pwrite signal value")
endcase
end
else begin
this.do_idle();
end
end
endtask : drive_response
task apb_slave_driver::reset_listener();
`uvm_info(get_type_name(), "reset_listener ...", UVM_HIGH)
fork
forever begin
@(negedge vif.rstn); // ASYNC reset
vif.prdata <= 0;
this.mem.delete(); // reset internal memory
end
join_none
endtask: reset_listener
task apb_slave_driver::do_idle();
`uvm_info(get_type_name(), "do_idle", UVM_HIGH)
vif.cb_slv.prdata <= 0;
endtask: do_idle
task apb_slave_driver::do_write();
bit[31:0] addr;
bit[31:0] data;
`uvm_info(get_type_name(), "do_write", UVM_HIGH)
@(vif.cb_slv);
addr = vif.cb_slv.paddr;
data = vif.cb_slv.pwdata;
mem[addr] = data;
endtask: do_write
task apb_slave_driver::do_read();
bit[31:0] addr;
bit[31:0] data;
`uvm_info(get_type_name(), "do_read", UVM_HIGH)
wait(vif.penable === 1'b1);
addr = vif.cb_slv.paddr;
if(mem.exists(addr))
data = mem[addr];
else
data = 0;
#1ps;
vif.prdata <= data;
@(vif.cb_slv);
endtask: do_read
`endif // APB_SLAVE_DRIVER_SV
1.4编写apb_slave_driver的drive_response()
还是APB的状态机在setup判断是读还是写,对应读写函数。
task apb_slave_driver::drive_response();
`uvm_info(get_type_name(), "drive_response", UVM_HIGH)
forever begin
@(vif.cb_slv);
if(vif.cb_slv.psel === 1'b1 && vif.cb_slv.penable === 1'b0) begin
case(vif.cb_slv.pwrite)
1'b1 : this.do_write();
1'b0 : this.do_read();
default : `uvm_error(get_type_name(), "ERROR pwrite signal value")
endcase
end
else begin
this.do_idle();
end
end
endtask : drive_response
读写函数另外编写,按照时序图给出对应的信号。
task apb_slave_driver::do_idle();
`uvm_info(get_type_name(), "do_idle", UVM_HIGH)
vif.cb_slv.prdata <= 0;
endtask: do_idle
task apb_slave_driver::do_write();
bit[31:0] addr;
bit[31:0] data;
`uvm_info(get_type_name(), "do_write", UVM_HIGH)
@(vif.cb_slv);
addr = vif.cb_slv.paddr;
data = vif.cb_slv.pwdata;
mem[addr] = data;
endtask: do_write
task apb_slave_driver::do_read();
bit[31:0] addr;
bit[31:0] data;
`uvm_info(get_type_name(), "do_read", UVM_HIGH)
wait(vif.penable === 1'b1);
addr = vif.cb_slv.paddr;
if(mem.exists(addr))
data = mem[addr];
else
data = 0;
#1ps;
vif.prdata <= data;
@(vif.cb_slv);
endtask: do_read
apb_slave_seq_lib
只是发送了一个item给driver,没法控制driver。
为什么要让sequence控制driver?
`ifndef APB_SLAVE_SEQ_LIB_SV
`define APB_SLAVE_SEQ_LIB_SV
class example_apb_slave_seq extends uvm_sequence #(apb_transfer);
function new(string name="");
super.new(name);
endfunction : new
`uvm_object_utils(example_apb_slave_seq)
apb_transfer this_transfer;
virtual task body();
`uvm_info(get_type_name(),"Starting example sequence", UVM_HIGH)
`uvm_do(this_transfer)
`uvm_info(get_type_name(),$psprintf("Done example sequence: %s",this_transfer.convert2string()), UVM_HIGH)
endtask
endclass : example_apb_slave_seq
`endif // apb_SLAVE_SEQ_LIB_SV