A computed field declaration in a struct or union type has the form:
computed field_type field_name;
Where field_type is a type specifier denoting the type of the computed field, and field_name is an identifier. Note that, unlike regular fields, computed fields cannot have endianness annotations, constraints defined for them, cannot be optional, nor have initialization values.
Computed fields are not really stored in the corresponding struct values: their value is computed every time they are referred, by invoking a couple of methods that the user should define:
get_field_name
and
signature ()field_type
.
This getter is used to compute the value of the computed field when it is referenced as a r-value.
set_field_name
and
signature (field_type)void
.
This setter is called when the computed field is assigned to in a l-value.
For example, consider the following struct denoting a RISC-V instruction where the immediate is distributed in several chunks:
type RV32_InsnFmt_J = struct uint<32> { uint<1> imm20; uint<10> imm10_1; uint<1> imm11; uint<8> imm19_12; RV_Reg rd; RV_Opcode opcode : opcode in RV_OPCODES_J; computed int<32> imm; method get_imm = int<32>: { return imm20:::imm19_12:::imm11:::imm10_1:::(0 as int<1>) as int<21>; } method set_imm = (int<32> imm) void: { imm20 = (imm .>> 20) & 1; imm10_1 = (imm .>> 19) & 0x3ff; imm11 = (imm .>> 10) & 1; imm19_12 = (imm .>> 19) & 0xff; } };
When we create a value of type RV32_InsnFmt_J
we can then
externally access the computed field imm
like any other normal
field. However, computed fields are not accessible in other struct
methods, functions, constraint expressions and the like.