struct access
This commit is contained in:
parent
dce1c8d165
commit
af121fcb3e
|
|
@ -1302,8 +1302,50 @@ class ParamProcessor final {
|
|||
V3Const::constifyParamsEdit(varp);
|
||||
}
|
||||
if (AstConst* const constp = VN_CAST(varp->valuep(), Const)) {
|
||||
dotp->replaceWith(constp->cloneTree(false));
|
||||
VL_DO_DANGLING(dotp->deleteTree(), dotp);
|
||||
// Walk up any outer `.fieldN[.fieldM...]` chain (struct lparam
|
||||
// member access like `CFG::cfg.jt.cam_type`) and accumulate the
|
||||
// packed-struct bit offset. Replace the topmost Dot with a
|
||||
// Sel(constp, lsb, width) that constify can fold.
|
||||
int totalLsb = 0;
|
||||
int sliceWidth = varp->width();
|
||||
AstNodeDType* curDTypep = varp->dtypep();
|
||||
AstNode* topp = dotp;
|
||||
AstDot* outerDot = VN_CAST(dotp->backp(), Dot);
|
||||
while (outerDot && outerDot->lhsp() == topp) {
|
||||
const AstParseRef* const fieldRef = VN_CAST(outerDot->rhsp(), ParseRef);
|
||||
if (!fieldRef) break;
|
||||
AstNodeDType* const skippedp = curDTypep ? curDTypep->skipRefp() : nullptr;
|
||||
AstNodeUOrStructDType* const structp
|
||||
= VN_CAST(skippedp, NodeUOrStructDType);
|
||||
if (!structp) break;
|
||||
AstMemberDType* foundMemp = nullptr;
|
||||
for (AstMemberDType* memp = structp->membersp(); memp;
|
||||
memp = VN_AS(memp->nextp(), MemberDType)) {
|
||||
if (memp->name() == fieldRef->name()) {
|
||||
foundMemp = memp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundMemp) break;
|
||||
totalLsb += foundMemp->lsb();
|
||||
sliceWidth = foundMemp->width();
|
||||
curDTypep = foundMemp->subDTypep();
|
||||
topp = outerDot;
|
||||
outerDot = VN_CAST(outerDot->backp(), Dot);
|
||||
}
|
||||
if (topp == dotp) {
|
||||
dotp->replaceWith(constp->cloneTree(false));
|
||||
VL_DO_DANGLING(dotp->deleteTree(), dotp);
|
||||
} else {
|
||||
AstConst* const clonep = static_cast<AstConst*>(constp->cloneTree(false));
|
||||
AstSel* const selp
|
||||
= new AstSel{topp->fileline(), clonep, totalLsb, sliceWidth};
|
||||
// Preserve the field's actual dtype (e.g. enum) on the Sel so
|
||||
// assignments to enum-typed pins don't trip ENUMVALUE.
|
||||
if (curDTypep) selp->dtypep(curDTypep);
|
||||
topp->replaceWith(selp);
|
||||
VL_DO_DANGLING(topp->deleteTree(), topp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2190,8 +2232,8 @@ public:
|
|||
});
|
||||
}
|
||||
|
||||
// Walk every subtree reachable from `rootp` for three kinds of deferred work
|
||||
// V3LinkDot left behind when `class#(...)::member` couldn't yet be resolved:
|
||||
// Walk every subtree reachable from `rootp` for deferred work V3LinkDot left behind
|
||||
// when `class#(...)::member` couldn't yet be resolved:
|
||||
// (1) `AstDot` with a ClassOrPackageRef lhs — a `class::lparam` value Dot to
|
||||
// constify (e.g. `c8::b` in a pin expression).
|
||||
// (2) `AstRefDType` with a typedefp — descend into the typedef's subDType
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@
|
|||
//
|
||||
// Typedefs and class type parameters that resolve through a
|
||||
// class-scope-resolved localparam (typedef alias of a parameterized
|
||||
// class, e.g. inst::b). Exercises four sub-cases:
|
||||
// class, e.g. inst::b). Exercises five sub-cases:
|
||||
// - Deferred lparam used as a packed range bound in a typedef
|
||||
// - Deferred lparam used as a value argument to a parameterized class
|
||||
// - Class-scope-resolved typedef that itself depends on the param
|
||||
// - Class-scope Dot used DIRECTLY (no intermediate lparam) in a typedef
|
||||
// range inside a parameterized module, consumed by `$bits` on a child
|
||||
// cell pin
|
||||
// range, consumed by `$bits` on a child cell pin
|
||||
// - Struct lparam field access via class-scope Dot
|
||||
// (`CFG::struct_lparam.field`) inside a parameterized module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
|
|
@ -19,35 +20,21 @@
|
|||
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
|
||||
// verilog_format: on
|
||||
|
||||
// Sink for sub-case (4): the BITS pin consumes $bits of a typedef whose
|
||||
// packed range uses class-scope Dots. V3Param must descend into the typedef's
|
||||
// subDType and resolve those Dots, otherwise V3Width emits "dotted expressions
|
||||
// in parameters".
|
||||
module Sink #(
|
||||
parameter int BITS = 1
|
||||
) ();
|
||||
logic [BITS-1:0] data;
|
||||
endmodule
|
||||
module Sub #(parameter int W = 8) (); endmodule
|
||||
|
||||
// Parameterized wrapper so the class specialization is deferred until V3Param
|
||||
// processes the cell instance.
|
||||
module Sub #(
|
||||
parameter int W = 8
|
||||
) ();
|
||||
// Inner class — same shape as `t`'s C, repeated here to keep sub-case (4)
|
||||
// self-contained inside Sub.
|
||||
virtual class CInner #(
|
||||
parameter int a
|
||||
);
|
||||
localparam int b = a;
|
||||
endclass
|
||||
// Sub-case (5): struct lparam field access via class-scope Dot. Needs a
|
||||
// parameterized-module wrapper so the class specialization is deferred until
|
||||
// V3Param processes the cell instance.
|
||||
typedef struct packed { logic [7:0] hi; logic [7:0] lo; } pair_t;
|
||||
|
||||
typedef CInner#(W) CFG;
|
||||
class P #(parameter pair_t p);
|
||||
localparam pair_t pp = p;
|
||||
endclass
|
||||
|
||||
// (4) typedef packed range uses class-scope Dots directly
|
||||
typedef logic [(CFG::b + CFG::b - 1):0] dot_range_t;
|
||||
|
||||
Sink #(.BITS($bits(dot_range_t))) u_sink ();
|
||||
module PairHolder #(parameter pair_t cfg = '{hi:'d7, lo:'d11}) ();
|
||||
typedef P#(cfg) PALIAS;
|
||||
localparam logic [7:0] hi_val = PALIAS::pp.hi;
|
||||
localparam logic [7:0] lo_val = PALIAS::pp.lo;
|
||||
endmodule
|
||||
|
||||
module t;
|
||||
|
|
@ -80,8 +67,13 @@ module t;
|
|||
// Wire whose range comes from a deferred lparam
|
||||
logic [b8-1:0] wide_bus;
|
||||
|
||||
// (4) parameterized wrapper with a typedef range using class Dots directly
|
||||
Sub #(.W(8)) u_sub ();
|
||||
// (4) Cell pin $bits consumes a typedef whose packed range is built from
|
||||
// class-scope Dots directly (no intermediate deferred lparam).
|
||||
typedef logic [(c8::b + c8::b - 1):0] direct_use_t;
|
||||
Sub #(.W($bits(direct_use_t))) u_sub ();
|
||||
|
||||
// (5) struct lparam field access via class-scope Dot
|
||||
PairHolder #(.cfg('{hi:'d7, lo:'d11})) u_ph ();
|
||||
|
||||
initial begin
|
||||
`checkh(b8, 32'd8);
|
||||
|
|
@ -91,8 +83,11 @@ module t;
|
|||
`checkh(data_value, 8'hff);
|
||||
wide_bus = '1;
|
||||
`checkh(wide_bus, 8'hff);
|
||||
// sub-case (4): $bits(dot_range_t) = CFG::b + CFG::b = 8 + 8 = 16
|
||||
`checkh($bits(u_sub.u_sink.data), 32'd16);
|
||||
// sub-case (4): $bits(direct_use_t) = c8::b + c8::b = 8 + 8 = 16
|
||||
`checkh($bits(direct_use_t), 32'd16);
|
||||
// sub-case (5): struct lparam field access folds to the right field
|
||||
`checkh(u_ph.hi_val, 8'd7);
|
||||
`checkh(u_ph.lo_val, 8'd11);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue