Jump to content
ashleywinn

workaround to control register write order for wide fields

Recommended Posts

In the UVM register model, if you create a register or a field which is wider than the bus width there is no way to control what order the multiple bus operations occur to update the register/field value. In my case I was using BIG_ENDIAN addressing and the register model would always write the largest address first. My design spec required that the largest address be written last. I searched in vain for a way to change this but ended up overriding the 'do_bus_write' function in 'uvm_reg_map'. If you've run into a similar issue, here's my workaround. 
 
reversed_reg_order_map.sv

`ifndef __REVERSED_REG_ORDER_MAP_SV__
 `define __REVERSED_REG_ORDER_MAP_SV__
  
class reversed_reg_order_map extends uvm_reg_map;
   `uvm_object_utils(reversed_reg_order_map)
   function new(string name="reversed_reg_order_map");
      super.new(name);
   endfunction

   extern virtual task do_write_bus_ops(uvm_reg_bus_op bus_ops[$],
                                        uvm_reg_item rw,
                                        uvm_sequencer_base sequencer,
                                        uvm_reg_adapter adapter);

   extern virtual task do_bus_write(uvm_reg_item rw,
                                    uvm_sequencer_base sequencer,
                                    uvm_reg_adapter adapter);
endclass


task reversed_reg_order_map::do_write_bus_ops(uvm_reg_bus_op bus_ops[$],
                                              uvm_reg_item rw,
                                              uvm_sequencer_base sequencer,
                                              uvm_reg_adapter adapter);
   foreach (bus_ops[i]) begin
      uvm_sequence_item bus_req;
      uvm_reg_bus_op    rw_access;
      
      adapter.m_set_item(rw);
      bus_req = adapter.reg2bus(bus_ops[i]);
      adapter.m_set_item(null);
      
      if (bus_req == null)
        `uvm_fatal("RegMem",{"adapter [",adapter.get_name(),"] didnt return a bus transaction"});
      
      bus_req.set_sequencer(sequencer);
      rw.parent.start_item(bus_req,rw.prior);

      if (rw.parent != null && i == 0)
        rw.parent.mid_do(rw);

      rw.parent.finish_item(bus_req);
      bus_req.end_event.wait_on();

      if (adapter.provides_responses) begin
         uvm_sequence_item bus_rsp;
         uvm_access_e op;
         // TODO: need to test for right trans type, if not put back in q
         rw.parent.get_base_response(bus_rsp);
         adapter.bus2reg(bus_rsp,rw_access);
      end
      else begin
         adapter.bus2reg(bus_req,rw_access);
      end

      if (rw.parent != null && i == bus_ops.size()-1)
        rw.parent.post_do(rw);

      rw.status = rw_access.status;

      `uvm_info(get_type_name(),
                $sformatf("Wrote 'h%0h at 'h%0h via map \"%s\": %s...",
                          bus_ops[i].data, bus_ops[i].addr, 
                          rw.map.get_full_name(), rw.status.name()), UVM_FULL)

      if (rw.status == UVM_NOT_OK)
        break;
   end
endtask 

task reversed_reg_order_map::do_bus_write (uvm_reg_item rw,
                                           uvm_sequencer_base sequencer,
                                           uvm_reg_adapter adapter);

  uvm_reg_addr_t     addrs[$];
  uvm_reg_map        system_map = get_root_map();
  int unsigned       bus_width  = get_n_bytes();
  uvm_reg_byte_en_t  byte_en    = -1;
  uvm_reg_map_info   map_info;
  int                n_bits;
  int                lsb;
  int                skip;
  int unsigned       curr_byte;
  int                n_access_extra, n_access;
  int                n_bits_init;
  uvm_reg_bus_op     bus_ops[$];
   

  Xget_bus_infoX(rw, map_info, n_bits_init, lsb, skip);
  addrs = map_info.addr;
  // `UVM_DA_TO_QUEUE(addrs,map_info.addr)

  // if a memory, adjust addresses based on offset
  if (rw.element_kind == UVM_MEM)
    foreach (addrs[i])
      addrs[i] = addrs[i] + map_info.mem_range.stride * rw.offset;

  foreach (rw.value[val_idx]) begin: foreach_value

     uvm_reg_data_t value = rw.value[val_idx];
     curr_byte = 0;
    /* calculate byte_enables */
    if (rw.element_kind == UVM_FIELD) begin
      int temp_be;
      int idx;
      n_access_extra = lsb%(bus_width*8);                
      n_access = n_access_extra + n_bits_init;
      temp_be = n_access_extra;
      value = value << n_access_extra;
      while(temp_be >= 8) begin
         byte_en[idx++] = 0;
         temp_be -= 8;
      end                        
      temp_be += n_bits_init;
      while(temp_be > 0) begin
         byte_en[idx++] = 1;
         temp_be -= 8;
      end
      byte_en &= (1<<idx)-1;
      for (int i=0; i<skip; i++)
        void'(addrs.pop_front());
      while (addrs.size() > (n_bits_init/(bus_width*8) + 1))
        void'(addrs.pop_back());
    end
    curr_byte=0;
    n_bits= n_bits_init;     
              
    foreach(addrs[i]) begin: foreach_addr
      uvm_reg_bus_op rw_access;
      uvm_reg_data_t data;

      data = (value >> (curr_byte*8)) & ((1'b1 << (bus_width * 8))-1);
      curr_byte += bus_width;
       
      `uvm_info(get_type_name(),
         $sformatf("Writing 'h%0h at 'h%0h via map \"%s\"...",
              data, addrs[i], rw.map.get_full_name()), UVM_FULL);

      if (rw.element_kind == UVM_FIELD) begin
        for (int z=0;z<bus_width;z++)
          rw_access.byte_en[z] = byte_en[curr_byte+z];
      end
                
      rw_access.kind    = rw.kind;
      rw_access.addr    = addrs[i];
      rw_access.data    = data;
      rw_access.n_bits  = (n_bits > bus_width*8) ? bus_width*8 : n_bits;
      rw_access.byte_en = byte_en;

      n_bits -= bus_width * 8;
       
      bus_ops.push_back(rw_access);

    end: foreach_addr

    foreach (addrs[i])
      addrs[i] = addrs[i] + map_info.mem_range.stride;

  end: foreach_value

   // reverse the write order
   begin
      uvm_reg_bus_op   reversed_bus_op_list[$];
      foreach (bus_ops[i])
        reversed_bus_op_list.push_front(bus_ops[i]);

      do_write_bus_ops(reversed_bus_op_list, rw, sequencer, adapter);
   end

endtask

`endif //  `ifndef __REVERSED_REG_ORDER_MAP_SV__

Then I used a factory type override:

set_type_override_by_type(uvm_reg_map::get_type(),
		          reversed_reg_order_map::get_type());

Share this post


Link to post
Share on other sites

UVM12 offers for this application a generic uvm_reg_transaction_order_policy which allows you to reorder transactions before they go to the bus.

 

/uwe

function void uvm_reg_map::set_transaction_order_policy(uvm_reg_transaction_order_policy pol);


// Class: uvm_reg_transaction_order_policy
virtual class uvm_reg_transaction_order_policy extends uvm_object;
function new(string name = "policy");
super.new(name);
endfunction


// Function: order
// the order() function may reorder the sequence of bus transactions
// produced by a single uvm_reg transaction (read/write).
// This can be used in scenarios when the register width differs from 
// the bus width and one register access results in a series of bus transactions.
// the first item (0) of the queue will be the first bus transaction (the last($) 
// will be the final transaction
pure virtual function void order(ref uvm_reg_bus_op q[$]);
endclass

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×