结构说明
此环境用于快速实验ral的各种基本功能,简化到接近最低的组件要求。
UVM环境只需要 env, sqr, driver, test,再加上RAL所需的uvm_reg, uvm_reg_model, adapter。sqr更是只有一句话。
代码部分
宏定义
addr_defines.svh
`ifndef ADDR_DEFINES
`define ADDR_DEFINES
`define REG1_ADDR 'h0
`define REG2_ADDR 'h4
`define REG3_ADDR 'h8
`define REG4_ADDR 'hC
parameter AW = 8;
parameter DW = 16;
`endif
仿真代码
simplest_ral.sv
`include "addr_defines.svh"
import uvm_pkg::*;
//dut
module reg_rw(
input [AW-1:0] addr,
input [DW-1:0] din,
input clk,
input rstn,
input wr,
input rd,
output logic [DW-1:0] dout
);
logic [DW-1:0] reg1,reg2,reg3,reg4;
logic is_reg1_addr;
logic is_reg2_addr;
logic is_reg3_addr;
logic is_reg4_addr;
logic [DW-1:0] dout_tmp;
//RO
always@(posedge clk or negedge rstn)
if(!rstn)
reg1 <= 0;
else if(rd && is_reg1_addr) //
reg1 <= reg2 + 1;
//RW
always@(posedge clk or negedge rstn)
if(!rstn)
reg2 <= 0;
else if(wr && is_reg2_addr)
reg2 <= din;
else if(rd && is_reg2_addr) //
reg2 <= reg2 + 2;
//RC
always@(posedge clk or negedge rstn)
if(!rstn)
reg3 <= 0;
else if(rd && is_reg3_addr) //
reg3 <= 0;
else if(rd && is_reg2_addr && reg3==0)
reg3 <= 1; //record reg2 is read
//RS
always@(posedge clk or negedge rstn)
if(!rstn)
reg4 <= 0;
else if(rd && is_reg4_addr) //record reg3
reg4 <= '1;
else if(rd && is_reg1_addr && reg3==0)
reg4 <= 0; //record reg1 is read, negtive
assign is_reg1_addr = addr == `REG1_ADDR;
assign is_reg2_addr = addr == `REG2_ADDR;
assign is_reg3_addr = addr == `REG3_ADDR;
assign is_reg4_addr = addr == `REG4_ADDR;
always_comb begin
case(addr)
`REG1_ADDR: dout_tmp = reg1;
`REG2_ADDR: dout_tmp = reg2;
`REG3_ADDR: dout_tmp = reg3;
`REG4_ADDR: dout_tmp = reg4;
endcase
end
always@(posedge clk or negedge rstn)
if(!rstn)
dout <= 0;
else if(rd)
dout <= dout_tmp;
endmodule
interface reg_if(input bit clk);
logic rstn;
logic wr;
logic rd;
logic [AW-1:0] addr;
logic [DW-1:0] din;
logic [DW-1:0] dout;
clocking cb @(posedge clk);
output addr, din, wr, rd;
input dout;
endclocking
modport test(clocking cb,output rstn);
endinterface
//uvm begins
class reg_tr extends uvm_sequence_item;
rand logic [AW-1:0] addr;
rand logic wr;
rand logic rd;
rand logic [DW-1:0] din;
rand logic [DW-1:0] dout;
function new(string name="reg_tr");
super.new(name);
endfunction
constraint at_reg2_addr {
addr <= 'h20;
};
`uvm_object_utils_begin(reg_tr)
`uvm_field_int(addr,UVM_ALL_ON)
`uvm_field_int(wr,UVM_ALL_ON)
`uvm_field_int(rd,UVM_ALL_ON)
`uvm_field_int(din,UVM_ALL_ON)
`uvm_field_int(dout,UVM_ALL_ON)
`uvm_object_utils_end
endclass
class driver extends uvm_driver #(reg_tr);
virtual reg_if drv_if;
`uvm_component_utils(driver)
function new(string name="",uvm_component parent=null);
super.new(name,parent);
endfunction
extern virtual function void build_phase(uvm_phase phase);
extern task main_phase(uvm_phase phase);
endclass
function void driver::build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(virtual reg_if)::get(this,"","drv_if",drv_if);
endfunction
task driver::main_phase(uvm_phase phase);
drv_if.wr <= 1'b0;
drv_if.rd <= 1'b0;
drv_if.din <= 0;
drv_if.addr <= 0;
@(posedge drv_if.rstn);
while(1) begin
seq_item_port.get_next_item(req);
//drive access
@(drv_if.cb);
drv_if.addr <= req.addr;
if(req.wr) begin
drv_if.wr <= 1'b1;
drv_if.din <= req.din;
@(drv_if.cb);
drv_if.wr <= 1'b0;
end
if(req.rd) begin
drv_if.rd <= 1'b1;
@(drv_if.cb);
req.dout <= drv_if.dout;
drv_if.rd <= 1'b0;
end
@(drv_if.cb);
seq_item_port.item_done();
end
endtask
typedef uvm_sequencer #(reg_tr) sequencer;
class environment extends uvm_env;
driver drv;
sequencer sqr;
`uvm_component_utils(environment)
function new(string name="environment", uvm_component parent=null);
super.new(name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
drv = driver::type_id::create("drv",this);
sqr = sequencer::type_id::create("sqr",this);
endfunction
virtual function void connect_phase(uvm_phase phase);
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction
endclass
class adapter extends uvm_reg_adapter;
string tID = get_type_name();
`uvm_object_utils(adapter)
function new(string name="adapter");
super.new(name);
endfunction
function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
reg_tr tr;
tr =new("tr");
tr.addr = rw.addr;
if(rw.kind == UVM_READ) begin
tr.wr = 1'b0;
tr.rd = 1'b1;
end
else begin
tr.wr = 1'b1;
tr.rd = 1'b0;
tr.din = rw.data;
end
return tr;
endfunction : reg2bus
function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
reg_tr tr;
//`uvm_info($sformatf("%s",bus_item.get_type_name()),$sformatf("%s",bus_item.sprint()),UVM_MEDIUM);
if(!($cast(tr,bus_item))) begin
`uvm_fatal(tID,"wrong bus_item for bus2reg");
return;
end
rw.kind = tr.rd ? UVM_READ : UVM_WRITE;
rw.addr = tr.addr;
rw.data = tr.rd ? tr.dout : tr.din ;
rw.status = UVM_IS_OK;
endfunction
endclass
//ralf file
class reg1 extends uvm_reg;
`uvm_object_utils(reg1)
rand uvm_reg_field reg1_ro;
function new(string name="reg1");
super.new(name,16,UVM_NO_COVERAGE);
endfunction
extern virtual function void build();
endclass
function void reg1::build();
reg1_ro = uvm_reg_field::type_id::create("reg1_ro");
//parent, size,lsb,acc, vola, reset, has_reset, is_rand,indivi
reg1_ro.configure(this, 16, 0, "RO", 0, 'b0, 1, 1, 0);
endfunction
class reg2 extends uvm_reg;
`uvm_object_utils(reg2)
rand uvm_reg_field reg2_rw;
function new(string name="reg2");
super.new(name,16,UVM_NO_COVERAGE);
endfunction
extern virtual function void build();
endclass
function void reg2::build();
reg2_rw = uvm_reg_field::type_id::create("reg2_rw");
//parent, size,lsb,acc, vola, reset, has_reset, is_rand,indivi
reg2_rw.configure(this, 16, 0, "RW", 0, 'b0, 1, 1, 0);
endfunction
class reg3 extends uvm_reg;
`uvm_object_utils(reg3)
rand uvm_reg_field reg3_rc;
function new(string name="reg3");
super.new(name,16,UVM_NO_COVERAGE);
endfunction
extern virtual function void build();
endclass
function void reg3::build();
reg3_rc = uvm_reg_field::type_id::create("reg3_rc");
//parent, size,lsb,acc, vola, reset, has_reset, is_rand,indivi
reg3_rc.configure(this, 16, 0, "WRC", 0, 'b0, 1, 1, 0);
endfunction
class reg4 extends uvm_reg;
`uvm_object_utils(reg4)
rand uvm_reg_field reg4_rs;
function new(string name="reg4");
super.new(name,16,UVM_NO_COVERAGE);
endfunction
extern virtual function void build();
endclass
function void reg4::build();
reg4_rs = uvm_reg_field::type_id::create("reg4_rs");
//parent, size,lsb,acc, vola, reset, has_reset, is_rand,indivi
reg4_rs.configure(this, 16, 0, "RS", 0, 'b0, 1, 1, 0);
endfunction
class regmodel extends uvm_reg_block;
`uvm_object_utils(regmodel)
rand reg1 reg1_reg;
rand reg2 reg2_reg;
rand reg3 reg3_reg;
rand reg4 reg4_reg;
function new(string name = "regmodel");
super.new(name,UVM_NO_COVERAGE);
endfunction
extern virtual function void build();
endclass
function void regmodel::build() ;
// name, base, bytes,endian, byte_addressing
default_map = create_map("",0, 2, UVM_LITTLE_ENDIAN,0);
reg1_reg = reg1::type_id::create("reg1_reg");
// parent, file handle, name
reg1_reg.configure(this,null,"reg1");
reg1_reg.build();
reg2_reg = reg2::type_id::create("reg2_reg");
// parent, file handle, name
reg2_reg.configure(this,null,"reg2");
reg2_reg.build();
reg3_reg = reg3::type_id::create("reg3_reg");
// parent, file handle, name
reg3_reg.configure(this,null,"reg3");
reg3_reg.build();
reg4_reg = reg4::type_id::create("reg4_reg");
// parent, file handle, name
reg4_reg.configure(this,null,"reg4");
reg4_reg.build();
default_map.add_reg(reg1_reg,`REG1_ADDR,"RO");
default_map.add_reg(reg2_reg,`REG2_ADDR,"RW");
default_map.add_reg(reg3_reg,`REG3_ADDR,"RO");
default_map.add_reg(reg4_reg,`REG4_ADDR,"RO");
endfunction
//end of ralf
class tests extends uvm_test;
environment env;
regmodel rgm;
adapter adp;
`uvm_component_utils(tests)
function new(string name="tests",uvm_component parent=null);
super.new(name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = environment::type_id::create("env",this);
rgm = regmodel::type_id::create("rgm",this);
rgm.build();
rgm.lock_model();
rgm.reset();
adp = new("adp");
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
rgm.default_map.set_sequencer(env.sqr,this.adp);
rgm.default_map.set_auto_predict(1);
endfunction
virtual function void start_of_simulation_phase(uvm_phase phase);
uvm_top.print_topology();
check_config_usage();
endfunction
virtual task main_phase(uvm_phase phase);
uvm_status_e status;
uvm_reg_data_t value;
phase.phase_done.set_drain_time(this,1000);
phase.raise_objection(this);
//first write and read reg2
rgm.reg2_reg.write(status,0,UVM_FRONTDOOR);
rgm.reg2_reg.read(status,value,UVM_FRONTDOOR);
`uvm_info(get_full_name(),$sformatf("%s:%0h",rgm.reg2_reg.get_full_name(),value),UVM_MEDIUM);
rgm.reg2_reg.write(status,1,UVM_FRONTDOOR);
rgm.reg2_reg.read(status,value,UVM_FRONTDOOR);
`uvm_info(get_full_name(),$sformatf("%s:%0h",rgm.reg2_reg.get_full_name(),value),UVM_MEDIUM);
rgm.reg2_reg.read(status,value,UVM_FRONTDOOR);
`uvm_info(get_full_name(),$sformatf("%s:%0h",rgm.reg2_reg.get_full_name(),value),UVM_MEDIUM);
rgm.reg2_reg.read(status,value,UVM_FRONTDOOR);
`uvm_info(get_full_name(),$sformatf("%s:%0h",rgm.reg2_reg.get_full_name(),value),UVM_MEDIUM);
//reg2 should be 7
//reg1 = reg2 +1; reg3 records if reg2 is read; reg4 records (low active) if reg1 is read;
//then write and read reg1, reg3
rgm.reg4_reg.read(status,value,UVM_FRONTDOOR);
`uvm_info(get_full_name(),$sformatf("%s:%0h",rgm.reg4_reg.get_full_name(),value),UVM_MEDIUM);
rgm.reg1_reg.read(status,value,UVM_FRONTDOOR);
`uvm_info(get_full_name(),$sformatf("%s:%0h",rgm.reg1_reg.get_full_name(),value),UVM_MEDIUM);
rgm.reg1_reg.read(status,value,UVM_FRONTDOOR);
`uvm_info(get_full_name(),$sformatf("%s:%0h",rgm.reg1_reg.get_full_name(),value),UVM_MEDIUM);
rgm.reg3_reg.read(status,value,UVM_FRONTDOOR);
`uvm_info(get_full_name(),$sformatf("%s:%0h",rgm.reg3_reg.get_full_name(),value),UVM_MEDIUM);
rgm.reg3_reg.read(status,value,UVM_FRONTDOOR);
`uvm_info(get_full_name(),$sformatf("%s:%0h",rgm.reg3_reg.get_full_name(),value),UVM_MEDIUM);
rgm.reg4_reg.read(status,value,UVM_FRONTDOOR);
`uvm_info(get_full_name(),$sformatf("%s:%0h",rgm.reg4_reg.get_full_name(),value),UVM_MEDIUM);
#100;
phase.drop_objection(this);
endtask
endclass
//end of uvm
//test top
module tb_top;
bit clk, rstn;
initial begin
clk = 1'b0;
rstn = 1'b0;
#100;
rstn = 1'b1;
#10ms;
$finish;
end
initial begin
forever begin
#10 clk = ~clk;
end
end
initial begin
$fsdbDumpvars;
$fsdbDumpon;
end
reg_if reg2_if(clk);
assign reg2_if.rstn = rstn;
reg_rw u_dut(.clk(clk),
.rstn(reg2_if.rstn),
.wr(reg2_if.wr),
.rd(reg2_if.rd),
.addr(reg2_if.addr),
.din(reg2_if.din),
.dout(reg2_if.dout)
);
initial begin
uvm_config_db#(virtual reg_if)::set(
null,"uvm_test_top.env.drv","drv_if",reg2_if);
run_test();
end
endmodule
VCS仿真环境
demo1 — src
| – sim
sim/makefile
TIMESCALE := +timescale=1ns/1ps
INC_DIR := +incdir+../src
DEMO1_FILES := ../src/simplest_ral.sv
CASENAME := tests
SV := -full64 -sverilog -debug_access+all -kdb -lca ${INC_DIR} -timescale=1ns/10ps
UVM := -ntb_opts uvm +UVM_TESTNAME=${CASENAME}
compile_demo1:
vcs ${SV} ${UVM} +memcbk -top tb_top ${DEMO1_FILES}
run_demo1:
./simv +UVM_TESTNAME=${CASENAME} +UVM_VERDI_TRACE="UVM_AWARE|RAL|TLM|TLM2|IMP|HIER" +UVM_LOG_RECORD +UVM_TR_RECORD
verdi:
verdi -ssf novas.fsdb -nologo &