What are registers ?

Registers are essential components in hardware designs, used to store configuration settings, control information, and status data. In modern designs, these registers are often accessed by software or firmware using bus protocols like AXI/APB and can be programmed with certain values. For example, there could be a 32-bit register with several individual fields within it. Each field represents a particular feature that can be configured by software when required.

hardware-register-example

The figure above is an example of a single register within the design with five different functional fields.

Bit OffsetNameRead/WriteWidthDescription
0EnRW1Enable. Set this bit to 1 to turn on the entire module, set it to 0 to disable
1ModeRW3Supports the following modes :
  • Low Energy : 000
  • Medium Energy : 001
  • High Speed : 010
  • Auto Adjust : 011
  • Cruise Mode : 100
  • Balanced : 101
4HaltRW1Halt. Set this bit to 1 to stop this module temporarily and set it to 0 to resume
5AutoRW1Auto shutdown mode. Set this bit to 1 to start auto shutdown after 5 min
6ReservedRO5Reserved for future additions
11SpeedRW5Set a value to control the speed from 100 rpm to 2000 rpm.
16ReservedRO16Reserved for future additions

The design files may have a module in RTL to implement these registers.


// RTL representation

module reg_block (...);
	wire [31:0]     reg_ctl;
	
	// Declare reg variables to store register field values
	reg             ctl_en;      // Enable for the module
	reg [2:0]       ctl_mode;    // Mode 
	reg             ctl_halt;    // Halt 
	reg             ctl_auto;    // Auto shutdown 
	reg [4:0]       ctl_speed;   // Speed control
	
	assign reg_ctl = {16'b0, ctl_speed, 5'b0, ctl_auto, ctl_halt, ctl_mode, ctl_en};
	
	// Logic for getting individual fields from the bus
	...
endmodule

To model registers using the UVM RAL, you create a register model class hierarchy that reflects the registers and their fields in your design. Each register corresponds to a class, and each field within the register corresponds to a field within that class. A register class is derived from uvm_reg and a register field is derived from uvm_reg_field.

What is a register block ?

A register block is a higher-level abstraction that represents a collection of registers and memory-mapped elements within a digital design. It provides a way to organize and encapsulate related registers, making it easier to manage and verify complex designs that consist of multiple registers and associated logic.

For example, REG_CTL could be placed at an offset address of 0x0 to emphasize that it is the main control register for the module. Because it takes 32 bits (4 bytes), the next register REG_STAT would be at 0x4 and hold the design's status related flags.

hardware-register-file-example

A register block class is derived from uvm_reg_block base class in UVM.

What is a memory map ?

A memory map refers to the arrangement of memory and memory-mapped peripheral registers within the address space of the SoC's memory hierarchy. It defines the logical addresses at which various components, such as memory regions, registers, and peripherals, are accessible by the processor and other devices in the system.

For example, a System-On-Chip or SoC normally has one or more processor cores, DMA engines, bus network interconnects and many peripheral modules. Each peripheral module would have an associated register block and they are all laid out in a memory map. The processor would have access to these register blocks through a peripheral bus protocol like APB, or Wishbone. So each peripheral block would require a base address on which respective register blocks can reside.

memory-map

Assume the register block described above to be a part of the Timer design module which has a base address of 0xE7B3_0000. In order to access REG_CTL of the Timer block, the core would have to send a transaction with an address of 0xE7B3_0000 since REG_CTL has a base address offset of 0x0. To access REG_DMACTL, the core would send a transaction with address of (0xE7B3_0000 + 0xC) because REG_DMACTL has an offset of 0xC.

What is Register Abstraction Layer ?

The purpose of Register Abstraction Layer or RAL is to provide a structured and standardized way to model and verify registers and memory-mapped structures within a digital design. Verification of register behavior can include testing different access scenarios, checking field values after resets, verifying register side-effects, and more.

It defines many base classes that abstract the read and write operations to registers and memories in a DUT, when properly extended. This allows many verification environments to be reused from block level to sub-system level environments to full chip or SoC testbenches without any modifications.

The register model object can be used in a UVM sequence to write or read from a target register.

uvm_ral_write_example

class my_sequence extends uvm_sequence;
	...

	regmodel 	m_regmodel;

	virtual task body();				
		// Get regmodel from config_db
		
		// Use register model handle to write into the DUT register
		m_regmodel.reg_ctl.write(uvm_status, 8'h3A);

		// Use register model handle to read from the DUT register
		m_regmodel.reg_ctl.read(uvm_status, rdata);
	endtask
endclass

Why is RAL useful ?

It would be very cumbersome to write and read from registers without RAL. User will have to create a uvm_sequence_item and execute the item on the targeted bus agent. Such code cannot be reused as easily as it is with RAL and there would be no good way to know the current state of the design at any point in time.


class write_sequence extends uvm_sequence;
	int 	m_addr;
	int 	m_wdata;

	...
	virtual task body();
		bus_item 	m_item;

		m_item = bus_item::type_id::create("my_item");
		m_item.randomize() with { 	m.wdata == m_wdata;
									m.write == 1;
									m.addr 	== m_addr; 
								};
		start_item(m_item);
		
		...
	endtask
endclass