APB协议UVM验证环境的搭建

一、编译文件

只需编译这两个文件即可

apb_pkg.sv

里面包含了"apb.svh",即编译apb_pkg.sv这个文件的同时,也会编译所需要的所有的头文件。

`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.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 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_tests.svh

`ifndef APB_TESTS_SV
`define APB_TESTS_SVimport 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);endfunctionfunction void build_phase(uvm_phase phase);super.build_phase(phase);mst = apb_master_agent::type_id::create("mst", this);slv = apb_slave_agent::type_id::create("slv", this);endfunction
endclassclass 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);endfunctionfunction void build_phase(uvm_phase phase);super.build_phase(phase);env = apb_env::type_id::create("env", this);endfunction
endclassclass 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 : newfunction bit check_mem_data(bit[31:0] addr, bit[31:0] data);if(mem.exists(addr)) beginif(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;endelse begin`uvm_info("CMPDATA", $sformatf("addr 32'h%8x, READ DATA 32'h%8x comparing success!", addr, data), UVM_LOW)return 1;endendelse beginif(data != 0) begin`uvm_error("CMPDATA", $sformatf("addr 32'h%8x, READ DATA expected 32'h00000000 != actual 32'h%8x", addr, data))return 0;endelse begin`uvm_info("CMPDATA", $sformatf("addr 32'h%8x, READ DATA 32'h%8x comparing success!", addr, data), UVM_LOW)return 1;endendendfunction: check_mem_datatask wait_reset_release();@(negedge apb_tb.rstn);@(posedge apb_tb.rstn);endtasktask wait_cycles(int n);repeat(n) @(posedge apb_tb.clk);endtaskfunction 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
endclassclass 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 : newtask 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) beginaddr = 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) beginaddr = 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) beginaddr = 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) beginaddr = 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));endthis.wait_cycles(10);endtask
endclass: apb_single_transaction_sequenceclass 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);endfunctiontask 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_testclass 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 : newtask body();bit[31:0] addr;this.wait_reset_release();this.wait_cycles(10);// TEST continous write transactionrepeat(test_num) beginaddr = this.get_rand_addr();`uvm_do_with(burst_write_seq, {addr == local::addr;})foreach(burst_write_seq.data[i]) beginmem[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]) beginvoid'(this.check_mem_data(addr+(i<<2), burst_write_seq.data[i]));endendthis.wait_cycles(10);endtask
endclass: apb_burst_transaction_sequenceclass 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);endfunctiontask 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_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

