descend into dots in typedefs

This commit is contained in:
Edmund Lam 2026-06-09 13:07:17 -04:00
parent f18aaeea83
commit 04eabc62fe
2 changed files with 43 additions and 4 deletions

View File

@ -21,7 +21,7 @@ interface SubIface #(
logic [IW-1:0] data;
endinterface
// Module that takes an interface port must elaborate after the iface
// Module that takes an interface port - must elaborate after the iface
// cell pin is fully constified.
module Consumer (
SubIface si
@ -50,7 +50,7 @@ module t;
SubIface #(b8_alias) i_chain ();
// Iface pin = expression mixing deferred lparams
SubIface #(b8 + b13) i_expr ();
// Iface used as a module port exercises the iface-cell-first path
// Iface used as a module port - exercises the iface-cell-first path
Consumer cons (.si(i_bare));
initial begin

View File

@ -2,10 +2,13 @@
//
// Typedefs and class type parameters that resolve through a
// class-scope-resolved localparam (typedef alias of a parameterized
// class, e.g. inst::b). Exercises three sub-cases:
// class, e.g. inst::b). Exercises four 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
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
@ -16,13 +19,44 @@
`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
// 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
typedef CInner#(W) CFG;
// (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 ();
endmodule
module t;
virtual class C #(
parameter int a
);
localparam int b = a;
typedef logic [a-1:0] inner_t;
// localparam derived from a typedef inside the same class —
// localparam derived from a typedef inside the same class -
// resolveDotToTypedef must constify this when reached via CFG::width.
localparam int width = $bits(inner_t);
endclass
@ -46,6 +80,9 @@ 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 ();
initial begin
`checkh(b8, 32'd8);
`checkh(from_def_b, 32'd8);
@ -54,6 +91,8 @@ 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);
$write("*-* All Finished *-*\n");
$finish;
end