目录
1.什么是寄存器
寄存器可以理解为数字设计模块中,软件可以控制的配置单元。硬件在特定的配置下以特定的方式运行。
例如我们可以设计一个32位的寄存器,其中包含了多个独立的字段,每个字段代表特定的功能,在需要时,可以由软件对其进行配置。
偏移 | 名字 | 类型 | 宽度 | 含义 |
---|---|---|---|---|
0 | en | RW | 1 | 使能,1表示使用,0表示禁用 |
1 | mode | RW | 3 | 支持的模式: 000: xxx 001: xxx …… |
4 | halt | RW | 1 | 暂停。1表示暂停,0表示继续 |
5 | auto | RW | 1 | 自动。1表示自动。 |
6 | reserved | RO | 5 | 保留 |
11 | speed | RW | 5 | 设置速度 |
16 | reserved | RO | 16 | 保留 |
2.寄存器块
将寄存器组合到一起,每个寄存器有自己的字段和配置,我们可以根据相关地址进行访问,这就是一个寄存器块。
因为我们的寄存器是32bit,所以每个的寄存器的偏移是0x4。
3.UVM寄存器模型
我们可以通过UVM RAL来定义单个字段,寄存器和寄存器块。寄存器模型是一个实体,它包含了我们需要描述的每个寄存器,以及每个寄存器的各个字段。我们可以通过寄存器模型对设计执行读写的操作。
3.1 期望值
字面意思。就是我们预期寄存器的值。在寄存器模型内部需要通过一个更新动作,更新对应寄存器的值。
3.2 镜像值
寄存器模型中对寄存器内部值的一个镜像。每次对寄存器的读写操作,都可能更新寄存器内部的值,有了镜像值后,我们不用每次都去读取寄存器的值,可以直接读取镜像值即可。
3.3 创建寄存器类
class my_reg extends uvm_reg;
//申明寄存器字段
rand vum_reg_field en;
rand uvm_reg_field mode;
rand uvm_reg_field halt;
rand uvm_reg_field auto;
rand uvm_reg_field speed;
function new (string name = "my_reg");
super.new(name);
endfunction
//定义寄存器相关内容
virtual function void build();
//创建每个字段
this.en =uvm_reg_field::type_id::create("en");
this.mode =uvm_reg_field::type_id::create("mode");
this.halt =uvm_reg_field::type_id::create("halt");
this.auto =uvm_reg_field::type_id::create("auto");
this.speed =uvm_reg_field::type_id::create("speed");
//配置每个字段
this.en.configure(this, 1, 0, "RW", 0, 1'h0, 1, 1, 1);
this.mode.configure(this, 3, 1, "RW", 0, 3'h2, 1, 1, 1);
this.halt.configure(this, 1, 4, "RW", 0, 1'h1, 1, 1, 1);
this.auto.configure(this, 1, 5, "RW", 0, 1'h0, 1, 1, 1);
this.speed.configure(this, 5, 11, "RW", 0, 5'h1c, 1, 1, 1);
endfunction
endclass
类似的,我们可以对每个寄存器进行定义。
3.4 定义寄存器块
寄存器块就是寄存器模型,可以用于对寄存器的访问。
class my_reg_block extends uvm_reg_block;
rand reg_1 reg1;
rand reg_2 reg2;
`uvm_object_utils(my_reg_block )
function new(string name = "my_reg_block ");
super.new(name, build_coverage(UVM_NO_COVERAGE));
endfunction
virtual function void build();
this.default_map = create_map("", 0, 4, UVM_LITTLE_ENDIAN, 0);
this.reg1 = reg_ctl::type_id::create("reg1", get_full_name());
this.reg1.configure(this, null, "");
this.reg1.build();
this.default_map.add_reg(this.reg1, `UVM_REG_ADDR_WIDTH'h0, "RW", 0);
this.reg2 = reg_stat::type_id::create("reg2", get_full_name());
this.reg2.configure(this, null, "");
this.reg2.build();
this.default_map.add_reg(this.reg2, `UVM_REG_ADDR_WIDTH'hc, "RO", 0);
endfunction
3.5 寄存器env
class my_env extends uvm_env;
`uvm_component_utils(my_env)
function new(string name = "my_env", uvm_component parent);
super.new(name, parent);
endfunction
reg_env m_reg_env;
my_agent m_agent;
virtual function void build_phase (uvm_phase phase);
super.build_phase(phase);
m_reg_env = reg_env::type_id::create("m_reg_env", this);
m_agent = my_agent::type_id::create("m_agent", this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
m_agent.m_mon.mon_ap.connect(m_reg_env.m_apb2reg_predictor.bus_in);
m_reg_env.m_ral_model.default_map.set_sequencer(m_agent.m_seqr, m_reg_env.m_reg2apb);
endfunction
endclass
4.完整例子
4.1设计
基于APB协议的一个模型
module traffic (
input pclk,
input presetn,
input[31:0] paddr,
input[31:0] pwdata,
input psel,
input pwrite,
input penable,
output[31:0] prdata
);
reg [3:0] ctl_reg;
reg [1:0] stat_reg;
reg [31:0] timer_0;
reg [31:0] timer_1;
reg [31:0] data_in;
reg [31:0] rdata_tmp;
always @(posedge pclk) begin
if (!presetn) begin
data_in <= 0;
ctl_reg <= 0;
stat_reg <= 0;
timer_0 <= 32'hcafe_1234;
timer_1 <= 32'hface_5678;
end
end
always @(posedge pclk) begin
if (presetn & psel & penable) begin
if (pwrite) begin
case (paddr)
'h0 : ctl_reg <= pwdata;
'h4 : timer_0 <= pwdata;
'h8 : timer_1 <= pwdata;
'hc : stat_reg <= pwdata;
// default:
endcase
end
end
end
always @(penable) begin
if (psel & !pwrite) begin
case(paddr)
'h0 : rdata_tmp <= ctl_reg ;
'h4 : rdata_tmp <= timer_0 ;
'h8 : rdata_tmp <= timer_1 ;
'hc : rdata_tmp <= stat_reg ;
endcase
end
end
assign prdata = (psel & penable & !pwrite) ? rdata_tmp : 'hz;
endmodule
4.2 interface
interface bus_if (input pclk);
logic[31:0] paddr;
logic[31:0] pwdata;
logic[31:0] prdata;
logic pwrite;
logic psel;
logic penable;
logic presetn;
endinterface //interfacename
4.3 寄存器
class reg_ctl extends uvm_reg;
rand uvm_reg_field mod_en;
rand uvm_reg_field bl_yellow;
rand uvm_reg_field bl_red;
rand uvm_reg_field profile;
`uvm_object_utils(reg_ctl)
function new(string name = "reg_ctl");
super.new(name, 32, build_coverage(UVM_NO_COVERAGE));
endfunction
virtual function void build();
this.mod_en = uvm_reg_field::type_id::create("mod_en", , get_full_name());
this.bl_yellow = uvm_reg_field::type_id::create("bl_yellow", , get_full_name());
this.bl_red = uvm_reg_field::type_id::create("bl_red", , get_full_name());
this.profile = uvm_reg_field::type_id::create("profile", , get_full_name());
this.mod_en.configure(this, 1, 0, "RW", 0, 0, 1, 0, 0);
this.bl_yellow.configure(this, 1, 1, "RW", 0, 0, 1, 0, 0);
this.bl_red.configure(this, 1, 2, "RW", 0, 0 , 1, 0, 0);
this.profile.configure(this, 1, 3, "RW", 0, 0, 1, 0, 0);
endfunction
endclass
class reg_stat extends uvm_reg;
uvm_reg_field state;
`uvm_object_utils(reg_stat)
function new(string name = "reg_stat");
super.new(name, 32, build_coverage(UVM_NO_COVERAGE));
endfunction //new()
virtual function void build();
this.state = uvm_reg_field::type_id::create("state", , get_full_name());
this.state.configure(this, 2, 0, "RO", 0, 0, 0, 0, 0);
endfunction
endclass //reg_stat
class reg_timer extends uvm_reg;
uvm_reg_field timer;
`uvm_object_utils(reg_timer)
function new(string name = "reg_timer");
super.new(name, 32, build_coverage(UVM_NO_COVERAGE));
endfunction //new()
virtual function void build();
this.timer = uvm_reg_field::type_id::create("timer", , get_full_name());
this.timer.configure(this, 32, 0, "RW", 0, 32'hcafe1234, 1, 0, 1);
this.timer.set_reset(0, "SOFT");
endfunction
endclass //reg_timer
4.4 寄存器block
class reg_traffic_block extends uvm_reg_block;
rand reg_ctl ctrl;
rand reg_timer timer[2];
reg_stat stat;
`uvm_object_utils(reg_traffic_block)
function new(string name = "reg_traffic_block");
super.new(name, build_coverage(UVM_NO_COVERAGE));
endfunction
virtual function void build();
this.default_map = create_map("", 0, 4, UVM_LITTLE_ENDIAN, 0);
this.ctrl = reg_ctl::type_id::create("ctrl", ,get_full_name());
this.ctrl.configure(this, null, "");
this.ctrl.build();
this.default_map.add_reg(this.ctrl, `UVM_REG_ADDR_WIDTH'h0, "RW", 0);
this.timer[0] = reg_timer::type_id::create("timer[0]", , get_full_name());
this.timer[0].configure(this, null, "");
this.timer[0].build();
this.default_map.add_reg(this.timer[0], `UVM_REG_ADDR_WIDTH'h4, "RW", 0);
this.timer[1] = reg_timer::type_id::create("timer[1]", , get_full_name());
this.timer[1].configure(this, null, "");
this.timer[1].build();
this.default_map.add_reg(this.timer[1], `UVM_REG_ADDR_WIDTH'h8, "RW", 0);
this.stat = reg_stat::type_id::create("stat", , get_full_name());
this.stat.configure(this, null, "");
this.stat.build();
this.default_map.add_reg(this.stat, `UVM_REG_ADDR_WIDTH'hc, "RO", 0);
endfunction
endclass
class reg_sys_block extends uvm_reg_block;
rand reg_traffic_block traffic;
`uvm_object_utils(reg_sys_block)
function new(string name = "reg_sys_block");
super.new(name);
endfunction
virtual function void build();
this.default_map = create_map("", 0, 4, UVM_LITTLE_ENDIAN, 0);
this.traffic = reg_traffic_block::type_id::create("traffic",,get_full_name());
this.traffic.configure(this, "top.pB0");
this.traffic.build();
this.default_map.add_submap(this.traffic.default_map, `UVM_REG_ADDR_WIDTH'h0);
endfunction
endclass
4.5 适配器adapter
adapter依赖总线协议的实现。可以在总线协议和模型数据之间做一个转化。继承自uvm_reg_adapter,主要实现2个方法:reg2bus和bus2reg。
class reg_adapter extends uvm_reg_adapter;
`uvm_object_utils(reg_adapter)
function new(string name = "reg_adapter");
super.new(name);
endfunction //new()
virtual function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
bus_pkt pkt = bus_pkt::type_id::create("pkt");
pkt.write = rw.kind == UVM_WRITE ? 1 : 0;
pkt.addr = rw.addr;
pkt.data = rw.data;
return pkt;
endfunction
virtual function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
bus_pkt pkt;
if (!$cast(pkt, bus_item)) begin
`uvm_fatal("reg_adapter", "failed to case bus_item to pkt");
end
rw.kind = pkt.write ? UVM_WRITE : UVM_READ;
rw.addr = pkt.addr;
rw.data = pkt.data;
endfunction
endclass //reg_adapter extends uvm_reg_adapter
4.6 寄存器env
class reg_env extends uvm_env;
`uvm_component_utils(reg_env)
function new(string name = "reg_env", uvm_component parent);
super.new(name, parent);
endfunction
reg_sys_block m_reg_model;
reg_adapter m_adapter;
uvm_reg_predictor#(bus_pkt) m_predictor;
reg_agent m_agent;
virtual function void build_phase (uvm_phase phase);
super.build_phase(phase);
m_reg_model = reg_sys_block::type_id::create("m_reg_model", this);
m_adapter = reg_adapter::type_id::create("m_adapter", this);
m_predictor = uvm_reg_predictor#(bus_pkt)::type_id::create("m_predictor", this);
m_reg_model.build();
m_reg_model.lock_model();
uvm_config_db #(reg_sys_block)::set(null, "uvm_test_top", "m_reg_model", m_reg_model);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
m_predictor.map = m_reg_model.default_map;
m_predictor.adapter = m_adapter;
endfunction
endclass
4.7 item
class bus_pkt extends uvm_sequence_item;
rand bit[31:0] addr;
rand bit[31:0] data;
rand bit write;
`uvm_object_utils_begin(bus_pkt)
`uvm_field_int(addr, UVM_ALL_ON)
`uvm_field_int(data, UVM_ALL_ON)
`uvm_field_int(write, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name = "bus_pkt");
super.new(name);
endfunction
constraint c_addr {addr inside {0, 4, 8};}
endclass
4.8 driver
class reg_driver extends uvm_driver#(bus_pkt);
`uvm_component_utils(reg_driver)
bus_pkt pkt;
virtual bus_if vif;
function new(string name = "reg_driver", uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual bus_if)::get(this, "*", "bus_if", vif)) begin
`uvm_error("reg_driver", "do not get bus if handle");
end
endfunction
virtual task run_phase(uvm_phase phase);
bit[31:0] data;
vif.psel <= 0;
vif.penable <= 0;
vif.pwrite <= 0;
vif.paddr <= 0;
vif.pwdata <= 0;
forever begin
seq_item_port.get_next_item(pkt);
if (pkt.write) begin
write(pkt.addr, pkt.data);
end
else begin
read(pkt.addr, data);
pkt.data = data;
end
seq_item_port.item_done();
end
endtask
virtual task read(input bit[31:0] addr, output logic[31:0] data);
vif.paddr <= addr;
vif.pwrite <= 0;
vif.psel <= 1;
@(posedge vif.pclk);
vif.penable <= 1;
@(posedge vif.pclk);
data = vif.prdata;
vif.psel <= 0;
vif.penable <= 0;
endtask
virtual task write(input bit[31:0] addr, input [31:0] data);
vif.paddr <= addr;
vif.pwdata <= data;
vif.pwrite <= 1;
vif.psel <= 1;
@(posedge vif.pclk);
vif.penable <= 1;
@(posedge vif.pclk);
vif.psel <= 0;
vif.penable <= 0;
endtask
endclass
4.9 monitor
class reg_monitor extends uvm_monitor;
`uvm_component_utils(reg_monitor)
function new(string name = "reg_monitor", uvm_component parent);
super.new(name, parent);
endfunction
uvm_analysis_port#(bus_pkt) mon_ap;
virtual bus_if vif;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
mon_ap = new("mon_ap", this);
uvm_config_db#(virtual bus_if)::get(null, "uvm_test_top.*", "bus_if", vif);
endfunction
virtual task run_phase(uvm_phase phase);
fork
forever begin
@(posedge vif.pclk);
if (vif.psel &vif.penable & vif.penable) begin
bus_pkt pkt = bus_pkt::type_id::create("pkt");
pkt.addr = vif.paddr;
if (vif.pwrite) begin
pkt.data = vif.pwdata;
end
else begin
pkt.data = vif.prdata;
end
pkt.write = vif.pwrite;
mon_ap.write(pkt);
end
end
join_none
endtask
endclass
4.10 agent
class reg_agent extends uvm_agent;
`uvm_component_utils(reg_agent)
function new(string name = "reg_agent", uvm_component parent);
super.new(name, parent);
endfunction
reg_driver m_drv;
reg_monitor m_mon;
uvm_sequencer#(bus_pkt) m_sqr;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
m_drv = reg_driver::type_id::create("m_drv", this);
m_mon = reg_monitor::type_id::create("m_mon", this);
m_sqr = uvm_sequencer#(bus_pkt)::type_id::create("m_sqr", this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
m_drv.seq_item_port.connect(m_sqr.seq_item_export);
endfunction
endclass
4.11 env
class reg_test_env extends uvm_env;
`uvm_component_utils(reg_test_env)
reg_agent m_agt;
reg_env m_reg_env;
function new(string name= "reg_test_env", uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
m_agt = reg_agent::type_id::create("m_agt", this);
m_reg_env = reg_env::type_id::create("m_reg_env", this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
m_reg_env.m_agent = m_agt;
m_agt.m_mon.mon_ap.connect(m_reg_env.m_predictor.bus_in);
m_reg_env.m_reg_model.default_map.set_sequencer(m_agt.m_sqr, m_reg_env.m_adapter);
endfunction
endclass
4.11 reset sequence
用于在跑case前的reset
class reset_sequence extends uvm_sequence;
`uvm_object_utils(reset_sequence)
function new(string name = "reset_sequence");
super.new(name);
endfunction
virtual bus_if vif;
task body();
if(!uvm_config_db#(virtual bus_if)::get(null, "uvm_test_top.*", "bus_if", vif)) begin
`uvm_fatal("reset_sequence", "No vif");
end
vif.presetn <= 0;
@(posedge vif.pclk);
vif.presetn <= 1;
@(posedge vif.pclk);
endtask
endclass
4.12 test
class reg_base_test extends uvm_test;
`uvm_component_utils(reg_base_test)
reg_test_env m_env;
reset_sequence m_reset_seq;
uvm_status_e status;
function new(string name = "reg_base_test", uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
m_env = reg_test_env::type_id::create("m_env", this);
m_reset_seq = reset_sequence::type_id::create("m_reset_seq", this);
endfunction
virtual task reset_phase(uvm_phase phase);
super.reset_phase(phase);
phase.raise_objection(this);
m_reset_seq.start(m_env.m_agt.m_sqr);
phase.drop_objection(this);
endtask
endclass
class reg_rw_test extends reg_base_test;
`uvm_component_utils(reg_rw_test)
function new(string name = "reg_rw_test", uvm_component parent);
super.new(name, parent);
endfunction
virtual task main_phase(uvm_phase phase);
reg_sys_block reg_model;
uvm_status_e status;
int rdata;
phase.raise_objection(this);
m_env.m_reg_env.set_report_verbosity_level(UVM_HIGH);
uvm_config_db#(reg_sys_block)::get(null, "uvm_test_top", "m_reg_model", reg_model);
reg_model.traffic.timer[1].write(status, 32'hcafe_feed);
reg_model.traffic.timer[1].read(status, rdata);
reg_model.traffic.timer[1].set(32'hface);
reg_model.traffic.timer[1].predict(32'hcafe_feed);
reg_model.traffic.timer[1].mirror(status, UVM_CHECK);
reg_model.traffic.ctrl.bl_yellow.set(1);
reg_model.traffic.update(status);
reg_model.traffic.stat.write(status, 32'h12345678);
phase.drop_objection(this);
endtask
virtual task shutdown_phase(uvm_phase phase);
super.shutdown_phase(phase);
phase.raise_objection(this);
#100ns;
phase.drop_objection(this);
endtask
endclass
4.13 仿真结果
完整代码路径:verify/uvm_reghttps://github.com/zljuft/verify/tree/main/uvm_reg