Reputation: 171
I was wondering if there is a standard function in verilog or systemverilog that will return the bit offset of a certain field within a packed struct. For instance, see use of hypothetical function $find_field_offset below:
typedef struct packed
{
logic [31:0] field_1,
logic [15:0] field_2,
logic [63:0] field_3
} struct_type;
struct_type the_struct;
int field_1_offset;
assign field_1_offset = $find_field_offset(the_struct.field_1);
Thanks!
Upvotes: 2
Views: 3502
Reputation: 567
I used @jclin's function and turned it into a macro, so you can call it like a function, no need to replicate code.
`define get_offset(struct_name, field_name, bit_offset)\
struct_name = '0;\
struct_name.field_name = '1;\
for (integer i = 0; i < $bits(struct_name); i=i+1) begin\
if (struct_name[i] == 1) begin\
$display("%s offset=%4d", `"field_name`", i);\
bit_offset = i;\
break;\
end\
end
example_struct_t example_struct;
int return_value;
initial begin
`get_offset(example_struct, field_0, return_value);
`get_offset(example_struct, field_1, return_value);
`get_offset(example_struct, field_2.subfield_0, return_value);
end
The return value is useful if you want to collect the values and do some further calculation
Upvotes: 1
Reputation: 11
Thanks for the examples. Below are some pedantic checks and messaging to confirm offsets and sizes of all fields of some hierarchical structs. char_idx is the character position of a nibble in the struct if read as a string from a $writememh or similar. This taks is invoked within an initial block to confirm downstream parsing will be able to interpret the hexadecimal representation correctly.
task report_offsets(
);
phy_mon_info_s tr;
integer char_idx;
$display("tr is made of %0d bits",$bits(tr));
for (integer idx = 0;idx< $bits(tr);idx++) begin
char_idx = ($bits(tr) - idx - 1) >> 2;
tr = 1'b1 << idx;
if (|tr.inst > 0) $display("tr.inst claims bit %0d hex char %0d" ,idx, char_idx);
if (|tr.clock_count) $display("tr.clock_count claims bit %0d hex char %0d" ,idx, char_idx);
if (|tr.phy_info.dir) $display("tr.phy_info.dir claims bit %0d hex char %0d" ,idx, char_idx);
if (|tr.phy_info.data_type) $display("tr.phy_info.data_type claims bit %0d hex char %0d" ,idx, char_idx);
for (int inner = 0;inner< PHY_MON_FRAME_DWS;inner++) begin
if (|tr.phy_info.header[inner]) $display("tr.phy_info.header[%0d] claims bit %0d hex char %0d",inner,idx, char_idx);
end
if (|tr.phy_info.payload_dws) $display("tr.phy_info.payload_dws claims bit %0d hex char %0d",idx, char_idx);
if (|tr.phy_info.prim) $display("tr.phy_info.prim claims bit %0d hex char %0d" ,idx, char_idx);
if (|tr.phy_info.num_prims) $display("tr.phy_info.num_prims claims bit %0d hex char %0d" ,idx, char_idx);
if (|tr.phy_clock) $display("tr.phy_info.phy_clk claims bit %0d hex char %0d" ,idx, char_idx);
end
assert($bits(tr.inst ) % 4 == 0) else $error("total bits in tr.inst %0d is not a multiple of 4!",$bits(tr.inst ));
assert($bits(tr.clock_count ) % 4 == 0) else $error("total bits in tr.clock_count %0d is not a multiple of 4!",$bits(tr.clock_count ));
assert($bits(tr.phy_info.dir ) % 4 == 0) else $error("total bits in tr.phy_info.dir %0d is not a multiple of 4!",$bits(tr.phy_info.dir ));
assert($bits(tr.phy_info.data_type ) % 4 == 0) else $error("total bits in tr.phy_info.data_type %0d is not a multiple of 4!",$bits(tr.phy_info.data_type ));
assert($bits(tr.phy_info.header ) % 4 == 0) else $error("total bits in tr.phy_info.header %0d is not a multiple of 4!",$bits(tr.phy_info.header ));
assert($bits(tr.phy_info.payload_dws ) % 4 == 0) else $error("total bits in tr.phy_info.payload_dws %0d is not a multiple of 4!",$bits(tr.phy_info.payload_dws ));
assert($bits(tr.phy_info.prim ) % 4 == 0) else $error("total bits in tr.phy_info.prim %0d is not a multiple of 4!",$bits(tr.phy_info.prim ));
assert($bits(tr.phy_info.num_prims ) % 4 == 0) else $error("total bits in tr.phy_info.num_prims %0d is not a multiple of 4!",$bits(tr.phy_info.num_prims ));
assert($bits(tr.phy_clock ) % 4 == 0) else $error("total bits in tr.phy_clock %0d is not a multiple of 4!",$bits(tr.phy_clock ));
assert($bits(tr ) % 4 == 0) else $error("total bits in tr %0d is not a multiple of 4!",$bits(tr ));
endtask
Upvotes: 1
Reputation: 2559
It might not be a good and convenient way. But this is the native SV code to find out the offset field_1
inside struct_type
function automatic int get_offset_struct_type_field_1;
struct_type x = '0;
x.field_1 = '1;
for (integer i = 0; i < $bits(x); i=i+1) begin
if (x[i] == 1) return i;
end
endfunction
Upvotes: 4
Reputation: 7573
If you're ok with using the VPI, then you can do what you want with a little bit of type introspection. The problem with this is that you can't call the function in the way you like, because, to my knowledge, the compiler loses the context of what field1
actually is. What I mean by this is that the function would see a logic vector value, but not know it originated from a struct.
If you're okay with changing the function call to:
$find_field_offset(the_struct, "field1"); // note "field1" in quotes
then it would technically be possible to figure out that the_struct
is of type struct_type
and loop over all of its fields to find the field called "field1"
and return the offset of that.
The problem with using VPI code is that support for the VPI object model varies from vendor to vendor. You have to be lucky enough to use a vendor that has support for the functions we would need here.
Upvotes: 0
Reputation: 7573
It doesn't really make sense to have a function that returns this in the form that you presented. The reason is that you don't have anything variable in there. You just pass in a value to the method and there is nothing the compiler could use to determine that you want to get the offset of field1
in struct_type
.
The best you can do with native SystemVerilog is to define your own function that returns the offset based on an enumerated argument. Off the top of my head:
typedef enum { FIELD1, FIELD2, FIELD3 } struct_type_fields_e;
function int unsigned get_offset(struct_type_fields_e field);
case (field)
FIELD1 : return 0;
FIELD2 : return 32;
FIELD3 : return 48;
endcase
endfunction
You could probably do more with some VPI code, but you would need to change the way you call your function.
Upvotes: 0