目录
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改造。
所有的接口都发生了改变,对应在硬件中都做了修改。
特点是一总(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文件,画了个框图如下:
接下来再逐个看各个组件
这是等下用来配置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::
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
-
- `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
- 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
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
-
用于从接口获取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
包含base seq和各个seq,包括写1个,读1个,写完读1个,连续写,连续读,每次读写都需要一次`uvm_do_with,读完要把总线设为idle
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
只有注册和new函数例化
agent是一样的
由于要接收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
还是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
只是发送了一个item给driver,没法控制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