Repository navigation
Enhancement-563: paramsets per LRM 6.4 — the module's names reused, chains, aliases, output variables and statements, out-of-module localparams, and instantiation inside a module
paramsets per LRM 6.4 — the module's names reused, chains, aliases, output variables and statements, out-of-module localparams, and instantiation inside a module
Scope: §3.3 and the crash of §3.1 of the
coverage audit of A Practical Guide to Verilog-A.
The grammar and parser (openvaf/syntax/veriloga.ungram, openvaf/parser/src/grammar/{items,expressions}.rs,
the regenerated AST), the item tree and body lowering (openvaf/hir_def/src/item_tree.rs,
item_tree/{lower,diagnostics}.rs, body.rs, body/lower.rs), validation
(openvaf/hir_ty/src/validation.rs, validation/body.rs), the elaboration passes
(openvaf/hir/src/{elaborate,db}.rs) and the OSDI output-variable list
(openvaf/sim_back/src/module_info.rs). Compiler only; ngspice is unchanged.
Suites: new paramsetlrm_examples (22 checks per
solver, both solvers); paramset, paramsetguard, paramsethsp, modelparamset,
lrmdata, alias, hierdev, hiername, hiernode, hierbranch, lrmhier, lrm,
langguard, defparam, mfactor, hierparam, instarray, implicitnet, portbranch
pass; full sweep 461 of 461; the compiler's front-end crate tests pass; the 92-model
corpus compiles as before (91 of 92 standalone, the EPFL-HEMT baseline). Handbook
§2.1, the
compliance matrix §3.2 and §6,
the suite README.
What was wrong
Enhancement-21 compiled a paramset into a twin module: the target module's items
under the paramset's name, the paramset's own parameters added as card parameters, each
.x = e; turning the target's x into a localparam holding e. That covered the
.model card route for a paramset that invents its own parameter names, and nothing
the chapter — or LRM 6.4's own nch example — actually writes:
parameter real L = 3u; .L = L;— the paramset'sLand the module'sLlanded in
one scope: 'L' was already declared (u07). Every paramset in the book reuses the
module's names.paramset child base; .KIND = "metal";— a paramset of a paramset shared the parent's
parameter items by id, so the twin's parameters were not in textual order and the
forward-reference check refused them: definition of 'MAT' references parameter 'KIND'
defined afterwards (u08).aliasparam LL = LEN;in a paramset was a parse error, and an alias named in an
instance override (rp #(.LL(3u)), a module's alias too) was names no parameter
(w03).(* desc="dissipated power" *) real pdis; pdis = .reff * 1e-6;— variables and
statements in a paramset (LRM 6.4.1, 6.4.3) were a parse error (u11)..RSH = fab.rsh_poly * fab.bias;— a hierarchical out-of-module reference to another
module'slocalparam, which 6.4.1 allows, resolved, typed, and then crashed the
compiler in code generation, which has no value for another module's parameter
(u10, the only crash the audit found).- And a paramset instantiated inside a module rendered the target module's text at
its defaults:rk #(.KOHM(4.0)) r1(a, b)over.R = KOHM*1000.0ran at the module's
1 kΩ, silently — the divider the book builds from paramsets never computed what it
said.
What changed
- Two namespaces, one twin. The twin now clones every target parameter, variable
and function into fresh items (in textual order, which also settles the
paramset-of-paramset ordering); a target declaration whose name the paramset reuses
is cloned asname$paramset, and every piece of target-authored text — the analog
body, parameter defaults, variable initialisers, function bodies — is read through a
rename map when it is lowered. The map composes level by level through a chain, so
each text is read in the namespace it was written in. A target parameter the paramset
reuses but does not assign becomes a localparam at its default: the paramset's own
declaration is the card parameter, as LRM 6.4 says (an instance "would generate an
error" overriding the module's). A paramset parameter named like a net, branch
or array of the module is refused, since those cannot step aside. - Chains. A paramset may target another paramset and assign the parent's own
parameters; assigning alocalparam, or a parameter an earlier level already fixed,
is paramset assigns 'R', which is not a parameter of 'base' with the rule.
.$mfactorat an inner level is replaced by an outer one's. - Aliases.
aliasparamis a paramset item; the twin exports it on the card, and
an instance override naming an alias of a paramset or of a module binds the parameter
it aliases (LRM 3.4.7). - Variables and statements.
integer/realdeclarations (with(* desc *),
output variables per 6.4.3) and the statements of an analog function are paramset
items;.namein an expression is the target's declaration of that name. The
statements join the twin's analog body after the target's;.var = expr;on a target
variable is such a statement. A paramset output variable named like the module's
replaces it (the module's is hidden from the OSDI list), one without a description
hides the module's. A contribution, an event control, a named block or an access
function in a paramset statement is refused with the clause. - Out-of-module
localparams. A new elaboration pass substitutesM.pin a
paramset's text —Ma module of the file,pitslocalparam— by the folded
default in parentheses (sibling localparams substituted in turn), before the item
tree is built; a reference to a non-localparameteris refused, as 6.4.1 requires. - Instantiation. An instance of a paramset is an instance of the module at the
end of its chain: each level's own parameters take the instance's override (alias
names resolved) or their default, its.x = e;texts become the next level's values,
.$mfactorcomposes with the instance's, an override of a parameter the paramset
fixes is refused, and the levels' variables and statements are appended to the
flattened body —.nameresolving to the module's flattened declarations and each
inner level's — so@n1[rc__pdis]reads the paramset's output variable of instance
rc.
Verification
| check | result |
|---|---|
rp: own L, W, KIND; .RSH = fab.rsh_eff (localparam of localparams); .$mfactor = 2 |
reff = 290.939 at the defaults, 289.455 with L=6u W=2u on the card; RSH=5 on the card ignored |
rmetal rp: .KIND = "metal"; .L = LEN; with aliasparam LL = LEN; |
reff = 192.98; LL=5u → 486.857 |
rpd: pdis = scratch * 2.0 from scratch = .reff * 1e-6; fig = .reff / 100.0 |
pdis = 6e-4, fig = 3 (replacing the module's −1); WID=2u → 3e-4 |
the divider: rmetal #(.LL(LA)), rp #(.W, .L, .$mfactor(3)), rpd #(.WID(2u)) inside a module |
i(vin), v(out), ra__reff, rb__reff (m = 6), rc__pdis, rc__fig exact |
the audit's u07, u08, u10, u11, w03, and t14 without its overloaded paramset |
compile; t14's divider draws 2.58442 mA as computed |
| forbidden statements; a net-named parameter; a fixed parameter assigned; a non-local hierarchical reference; an instance overriding a fixed parameter | each refused with its named diagnostic |
paramsetlrm_examples; nineteen paramset, alias and hierarchy suites; full sweep |
22 / 22 both solvers; all pass; 461 of 461 |
| front-end crate tests; model corpus | pass; 91 of 92 standalone (baseline) |
Still open, and recorded in the compliance matrix: overloaded (same-name) paramsets
with the 6.4.2 selection rules, and a random draw in an override (Enhancement-545's
documented statistics design).
Write-up: enhancements_doc/Enhancement-563.md.