Modeling behavior of a register that is dependant on another register

If I have two registers, a and b. Register a has a 8-bit field named “data” and register b has a one bit field “input_en”. Register a is RW and register b is RO. Even though register a is RW it will only write to the data field when b.input_en is high. I have explicit prediction. How would I model this?

I know I can overwrite register a’s predict method for example, pseudo code below:

  virtual function bit predict(...);
    bit status_input_ready_field;

    status_input_ready_field =  b.input_en.get_mirrored_value();

    if (status_input_ready_field == 1'b1) begin
      return super.predict(value, be, kind, path, map, fname, lineno);
    end else begin
      return 0;
    end
  endfunction: predict

Or would it be better to override the uvm_reg_predictor’s pre_predict() method similarly to above:

  virtual function void pre_predict(uvm_reg_item rw);
    uvm_reg my_reg;
    bit     status_input_ready_field;

    if (rw.element_kind == UVM_REG) begin
      if (!$cast(my_reg, rw.element)) begin
        `uvm_fatal(get_name(), "Failed casting rw.element to register")
      end

      if (my_reg.get_name() == "a") begin
        status_input_ready_field = b.input_en.get_mirrored_value();

        if (status_input_ready_field != 1'b1) begin
          rw.value[0] = a.get_mirrored_value();
        end
      end
    end
  endfunction

Or should I use callbacks or some other method?

For me, it looks like it would be best to do it in the uvm_reg_predictor, because I can put all the code for existing register dependancies there.

But, I would like to know what would be the best way in your opinion to model this dependancy?

Both your suggestions will work in your case, but I would still recommend using callback for these reasons :

  • If you have an existing automated register model generation flow(e.g. scripts), not just 2 registers, suggestion 1 would require updating those scripts.
    You could argue that using factory type_override will eliminate the need to update scripts, but it might get messy to pass a handle of b.input_en after creating register a.
  • Give more flexibility/guard against any future updates. For example, 3 more fields are added to register a and 2 of which could be updated unconditioned by inpu_en value, then handling which fields should be updated and which aren’t in solution 2 will require extra calculation.

Here’s how it would look like if callback suggestion is considered

class rega_data_field_cb extends uvm_reg_cbs;
	
     local uvm_reg_field input_en;
	
     function new (string name, uvm_reg_field input_en);
		super.new (name);
		this.input_en = input_en;
	 endfunction
	 
	 virtual function void post_predict(....,
										input uvm_reg_data_t previous,
										inout uvm_reg_data_t value, 
										...);
		if (kind == UVM_PREDICT_WRITE) begin
			if (input_en.get_mirrored_value()) begin
				// Restore the previous value if input_en is active
				value = previous;
			end
		end
	 endfunction
endclass
// In a base test or top environment 
rega_data_field_cb a_data_cb = new("a_data_cb", regmodel.b.input_en);
uvm_reg_field_cb::add(regmodel.a.data, a_data_cb);

The downside of the callback is that it’s per register field, not the whole register, then in case of all fields should be guarded an extra
step is required to extract all fields using get_fields.
One thing though that should be considered in all suggestions, as input_en is RO, which means either there should be a model that updates its mirrored value as per spec or calling mirror() before each write on register a

1 Like

Thanks for the answer and for the recommendation.
Your first bullet makes a lot of sense, and I didn’t thing about it.

One question about your second point about putting it in the uvm_reg_predictor pre_predict() method. What did you mean with extra calculation when more fields are added to register a? Do you mean in this part of updating the value of the register that we would need to make sure which fields we are updating or something else?

if (status_input_ready_field != 1'b1) begin
  // process right value of register a depending on fields that should be updated
  rw.value[0] = calc_val_a;

Yes, that’s what I meant :)

1 Like