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 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
lvc_apb_if intf(clk, rstn);
initial begin
uvm_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");
end
endmodule
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 : new
endclass : lvc_apb_master_base_sequence
// USER: Add your sequences here
class 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 : 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);
trans_status = rsp.trans_status;
`uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
endtask: body
endclass: lvc_apb_master_single_write_sequence
class 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 : 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);
trans_status = rsp.trans_status;
data = rsp.data;
`uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
endtask: body
endclass: lvc_apb_master_single_read_sequence
class 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 : 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;
trans_status = rsp.trans_status;
`uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
endtask: body
endclass: lvc_apb_master_write_read_sequence
class 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 : new
virtual 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_sequence
class 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 : new
virtual 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_SV
import 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);
endfunction
function 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
endclass
class 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);
endfunction
function 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
endclass
class 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 : 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 != 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;
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 lvc_apb_tb.rstn);
@(posedge lvc_apb_tb.rstn);
endtask
task wait_cycles(int n);
repeat(n) @(posedge lvc_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;addr != 0;});
return addr;
endfunction
endclass
class 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 : 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;})
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) 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;})
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) begin
addr = 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) begin
addr = 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));
end
this.wait_cycles(10);
endtask
endclass: lvc_apb_single_transaction_sequence
class 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);
endfunction
task 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_test
class 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 : 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: lvc_apb_burst_transaction_sequence
class 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);
endfunction
task 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