diff --git a/CHANGELOG.md b/CHANGELOG.md index aeee2bf..97e40da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ * Added support for `bufif0`, `bufif1`, `notif0`, `notif1`, `cmos`, `rcmos`, `nmos`, `pmos`, `rnmos`, and `rpmos`. +### Bug Fixes + +* Fixed conversion of struct field accesses when the struct's field widths + depend on a member of a struct-typed parameter + ### Other Enhancements * `always_comb` blocks with sensitivities inherited from called functions or diff --git a/src/Convert/Struct.hs b/src/Convert/Struct.hs index de8d5ba..e86294f 100644 --- a/src/Convert/Struct.hs +++ b/src/Convert/Struct.hs @@ -385,10 +385,12 @@ convertSubExpr scopes (Dot e x) = where (subExprType, e') = convertSubExpr scopes e (isHier, fieldType, bounds, dims) = lookupFieldInfo scopes subExprType e' x - base = fst bounds - len = rangeSize bounds + -- the offset and size are derived from the struct layout, whose field + -- widths may contain member accesses that must themselves be lowered + (_, base) = convertSubExpr scopes $ fst bounds + (_, len) = convertSubExpr scopes $ rangeSize bounds undotted = if null dims || rangeSize (head dims) == RawNum 1 - then Bit e' (fst bounds) + then Bit e' base else Range e' IndexedMinus (base, len) -- retain signedness of fields which would otherwise be lost via the -- resulting bit or range selection diff --git a/test/core/class_paramstruct.sv b/test/core/class_paramstruct.sv new file mode 100644 index 0000000..0fb9af7 --- /dev/null +++ b/test/core/class_paramstruct.sv @@ -0,0 +1,26 @@ +package P; + typedef struct packed { int unsigned a; int unsigned b; } cfg_t; + localparam cfg_t cfg = '{a: 8, b: 4}; + typedef enum logic [1:0] { S_A, S_B } enum_e; +endpackage + +class C #(parameter P::cfg_t cfg = P::cfg); + typedef logic [$clog2(cfg.a) - 1:0] x_t; + typedef logic [cfg.b - 1:0] y_t; + typedef struct packed { P::enum_e e; x_t x; y_t y; } s_t; +endclass + +module child #( + parameter P::cfg_t cfg = P::cfg, + parameter type s_t = C#(cfg)::s_t, + localparam type x_t = C#(cfg)::x_t +) ( + input s_t ins, + output x_t out +); + always_comb + unique case (ins.e) + P::S_A: out = ins.x; + default: out = '0; + endcase +endmodule diff --git a/test/core/class_paramstruct.v b/test/core/class_paramstruct.v new file mode 100644 index 0000000..6f1c52d --- /dev/null +++ b/test/core/class_paramstruct.v @@ -0,0 +1,11 @@ +module child( + input wire [8:0] inp, + output reg [2:0] out +); + always @* begin + case (inp[8:7]) + 2'd0: out = inp[6:4]; + default: out = 3'd0; + endcase + end +endmodule diff --git a/test/core/class_paramstruct_tb.v b/test/core/class_paramstruct_tb.v new file mode 100644 index 0000000..8701d2c --- /dev/null +++ b/test/core/class_paramstruct_tb.v @@ -0,0 +1,11 @@ +module top; + reg [8:0] inp; + wire [2:0] out; + child c(inp, out); + initial begin + $monitor("%2d %b %b", $time, inp, out); + inp = 9'b00_101_0000; #1; + inp = 9'b00_010_1111; #1; + inp = 9'b01_111_0000; #1; + end +endmodule