Register Access without RAL Model
In this section will see an example that shows one of the ways to access DUT registers without the UVM RAL Model.Let’s consider a DMA design which consists of registers in it and reg_interface is used to access the registers.
- table of Contents
一、Below is the block diagram of DMA.
二、Below are the DMA registers,
三、Below is the testbench block diagram,
3.1、The testbench component DMA agent is used to access the reg_interface.
7.4、Write and Read operation using defines
八、Accessing registers from TestCase
一、Below is the block diagram of DMA.
二、Below are the DMA registers,
- IN
- CTRL
- IO ADDR
- MEM ADDR
Address of each register and register field description is given below,
- See blog post: [UVM] UVM Register Model Application Example (Example)
三、Below is the testbench block diagram,
3.1、The testbench component DMA agent is used to access the reg_interface.
DMA Agent consists of,
- Driver
- Monitor
- Sequencer
- Sequences (Write sequence and Read Sequence)
3.2、In testbench,
- Write to the register is done by calling the write sequence
- Read of the register is done by calling the read sequence
This testbench is UVM based testbench. For ease understanding, only the sequence and test case is explained in this section. For detailed steps on writing UVM Testbench refer to UVM Testbench Architecture.
四、Base Sequence
class dma_sequence extends uvm_sequence#(dma_seq_item);
`uvm_object_utils(dma_sequence)
//---------------------------------------
//Constructor
//---------------------------------------
function new(string name = "dma_sequence");
super.new(name);
endfunction
`uvm_declare_p_sequencer(dma_sequencer)
//---------------------------------------
// create, randomize and send the item to driver
//---------------------------------------
virtual task body();
repeat(2) begin
req = dma_seq_item::type_id::create("req");
wait_for_grant();
req.randomize();
send_request(req);
wait_for_item_done();
end
endtask
endclass
五、Write Sequence
class write_sequence extends uvm_sequence#(dma_seq_item);
bit [31:0] t_addr,t_data;
`uvm_object_utils(write_sequence)
//---------------------------------------
//Constructor
//---------------------------------------
function new(string name = "write_sequence");
super.new(name);
endfunction
virtual task body();
`uvm_do_with(req,{req.wr_en==1;req.addr==t_addr;req.wdata==t_data;})
endtask
endclass
六、Read Sequence
class read_sequence extends uvm_sequence#(dma_seq_item);
bit [31:0] t_addr;
`uvm_object_utils(read_sequence)
//---------------------------------------
//Constructor
//---------------------------------------
function new(string name = "read_sequence");
super.new(name);
endfunction
virtual task body();
`uvm_do_with(req,{req.wr_en==0;req.addr==t_addr;})
endtask
endclass
七、Register Accessing
Write or Read operation to any SFR is done by calling the write or read sequence respectively.
7.1、Write Operation:
//Write to register INTR
wr_seq.t_addr = 32'h400;
wr_seq.t_data = 32'hFFFF_0F0F;
wr_seq.start(env.dma_agnt.sequencer);
//Write to register CTRL
wr_seq.t_addr = 32'h404;
wr_seq.t_data = 32'h1234_5678;
wr_seq.start(env.dma_agnt.sequencer);
//Write to register IO_ADDR
wr_seq.t_addr = 32'h408;
wr_seq.t_data = 32'hABCD_EF12;
wr_seq.start(env.dma_agnt.sequencer);
//Write to register MEM_ADDR
wr_seq.t_addr = 32'h40C;
wr_seq.t_data = 32'h9731_2345;
wr_seq.start(env.dma_agnt.sequencer);
7.2、Read Operation:
//Read from register INTR
rd_seq.t_addr = 32'h400;
rd_seq.start(env.dma_agnt.sequencer);
//Read from register CTRL
rd_seq.t_addr = 32'h404;
rd_seq.start(env.dma_agnt.sequencer);
//Read from register IO_ADDR
rd_seq.t_addr = 32'h408;
rd_seq.start(env.dma_agnt.sequencer);
//Read from register MEM_ADDR
rd_seq.t_addr = 32'h40C;
rd_seq.start(env.dma_agnt.sequencer);
In the above code, Register Address is assigned for register access. for the complex designs, the number of registers will be more. readability and debug will be difficult. to overcome this we can use define for register address.
7.3、Register address defines
`define INTR_SFR_ADDR 32'h400
`define CTRL_SFR_ADDR 32'h404
`define IO_ADDR_SFR_ADDR 32'h408
`define MEM_ADDR_SFR_ADDR 32'h40C
7.4、Write and Read operation using defines
- WRITE OPERATION
wr_seq.t_addr = `INTR_SFR_ADDR;
wr_seq.t_data = 32'hFFFF_0F0F;
wr_seq.start(env.dma_agnt.sequencer);
wr_seq.t_addr = `CTRL_SFR_ADDR;
wr_seq.t_data = 32'h1234_5678;
wr_seq.start(env.dma_agnt.sequencer);
wr_seq.t_addr = `IO_ADDR_SFR_ADDR;
wr_seq.t_data = 32'hABCD_EF12;
wr_seq.start(env.dma_agnt.sequencer);
wr_seq.t_addr = `MEM_ADDR_SFR_ADDR;
wr_seq.t_data = 32'h9731_2345;
wr_seq.start(env.dma_agnt.sequencer);
- READ OPERATION
rd_seq.t_addr = `INTR_SFR_ADDR;
rd_seq.start(env.dma_agnt.sequencer);
rd_seq.t_addr = `CTRL_SFR_ADDR;
rd_seq.start(env.dma_agnt.sequencer);
rd_seq.t_addr = `IO_ADDR_SFR_ADDR;
rd_seq.start(env.dma_agnt.sequencer);
rd_seq.t_addr = `MEM_ADDR_SFR_ADDR;
rd_seq.start(env.dma_agnt.sequencer);
八、Accessing registers from TestCase
class dma_reg_test extends uvm_test;
`uvm_component_utils(dma_reg_test)
//---------------------------------------
// env instance
//---------------------------------------
dma_model_env env;
//---------------------------------------
// sequence instance
//---------------------------------------
write_sequence wr_seq;
read_sequence rd_seq;
//---------------------------------------
// constructor
//---------------------------------------
function new(string name = "dma_reg_test",uvm_component parent=null);
super.new(name,parent);
endfunction : new
//---------------------------------------
// build_phase
//---------------------------------------
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
// Create the env
env = dma_model_env::type_id::create("env", this);
// Create the sequence
wr_seq = write_sequence::type_id::create("wr_seq");
rd_seq = read_sequence::type_id::create("rd_seq");
endfunction : build_phase
//---------------------------------------
// end_of_elobaration phase
//---------------------------------------
virtual function void end_of_elaboration();
//print's the topology
print();
endfunction
//---------------------------------------
// run_phase - starting the test
//---------------------------------------
task run_phase(uvm_phase phase);
phase.raise_objection(this);
wr_seq.t_addr = `INTR_SFR_ADDR;
wr_seq.t_data = 32'hFFFF_0F0F;
wr_seq.start(env.dma_agnt.sequencer);
wr_seq.t_addr = `CTRL_SFR_ADDR;
wr_seq.t_data = 32'h1234_5678;
wr_seq.start(env.dma_agnt.sequencer);
wr_seq.t_addr = `IO_ADDR_SFR_ADDR;
wr_seq.t_data = 32'hABCD_EF12;
wr_seq.start(env.dma_agnt.sequencer);
wr_seq.t_addr = `MEM_ADDR_SFR_ADDR;
wr_seq.t_data = 32'h9731_2345;
wr_seq.start(env.dma_agnt.sequencer);
rd_seq.t_addr = `INTR_SFR_ADDR;
rd_seq.start(env.dma_agnt.sequencer);
rd_seq.t_addr = `CTRL_SFR_ADDR;
rd_seq.start(env.dma_agnt.sequencer);
rd_seq.t_addr = `IO_ADDR_SFR_ADDR;
rd_seq.start(env.dma_agnt.sequencer);
rd_seq.t_addr = `MEM_ADDR_SFR_ADDR;
rd_seq.start(env.dma_agnt.sequencer);
phase.drop_objection(this);
endtask : run_phase
endclass : dma_reg_test
- Simulator output
UVM_INFO @ 0: reporter [RNTST] Running test dma_reg_test…
—————————————————————-
Name Type Size Value
—————————————————————-
uvm_test_top dma_reg_test – @1881
env dma_model_env – @1949
dma_agnt dma_agent – @2017
driver dma_driver – @2128
rsp_port uvm_analysis_port – @2200
seq_item_port uvm_seq_item_pull_port – @2163
monitor dma_monitor – @2048
item_collected_port uvm_analysis_port – @2095
sequencer dma_sequencer – @2231
rsp_export uvm_analysis_export – @2276
seq_item_export uvm_seq_item_pull_imp – @2686
arbitration_queue array 0 –
lock_queue array 0 –
num_last_reqs integral 32 ‘d1
num_last_rsps integral 32 ‘d1
—————————————————————-
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 195: reporter [TEST_DONE]
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 195: reporter
— UVM Report Summary —
** Report counts by severity
UVM_INFO : 3
UVM_WARNING : 0
UVM_ERROR : 0
UVM_FATAL : 0
** Report counts by id
[RNTST] 1
[TEST_DONE] 1
[UVM/RELNOTES] 1