目录

MCDF改造

APB接口

验证环境的改造

实战任务

apb_config

apb_master_agent

apb_master_driver

1.1实现apb_master_driver的drive_transfer()

apb_master_monitor

1.3编写apb_master_monitor中的collect_transfer()

apb_master_sequence_Lib

1.2编写apb_master_sequence中的apb_master_write_seq::body()任务

apb_master_sequencer

slave端

apb_slave_driver

1.4编写apb_slave_driver的drive_response()

apb_slave_seq_lib

为什么要让sequence控制driver?


这里涉及的是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_SVpackage 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 beginforkbegin forever #5ns clk = !clk;endbegin#100ns;rstn <= 1'b1;#100ns;rstn <= 1'b0;#100ns;rstn <= 1'b1;endjoin_noneendapb_if intf(clk, rstn);initial beginuvm_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");endendmodule

在apb_if中,包括了接口+时钟块,覆盖组covergroup。接口信号按照时序图给出,时钟块分为master和slave,信号的方向互补,monitor的时钟块所有信号都是input。

这里又遇到覆盖率收集的问题,复习一下。定义覆盖组后再initial块中例化各个覆盖组。

https://mp.csdn.net/mp_blog/creation/editor/125731520

`ifndef APB_IF_SV
`define APB_IF_SVinterface 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 flagsbit                has_checks = 1;bit                has_coverage = 1;// Actual Signals // USER: Add interface signalsclocking cb_mst @(posedge clk);// USER: Add clocking block detaildefault input #1ps output #1ps;output paddr, pwrite, psel, penable, pwdata;input prdata;endclocking : cb_mstclocking cb_slv @(posedge clk);// USER: Add clocking block detaildefault input #1ps output #1ps;input paddr, pwrite, psel, penable, pwdata;output prdata;endclocking : cb_slvclocking cb_mon @(posedge clk);// USER: Add clocking block detaildefault 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 covergroupcovergroup 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 groupcovergroup 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 groupcovergroup 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);} endgroupinitial beginautomatic cg_apb_command cg0 = new();automatic cg_apb_trans_timing_group cg1 = new();automatic cg_apb_write_read_order_group cg2 = new();endendinterface : 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 itemsuvm_active_passive_enum  is_active = UVM_ACTIVE;function new (string name = "apb_config");super.new(name);endfunction : newendclass`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_SVHclass 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 - constructorextern function new (string name, uvm_component parent);// uvm build phaseextern function void build();// uvm connection phaseextern function void connect();// This method assigns the virtual interfaces to the agent's childrenextern 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 : newfunction void apb_master_agent::build();super.build();// get configif( !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 interfaceif( !uvm_config_db#(virtual apb_if)::get(this,"","vif", vif)) begin`uvm_fatal("GETVIF","cannot get vif handle from config DB")endmonitor = apb_master_monitor::type_id::create("monitor",this);monitor.cfg = cfg;if(cfg.is_active == UVM_ACTIVE) beginsequencer = 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 : buildfunction void apb_master_agent::connect();assign_vi(vif);if(is_active == UVM_ACTIVE) begindriver.seq_item_port.connect(sequencer.seq_item_export);       endendfunction : connectfunction void apb_master_agent::assign_vi(virtual apb_if vif);monitor.vif = vif;if (is_active == UVM_ACTIVE) beginsequencer.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_SVHclass apb_master_driver extends uvm_driver #(apb_transfer);apb_config cfg;`uvm_component_utils_begin(apb_master_driver)`uvm_component_utils_endextern 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_SVfunction apb_master_driver::new (string name, uvm_component parent);super.new(name, parent);
endfunction : newtask apb_master_driver::run();forkget_and_drive();reset_listener();join_none
endtask : runtask apb_master_driver::get_and_drive();forever beginseq_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_drivetask 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_transfertask 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_writetask 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 cyclet.data = vif.prdata;repeat(t.idle_cycles) this.do_idle();
endtask: do_readtask 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_idletask apb_master_driver::reset_listener();`uvm_info(get_type_name(), "reset_listener ...", UVM_HIGH)forkforever begin@(negedge vif.rstn); // ASYNC resetvif.paddr <= 0;vif.pwrite <= 0;vif.psel <= 0;vif.penable <= 0;vif.pwdata <= 0;endjoin_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_endfunction new (string name = "apb_transfer_inst");super.new(name);endfunction : newendclass : apb_transfer`endif // APB_TRANSFER_SV

再来看monitor

apb_master_monitor

TLM通信端口是uvm_analysis_port


