RAL Model Example
Let’s understand how the register model is constructed, integrate it with the verification environment, and access the DUT register using read and write methods.
The design has four registers as control, interrupt status, mask status, debug, etc.
Register description
Register Name |
Address |
Range |
Access |
Reset |
Field Name |
Control |
‘h0 |
[31:0] |
32‘h5 |
||
[31:3] |
RO |
29’h0 |
Reserved |
||
2 |
RW |
1’h1 |
parity_en |
||
1 |
RW |
1’h0 |
dbg_en |
||
0 |
RW |
1’h1 |
mod_en |
Register Name |
Address |
Range |
Access |
Reset |
Field Name |
Interrupt Status |
‘h4 |
[31:0] |
32‘h0 |
||
[31:2] |
RO |
30’h0 |
Reserved |
||
1 |
W1C |
1’h0 |
r_axi_err |
||
0 |
W1C |
1’h0 |
w_axi_err |
Register Name |
Address |
Range |
Access |
Reset |
Field Name |
Interrupt Mask |
‘h8 |
[31:0] |
32‘h0 |
||
[31:2] |
RO |
30’h0 |
Reserved |
||
1 |
RW |
1’h0 |
r_axi_err_msk |
||
0 |
RW |
1’h0 |
w_axi_err_msk |
Register Name |
Address |
Range |
Access |
Reset |
Field Name |
Debug |
‘hc |
[31:0] |
32‘h0 |
||
[31:2] |
RO |
30’h0 |
Reserved |
||
1 |
RO |
1’h0 |
r_axi_resp |
||
0 |
RO |
1’h0 |
w_axi_resp |
Testbench block diagram
Testbench hierarchy
--------------------------------------------------------------
Name Type Size Value
--------------------------------------------------------------
uvm_test_top reg_test - @1878
env_o env - @1944
agt agent - @1976
drv driver - @2301
rsp_port uvm_analysis_port - @2332
seq_item_port uvm_seq_item_pull_port - @2238
mon monitor - @3013
item_collect_port uvm_analysis_port - @3063
seqr seqcr - @2365
rsp_export uvm_analysis_export - @2422
seq_item_export uvm_seq_item_pull_imp - @2982
...
--------------------------------------------------------------
Now, let’s see how the register model, adapter is created and integrated with the testbench environment.
Overall steps
- Create a register (RAL) model
- Write an adapter/Predictor class
- Integrate register model and adapter with the testbench
- Access registers using RAL methods.
Step 1: Steps to create a register (RAL) model
Step A: Write register classes
Write register classes (derived from uvm_reg) for all registers in DUT
Control register:
class ral_control_reg extends uvm_reg;
rand uvm_reg_field rsvd;
rand uvm_reg_field parity_en;
rand uvm_reg_field dbg_en;
rand uvm_reg_field mod_en;
`uvm_object_utils(ral_control_reg)
function new(string name = "ral_control_reg");
super.new(name, 32, build_coverage(UVM_NO_COVERAGE));
endfunction
virtual function void build();
rsvd = uvm_reg_field::type_id::create("rsvd");
parity_en = uvm_reg_field::type_id::create("parity_en");
dbg_en = uvm_reg_field::type_id::create("dbg_en");
mod_en = uvm_reg_field::type_id::create("mod_en");
rsvd.configure (this, 29, 3, "RO", 0, 1'b0, 1, 1, 0);
parity_en.configure(this, 1, 2, "RW", 0, 1'b1, 1, 1, 0);
dbg_en.configure (this, 1, 1, "RW", 0, 1'b0, 1, 1, 0);
mod_en.configure (this, 1, 0, "RW", 0, 1'b1, 1, 1, 0);
endfunction
endclass
Interrupt status register:
class ral_intr_sts_reg extends uvm_reg;
rand uvm_reg_field rsvd;
rand uvm_reg_field r_axi_err;
rand uvm_reg_field w_axi_err;
`uvm_object_utils(ral_intr_sts_reg)
function new(string name = "ral_intr_sts_reg");
super.new(name, 32, build_coverage(UVM_NO_COVERAGE));
endfunction
virtual function void build();
rsvd = uvm_reg_field::type_id::create("rsvd");
r_axi_err = uvm_reg_field::type_id::create("r_axi_err");
w_axi_err = uvm_reg_field::type_id::create("w_axi_err");
rsvd.configure (this, 30, 2, "RO", 0, 1'b0, 1, 1, 0);
r_axi_err.configure (this, 1, 1, "W1C", 0, 1'b0, 1, 1, 0);
w_axi_err.configure (this, 1, 0, "W1C", 0, 1'b0, 1, 1, 0);
endfunction
endclass
Interrupt mask register:
class ral_intr_msk_reg extends uvm_reg;
rand uvm_reg_field rsvd;
rand uvm_reg_field r_axi_err_msk;
rand uvm_reg_field w_axi_err_msk;
`uvm_object_utils(ral_intr_msk_reg)
function new(string name = "ral_intr_msk_reg");
super.new(name, 32, build_coverage(UVM_NO_COVERAGE));
endfunction
virtual function void build();
rsvd = uvm_reg_field::type_id::create("rsvd");
r_axi_err_msk = uvm_reg_field::type_id::create("r_axi_err_msk");
w_axi_err_msk = uvm_reg_field::type_id::create("w_axi_err_msk");
rsvd.configure (this, 30, 2, "RO", 0, 1'b0, 1, 1, 0);
r_axi_err_msk.configure (this, 1, 1, "RW", 0, 1'b0, 1, 1, 0);
w_axi_err_msk.configure (this, 1, 0, "RW", 0, 1'b1, 1, 1, 0);
endfunction
endclass
Debug register:
class ral_debug_reg extends uvm_reg;
rand uvm_reg_field rsvd;
rand uvm_reg_field r_axi_resp;
rand uvm_reg_field w_axi_resp;
`uvm_object_utils(ral_debug_reg)
function new(string name = "ral_debug_reg");
super.new(name, 32, build_coverage(UVM_NO_COVERAGE));
endfunction
virtual function void build();
rsvd = uvm_reg_field::type_id::create("rsvd",);
r_axi_resp = uvm_reg_field::type_id::create("r_axi_resp");
w_axi_resp = uvm_reg_field::type_id::create("w_axi_resp");
rsvd.configure (this, 30, 2, "RO", 0, 1'b0, 1, 1, 0);
r_axi_resp.configure(this, 1, 1, "RO", 0, 1'b0, 1, 1, 0);
w_axi_resp.configure(this, 1, 0, "RO", 0, 1'b0, 1, 1, 0);
endfunction
endclass
Step B: Write a register block
Write a register block (derived from uvm_reg_block) that captures all the registers.
class module_reg extends uvm_reg_block;
rand ral_control_reg control_reg;
rand ral_intr_sts_reg intr_sts_reg;
rand ral_intr_msk_reg intr_msk_reg;
rand ral_debug_reg debug_reg;
`uvm_object_utils(module_reg)
function new(string name = "module_reg");
super.new(name);
endfunction
virtual function void build();
control_reg = ral_control_reg::type_id::create("control_reg");
control_reg.configure(this, null);
control_reg.build();
intr_sts_reg = ral_intr_sts_reg::type_id::create("intr_sts_reg");
intr_sts_reg.configure(this, null);
intr_sts_reg.build();
intr_msk_reg = ral_intr_msk_reg::type_id::create("intr_msk_reg");
intr_msk_reg.configure(this, null);
intr_msk_reg.build();
debug_reg = ral_debug_reg::type_id::create("debug_reg");
debug_reg.configure(this, null);
debug_reg.build();
default_map = create_map("", `UVM_REG_ADDR_WIDTH'h0, 4, UVM_LITTLE_ENDIAN, 1);
this.default_map.add_reg(control_reg, `UVM_REG_ADDR_WIDTH'h0, "RW");
this.default_map.add_reg(intr_sts_reg, `UVM_REG_ADDR_WIDTH'h4, "RW");
this.default_map.add_reg(intr_msk_reg, `UVM_REG_ADDR_WIDTH'h8, "RW");
this.default_map.add_reg(debug_reg, `UVM_REG_ADDR_WIDTH'hc, "RW");
endfunction
endclass
Step C: Write a top-level register block
Write a top-level register block that captures multiple register blocks known as register models.
class RegModel_SFR extends uvm_reg_block;
rand module_reg mod_reg;
uvm_reg_map axi_map;
`uvm_object_utils(RegModel_SFR)
function new(string name = "RegModel_SFR");
super.new(name, .has_coverage(UVM_NO_COVERAGE));
endfunction
virtual function void build();
default_map = create_map("axi_map", 'h0, 4, UVM_LITTLE_ENDIAN, 0);
mod_reg = module_reg::type_id::create("mod_reg");
mod_reg.configure(this);
mod_reg.build();
default_map.add_submap(this.mod_reg.default_map, 0);
endfunction
endclass
Note: In this example, we have only one block module_reg class. Hence, this step is optional and module_reg can be a top level register model.
Step 2: Write an adapter class
class reg_axi_adapter extends uvm_reg_adapter;
`uvm_object_utils(reg_axi_adapter)
function new(string name = "reg_axi_adapter");
super.new(name);
endfunction
virtual function uvm_sequence_item reg2bus (const ref uvm_reg_bus_op rw);
seq_item bus_item = seq_item::type_id::create("bus_item");
bus_item.addr = rw.addr;
bus_item.data = rw.data;
bus_item.rd_or_wr = (rw.kind == UVM_READ) ? 1: 0;
`uvm_info(get_type_name, $sformatf("reg2bus: addr = %0h, data = %0h, rd_or_wr = %0h", bus_item.addr, bus_item.data, bus_item.rd_or_wr), UVM_LOW);
return bus_item;
endfunction
virtual function void bus2reg (uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
seq_item bus_pkt;
if(!$cast(bus_pkt, bus_item))
`uvm_fatal(get_type_name(), "Failed to cast bus_item transaction")
rw.addr = bus_pkt.addr;
rw.data = bus_pkt.data;
rw.kind = (bus_pkt.rd_or_wr) ? UVM_READ: UVM_WRITE;
endfunction
endclass
Step 3: Integrate register model and adapter with the testbench
class env extends uvm_env;
`uvm_component_utils(env)
agent agt;
reg_axi_adapter adapter;
RegModel_SFR reg_model;
function new(string name = "env", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
agt = agent::type_id::create("agt", this);
adapter = reg_axi_adapter::type_id::create("adapter");
reg_model = RegModel_SFR::type_id::create("reg_model");
reg_model.build();
reg_model.reset();
reg_model.lock_model();
reg_model.print();
uvm_config_db#(RegModel_SFR)::set(uvm_root::get(), "*", "reg_model", reg_model);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
reg_model.default_map.set_sequencer( .sequencer(agt.seqr), .adapter(adapter) );
reg_model.default_map.set_base_addr('h0);
//regmodel.add_hdl_path("tb_top.DUT");
endfunction
endclass
Register Model hierarchy
--------------------------------------------------------------------------------
Name Type Size Value
--------------------------------------------------------------------------------
reg_model RegModel_SFR - @2008
mod_reg module_reg - @2030
control_reg ral_control_reg - @2055
mod_en uvm_reg_field ... RW control_reg[0:0]=1'h1
dbg_en uvm_reg_field ... RW control_reg[1:1]=1'h0
parity_en uvm_reg_field ... RW control_reg[2:2]=1'h1
rsvd uvm_reg_field ... RO control_reg[31:3]=29'h00000000
intr_sts_reg ral_intr_sts_reg - @2092
w_axi_err uvm_reg_field ... W1C intr_sts_reg[0:0]=1'h0
r_axi_err uvm_reg_field ... W1C intr_sts_reg[1:1]=1'h0
rsvd uvm_reg_field ... RO intr_sts_reg[31:2]=30'h00000000
intr_msk_reg ral_intr_msk_reg - @2116
w_axi_err_msk uvm_reg_field ... RW intr_msk_reg[0:0]=1'h1
r_axi_err_msk uvm_reg_field ... RW intr_msk_reg[1:1]=1'h0
rsvd uvm_reg_field ... RO intr_msk_reg[31:2]=30'h00000000
debug_reg ral_debug_reg - @2140
w_axi_resp uvm_reg_field ... RO debug_reg[0:0]=1'h0
r_axi_resp uvm_reg_field ... RO debug_reg[1:1]=1'h0
rsvd uvm_reg_field ... RO debug_reg[31:2]=30'h00000000
uvm_reg_map uvm_reg_map - @2164
endian ... UVM_LITTLE_ENDIAN
control_reg ral_control_reg ... @2055 +'h0
intr_sts_reg ral_intr_sts_reg ... @2092 +'h4
intr_msk_reg ral_intr_msk_reg ... @2116 +'h8
debug_reg ral_debug_reg ... @2140 +'hc
axi_map uvm_reg_map - @2021
endian ... UVM_LITTLE_ENDIAN
uvm_reg_map uvm_reg_map - @2164
endian ... UVM_LITTLE_ENDIAN
control_reg ral_control_reg ... @2055 +'h0
intr_sts_reg ral_intr_sts_reg ... @2092 +'h4
intr_msk_reg ral_intr_msk_reg ... @2116 +'h8
debug_reg ral_debug_reg ... @2140 +'hc
--------------------------------------------------------------------------------
Step 4: Access registers using RAL methods
reg_model.mod_reg.control_reg.write(status, 32'h1234_1234);
reg_model.mod_reg.control_reg.read(status, read_data);
reg_model.mod_reg.intr_msk_reg.write(status, 32'h5555_5555);
reg_model.mod_reg.intr_msk_reg.read(status, read_data);
reg_model.mod_reg.debug_reg.write(status, 32'hAAAA_AAAA);
reg_model.mod_reg.debug_reg.read(status, read_data);
Execute Complete code
Output:
UVM_INFO base_seq.sv(27) @ 0: uvm_test_top.env_o.agt.seqr@@rseq [reg_seq] Reg seq: Inside Body
UVM_INFO reg2axi_adapter.sv(14) @ 0: reporter [reg_axi_adapter] reg2bus: addr = 0, data = 12341234, rd_or_wr = 0
UVM_INFO driver.sv(37) @ 6: uvm_test_top.env_o.agt.drv [driver] waddr = 0, wdata = 12341234
UVM_INFO reg2axi_adapter.sv(14) @ 6: reporter [reg_axi_adapter] reg2bus: addr = 0, data = 0, rd_or_wr = 1
UVM_INFO driver.sv(30) @ 14: uvm_test_top.env_o.agt.drv [driver] raddr = 0, rdata = 12341234
UVM_INFO reg2axi_adapter.sv(14) @ 14: reporter [reg_axi_adapter] reg2bus: addr = 8, data = 55555555, rd_or_wr = 0
UVM_INFO driver.sv(37) @ 22: uvm_test_top.env_o.agt.drv [driver] waddr = 8, wdata = 55555555
UVM_INFO reg2axi_adapter.sv(14) @ 22: reporter [reg_axi_adapter] reg2bus: addr = 8, data = 0, rd_or_wr = 1
UVM_INFO driver.sv(30) @ 30: uvm_test_top.env_o.agt.drv [driver] raddr = 8, rdata = 55555555
UVM_INFO reg2axi_adapter.sv(14) @ 30: reporter [reg_axi_adapter] reg2bus: addr = c, data = aaaaaaaa, rd_or_wr = 0
UVM_INFO driver.sv(37) @ 38: uvm_test_top.env_o.agt.drv [driver] waddr = c, wdata = aaaaaaaa
UVM_INFO reg2axi_adapter.sv(14) @ 38: reporter [reg_axi_adapter] reg2bus: addr = c, data = 0, rd_or_wr = 1
UVM_INFO driver.sv(30) @ 46: uvm_test_top.env_o.agt.drv [driver] raddr = c, rdata = aaaaaaaa
UVM_INFO base_test.sv(19) @ 46: uvm_test_top [reg_test] End of testcase
RAL Tutorials