apb_tb:tb是在dut侧;
导入tests、if文件;设定周期、复位;例化接口,将接口config_db到uvm_test_top.env.mst和slv中;
`timescale 1ps/1ps
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "lvc_apb_tests.svh"
`include "lvc_apb_if.sv"
module lvc_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_noneendlvc_apb_if intf(clk, rstn);initial beginuvm_config_db#(virtual lvc_apb_if)::set(uvm_root::get(), "uvm_test_top.env.mst", "vif", intf);uvm_config_db#(virtual lvc_apb_if)::set(uvm_root::get(), "uvm_test_top.env.slv", "vif", intf);run_test("lvc_apb_single_transaction_test");endendmodule
apb_master_seq_lib:
base_sequence extends uvm_sequence #(lvc_apb_transfer): new();
single_write:两个随机变量addr、data和表示状态的trans_status;body()中`uvm_do_with有约束的随机发送→get_response→将rsp返回的状态给trans_status;
single_read:`uvm_do_with状态改为READ,最后将rsp返回的data给data变量;
write_read:增加了变量idle_cycles,body()中两个`uvm_do_with;
burst_write:变量data变成data[ ]数组,软约束随机data的size和值;`uvm_do_with嵌入循环中地址进行移位和发送data;判断rsp返回的状态;
burst_read:uvm_do_with嵌入循环中地址进行移位,每次返回rsp中的data不断存入data[ ];判断rsp返回的状态;
`ifndef LVC_APB_MASTER_SEQ_LIB_SV
`define LVC_APB_MASTER_SEQ_LIB_SV//------------------------------------------------------------------------------
// SEQUENCE: default
//------------------------------------------------------------------------------
typedef class lvc_apb_transfer;
typedef class lvc_apb_master_sequencer;class lvc_apb_master_base_sequence extends uvm_sequence #(lvc_apb_transfer);`uvm_object_utils(lvc_apb_master_base_sequence) function new(string name=""); super.new(name);endfunction : newendclass : lvc_apb_master_base_sequence // USER: Add your sequences hereclass lvc_apb_master_single_write_sequence extends lvc_apb_master_base_sequence;rand bit [31:0] addr;rand bit [31:0] data;lvc_apb_trans_status trans_status;`uvm_object_utils(lvc_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);trans_status = rsp.trans_status;`uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)endtask: bodyendclass: lvc_apb_master_single_write_sequenceclass lvc_apb_master_single_read_sequence extends lvc_apb_master_base_sequence;rand bit [31:0] addr;rand bit [31:0] data;lvc_apb_trans_status trans_status;`uvm_object_utils(lvc_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);trans_status = rsp.trans_status;data = rsp.data;`uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)endtask: bodyendclass: lvc_apb_master_single_read_sequenceclass lvc_apb_master_write_read_sequence extends lvc_apb_master_base_sequence;rand bit [31:0] addr;rand bit [31:0] data;rand int idle_cycles; lvc_apb_trans_status trans_status;constraint cstr{idle_cycles == 0;}`uvm_object_utils(lvc_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;trans_status = rsp.trans_status;`uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)endtask: bodyendclass: lvc_apb_master_write_read_sequenceclass lvc_apb_master_burst_write_sequence extends lvc_apb_master_base_sequence;rand bit [31:0] addr;rand bit [31:0] data[];lvc_apb_trans_status trans_status;constraint cstr{soft data.size() inside {4, 8, 16, 32};foreach(data[i]) soft data[i] == addr + (i << 2);}`uvm_object_utils(lvc_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)trans_status = OK;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);trans_status = rsp.trans_status == ERROR ? ERROR : trans_status;`uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)endtask: body
endclass: lvc_apb_master_burst_write_sequenceclass lvc_apb_master_burst_read_sequence extends lvc_apb_master_base_sequence;rand bit [31:0] addr;rand bit [31:0] data[];lvc_apb_trans_status trans_status;constraint cstr{soft data.size() inside {4, 8, 16, 32};}`uvm_object_utils(lvc_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)trans_status = OK;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);trans_status = rsp.trans_status == ERROR ? ERROR : trans_status;`uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)endtask: body
endclass: lvc_apb_master_burst_read_sequence`endif // LVC_APB_MASTER_SEQ_LIB_SV
apb_test: 包括apb的env
apb_env extends uvm_env:例化mst、slv agent,并工厂创建;
apb_base_test extends uvm_test:例化apb_env、apb_cfg;注册、new();build()中工厂创建cfg,对cfg中控制pready次数、pslverr状态变量赋值,并set到env中,工厂创建env;
base_test_sequence extends uvm_sequence #(lvc_apb_transfer):定义bit[31:0] mem[bit[31:0]]; 函数check_mem_data是判断给入的addr在mem中有没有数据,有的话数据是不是等于给入的data,没有的话数据是不是等于默认数据;任务wait_reset_release等待一个reset下降沿和上升沿(重置和释放);任务wait_cycles 等待多个时钟周期;函数get_rand_addr 定义32位addr保证[31:12]和[1:0]位为0,其余随机,并返回addr;
single_transaction_sequence extends apb_base_test_sequence:例化mst中的单读 单写 先写后读的seq;定义控制发生次数的变量test_num;body()中定义32位addr变量,调用base test中任务wait_reset_release、wait_cycles 和 调用get_rand_addr返回一个addr赋值给addr做单写,并存放mem[addr]=addr中;做单读,并判断trans_status为OK后,调用check_mem_data检查mem[addr]中存放的addr数据和读回来的是否相同;和先写后读、写两次立刻读;
single_transaction_test:例化上一个seq,raise_objection,seq.start(env.mst.sequencer)开始传输将seq挂载到mst.sqr,drop_objection;
burst_transaction_sequence:例化burst_write_seq、burst_read_seq;定义传输次数;基本等同single_transaction_sequence;
burst_transaction_test;
`ifndef LVC_APB_TESTS_SV
`define LVC_APB_TESTS_SVimport lvc_apb_pkg::*;class lvc_apb_env extends uvm_env;lvc_apb_master_agent mst;lvc_apb_slave_agent slv;`uvm_component_utils(lvc_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 = lvc_apb_master_agent::type_id::create("mst", this);slv = lvc_apb_slave_agent::type_id::create("slv", this);endfunction
endclassclass lvc_apb_base_test extends uvm_test;lvc_apb_env env;lvc_apb_config cfg;`uvm_component_utils(lvc_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);cfg = lvc_apb_config::type_id::create("cfg");// USER TODO// Option-1 manually set the config parameters // cfg.slave_pready_default_value = 1;// cfg.slave_pready_random = 1;// cfg.slave_pslverr_random = 1;// Option-2 randomize the config parameters// void'(cfg.randomize());uvm_config_db#(lvc_apb_config)::set(this,"env.*","cfg", cfg);env = lvc_apb_env::type_id::create("env", this);endfunction
endclassclass lvc_apb_base_test_sequence extends uvm_sequence #(lvc_apb_transfer);bit[31:0] mem[bit[31:0]];`uvm_object_utils(lvc_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 != DEFAULT_READ_VALUE) begin`uvm_error("CMPDATA", $sformatf("addr 32'h%8x, READ DATA expected 32'h%8x != actual 32'h%8x", addr, DEFAULT_READ_VALUE, 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 lvc_apb_tb.rstn);@(posedge lvc_apb_tb.rstn);endtasktask wait_cycles(int n);repeat(n) @(posedge lvc_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;addr != 0;});return addr;endfunction
endclassclass lvc_apb_single_transaction_sequence extends lvc_apb_base_test_sequence;lvc_apb_master_single_write_sequence single_write_seq;lvc_apb_master_single_read_sequence single_read_seq;lvc_apb_master_write_read_sequence write_read_seq;rand int test_num = 100;constraint cstr{soft test_num == 100;}`uvm_object_utils(lvc_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;})if(single_read_seq.trans_status == OK)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;})if(single_read_seq.trans_status == OK)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;if(write_read_seq.trans_status == OK)void'(this.check_mem_data(addr, write_read_seq.data));end// TODO// TEST write twice and read immediately with burst transaction`uvm_info(get_type_name(), "TEST write twice and read immediately with burst transaction...", UVM_LOW)repeat(test_num) beginaddr = this.get_rand_addr();// WRITE first time`uvm_do_with(req, {trans_kind == WRITE; addr == local::addr; data == local::addr;idle_cycles == 0;})mem[addr] = addr;get_response(rsp);// WRITE second time`uvm_do_with(req, {trans_kind == WRITE; addr == local::addr; data == local::addr<<2;idle_cycles == 0;})mem[addr] = addr<<2;get_response(rsp);// READ immediately after WRITE`uvm_do_with(req, {trans_kind == READ; addr == local::addr;})get_response(rsp);if(rsp.trans_status == OK)void'(this.check_mem_data(addr, rsp.data));endthis.wait_cycles(10);endtask
endclass: lvc_apb_single_transaction_sequenceclass lvc_apb_single_transaction_test extends lvc_apb_base_test;`uvm_component_utils(lvc_apb_single_transaction_test)function new(string name, uvm_component parent);super.new(name, parent);endfunctiontask run_phase(uvm_phase phase);lvc_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: lvc_apb_single_transaction_testclass lvc_apb_burst_transaction_sequence extends lvc_apb_base_test_sequence;lvc_apb_master_burst_write_sequence burst_write_seq;lvc_apb_master_burst_read_sequence burst_read_seq;rand int test_num = 100;constraint cstr{soft test_num == 100;}`uvm_object_utils(lvc_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: lvc_apb_burst_transaction_sequenceclass lvc_apb_burst_transaction_test extends lvc_apb_base_test;`uvm_component_utils(lvc_apb_burst_transaction_test)function new(string name, uvm_component parent);super.new(name, parent);endfunctiontask run_phase(uvm_phase phase);lvc_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: lvc_apb_burst_transaction_test`endif // LVC_APB_TESTS_SV