`ifndef APB_MASTER_MONITOR_SVH
`define APB_MASTER_MONITOR_SVHclass 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_endextern 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_transprotected 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_SVfunction 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 transactioncollect_transfer();// Check transactionif (checks_enable)perform_transfer_checks();// Update coverageif (coverage_enable)perform_transfer_coverage();// Publish to subscribersitem_collected_port.write(trans_collected);end
endtask // monitor_transactionstask apb_master_monitor::run();forkmonitor_transactions();join_none
endtask // runtask 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) begint = 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;enddefault : `uvm_error(get_type_name(), "ERROR pwrite signal value")endcaseitem_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 hereendfunction : 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) begint = 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;enddefault : `uvm_error(get_type_name(), "ERROR pwrite signal value")endcaseitem_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_SVfunction apb_slave_driver::new (string name, uvm_component parent);super.new(name, parent);
endfunction : newtask apb_slave_driver::run();forkget_and_drive();reset_listener();drive_response();join_none
endtask : runtask apb_slave_driver::get_and_drive();forever beginseq_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_drivetask 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) begincase(vif.cb_slv.pwrite)1'b1    : this.do_write();1'b0    : this.do_read();default : `uvm_error(get_type_name(), "ERROR pwrite signal value")endcaseendelse beginthis.do_idle();endend
endtask : drive_responsetask apb_slave_driver::reset_listener();`uvm_info(get_type_name(), "reset_listener ...", UVM_HIGH)forkforever begin@(negedge vif.rstn); // ASYNC resetvif.prdata <= 0;this.mem.delete(); // reset internal memoryendjoin_none
endtask: reset_listenertask apb_slave_driver::do_idle();`uvm_info(get_type_name(), "do_idle", UVM_HIGH)vif.cb_slv.prdata <= 0;
endtask: do_idletask 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_writetask 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];elsedata = 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) begincase(vif.cb_slv.pwrite)1'b1    : this.do_write();1'b0    : this.do_read();default : `uvm_error(get_type_name(), "ERROR pwrite signal value")endcaseendelse beginthis.do_idle();endend
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_idletask 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_writetask 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];elsedata = 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_SVclass 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)endtaskendclass : example_apb_slave_seq`endif // apb_SLAVE_SEQ_LIB_SV

电力电子转战数字IC20220819day64——uvm实战1A相关推荐

  1. 电力电子转战数字IC——路科MCDF全览(持续更新)

    经过两次面试后,对MCDF做一次全面的深入总结. 目前进度:硬件部分的node,fifo,寄存器,formatter,MCDF顶层,APB接口,TB接口 软件部分的chnl_pkg,fmt_pkg,a ...

  2. 电力电子转战数字IC——我的IC笔试(2022.10.14更新)

    IC笔试有:JL科技.TR半导体.HZW.MX半导体.RSKX.TCL 部分题目暂时还是做不出来,先好好复习一遍,会有柳暗花明的时候的. 目录 RY10.11 TCL10.9 位宽定义正确的是 逻辑与 ...

  3. 电力电子转战数字IC20220727day57——寄存器模型(续)

    rgm的常规方法 关于reg的三个值 mirrored value镜像值:由模型预测给出,即在前门访问时通过观察总线.在后门访问时通过自动预测等方式给出 desired value期望值:先利用rgm ...

  4. 电力电子转战数字IC——我的IC面试(2022.10.14更新)

    目录 感谢信 HKWS10.14面试 25mins JXC10.13面试 30mins JDSK9.23面试 42mins 快速的自我介绍 介绍一下这个MCDF的项目 你这里写SV搭建的验证环境,和U ...

  5. 电力电子转战数字IC20220629day35——路科实验2b

    目录 tb3代码改造 tb3代码 tb4改造思路: tb4结构示意图 tb4代码 basic_test burst_test full_test 首先复习一下类的知识点 CSDNhttps://mp. ...

  6. 电力电子转战数字IC20220613day23——江哥nb!

    江哥nb! 今天的头版必须给到江哥,华为树枝江哥,FPGA设计岗,两年工资翻倍,年薪突破50,月薪突破30,向江哥学习! 想做江哥的水花兄弟,差个11号球衣  -------------------- ...

  7. 电力电子转战数字IC20220711day45——SV终章

    至此,SV的学习结束,实验4和实验5等回头过来重做,一定要到完全理解为止.SV DAY30 类型转换 分类:静态转换,动态转换 静态转换:在需要转换的表达式前面加上单引号,不会对转换值检查,如果转换失 ...

  8. altera fpga sdi输出方案_FPGA在电力电子中的应用有哪些?

    大家好,很抱歉上周末没有及时更新公众号,本来这期想聊聊IGBT的拖尾电流,但是由于周末去深圳高交会(高新技术成果交易会)逛了一天,时间给耽搁了,感觉要想把拖尾电流讲清楚也不太容易,还得需要点时间,为了 ...

  9. 电力电子、电机控制系统的建模和仿真_电力传动控制系统:运动控制系统

    本书是普通高等教育电气工程与自动化类"十一五"规划教材,主要是针对电气工程及其自动化.自动化等专业大学本科编写的.为适应教学改革和学科发展的需要,本书在陈伯时主编的<电力拖动 ...

最新文章

  1. Java Calendar使用指南
  2. html中链接不自动变色,HTML 点击 标签 链接 点击之后变色-转发【已解决】
  3. nginx优化——包括https、keepalive等
  4. cisco 2610 2950 单臂路由得一些心得
  5. java ftp取远程服务器时间_在 Java 中如何获取 FTP 服务器上的文件修改时间
  6. c++ list遍历_List集合就这么简单「源码剖析」
  7. WebSocket 测试
  8. 博客堂服务器转移成功!
  9. python虚拟环境 pyenv_Python多版本管理和虚拟环境维护(pyenv, pyenv-virtualenv)
  10. VLAN Mapping实现同一网段不同VLAN的主机通信
  11. VLFeat中SIFT特征点检测
  12. DFA敏感词过滤算法详解
  13. 国内国外常用外包平台大全汇总!
  14. Unity3d编辑器的使用
  15. 工具说明书 - FTDI芯片的USB转UART串口线
  16. _itemmod_creation_enchant
  17. FPGA基础之HLS
  18. android:简单包装实现伪自定义DatePickerDialog和TimePickerDialog
  19. OpenCV之视频截取
  20. 美团Java后台一面

热门文章

  1. VR看房是怎么做出来的呀?
  2. sony k750c耳机改诺基亚耳机
  3. swlzky央视315晚会
  4. 【C库函数】memcpy函数详解
  5. system.runtime.Serialization.SerializationException
  6. JAVA-千位分隔符插入方法
  7. C 库函数 - strtol()
  8. 【js与jquery】文字提示与图片提示的制作
  9. 数据库中复合主键的应用场景
  10. 双目视觉立体匹配matlab,双目视觉立体匹配方法