Register access using sequence items, but reflect the changes in the register model

Hi,

I have read thru the documents about building up the UVM register models would allow me to directly publish my register update using the REG model, like below code. This code is great that it would program my DUT registers content thru the register adapter, and also update the register model values.

data = a11;
rm.a11.write(status, data, .parent(this));
data = a12;
rm.a12.write(status, data, .parent(this));
data = b10;
rm.b10.write(status, data, .parent(this));
data = b11;
rm.b11.write(status, data, .parent(this));
data = b12;
rm.b12.write(status, data, .parent(this));

However, my design is a huge design, consists of over few hundred or registers. Using this approach in my tests to update the registers is not modular.
So, my actual test setup would be to have my register access, to pass on the register address, data, to my APB interface sequence items. In my test, I would publish the changes like this:

`uvm_do_on_with(apb_seq, p_sequencer.apb_seqr, {pwrite==CMD_WR; paddr=={register_addr[31:0]}; pwdata==data;})

Using this method would still allow me to program my DUT using my APB sequence item passed to my APB agent. However, I found that even I have my UVM register predictor setup properly, the APB monitor is able to detect the APB bus changes, and translate it to the corresponding update in the register map, it would not update my UVM register model. I wants to poll my DUT’s register configuration to capture the setting programmed, by query the register model values. Is this method (register access without the RAL model) workable at all?

In reply to marco5polo:

You need to make sure that implicit prediction is turned off and you are set up for explicit prediction, or in your case it is sometimes called passive prediction.

Also I fail to see how using the Reg model is not modular. The model provides address look up and implicit sequence usage. If you don’t use it you’ll have to look up the address yourself , calculate any offsets, and add base addresses manually.

In reply to dave_59:

Dave,

Thanks a lot for pointing out the specific topic I should look into. I’ve read up a bit about explicit the last few days, and I have also ensure that I have disabled auto-prediction in my env setup.
m_config.rm.default_map.set_auto_predict(0);

Now, I’ve come to the point that I’m debugging what’s going wrong in my bus2reg operation. I took the code from uvm_reg_predictor, and added in pointers print out for debugging.

In the write function of the my_uvm_reg_predictor (clone from uvm_reg_predictor), I added some print lines and put them in BOLD in code snippet below:

  virtual function void write(BUSTYPE tr);
     uvm_reg rg;
     uvm_reg_bus_op rw;
    if (adapter == null)
     `uvm_fatal("REG/WRITE/NULL","write: adapter handle is null")

     // In case they forget to set byte_en
     rw.byte_en = -1;
     adapter.bus2reg(tr,rw);
     rg = map.get_reg_by_offset(rw.addr, (rw.kind == UVM_READ));
**`uvm_info(get_type_name(),$sformatf("REG::\nkind: %s\naddr: 0x%8h\ndata: 0x%8h", rw.kind, rw.addr, rw.data), UVM_LOW)**

     if (rg != null) begin
       bit found;
       uvm_reg_item reg_item;
       uvm_reg_map local_map;
       uvm_reg_map_info map_info;
       uvm_predict_s predict_info;
       uvm_reg_indirect_data ireg;
       uvm_reg ir;

       if (!m_pending.exists(rg)) begin
         uvm_reg_item item = new;
         predict_info =new;
         item.element_kind = UVM_REG;
         item.element      = rg;
         item.path         = UVM_PREDICT;
         item.map          = map;
         item.kind         = rw.kind;
         predict_info.reg_item = item;
         m_pending[rg] = predict_info;
       end
       predict_info = m_pending[rg];
       reg_item = predict_info.reg_item;

       if (predict_info.addr.exists(rw.addr)) begin
          `uvm_error("REG_PREDICT_COLLISION",{"Collision detected for register '",
                     rg.get_full_name(),"'"})
          // TODO: what to do with subsequent collisions?
          m_pending.delete(rg);
       end

       local_map = rg.get_local_map(map,"predictor::write()");
       map_info = local_map.get_reg_map_info(rg);
       ir=($cast(ireg, rg))?ireg.get_indirect_reg():rg;

       foreach (map_info.addr[i]) begin
         if (rw.addr == map_info.addr[i]) begin
            found = 1;
           reg_item.value[0] |= rw.data << (i * map.get_n_bytes()*8);
           predict_info.addr[rw.addr] = 1;
           if (predict_info.addr.num() == map_info.addr.size()) begin
              // We've captured the entire abstract register transaction.
              uvm_predict_e predict_kind =
                  (reg_item.kind == UVM_WRITE) ? UVM_PREDICT_WRITE : UVM_PREDICT_READ;

              if (reg_item.kind == UVM_READ &&
                  local_map.get_check_on_read() &&
                  reg_item.status != UVM_NOT_OK) begin
                 void'(rg.do_check(ir.get_mirrored_value(), reg_item.value[0], local_map));
              end

              pre_predict(reg_item);

              ir.XsampleX(reg_item.value[0], rw.byte_en,
                          reg_item.kind == UVM_READ, local_map);
              begin
                 uvm_reg_block blk = rg.get_parent();
                 blk.XsampleX(map_info.offset,
                              reg_item.kind == UVM_READ,
                              local_map);
              end

              rg.do_predict(reg_item, predict_kind, rw.byte_en);
