2

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

toffe
  • 63
  • 5
  • According to documentation, "The implementation [of read_register] invokes the `read_field` method of all sub-fields [..] in order from least to most significant bit", so assuming little-endian bitorder I think EN *should* be written before CSS.write? – Erik Carstensen Jan 23 '23 at 13:41
  • Oh nevermind, sloppy reading from my side. The semantics you desire is that the EN value from *before* the write should apply to CSS of the current write. So in your code, the field order means that EN is overwritten before CSS reads it, which is not what you wanted. – Erik Carstensen Jan 23 '23 at 13:49

3 Answers3

2

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];
}
SvenAron
  • 111
  • 3
1

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...
        }
    }
}
0

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.

Erik Carstensen
  • 634
  • 4
  • 14