Jump to content

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

Link to comment
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
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...