UVM REG EXAMPLE WITHOUT RAL

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.

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 component DMA agent is used to access the reg_interface.
DMA Agent consists of,

  • Driver
  • Monitor
  • Sequencer
  • Sequences (Write sequence and Read Sequence)

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.

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);

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.

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

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 

Click to execute on   

Write and Read Waveform:

UVM Register WR and RD Operation
UVM Register WR and RD Operation

Write Waveform:

UVM Register WR
UVM Register WR

Read Waveform:

UVM Register RD
UVM Register RD

❮ Previous Next ❯