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