**`uvm_info(get_type_name(), $sformatf("Register %s\n%s", ir.get_full_name(),ir.sprint()), UVM_LOW)**
              if(reg_item.kind == UVM_WRITE)
**`uvm_info("REG_PREDICT", {"Observed WRITE transaction to register ",
                         ir.get_full_name(), ": value='h",
                         $sformatf("%0h",reg_item.value[0]), " : updated value = 'h",
                         //$sformatf("%0h",ir.get())},UVM_HIGH)
                         $sformatf("%0h",ir.get())},UVM_LOW)**
              else
                `uvm_info("REG_PREDICT", {"Observed READ transaction to register ",
                         ir.get_full_name(), ": value='h",
                         $sformatf("%0h",reg_item.value[0])},UVM_HIGH)
              reg_ap.write(reg_item);
              m_pending.delete(rg);
           end
           break;
         end
       end
       if (!found)
         `uvm_error("REG_PREDICT_INTERNAL",{"Unexpected failed address lookup for register '",
                  rg.get_full_name(),"'"})
     end
     else begin
       `uvm_info("REG_PREDICT_NOT_FOR_ME",
          {"Observed transaction does not target a register: ",
            $sformatf("%p",tr)},UVM_FULL)
            //$sformatf("%p",tr)},UVM_LOW)
       `uvm_info(get_type_name(),"REG_PREDICT_NOT_FOR_ME!", UVM_LOW)
     end
  endfunction

In my env, I was setting the register access using my own I2C agent instead this time. My sequence transaction is as this:
`uvm_do_on_with(i2c_seq, p_sequencer.i2c_seqr, {reg_rw==CMD_WR; dev_addr==chipid; reg_addr==32’h000200b0; reg_wdata==32’h118081a2; reg_cycle==4;})
model[0].dclp_ctrl0_reg.predict(32’h1180_81A2);
$display(“DEBUG: dclp_ctrl0_reg value=0x%8h”, model[0].dclp_ctrl0_reg.get_mirrored_value());

From the logfile below, it shows that the register-predictor is able to understand I’m programming my register at address 0x12000200B0, with data 0x118081A2. However, when I print out the reg_item after the do_predict line in uvm_reg_predcitor, it shows the value is still not update (I expect the LSB 3-bit, adc_lsb_raw updated to 2), and stays at the default value. It’s only when I manually call the predict function, the above bold line, I can finally see the mirrored value is updated. In line 232 of my_uvm_reg_predictor, it also indicated that it understand I’m trying to update the register content to value 'h118081a2, but it’s updated value is still 'h118081a4. What did I not set right? I have no clues for now, and hope you can help to provide some pointers.

If I switch to use the RAL register access method, then it works.
model[0].dclp_ctrl0_reg.write(status, 32’h1180_81A2, .parent(this));

# UVM_INFO ./tb/my_uvm_reg_predictor.svh(153) @ 9840500000: uvm_test_top.top_e.reg_predictor [my_uvm_reg_predictor #(i2c_transaction)] REG::
# kind: UVM_WRITE
# addr: 0x12000200b0
# data: 0x118081a2
# UVM_INFO ./tb/my_uvm_reg_predictor.svh(230) @ 9840500000: uvm_test_top.top_e.reg_predictor [my_uvm_reg_predictor #(i2c_transaction)] Register rm.rx_reg[0].dclp_ctrl0_reg
# ------------------------------------------------------------------------
# Name             Type              Size  Value                          
# ------------------------------------------------------------------------
# dclp_ctrl0_reg   r_dclp_ctrl0_reg  -     @12553                         
#   adc_lsb_raw    uvm_reg_field     ...    RW dclp_ctrl0_reg[2:0]=3'h4   
#   adc_lsb_dp     uvm_reg_field     ...    RW dclp_ctrl0_reg[5:3]=3'h4   
#   d_dc_msb       uvm_reg_field     ...    RW dclp_ctrl0_reg[8:6]=3'h6   
#   dc_init        uvm_reg_field     ...    RW dclp_ctrl0_reg[15:9]=7'h40 
#   dc_dac         uvm_reg_field     ...    RO dclp_ctrl0_reg[23:17]=7'h40
#   kdc            uvm_reg_field     ...    RO dclp_ctrl0_reg[26:24]=3'h1 
#   kdc_d          uvm_reg_field     ...    RO dclp_ctrl0_reg[30:28]=3'h1 
#   d_dclp_lp_clk  uvm_reg_field     ...    RW dclp_ctrl0_reg[31:31]=1'h0 
# ------------------------------------------------------------------------
#
# UVM_INFO ./tb/my_uvm_reg_predictor.svh(232) @ 9840500000: uvm_test_top.top_e.reg_predictor [REG_PREDICT] Observed WRITE transaction to register rm.rx_reg[0].dclp_ctrl0_reg: value='h118081a2 : updated value = 'h118081a4
# DEBUG: dclp_ctrl0_reg value=0x118081a2