APB协议UVM验证环境的搭建
一、编译文件
只需编译这两个文件即可
apb_pkg.sv
里面包含了"apb.svh",即编译apb_pkg.sv这个文件的同时,也会编译所需要的所有的头文件。
`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.svh
`ifndef APB_SVH `define APB_SVH `include "apb_transfer.sv" `include "apb_config.sv" //master所有的头文件 `include "apb_master_driver.svh" `include "apb_master_monitor.svh" `include "apb_master_sequencer.svh" `include "apb_master_agent.svh" //slave所有的头文件 `include "apb_slave_driver.svh" `include "apb_slave_monitor.svh" `include "apb_slave_sequencer.svh" `include "apb_slave_agent.svh" //master头文件里面具体的实现方法 `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" //slave头文件里面具体的实现方法 `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.sv文件
编译的同时,也会编译"apb_tests.svh"、"apb_if.sv"这两个文件。例化协议接口,配置顶层环境的master和slave,默认执行“apb_single_transaction_test”这个测试用例。
`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_tests.svh
`ifndef APB_TESTS_SV `define APB_TESTS_SV import apb_pkg::*; class apb_env extends uvm_env; apb_master_agent mst; apb_slave_agent slv; `uvm_component_utils(apb_env) function new(string name, uvm_component parent); super.new(name, parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); mst = apb_master_agent::create("mst", this); slv = apb_slave_agent::create("slv", this); endfunction endclass class apb_base_test extends uvm_test; apb_env env; `uvm_component_utils(apb_base_test) function new(string name, uvm_component parent); super.new(name, parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); env = apb_env::create("env", this); endfunction endclass class apb_base_test_sequence extends uvm_sequence #(apb_transfer); bit[31:0] mem[bit[31:0]]; //关联数组mem,用来master和slave之间的数据比对,test和slave中都有一个mem `uvm_object_utils(apb_base_test_sequence) function new(string name=""); super.new(name); endfunction : new function bit check_mem_data(bit[31:0] addr, bit[31:0] data); if(mem.exists(addr)) begin if(data != mem[addr]) begin `uvm_error("CMPDATA", $sformatf("addr 32'h%8x, READ DATA expected 32'h%8x != actual 32'h%8x", addr, mem[addr], data)) return 0; end else begin `uvm_info("CMPDATA", $sformatf("addr 32'h%8x, READ DATA 32'h%8x comparing success!", addr, data), UVM_LOW) return 1; end end else begin if(data != 0) begin `uvm_error("CMPDATA", $sformatf("addr 32'h%8x, READ DATA expected 32'h00000000 != actual 32'h%8x", addr, data)) return 0; end else begin `uvm_info("CMPDATA", $sformatf("addr 32'h%8x, READ DATA 32'h%8x comparing success!", addr, data), UVM_LOW) return 1; end end endfunction: check_mem_data task wait_reset_release(); @(negedge apb_tb.rstn); @(posedge apb_tb.rstn); endtask task wait_cycles(int n); repeat(n) @(posedge apb_tb.clk); endtask function bit[31:0] get_rand_addr(); bit[31:0] addr; void'(std::randomize(addr) with {addr[31:12] == 0; addr[1:0] == 0;}); return addr; endfunction endclass class apb_single_transaction_sequence extends apb_base_test_sequence; apb_master_single_write_sequence single_write_seq; apb_master_single_read_sequence single_read_seq; apb_master_write_read_sequence write_read_seq; rand int test_num = 100; constraint cstr{ soft test_num == 100; } `uvm_object_utils(apb_single_transaction_sequence) function new(string name=""); super.new(name); endfunction : new task body(); bit[31:0] addr; this.wait_reset_release(); this.wait_cycles(10); // TEST continous write transaction `uvm_info(get_type_name(), "TEST continous write transaction...", UVM_LOW) repeat(test_num) begin addr = this.get_rand_addr(); `uvm_do_with(single_write_seq, {addr == local::addr; data == local::addr;}) mem[addr] = addr; end // TEST continous read transaction `uvm_info(get_type_name(), "TEST continous read transaction...", UVM_LOW) repeat(test_num) begin addr = this.get_rand_addr(); `uvm_do_with(single_read_seq, {addr == local::addr;}) void'(this.check_mem_data(addr, single_read_seq.data)); end // TEST read transaction after write transaction `uvm_info(get_type_name(), "TEST read transaction after write transaction...", UVM_LOW) repeat(test_num) begin addr = this.get_rand_addr(); `uvm_do_with(single_write_seq, {addr == local::addr; data == local::addr;}) mem[addr] = addr; `uvm_do_with(single_read_seq, {addr == local::addr;}) void'(this.check_mem_data(addr, single_read_seq.data)); end // TEST read transaction immediately after write transaction `uvm_info(get_type_name(), "TEST read transaction immediately after write transaction", UVM_LOW) repeat(test_num) begin addr = this.get_rand_addr(); `uvm_do_with(write_read_seq, {addr == local::addr; data == local::addr;}) mem[addr] = addr; void'(this.check_mem_data(addr, write_read_seq.data)); end this.wait_cycles(10); endtask endclass: apb_single_transaction_sequence class apb_single_transaction_test extends apb_base_test; `uvm_component_utils(apb_single_transaction_test) function new(string name, uvm_component parent); super.new(name, parent); endfunction task run_phase(uvm_phase phase); apb_single_transaction_sequence seq = new(); phase.raise_objection(this); super.run_phase(phase); seq.start(env.mst.sequencer); phase.drop_objection(this); endtask endclass: apb_single_transaction_test class apb_burst_transaction_sequence extends apb_base_test_sequence; apb_master_burst_write_sequence burst_write_seq; apb_master_burst_read_sequence burst_read_seq; rand int test_num = 100; constraint cstr{ soft test_num == 100; } `uvm_object_utils(apb_burst_transaction_sequence) function new(string name=""); super.new(name); endfunction : new task body(); bit[31:0] addr; this.wait_reset_release(); this.wait_cycles(10); // TEST continous write transaction repeat(test_num) begin addr = this.get_rand_addr(); `uvm_do_with(burst_write_seq, {addr == local::addr;}) foreach(burst_write_seq.data[i]) begin mem[addr+(i<<2)] = burst_write_seq.data[i]; end `uvm_do_with(burst_read_seq, {addr == local::addr; data.size() == burst_write_seq.data.size();}) foreach(burst_read_seq.data[i]) begin void'(this.check_mem_data(addr+(i<<2), burst_write_seq.data[i])); end end this.wait_cycles(10); endtask endclass: apb_burst_transaction_sequence class apb_burst_transaction_test extends apb_base_test; `uvm_component_utils(apb_burst_transaction_test) function new(string name, uvm_component parent); super.new(name, parent); endfunction task run_phase(uvm_phase phase); apb_burst_transaction_sequence seq = new(); phase.raise_objection(this); super.run_phase(phase); seq.start(env.mst.sequencer); phase.drop_objection(this); endtask endclass: apb_burst_transaction_test `endif // APB_TESTS_SV
apb_if.sv
`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二、apb_tests.sv代码分析
apb_base_test_sequence类
check_mem_data()方法原理结构框图:
关联数组mem,用来master和slave之间的数据比对,test和slave中都有一个mem,master通过接口发送数据给slave,slave中的mem和test中的mem都会存储这个数据,等从slave读回数据时,就可以和test中mem里面的数据进行比较。
apb_single_transaction_sequence类
随机化addr,测试连续写操作
// TEST continous write transaction `uvm_info(get_type_name(), "TEST continous write transaction...", UVM_LOW) repeat(test_num) begin addr = this.get_rand_addr(); `uvm_do_with(single_write_seq, {addr == local::addr; data == local::addr;}) mem[addr] = addr; end
随机化addr,测试连续读操作,并比较数据是否一致
// TEST continous read transaction `uvm_info(get_type_name(), "TEST continous read transaction...", UVM_LOW) repeat(test_num) begin addr = this.get_rand_addr(); `uvm_do_with(single_read_seq, {addr == local::addr;}) void'(this.check_mem_data(addr, single_read_seq.data)); end
随机化addr,先进行写操作,再进行读操作,并比较读取的数据是否一致
// TEST read transaction after write transaction `uvm_info(get_type_name(), "TEST read transaction after write transaction...", UVM_LOW) repeat(test_num) begin addr = this.get_rand_addr(); `uvm_do_with(single_write_seq, {addr == local::addr; data == local::addr;}) mem[addr] = addr; `uvm_do_with(single_read_seq, {addr == local::addr;}) void'(this.check_mem_data(addr, single_read_seq.data)); end
随机化addr,写完立即读,中间没有idle空闲,并检查读取数据是否一致
// TEST read transaction immediately after write transaction `uvm_info(get_type_name(), "TEST read transaction immediately after write transaction", UVM_LOW) repeat(test_num) begin addr = this.get_rand_addr(); `uvm_do_with(write_read_seq, {addr == local::addr; data == local::addr;}) mem[addr] = addr; void'(this.check_mem_data(addr, write_read_seq.data)); end
例化并挂载
class apb_single_transaction_test extends apb_base_test; `uvm_component_utils(apb_single_transaction_test) function new(string name, uvm_component parent); super.new(name, parent); endfunction task run_phase(uvm_phase phase); apb_single_transaction_sequence seq = new(); phase.raise_objection(this); super.run_phase(phase); seq.start(env.mst.sequencer); phase.drop_objection(this); endtask endclass: apb_single_transaction_test
apb_burst_transaction_sequence类
先全部写操作完毕,在完全读出来,地址是连续增长的
// TEST continous write transaction repeat(test_num) begin addr = this.get_rand_addr(); `uvm_do_with(burst_write_seq, {addr == local::addr;}) foreach(burst_write_seq.data[i]) begin mem[addr+(i<<2)] = burst_write_seq.data[i]; end `uvm_do_with(burst_read_seq, {addr == local::addr; data.size() == burst_write_seq.data.size();}) foreach(burst_read_seq.data[i]) begin void'(this.check_mem_data(addr+(i<<2), burst_write_seq.data[i])); end end
例化并挂载
class apb_burst_transaction_test extends apb_base_test; `uvm_component_utils(apb_burst_transaction_test) function new(string name, uvm_component parent); super.new(name, parent); endfunction task run_phase(uvm_phase phase); apb_burst_transaction_sequence seq = new(); phase.raise_objection(this); super.run_phase(phase); seq.start(env.mst.sequencer); phase.drop_objection(this); endtask endclass: apb_burst_transaction_test
三、apb_master_agent.sv代码分析
agent包括三个组件driver、sequencer、monitor,以及config和interface。
例化monitor,根据配置决定是否例化driver和sequencer
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::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::create("monitor",this); monitor.cfg = cfg; if(cfg.is_active == UVM_ACTIVE) begin sequencer = apb_master_sequencer::create("sequencer",this); sequencer.cfg = cfg; driver = apb_master_driver::create("driver",this); driver.cfg = cfg; end endfunction : build
根据配置决定是否连接driver和sequencer
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
根据配置决定是否vif和driver、sequencer之间的连接
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
四、apb_master_driver.sv代码分析
并行触发get_and_drive()、reset_listener()
task apb_master_driver::run(); fork get_and_drive(); reset_listener(); join_none endtask : run
捕捉到复位信号以后,所以信号清零
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
sequence和sequencer需要握手,获取transaction以后调用driver_transfer()发送。发送成功以后克隆request生成新的response,作为响应发送回去。
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
根据trans_kind判断操作命令,分别调用相对应的方法。
task apb_master_driver::do_write(apb_transfer t); `uvm_info(get_type_name(), "do_write ...", UVM_HIGH) //写操作一共分为两个周期,根据协议第一个周期setup准备阶段需要如下操作 @(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; //第二个阶段拉高penable信号,发送数据 @(vif.cb_mst); vif.cb_mst.penable <= 1; repeat(t.idle_cycles) this.do_idle(); //取决于transaction里面的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; //需要采样数据,人为添加100ps的delay,是为了避免delta-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); //根据协议,paddr、pwrite可以保持不变,等待下一次的传输,这是为了省电 //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
五、apb_master_monitor.sv代码分析
collect_transfer()方法
在时钟上升沿,同时psel=1和penabl=0的时候,判断当前情况下pwrite信号,在第二个周期进行读或者写操作。
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::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_seq_lib.sv代码分析
apb_master_single_write_sequence类
使用宏'uvm_do_with发送数据。
class apb_master_single_write_sequence extends apb_master_base_sequence; rand bit [31:0] addr; rand bit [31:0] data; `uvm_object_utils(apb_master_single_write_sequence) function new(string name=""); super.new(name); endfunction : new 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 endclass: apb_master_single_write_sequence
apb_master_single_read_sequence类
读操作,拿到返回的rsp的数据后存储在成员变量data里。
class apb_master_single_read_sequence extends apb_master_base_sequence; rand bit [31:0] addr; rand bit [31:0] data; `uvm_object_utils(apb_master_single_read_sequence) function new(string name=""); super.new(name); endfunction : new 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 endclass: apb_master_single_read_sequence
apb_master_write_read_sequence类
写操作后进行读操作,所以idle_cycles == 0
class apb_master_write_read_sequence extends apb_master_base_sequence; rand bit [31:0] addr; rand bit [31:0] data; rand int idle_cycles; constraint cstr{ idle_cycles == 0; } `uvm_object_utils(apb_master_write_read_sequence) function new(string name=""); super.new(name); endfunction : new 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 endclass: apb_master_write_read_sequence
apb_master_burst_write_sequence类
连续的写操作,按照地址增长的顺序,把所有的数据写到data数组中。因为是连续写操作,所以idle_cycles == 0,即数据之间没有空闲周期。
class apb_master_burst_write_sequence extends apb_master_base_sequence; rand bit [31:0] addr; rand bit [31:0] data[]; constraint cstr{ soft data.size() inside {4, 8, 16, 32}; foreach(data[i]) soft data[i] == addr + (i << 2); } `uvm_object_utils(apb_master_burst_write_sequence) function new(string name=""); super.new(name); endfunction : new 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 endclass: apb_master_burst_write_sequence
apb_master_burst_read_sequence类
连续的读操作,每次读取回来的数据,从rsp中拿出来放到data数组中。全部读取完成之后,将总线置为IDLE。
class apb_master_burst_read_sequence extends apb_master_base_sequence; rand bit [31:0] addr; rand bit [31:0] data[]; constraint cstr{ soft data.size() inside {4, 8, 16, 32}; } `uvm_object_utils(apb_master_burst_read_sequence) function new(string name=""); super.new(name); endfunction : new 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 endclass: apb_master_burst_read_sequence
七、apb_slave_driver.sv代码分析
slave要接收master发送过来的数据,所以要模拟一个存储功能,即关联数组mem。
bit[31:0] mem [bit[31:0]];
run()方法
三个方法并行执行
task apb_slave_driver::run(); fork get_and_drive(); reset_listener(); drive_response(); join_none endtask : run
get_and_drive()方法
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
reset_listener()方法
等待复位信号,将prdata <= 0,同时清空mem里面的数据。
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
drive_response()方法
如果当前这一周期是SETUP阶段,即psel = 1 && penable = 0,进而判断是写操作还是读操作,然后调用相对应的方法。
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
do_write()方法
如果是写操作,那么等待时钟下一拍,拿到addr和data并放到mem中。
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
do_read()方法
如果是读操作,等待penable=1,并且判断mem中是否写过该addr,如果有则写入data,没有则将data置为0,即还是初始化的数据。等待一个延迟后,将data驱动到总线上面。
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
八、运行仿真
执行命令
run -all
验证环境结构
写操作:写入地址和写入数据相同,只有penable拉高才会写入,数据之间有一个空闲。
读操作:只有penable拉高才会读数据,没有写入过数据的地址,读出来的值为0。
先写后读:
写完立即读操作:
仿真结果:
覆盖率:
// 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
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !