UVM RAL Example DMA

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.

UVM RAL Example
UVM RAL Example

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,

UVM TestBench Register Access
UVM TestBench Register Access

The testbench components are,

  • Environment
    • DMA Agent
      • Driver
      • Monitor
      • Sequencer and Sequences
    • RAL Model
      • DMA Reg package
      • Adapter

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

Click to execute on   

Write and Read Waveform:

UVM RAL Register Write Read
UVM RAL Register Write Read

Write Waveform:

UVM RAL Register Write
UVM RAL Register Write

Read Waveform:

UVM RAL Register Read
UVM RAL Register Read

❮ Previous Next ❯