Reputation: 63
I have part of a register that looks something like this
register CC {
field CSS @ [6:4] is write {
method write(uint64 value) {
if (CC.EN.val == 0b1) {
log spec_viol: "Command set selection should not " +
"occur when controller has been enabled.";
return;
}
default(value);
}
}
field EN @ [0];
}
How can I ensure that when CC.EN
is set to 1 (for the first time) by setting the value of the register CC
, that the spec-viol in CC.CSS
does not occur?
I tried writing to the register for the first time and the spec-viol was triggered
Upvotes: 2
Views: 68
Reputation: 898
For completeness I want to present the following anti-pattern that occasionally is seen:
register CC {
session bool was_enabled;
method write_register(uint64 value, uint64 enabled_bytes, void *aux) {
was_enabled = EN.val;
default(value, enabled_bytes, aux);
}
field CSS @ [6:4] is write_field {
method write_field(uint64 value, uint64 enabled_bits, void *aux) {
if ((this.val & enabled_bits) == (value & enabled_bits))
return; // no change, ignore
if (was_enabled) {
log spec_viol: "Command set selection should not " +
"occur when controller has been enabled.";
return;
}
this.val = (this.val & ~enabled_bits) | (value & enabled_bits);
}
}
field EN @ [0];
}
This approach may look compelling compared to the aux
approach for type safety reasons; it avoid the need to fiddle with void
pointers. However, this approach is problematic both because it allocates permanent storage for a variable that is only used temporarily, and because it is not reentrant (will break if the write_field
method of some field indirectly triggers another write to CC synchronously). The reentrancy problem can be resolved by restoring the old value through a local variable, but the added type safety is not worth the complexity.
Upvotes: 0
Reputation: 121
Fields in a register in DML are accessed in order of increasing least significant bit. This means that in your example, the EN
field will be written before the CCS
field. So to achieve this we must pass the state of EN
before the write to the register. We do this by utilizing the void *aux
argument in the write_register
and write_field
templates:
register CC {
method write_register(uint64 value, uint64 enabled_bytes, void *aux) {
local bool was_enabled = EN.val;
default(value, enabled_bytes, &was_enabled);
}
field CSS @ [6:4] is write_field {
method write_field(uint64 value, uint64 enabled_bits, void *aux) {
if ((this.val & enabled_bits) == (value & enabled_bits))
return; // no change, ignore
if (*cast(aux, *bool)) {
log spec_viol: "Command set selection should not " +
"occur when controller has been enabled.";
return;
}
this.val = (this.val & ~enabled_bits) | (value & enabled_bits);
}
}
field EN @ [0];
}
Upvotes: 2
Reputation: 97
Another option that does not rely on order:
register CC {
method write_register(uint64 value, uint64 enabled_bytes, void *aux) {
if (this.val[EN.msb] && value[EN.msb]) {
if ((CSS.val & (enabled_bytes[CSS.msb:CSS.lsb])) != (value[CSS.msb : CSS.lsb])) {
log spec_viol: "%s Command set selection should not " +
"occur when controller has been enabled.", this.qname;
value[CSS.msb : CSS.lsb] = CSS.val;
}
}
default(value, enabled_bytes, aux);
}
field CSS @ [6:4];
field EN @ [0] is (write) {
method write(uint64 value) {
// Do something...
}
}
}
Upvotes: 1