UVM Register Model Example
In this section will see an example that shows one of the ways to access DUT registers with 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.
Below is the block diagram of DMA.
Below are the DMA registers,
- INTR
- CTRL
- IO ADDR
- MEM ADDR
Address of each register and register field description is given below,
Reg Name | Address | Field Description | ||||||
INTR | h400 | 31:16 | 15:0 | |||||
MASK | STATUS | |||||||
CTRL | h404 | 31:10 | 9 | 8:1 | 0 | |||
RESVD | IO_MEM | W_COUNT | START_DMA | |||||
IO_ADDR | h408 | 31:0 | ||||||
ADDRESS | ||||||||
MEM_ADDR | h40C | 31:0 | ||||||
ADDRESS | ||||||||
Below is the testbench block diagram,
The testbench components are,
- Environment
- DMA Agent
- Driver
- Monitor
- Sequencer and Sequences
- RAL Model
- DMA Reg package
- Adapter
- DMA Agent
UVM Tetsbench prior knowledge is required before going through this section. For detailed steps on writing UVM Testbench refer to UVM Testbench Architecture.
UVM RAL Model creation involves the below steps,
- Writing register classes
- Writing register package
- Instantiation of register classes in register package
- Writing Adapter class
- Integrating register package and adapter in environment
- Accessing registers with RAL
Writing register classes
Writing reg class for INTR
Below is the register filed details of INTR reg.
Reg Name | Address | Field Description | ||||||
INTR | h400 | 31:16 | 15:0 | |||||
MASK | STATUS | |||||||
- Register class is written by extending the uvm_reg class
class intr extends uvm_reg; `uvm_object_utils(intr) //--------------------------------------- // Constructor //--------------------------------------- function new (string name = "intr"); super.new(name,32,UVM_NO_COVERAGE); //32 -> Register Width endfunction endclass
- Declare the fields with type uvm_reg_field
//--------------------------------------- // fields instance //--------------------------------------- rand uvm_reg_field status; rand uvm_reg_field mask;
- Create the fields
function void build; // Create bitfield status = uvm_reg_field::type_id::create("status"); mask = uvm_reg_field::type_id::create("mask"); endfunction
- Configure the fields with access type, bit width and etc,
// Create bitfield status = uvm_reg_field::type_id::create("status"); // Configure status.configure(.parent(this), .size(16), .lsb_pos(0), .access("RW"), .volatile(0), .reset(0), .has_reset(1), .is_rand(1), .individually_accessible(0)); mask = uvm_reg_field::type_id::create("mask"); mask.configure(.parent(this), .size(16), .lsb_pos(16), .access("RW"), .volatile(0), .reset(0), .has_reset(1), .is_rand(1), .individually_accessible(0));
- Complete register class
class intr extends uvm_reg; `uvm_object_utils(intr) //--------------------------------------- // fields instance //--------------------------------------- rand uvm_reg_field status; rand uvm_reg_field mask; //--------------------------------------- // Constructor //--------------------------------------- function new (string name = "intr"); super.new(name,32,UVM_NO_COVERAGE); //32 -> Register Width endfunction //--------------------------------------- // build_phase - // 1. Create the fields // 2. Configure the fields //--------------------------------------- function void build; // Create bitfield status = uvm_reg_field::type_id::create("status"); // Configure status.configure(.parent(this), .size(16), .lsb_pos(0), .access("RW"), .volatile(0), .reset(0), .has_reset(1), .is_rand(1), .individually_accessible(0)); // Below line is equivalen to above one // status.configure(this, 32, 0, "RW", 0, 0, 1, 1, 0); // reg, bitwidth, lsb, access, volatile, reselVal, hasReset, isRand, fieldAccess mask = uvm_reg_field::type_id::create("mask"); mask.configure(.parent(this), .size(16), .lsb_pos(16), .access("RW"), .volatile(0), .reset(0), .has_reset(1), .is_rand(1), .individually_accessible(0)); endfunction endclass
Writing reg class for CTRL
This register has multiple fields, each field has to be declared, created and configured.
class ctrl extends uvm_reg; `uvm_object_utils(ctrl) //--------------------------------------- // fields instance //--------------------------------------- rand uvm_reg_field start_dma; rand uvm_reg_field w_count; rand uvm_reg_field io_mem; rand uvm_reg_field reserved; //--------------------------------------- // Constructor //--------------------------------------- function new (string name = "ctrl"); super.new(name,32,UVM_NO_COVERAGE); //32 -> Register Width endfunction //--------------------------------------- // build_phase - // 1. Create the fields // 2. Configure the fields //--------------------------------------- function void build; start_dma = uvm_reg_field::type_id::create("start_dma"); start_dma.configure(.parent(this), .size(1), .lsb_pos(0), .access("RW"), .volatile(0), .reset(0), .has_reset(1), .is_rand(1), .individually_accessible(0)); w_count = uvm_reg_field::type_id::create("w_count"); w_count.configure(.parent(this), .size(8), .lsb_pos(1), .access("RW"), .volatile(0), .reset(0), .has_reset(1), .is_rand(1), .individually_accessible(0)); io_mem = uvm_reg_field::type_id::create("io_mem"); io_mem.configure(.parent(this), .size(1), .lsb_pos(9), .access("RW"), .volatile(0), .reset(0), .has_reset(1), .is_rand(1), .individually_accessible(0)); reserved = uvm_reg_field::type_id::create("reserved"); reserved.configure(.parent(this), .size(22), .lsb_pos(10), .access("RW"), .volatile(0), .reset(0), .has_reset(1), .is_rand(1), .individually_accessible(0)); endfunction endclass
Writing reg class for IO_Addr
class io_addr extends uvm_reg; `uvm_object_utils(io_addr) //--------------------------------------- // fields instance //--------------------------------------- rand uvm_reg_field addr; //--------------------------------------- // Constructor //--------------------------------------- function new (string name = "io_addr"); super.new(name,32,UVM_NO_COVERAGE); //32 -> Register Width endfunction //--------------------------------------- // build_phase - // 1. Create the fields // 2. Configure the fields //--------------------------------------- function void build; // Create bitfield addr = uvm_reg_field::type_id::create("addr"); // Configure addr.configure(.parent(this), .size(32), .lsb_pos(0), .access("RW"), .volatile(0), .reset(0), .has_reset(1), .is_rand(1), .individually_accessible(0)); endfunction endclass
Writing reg class for Mem_Addr
class mem_addr extends uvm_reg; `uvm_object_utils(mem_addr) //--------------------------------------- // fields instance //--------------------------------------- rand uvm_reg_field addr; //--------------------------------------- // Constructor //--------------------------------------- function new (string name = "mem_addr"); super.new(name,32,UVM_NO_COVERAGE); //32 -> Register Width endfunction //--------------------------------------- // build_phase - // 1. Create the fields // 2. Configure the fields //--------------------------------------- function void build; // Create bitfield addr = uvm_reg_field::type_id::create("addr"); // Configure addr.configure(.parent(this), .size(32), .lsb_pos(0), .access("RW"), .volatile(0), .reset(0), .has_reset(1), .is_rand(1), .individually_accessible(0)); endfunction endclass
Writing register package
register package is written by extending the uvm_reg_block
class dma_reg_model extends uvm_reg_block; `uvm_object_utils(dma_reg_model) //--------------------------------------- // Constructor //--------------------------------------- function new (string name = ""); super.new(name, build_coverage(UVM_NO_COVERAGE)); endfunction endclass
Instantiation of register classes in register package
- Instantiating the register classes
//--------------------------------------- // register instances //--------------------------------------- rand intr reg_intr; rand ctrl reg_ctrl; rand io_addr reg_io_addr; rand mem_addr reg_mem_addr;
- Creating, building and configuring the register instances
reg_intr = intr::type_id::create("reg_intr"); reg_intr.build(); reg_intr.configure(this); reg_ctrl = ctrl::type_id::create("reg_ctrl"); reg_ctrl.build(); reg_ctrl.configure(this); reg_io_addr = io_addr::type_id::create("reg_io_addr"); reg_io_addr.build(); reg_io_addr.configure(this); reg_mem_addr = mem_addr::type_id::create("reg_mem_addr"); reg_mem_addr.build(); reg_mem_addr.configure(this);
- Creating the memory map
default_map = create_map("my_map", 0, 4, UVM_LITTLE_ENDIAN);
- Adding registers to memory map
default_map.add_reg(reg_intr , 'h0, "RW"); // reg, offset, access default_map.add_reg(reg_ctrl , 'h4, "RW"); default_map.add_reg(reg_io_addr , 'h8, "RW"); default_map.add_reg(reg_mem_addr, 'hC, "RW");
- Complete register package class
class dma_reg_model extends uvm_reg_block; `uvm_object_utils(dma_reg_model) //--------------------------------------- // register instances //--------------------------------------- rand intr reg_intr; rand ctrl reg_ctrl; rand io_addr reg_io_addr; rand mem_addr reg_mem_addr; //--------------------------------------- // Constructor //--------------------------------------- function new (string name = ""); super.new(name, build_coverage(UVM_NO_COVERAGE)); endfunction //--------------------------------------- // Build Phase //--------------------------------------- function void build; //--------------------------------------- //reg creation //--------------------------------------- reg_intr = intr::type_id::create("reg_intr"); reg_intr.build(); reg_intr.configure(this); reg_ctrl = ctrl::type_id::create("reg_ctrl"); reg_ctrl.build(); reg_ctrl.configure(this); reg_io_addr = io_addr::type_id::create("reg_io_addr"); reg_io_addr.build(); reg_io_addr.configure(this); reg_mem_addr = mem_addr::type_id::create("reg_mem_addr"); reg_mem_addr.build(); reg_mem_addr.configure(this); //--------------------------------------- //Memory map creation and reg map to it //--------------------------------------- default_map = create_map("my_map", 0, 4, UVM_LITTLE_ENDIAN); default_map.add_reg(reg_intr , 'h0, "RW"); // reg, offset, access default_map.add_reg(reg_ctrl , 'h4, "RW"); default_map.add_reg(reg_io_addr , 'h8, "RW"); default_map.add_reg(reg_mem_addr, 'hC, "RW"); lock_model(); endfunction endclass
Writing Adapter Class
- An adapter class is written by extending the uvm_reg_adapter
class dma_adapter extends uvm_reg_adapter; `uvm_object_utils (dma_adapter) //--------------------------------------- // Constructor //--------------------------------------- function new (string name = "dma_adapter"); super.new (name); endfunction endclass
- Writing reg2bus method
//--------------------------------------- // reg2bus method //--------------------------------------- function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw); dma_seq_item tx; tx = dma_seq_item::type_id::create("tx"); tx.wr_en = (rw.kind == UVM_WRITE); tx.addr = rw.addr; if (tx.wr_en) tx.wdata = rw.data; if (!tx.wr_en) tx.rdata = rw.data; return tx; endfunction
- Writing bus2reg method
//--------------------------------------- // bus2reg method //--------------------------------------- function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw); dma_seq_item tx; assert( $cast(tx, bus_item) ) else `uvm_fatal("", "A bad thing has just happened in my_adapter") rw.kind = tx.wr_en ? UVM_WRITE : UVM_READ; rw.addr = tx.addr; rw.data = tx.rdata; rw.status = UVM_IS_OK; endfunction
- Complete adapter class
class dma_adapter extends uvm_reg_adapter; `uvm_object_utils (dma_adapter) //--------------------------------------- // Constructor //--------------------------------------- function new (string name = "dma_adapter"); super.new (name); endfunction //--------------------------------------- // reg2bus method //--------------------------------------- function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw); dma_seq_item tx; tx = dma_seq_item::type_id::create("tx"); tx.wr_en = (rw.kind == UVM_WRITE); tx.addr = rw.addr; if (tx.wr_en) tx.wdata = rw.data; if (!tx.wr_en) tx.rdata = rw.data; return tx; endfunction //--------------------------------------- // bus2reg method //--------------------------------------- function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw); dma_seq_item tx; assert( $cast(tx, bus_item) ) else `uvm_fatal("", "A bad thing has just happened in my_adapter") rw.kind = tx.wr_en ? UVM_WRITE : UVM_READ; rw.addr = tx.addr; rw.data = tx.rdata; rw.status = UVM_IS_OK; endfunction endclass
Integrating register package and adapter in environment
This involves below steps,
- Instantiating the regmodel and adapter
dma_reg_model regmodel; dma_adapter m_adapter;
- Creating the register model and adapter
regmodel = dma_reg_model::type_id::create("regmodel", this); regmodel.build(); m_adapter = dma_adapter::type_id::create("m_adapter",, get_full_name());
- Connecting the adapter sequencer with agent sequencer
regmodel.default_map.set_sequencer( .sequencer(dma_agnt.sequencer), .adapter(m_adapter) );
- Setting the base address to regmodel address map
regmodel.default_map.set_base_addr('h400);
- Complete env code
class dma_model_env extends uvm_env; //--------------------------------------- // agent and scoreboard instance //--------------------------------------- dma_agent dma_agnt; //--------------------------------------- // Reg Model and Adapter instance //--------------------------------------- dma_reg_model regmodel; dma_adapter m_adapter; `uvm_component_utils(dma_model_env) //--------------------------------------- // constructor //--------------------------------------- function new(string name, uvm_component parent); super.new(name, parent); endfunction : new //--------------------------------------- // build_phase - create the components //--------------------------------------- function void build_phase(uvm_phase phase); super.build_phase(phase); dma_agnt = dma_agent::type_id::create("dma_agnt", this); //--------------------------------------- // Register model and adapter creation //--------------------------------------- regmodel = dma_reg_model::type_id::create("regmodel", this); regmodel.build(); m_adapter = dma_adapter::type_id::create("m_adapter",, get_full_name()); endfunction : build_phase //--------------------------------------- // connect_phase - connecting regmodel sequencer and adapter with // mem agent sequencer and adapter //--------------------------------------- function void connect_phase(uvm_phase phase); regmodel.default_map.set_sequencer( .sequencer(dma_agnt.sequencer), .adapter(m_adapter) ); regmodel.default_map.set_base_addr('h400); endfunction : connect_phase endclass : dma_model_env
Accessing registers with RAL
Write to the register
- Write to the register is performed by calling the register write method
- Data to be written to the registers is passed as an argument to the write method
Syntax:
reg_model.rag_name.write(status, write_data)
Read from the register
- Read from the register is performed by calling the register read method
- The read method returns the Data
Syntax:
reg_model.rag_name.read(status, read_data)
Access to the registers,
//Write to the Registers regmodel.reg_intr.write(status, 32'h1234_1234); regmodel.reg_ctrl.write(status, 32'h1234_5678); regmodel.reg_io_addr.write(status, 32'h1234_9ABC); regmodel.reg_mem_addr.write(status, 32'h1234_DEF0); //Read from the register regmodel.reg_intr.read(status, rdata); regmodel.reg_ctrl.read(status, rdata); regmodel.reg_io_addr.read(status, rdata); regmodel.reg_mem_addr.read(status, rdata);
Complete Sequence Code
class dma_reg_seq extends uvm_sequence; `uvm_object_utils(dma_reg_seq) dma_reg_model regmodel; //--------------------------------------- // Constructor //--------------------------------------- function new (string name = ""); super.new(name); endfunction //--------------------------------------- // Sequence body //--------------------------------------- task body; uvm_status_e status; uvm_reg_data_t incoming; bit [31:0] rdata; if (starting_phase != null) starting_phase.raise_objection(this); //Write to the Registers regmodel.reg_intr.write(status, 32'h1234_1234); regmodel.reg_ctrl.write(status, 32'h1234_5678); regmodel.reg_io_addr.write(status, 32'h1234_9ABC); regmodel.reg_mem_addr.write(status, 32'h1234_DEF0); //Read from the registers regmodel.reg_intr.read(status, rdata); regmodel.reg_ctrl.read(status, rdata); regmodel.reg_io_addr.read(status, rdata); regmodel.reg_mem_addr.read(status, rdata); if (starting_phase != null) starting_phase.drop_objection(this); endtask endclass
Test case
register sequence is declared, created and started from the test case.
class dma_reg_test extends dma_model_base_test; `uvm_component_utils(dma_reg_test) //--------------------------------------- // sequence instance //--------------------------------------- dma_reg_seq reg_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 sequence reg_seq = dma_reg_seq::type_id::create("reg_seq"); endfunction : build_phase //--------------------------------------- // run_phase - starting the test //--------------------------------------- task run_phase(uvm_phase phase); phase.raise_objection(this); if ( !reg_seq.randomize() ) `uvm_error("", "Randomize failed") //Setting sequence in reg_seq reg_seq.regmodel = env.regmodel; reg_seq.starting_phase = phase; reg_seq.start(env.dma_agnt.sequencer); phase.drop_objection(this); //set a drain-time for the environment if desired phase.phase_done.set_drain_time(this, 50); endtask : run_phase endclass : dma_reg_test
Simulator Output
UVM_INFO @ 0: reporter [RNTST] Running test dma_reg_test... UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_spell_chkr.svh(123) @ 0: ---------------------------------------------------------------- Name Type Size Value ---------------------------------------------------------------- uvm_test_top dma_reg_test - @1912 env dma_model_env - @1980 dma_agnt dma_agent - @2031 driver dma_driver - @2299 rsp_port uvm_analysis_port - @2371 seq_item_port uvm_seq_item_pull_port - @2334 monitor dma_monitor - @2189 item_collected_port uvm_analysis_port - @2266 sequencer dma_sequencer - @2402 rsp_export uvm_analysis_export - @2447 seq_item_export uvm_seq_item_pull_imp - @2857 arbitration_queue array 0 - lock_queue array 0 - num_last_reqs integral 32 'd1 num_last_rsps integral 32 'd1 ---------------------------------------------------------------- Design WR addr 400 Data 12341234 Design WR addr 404 Data 12345678 Design WR addr 408 Data 12349abc Design WR addr 40c Data 1234def0 Design RD addr 400 Data 12341234 Design RD addr 404 Data 12345678 Design RD addr 408 Data 12349abc Design RD addr 40c Data 1234def0 UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 245: reporter [TEST_DONE] UVM_INFO dma_test.sv(58) @ 245: uvm_test_top [dma_reg_test] --------------------------------------- UVM_INFO dma_test.sv(59) @ 245: uvm_test_top [dma_reg_test] ---- TEST PASS ---- UVM_INFO dma_test.sv(60) @ 245: uvm_test_top [dma_reg_test] --------------------------------------- UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 245: reporter [UVM/REPORT/SERVER] --- UVM Report Summary --- ** Report counts by severity UVM_INFO : 7 UVM_WARNING : 0 UVM_ERROR : 0 UVM_FATAL : 0 ** Report counts by id [RNTST] 1 [TEST_DONE] 1 [UVM/CONFIGDB/SPELLCHK] 1 [UVM/RELNOTES] 1 [dma_reg_test] 3
Write and Read Waveform:
Write Waveform:
Read Waveform: