搭建文件框架
ahb_gpio_config
`ifndef AHB_GPIO_CONFIG_SV
`define AHB_GPIO_CONFIG_SV
class ahb_gpio_config extends uvm_object;
int seq_check_count;
int seq_check_error;
int scb_check_count;
int scb_check_error;
bit scb_enable = 1;
bit cov_enable = 1;
ahb_agent_configuration ahb_cfg;
virtual ahb_gpio_if vif;
ahb_gpio_rgm rgm;
`uvm_object_utils(ahb_gpio_config)
// USER to specify the config items
function new (string name = "ahb_gpio_config");
super.new(name);
ahb_cfg = ahb_agent_configuration::type_id::create("ahb_cfg");
endfunction : new
endclass
`endif // AHB_GPIO_CONFIG_SV
ahb_gpio_cov
`ifndef AHB_GPIO_COV_SV
`define AHB_GPIO_COV_SV
class ahb_gpio_cov extends ahb_gpio_subscriber;
`uvm_component_utils(ahb_gpio_cov)
function new (string name = "ahb_gpio_cov", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
endfunction
task do_listen_events();
endtask
virtual function void write(ahb_transaction tr);
endfunction
endclass
`endif // AHB_GPIO_COV_SV
env
ahb_gpio_env
`ifndef AHB_GPIO_ENV_SV
`define AHB_GPIO_ENV_SV
class ahb_gpio_env extends uvm_env;
ahb_master_agent ahb_mst;
ahb_gpio_config cfg;
ahb_gpio_virtual_sequencer virt_sqr;
ahb_gpio_rgm rgm;
ahb_gpio_adapter adapter;
uvm_reg_predictor #(ahb_transaction) predictor;
ahb_gpio_cov cov;
ahb_gpio_scoreboard scb;
`uvm_component_utils(ahb_gpio_env)
function new (string name = "ahb_gpio_env", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// Get configuration from test layer
if(!uvm_config_db#(ahb_gpio_config)::get(this,"","cfg", cfg)) begin
`uvm_fatal("GETCFG","cannot get config object from config DB")
end
uvm_config_db#(ahb_gpio_config)::set(this, "virt_sqr", "cfg", cfg);
uvm_config_db#(ahb_gpio_config)::set(this, "cov", "cfg", cfg);
uvm_config_db#(ahb_gpio_config)::set(this, "scb", "cfg", cfg);
uvm_config_db#(ahb_agent_configuration)::set(this, "ahb_mst", "cfg", cfg.ahb_cfg);
ahb_mst = ahb_master_agent::type_id::create("ahb_mst", this);
virt_sqr = ahb_gpio_virtual_sequencer::type_id::create("virt_sqr", this);
if(!uvm_config_db#(ahb_gpio_rgm)::get(this,"","rgm", rgm)) begin
rgm = ahb_gpio_rgm::type_id::create("rgm", this);
rgm.build();
end
uvm_config_db#(ahb_gpio_rgm)::set(this, "*", "rgm", rgm);
adapter = ahb_gpio_adapter::type_id::create("adapter");
predictor = uvm_reg_predictor#(ahb_transaction)::type_id::create("predictor", this);
scb = ahb_gpio_scoreboard::type_id::create("scb", this);
cov = ahb_gpio_cov::type_id::create("cov", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
virt_sqr.ahb_mst_sqr = ahb_mst.sequencer;
rgm.map.set_sequencer(ahb_mst.sequencer, adapter);
ahb_mst.monitor.item_observed_port.connect(predictor.bus_in);
predictor.map = rgm.map;
predictor.adapter = adapter;
ahb_mst.monitor.item_observed_port.connect(cov.ahb_trans_observed_imp);
ahb_mst.monitor.item_observed_port.connect(scb.ahb_trans_observed_imp);
endfunction
function void report_phase(uvm_phase phase);
string reports = "\n";
super.report_phase(phase);
reports = {reports, $sformatf("================================================= \n")};
reports = {reports, $sformatf("CURRENT TEST SUMMARY \n")};
reports = {reports, $sformatf("SEQUENCE CHECK COUNT : %0d \n", cfg.seq_check_count)};
reports = {reports, $sformatf("SEQUENCE CHECK ERROR : %0d \n", cfg.seq_check_error)};
reports = {reports, $sformatf("SCOREBOARD CHECK COUNT : %0d \n", cfg.scb_check_count)};
reports = {reports, $sformatf("SCOREBOARD CHECK COUNT : %0d \n", cfg.scb_check_error)};
reports = {reports, $sformatf("================================================= \n")};
`uvm_info("TEST_SUMMARY",reports, UVM_LOW);
endfunction
endclass
`endif // AHB_GPIO_ENV_SV
ahb_gpio_pkg
`ifndef AHB_GPIO_PKG_SV
`define AHB_GPIO_PKG_SV
package ahb_gpio_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
import ahb_pkg::*;
`include "ahb_gpio_reg.sv"
`include "ahb_gpio_config.sv"
`include "ahb_gpio_adapter.sv"
`include "ahb_gpio_subscriber.sv"
`include "ahb_gpio_cov.sv"
`include "ahb_gpio_scoreboard.sv"
`include "ahb_gpio_virtual_sequencer.sv"
`include "ahb_gpio_env.sv"
`include "ahb_gpio_seq_lib.svh"
`include "ahb_gpio_tests.svh"
endpackage
`endif // AHB_GPIO_PKG_SV
ahb_gpio_adapter
`ifndef AHB_GPIO_ADAPTER_SV
`define AHB_GPIO_ADAPTER_SV
class ahb_gpio_adapter extends uvm_reg_adapter;
`uvm_object_utils(ahb_gpio_adapter)
function new(string name = "ahb_gpio_adapter");
super.new(name);
endfunction
function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
endfunction
function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
endfunction
endclass
`endif //AHB_GPIO_ADAPTER_SV
ahb_gpio_subsriber
`ifndef AHB_GPIO_SUBSCRIBER_SV
`define AHB_GPIO_SUBSCRIBER_SV
`uvm_analysis_imp_decl(_apb)
class ahb_gpio_subscriber extends uvm_component;
// analysis import
uvm_analysis_imp #(ahb_transaction, ahb_gpio_subscriber) ahb_trans_observed_imp;
// Declare events
ahb_gpio_config cfg;
virtual ahb_gpio_if vif;
`uvm_component_utils(ahb_gpio_subscriber)
function new(string name = "ahb_gpio_subscriber", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
ahb_trans_observed_imp = new("ahb_trans_observed_imp", this);
// Get configuration from test layer
if(!uvm_config_db#(ahb_gpio_config)::get(this, "", "cfg", cfg)) begin
`uvm_fatal("GETCFG", "cannot get conifg object from config db")
end
vif = cfg.vif;
endfunction
task run_phase(uvm_phase phase);
super.run_phase(phase);
do_events_trigger();
do_listen_events();
endtask
virtual function void write(ahb_transaction tr);
endfunction
virtual task do_events_trigger();
endtask
virtual task do_listen_events();
endtask
endclass
`endif // AHB_GPIO_SUBSCRIBER_SV
ahb_gpio_scoreboard
`ifndef AHB_GPIO_SCOREBOARD_SV
`define AHB_GPIO_SCOREBOARD_SV
class ahb_gpio_scoreboard extends ahb_gpio_subscriber;
bit [31:0] mem [int unsigned];
// Events of scoreboard
`uvm_component_utils(ahb_gpio_scoreboard)
function new (string name = "ahb_gpio_scoreboard", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
endfunction
task run_phase(uvm_phase phase);
super.run_phase(phase);
do_data_check();
endtask
virtual function void write(ahb_transaction tr);
endfunction
task do_listen_events();
endtask
virtual task do_data_check();
endtask
endclass
`endif // AHB_GPIO_SCOREBOARD_SV
reg
ahb_gpio_reg
`ifndef AHB_GPIO_REG_SV
`define AHB_GPIO_REG_SV
class ahb_gpio_rgm extends uvm_reg_block;
`uvm_object_utils(ahb_gpio_rgm)
uvm_reg_map map;
function new (string name = "ahb_gpio_rgm");
super.new(name, UVM_NO_COVERAGE);
endfunction
virtual function build();
map = create_map("map", 'h0, 4, UVM_LITTLE_ENDIAN);
lock_model();
endfunction
endclass
`endif // AHB_GPIO_REG_SV
seq_lib
elem_seqs
ahb_element_sequences
`ifndef AHB_GPIO_ELEMENT_SEQUENCES_SVH
`define AHB_GPIO_ELEMENT_SEQUENCES_SVH
`include "ahb_gpio_element_base_seq.sv"
`include "ahb_gpio_single_write_seq.sv"
`include "ahb_gpio_single_read_seq.sv"
`endif // AHB_GPIO_ELEMENT_SEQUENCES_SVH
ahb_gpio_element_base_seq
`ifndef AHB_GPIO_ELEMENT_BASE_SEQ_SV
`define AHB_GPIO_ELEMENT_BASE_SEQ_SV
class ahb_gpio_element_base_seq extends uvm_sequence;
ahb_gpio_config cfg;
virtual ahb_gpio_if vif;
ahb_gpio_rgm rgm;
bit [31:0] rd_val, wr_val;
uvm_status_e status;
`uvm_object_utils(ahb_gpio_element_base_seq)
`uvm_declare_p_sequencer(ahb_gpio_virtual_sequencer)
function new(string name = "ahb_gpio_element_base_seq");
super.new(name);
endfunction
virtual task body();
`uvm_info("body","Entered...",UVM_LOW)
//Get cfg from p_sequencer
cfg = p_sequencer.cfg;
vif = cfg.vif;
rgm = cfg.rgm;
`uvm_info("body","Exiting...",UVM_LOW)
endtask
virtual function void compare_data(logic[31:0] val1, logic[31:0] val2);
cfg.seq_check_count++;
if(val1 === val2)
`uvm_info("CMPSUC", $sformatf("val1 'h%0x === val2 'h%0x", val1, val2), UVM_LOW)
else begin
cfg.seq_check_error++;
`uvm_error("CMPSUC", $sformatf("val1 'h%0x !== val2 'h%0x", val1, val2))
end
endfunction
endclass
`endif //AHB_GPIO_ELEMENT_BASE_SEQ_SV
ahb_gpio_seq_lib.svh
`ifndef AHB_GPIO_SEQ_LIB_SVH
`define AHB_GPIO_SEQ_LIB_SVH
`include "ahb_gpio_element_sequences.svh"
`include "ahb_gpio_base_virt_seq.sv"
`include "ahb_gpio_portout_set_virt_seq.sv"
`endif // AHB_GPIO_SEQ_LIB_SVH
ahb_gpio_base_virt_seq
`ifndef AHB_GPIO_BASE_VIRT_SEQ_SV
`define AHB_GPIO_BASE_VIRT_SEQ_SV
class ahb_gpio_base_virt_seq extends uvm_sequence;
ahb_gpio_config cfg;
virtual ahb_gpio_if vif;
ahb_gpio_rgm rgm;
bit [31:0] rd_val, wr_val;
uvm_status_e status;
// declaration of element sequence
ahb_gpio_single_write_seq single_write;
ahb_gpio_single_read_seq single_read;
`uvm_object_utils(ahb_gpio_base_virt_seq)
`uvm_declare_p_sequencer(ahb_gpio_virtual_sequencer)
function new(string name = "ahb_gpio_base_virt_seq");
super.new(name);
endfunction
virtual task body();
`uvm_info("body","Entered...",UVM_LOW)
//Get cfg from p_sequencer
cfg = p_sequencer.cfg;
vif = cfg.vif;
rgm = cfg.rgm;
wait_ready_for_stim();
// TO do in sub_class
`uvm_info("body","Exiting...",UVM_LOW)
endtask
virtual function void compare_data(logic[31:0] val1, logic[31:0] val2);
cfg.seq_check_count++;
if(val1 === val2)
`uvm_info("CMPSUC", $sformatf("val1 'h%0x === val2 'h%0x", val1, val2), UVM_LOW)
else begin
cfg.seq_check_error++;
`uvm_error("CMPSUC", $sformatf("val1 'h%0x !== val2 'h%0x", val1, val2))
end
endfunction
task wait_reset_asserted();
@(posedge vif.rst_n);
endtask
task wait_reset_released();
@(negedge vif.rst_n);
endtask
task wait_cycles(int n = 1);
repeat(n) @(posedge vif.clk);
endtask
task wait_ready_for_stim();
wait_reset_released();
wait_cycles(10);
endtask
endclass
`endif //AHB_GPIO_BASE_VIRT_SEQ
test
ahb_gpio_base_test
`ifndef AHB_GPIO_BASE_TEST_SV
`define AHB_GPIO_BASE_TEST_SV
virtual class ahb_gpio_base_test extends uvm_test;
ahb_gpio_config cfg;
ahb_gpio_env env;
ahb_gpio_rgm rgm;
function new(string name = "ahb_gpio_base_test", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
rgm = ahb_gpio_rgm::type_id::create("rgm");
rgm.build();
uvm_config_db#(ahb_gpio_rgm)::set(this, "env", "rgm", rgm);
cfg = ahb_gpio_config::type_id::create("cfg");
cfg.rgm = rgm;
// do pagpioeter configuration
cfg.addr_start = 32'h0;
cfg.addr_end = 32'h0000_FFFF;
if(!uvm_config_db#(virtual ahb_gpio_if)::get(this,"","vif", cfg.vif)) begin
`uvm_fatal("GETCFG","cannot get virtual interface from config DB")
end
uvm_config_db#(ahb_gpio_config)::set(this, "env", "cfg", cfg);
env = ahb_gpio_env::type_id::create("env", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
endfunction
task run_phase(uvm_phase phase);
super.run_phase(phase);
phase.phase_done.set_drain_time(this, 1us);
phase.raise_objection(this);
phase.drop_objection(this);
endtask
endclass
`endif //AHB_GPIO_BASE_TEST
ahb_gpio_portout_set_test
`ifndef AHB_RAM_SMOKE_TEST_SV
`define AHB_RAM_SMOKE_TEST_SV
class ahb_ram_smoke_test extends ahb_ram_base_test;
`uvm_component_utils(ahb_ram_smoke_test)
function new(string name = "ahb_ram_smoke_test", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
endfunction
task run_phase(uvm_phase phase);
ahb_ram_smoke_virt_seq seq = ahb_ram_smoke_virt_seq::type_id::create("this");
super.run_phase(phase);
phase.raise_objection(this);
seq.start(env.virt_sqr);
phase.drop_objection(this);
endtask
endclass
`endif //AHB_RAM_SMOKE_TEST_SV
tb
ahb_gpio_if
`ifndef AHB_GPIO_IF_SV
`define AHB_GPIO_IF_SV
interface ahb_gpio_if;
logic clk;
logic rst_n;
initial begin : rst_n_gen
assert_reset(10);
end
task automatic assert_reset(int nclks = 1, int delay = 0);
#(delay * 1ns);
repeat(nclks) @(posedge clk);
rst_n <= 0;
repeat(5) @(posedge clk);
rst_n <= 1;
endtask
endinterface
`endif // AHB_GPIO_IF_SV
ahb_gpio_tb
当我们完成了上述的这些步骤以后,接下来就需要搭建编辑tb文件了,完成tb和DUT端口的连接:
`ifndef AHB_RAM_TB_SV
`define AHB_RAM_TB_SV
module ahb_ram_tb;
import uvm_pkg::*;
`include "uvm_macros.svh"
import ahb_ram_pkg::*;
logic clk;
logic rst_n;
initial begin : generate_clk
clk = 0;
forever #2ns clk = !clk;
end
// AHB_RAM -> AHB_GPIO
ahb_blockram_32 #(.ADDRESSWIDTH(16)) dut(
.HCLK (ahb_if.hclk),
.HRESETn (ahb_if.hreset_n),
.HSELBRAM (1'b1),
.HREADY (ahb_if.hready),
.HTRANS (ahb_if.htrans),
.HSIZE (ahb_if.hsize),
.HWRITE (ahb_if.hwrite),
.HADDR (ahb_if.haddr),
.HWDATA (ahb_if.hwdata),
.HREADYOUT (ahb_if.hready),
.HRESP (ahb_if.hresp),
.HRDATA (ahb_if.hrdata)
);
module cmsdk_ahb_gpio
#(// Parameter to define valid bit pattern for Alternate functions
// If an I/O pin does not have alternate function its function mask
// can be set to 0 to reduce gate count.
//
// By default every bit can have alternate function
parameter ALTERNATE_FUNC_MASK = 16'hFFFF,
// Default alternate function settings
parameter ALTERNATE_FUNC_DEFAULT = 16'h0000,
// By default use little endian
parameter BE = 0
)
// ----------------------------------------------------------------------------
// Port Definitions
// ----------------------------------------------------------------------------
(// AHB Inputs
.HCLK, (ahb_if.hclk) // system bus clock
.HRESETn, (ahb_if.hreset_n) // system bus reset
.FCLK, (ahb_if.hclk) // system bus clock
.HSEL, (1'b1) // AHB peripheral select
.HREADY, (ahb_if.hready) // AHB ready input
.HTRANS, (ahb_if.htrans) // AHB transfer type
.HSIZE, (ahb_if.hsize) // AHB hsize
.HWRITE, (ahb_if.hwrite) // AHB hwrite
.HADDR, (ahb_if.haddr) // AHB address bus
.HWDATA, (ahb_if.hwdata) // AHB write data bus
.ECOREVNUM, (4'b0110) // Engineering-change-order revision bits
.PORTIN, (ahb_gpio_if.portin) // GPIO Interface input
.HREADYOUT, (ahb_if.hready) // AHB ready output to S->M mux
.HRESP, (ahb_if.hresp) // AHB response
.HRDATA, (ahb_if.hrdata)
.PORTOUT, (ahb_gpio_if.portout) // GPIO output
.PORTEN, (ahb_gpio_if.porten) // GPIO output enable
.PORTFUNC, (ahb_gpio_if.portfunc) // Alternate function control
.GPIOINT, (ahb_gpio_if.gpioint) // Interrupt output for each pin
.COMBINT (ahb_gpio_if.combint) // Combined interrupt
);
// ----------------------------------------------------------------------------
// Internal wires
// ----------------------------------------------------------------------------
wire [31:0] IORDATA; // I/0 read data bus
wire IOSEL; // Decode for peripheral
wire [11:0] IOADDR; // I/O transfer address
wire IOWRITE; // I/O transfer direction
wire [1:0] IOSIZE; // I/O transfer size
wire IOTRANS; // I/O transaction
wire [31:0] IOWDATA; // I/O write data bus
ahb_if ahb_if();
assign ahb_if.hclk = clk;
assign ahb_if.hreset_n = rst_n;
assign ahb_if.hgrant = 1'b1;
ahb_ram_if ahb_ram_if();
assign ahb_ram_if.hclk = clk;
assign ahb_ram_if.fclk = clk;
assign rst_n = ahb_ram_if.rst_n;
initial begin
uvm_config_db #(virtual ahb_if)::set(uvm_root::get(), "uvm_test_top.env.ahb_mst", "vif", ahb_if);
uvm_config_db #(virtual ahb_ram_if)::set(uvm_root::get(), "uvm_test_top", "vif", ahb_ram_if);
uvm_config_db #(virtual ahb_ram_if)::set(uvm_root::get(), "uvm_test_top.env", "vif", ahb_ram_if);
uvm_config_db #(virtual ahb_ram_if)::set(uvm_root::get(), "uvm_test_top.env.virt_sqr", "vif", ahb_ram_if);
run_test();
end
endmodule
`endif // AHB_RAM_TB_SV
- 在连接的过程中,我们可以将DUT中左边与AHB相关的,将他们的信号连接到ahb_if中的信号中,DUT右边的信号与AHB协议没有关系,因此我们需要在当前这个模块的if中声明这些信号并将他们进行连接;
- 另外我们需要注意的就是:
在整个GPIO的模块中是有hclk和fclk两个时钟的,所以我们需要在ahb_gpio_if中声明这两个信号并将他们与tb中的时钟信号进行连接。
后面的括号中放置的一些我们用于端口中的典型的特征值,因此我们不需要修改,直接使用设计代码中的默认值即可
Makefile & 查看波形文件
最后我们重新编辑一下makefile文件,并在sim_run.do文件中放入该波形文件的名称:
#############################
# User variables
#############################
TB = ahb_gpio_tb
SEED = 1
GUI ?= 0
COV ?= 0
DOTCL ?= 1
VERB ?= UVM_HIGH
OUT ?= out
TESTNAME ?= ahb_gpio_portout_set_test
DFILES = ../../verilog/{cmsdk_ahb_gpio.v,cmsdk_ahb_to_iop.v,cmsdk_iop_gpio.v}
VFILES += ../vip_lib/ahb_pkg/ahb_pkg.sv \
../vip_lib/ahb_pkg/ahb_if.sv \
../env/ahb_gpio_pkg.sv \
../tb/ahb_gpio_if.sv \
../tb/ahb_gpio_tb.sv
#############################
# Environment variables
#############################
VCOMP_INC = +incdir+../../verilog \
+incdir+../vip_lib/ahb_pkg/{.,sequence_lib} \
+incdir+../{cfg,cov,reg,env,seq_lib,seq_lib/elem_seqs,test}
VCOMP = vlogan -full64 -ntb_opts uvm-1.2 -sverilog -timescale=1ps/1ps -nc -l $(OUT)/log/comp.log $(VCOMP_INC)
ELAB = vcs -full64 -ntb_opts uvm-1.2 -debug_acc+all -l $(OUT)/log/elab.log -sim_res=1ps
RUN = $(OUT)/obj/$(TB).simv -l run.log -sml +ntb_random_seed=$(SEED) +UVM_TESTNAME=$(TESTNAME) +UVM_VERBOSITY=$(VERB) -cm_dir $(CM_DIR) -cm_name $(CM_NAME)
COV_OPTS = -full64 -dir $(CM_DIR)
CM_DIR ?= $(OUT)/cov.vdb
CM_NAME ?= $(TESTNAME)_$(SEED)
SIMRUNFILE = ahb_gpio_sim_run.do
ifeq ($(GUI),1)
RUN += -gui
endif
ifeq ($(DOTCL),1)
RUN += -ucli -do $(SIMRUNFILE)
endif
ifeq ($(COV),1)
ELAB += -cm line+cond+fsm+tgl+branch+assert -cm_dir $(CM_DIR)
RUN += -cm line+cond+fsm+tgl+branch+assert -covg_cont_on_error
endif
prepare:
mkdir -p $(OUT)/work
mkdir -p $(OUT)/log
mkdir -p $(OUT)/sim
mkdir -p $(OUT)/obj
mkdir -p .shadow
comp: prepare
$(VCOMP)
$(VCOMP) $(DFILES) $(VFILES)
elab: comp
$(ELAB) -top $(TB) -o $(OUT)/obj/$(TB).simv
run:
$(RUN)
mergecov:
urg -format both $(COV_OPTS)
dvecov:
dve $(COV_OPTS)
verdicov:
verdi -cov -covdir $(CM_DIR)
htmlcov:
firefox urgReport/dashboard.html
clean:
rm -rf $(OUT) 64 AN.DB DVEfiles csrc *.simv *.simv.daidir *.simv.vdb ucli.key
rm -rf *.log* *.vpd *.h urgReport
【调试及结果】
我们运行默认的ahb_gpio_portout_set_test,运行之后打打开uvm_debug,可以发现没有错误出现且所有phase均正常运行。