二、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) beginaddr = 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) beginaddr = 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) beginaddr = 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) beginaddr = 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);endfunctiontask 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 transactionrepeat(test_num) beginaddr = this.get_rand_addr();`uvm_do_with(burst_write_seq, {addr == local::addr;})foreach(burst_write_seq.data[i]) beginmem[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]) beginvoid'(this.check_mem_data(addr+(i<<2), burst_write_seq.data[i]));endend

例化并挂载

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);endfunctiontask 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包括三个组件driversequencermonitor,以及configinterface

例化monitor,根据配置决定是否例化driversequencer

function 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 : build

根据配置决定是否连接driversequencer

function void apb_master_agent::connect();assign_vi(vif);if(is_active == UVM_ACTIVE) begindriver.seq_item_port.connect(sequencer.seq_item_export);       endendfunction : connect

根据配置决定是否vif和driver、sequencer之间的连接

function 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

四、apb_master_driver.sv代码分析

并行触发get_and_drive()reset_listener()

task apb_master_driver::run();forkget_and_drive();reset_listener();join_none
endtask : run

捕捉到复位信号以后,所以信号清零

task 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

sequencesequencer需要握手,获取transaction以后调用driver_transfer()发送。发送成功以后克隆request生成新的response,作为响应发送回去。

task 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_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_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;  //需要采样数据,人为添加100ps的delay,是为了避免delta-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);//根据协议,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=1penabl=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) 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_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 : newvirtual 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: bodyendclass: 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 : newvirtual 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: bodyendclass: 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 : newvirtual 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: bodyendclass: 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 : newvirtual 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 : newvirtual 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();forkget_and_drive();reset_listener();drive_response();join_none
endtask : run

get_and_drive()方法

task 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_drive

reset_listener()方法

等待复位信号,将prdata <= 0,同时清空mem里面的数据。

task 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_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) 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

do_write()方法

如果是写操作,那么等待时钟下一拍,拿到addrdata并放到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];elsedata = 0;#1ps;vif.prdata <= data;@(vif.cb_slv);
endtask: do_read

八、运行仿真

执行命令

run -all

验证环境结构

写操作:写入地址和写入数据相同,只有penable拉高才会写入,数据之间有一个空闲。

读操作:只有penable拉高才会读数据,没有写入过数据的地址,读出来的值为0。

先写后读:

写完立即读操作:

仿真结果:


覆盖率:

  // 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);} endgroup

APB协议UVM验证环境的搭建相关推荐

  1. UART UVM验证平台平台搭建总结

    UART UVM验证平台平台搭建总结 tb_top是整个UVM验证平台的最顶层:tb_top中例化dut,提供时钟和复位信号,定义接口以及设置driver和monitor的virual interfa ...

  2. 【IC萌新虚拟项目】ppu整体uvm验证环境搭建

    关于整个虚拟项目,请参考: [HISI IC萌新虚拟项目]Package Process Unit项目全流程目录_尼德兰的喵的博客-CSDN博客 前言 本篇文章完成ppu整体uvm环境搭建的指导,在进 ...

  3. CPU系统级验证——验证环境——OpenHW core-v验证环境及文件分析

    本文记录的相关源工程和文件为: core-v RISCV核功能验证工程:https://github.com/openhwgroup/core-v-verif core-v 验证策略:https:// ...

  4. UVM学习之路(4)— 基本的UVM验证平台

    UVM学习之路(4)- 基本的UVM验证平台 一.前言 一般我们将设计输出的文件称为DUT(Design Under Test),即待测设计,验证是来找出DUT中的bug, 这个过程通常是把DUT放入 ...

  5. 使用 freeradius 搭建 EAP PEAP MS-CHAPv2 验证环境

    企业级 Wi-Fi 搭建起来有点小复杂,我们知道自己家使用的 Wi-Fi 非常简单,几乎只需要配置一下热点的 SSID 和密码就可以了,实际上这是两种 Wi-Fi 认证类型.想要快速部署企业级 Wi- ...

  6. 搭建一个SV验证环境(1)

    transaction transaction是验证平台内传输信息的基本单元,transaction会将信息从一个验证组件发送到另一个验证组件里面:transaction结构通常与协议帧结构相关: 在 ...

  7. 什么是TSN,如何搭建TSN验证环境?

    TSN历史 由于以太网的普及度高,针对以太网的实时应用衍生出多种技术.2001年,贝加莱推出了工业领域应用的以太网POWERLINK:2003年,Siemens基于Profibus开发了PROFINE ...

  8. UVM验证平台搭建三:spi_mst_agent-spi_slv_agent

    UVM验证平台搭建三:spi_mst_agent/spi_slv_agent 一.概述 二.spi mst transaction 三.spi mst sequencer 四.spi mst driv ...

  9. UVM验证平台搭建一:reg model 生成

    UVM验证平台搭建一:reg model 生成 一.概述 二.寄存器描述表格 三.生成.ralf寄存器文件 四.ralgen生成寄存器模型 一.概述 在做验证时,寄存器模型是验证平台中必不可少的,而且 ...

最新文章

  1. 表单必填_forms. 表单(中)
  2. JavaScript 面向对象实战思想
  3. Atitit. 破解  拦截 绕过 网站 手机 短信 验证码  方式 v2 attilax 总结
  4. sprintf、strcpy、strncpy及 memcpy 函数,请问这些函数功能有什么区别?配实例详解!
  5. SCSF 系列:Smart Client Software Factory 中 MVP 模式最佳实践
  6. 最全!最完整的设计词法分析程序的代码!!!
  7. 三级分类菜单的数据库设计
  8. Photoshop笔刷|如何正确导入笔刷?
  9. 大数据之-Hadoop3.x_Hadoop_HDFS_总结---大数据之hadoop3.x工作笔记0080
  10. javascript--Math相关
  11. 痞子衡嵌入式:语音处理工具Jays-PySPEECH诞生记(5)- 语音识别实现(SpeechRecognition, PocketSphinx0.1.15)
  12. Bundle Adjustment — A Modern Synthesis(一)
  13. smart210 dnw下载
  14. (1/2) sharex录制屏幕没有声音,升级高版本的sharex
  15. SEBASTIEN KWOK 2022春夏系列发布
  16. scikit-learn : LASSO
  17. 爬取网易云音乐评论《安河桥》,进行分析
  18. 关注按钮切换已关注_行业关注 | 粘胶短纤平稳运行下,或已暗潮涌动
  19. vc6创建dll文件的步骤_创建真正有用的产品支持页面的6步骤计划
  20. 使用NSIS制作安装包(1)

热门文章

  1. 问题 N: 算法设计与分析 输油管道
  2. GitHub无法打开或加载慢的解决方法
  3. 浅谈Go的Context相关方法
  4. MySQL高级(看视频整理的~~~)
  5. 黑马程序员linux运维上半部分笔记
  6. 安卓系统怎么安装软件_VMware安装Android x86虚拟机,实现在PC电脑上运行安卓系统...
  7. Pytorch学习之十九种损失函数
  8. 买保险怎么选保险公司?70%的人都错了
  9. webpack中配置vue
  10. 高仿喜马拉雅听Android客户端,Zhumulangma