Conversation
…edge branch of its own `VerilogProcToVHDL` Rule 2 only recognized an if-reset-else-clock process whose `else` was a single guard-less branch, so it could hand that branch the clock edge in place. An `else` holding nothing but a conditional is not that shape: it reads as `if (rst) ... else if (c) ...`, leaving no branch to carry the edge. The rule fell through, the process kept its edge-style sensitivity list, and the backend printed `process (rising_edge(clk), rising_edge(rst))`, which is not legal VHDL. Nothing reported it, and the Verilog backend was unaffected, so it only surfaced downstream. That shape is the ordinary way to write an FSM under an async reset, so the rule now covers the whole chain the reset heads. A guard-less `else` still takes the edge in place; otherwise a fresh `else if (clk.<edge>)` branch is chained after the reset branch and the rest of the chain is nested inside it under a header of its own. Restricted to a conditional statement, since restructuring the chain of a conditional expression would detach its branches from the header whose value they produce. Re-homing the tail needs both its ownership and its chain link redirected, which two reference patches on one member cannot express. The chain link is redirected by replacing what it points at instead, scoped with a `RefFilter` to the chain head alone, keeping it to a single patch list. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ot a second patch phase Banks what the `VerilogProcToVHDL` fix turned up, all of it general enough to catch the next stage author rather than specific to that stage. The `/new-stage` skill gains a recipe for the collision that makes a second `db.patch()` phase look inevitable: a member needing two of its references redirected at once. `Patch.ChangeRef` reaches the member-list patch table like any other patch, so two on one member throw, and `Replace + ChangeRef` does not merge either. The way out is to redirect one of them by replacing what the reference points at, keyed on the old target with `ChangeRefOnly` and a `RefFilter` narrowing it to the holder. `ChangeRefOnly` is dropped from the patch table outright, so it cannot collide even with an Add already keyed on that target. Two mistakes join the list. A `Patch.Replace` cannot carry a new `ownerRef`, since `replaceMember` keeps only `repMember.getRefs` and `getRefs` excludes ownership: the minted reference is purged and the next `getOwner` dies with an unrecognizable `key not found`, usually inside a later stage. And matching a conditional chain by arity skips the most common spelling, because an `else` holding nothing but a conditional flattens into an `else if` chain. `RefFilter.OfMembers` was documented as matching references *to* the given members, which is backwards: `originMember` is the member holding the reference, as its `Outside`/`Inside` siblings already say. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…hat branch away A `max` is at least each of its own branches and a `min` at most each of its, whatever those branches are, so a comparison between such a chain and one of its own branches is either an answer outright or a comparison with what is left of the chain. That shape is not exotic: it is what a width taken as the COMMON width of two operands meets when it comes back to one of them, which every binary operation over unrelated parametric widths does. The identity lands in two places, because it is asked at two levels. As an expression rewrite, so a design that has to hold `x(W1) + y(W2)` in `W1` bits requires `W1 >= W2` and says so, rather than restating the common width it went through. And as a fallback in the width-fit decision, so `max(W1, W2) >= W1` is proven rather than left undecided. The decision keeps its existing answers first: the max/min elimination reads a mixed chain by its constants and is deliberately lenient, so the identity only ever turns an undecided answer into a decided one. Proving it is what lets a stacked resize through a common width fold away, once the fold asks whether the inner resize loses anything rather than whether it strictly widens: a value resized to a width that is at least its own and back again recovers itself, so only the operand whose width really changes carries a resize. A condition can now fold to a constant where the width proof could not decide it, so a constraint whose condition folded to `true` requires nothing and is no longer recorded. One that folded to `false` is kept: an assumption that cannot hold is worth the noise. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ard's reach Fixes #482 `dfhdl.top` was a top-level member of the `dfhdl` package, so every `import dfhdl.*` bound the name `top`. A user's own design class named `top` (the standard Verilog top-module name) then lost name resolution to it from any OTHER compilation unit: a package member referenced across files is the lowest-precedence binding, below a wildcard import. `new top(WIDTH = 8)` was checked against the annotation's constructor and reported "dfhdl.top does not have a parameter WIDTH", naming neither the collision nor what the identifier had resolved to. #465 had fixed only the declaration-site half (#458), by qualifying the plugin's injected annotation. The annotation now lives at `dfhdl.hw.annotation.top`, beside the other user-facing hardware annotations, and reaches user code only through an explicit import or the `@hw.annotation.top` spelling. Nothing named `top` enters scope through `import dfhdl.*`. `dfhdl.hw.annotation` becomes a PACKAGE rather than an object to host it: `top` needs five option sets that live downstream in `lib` and cannot be compiled into core's object, while a package is open across subprojects. `constraints` stays in the same file, since `HWAnnotation` is sealed. The plugin's auto-injection is spelled `_root_.dfhdl.hw.annotation.top`, so an auto-topped design still needs no import; only a hand-written `@top` does. `rightmostName`-based detection keeps matching every spelling. `ElaborationChecksSpec` takes its import on line 1 rather than a new line, because 46 of its assertions pin absolute line numbers. A test-scope `type top = dfhdl.hw.annotation.top` alias would have spared the test files their import, and was rejected: it reproduces this very bug inside lib's test scope, where the regression fixture lives. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Points at the one submodule commit that adds `import dfhdl.hw.annotation.top` to the three `serv` entry points, so the benchmarks project still compiles after #482. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
What DFHDL does when a width relation is neither provably held nor provably violated, which is the normal state of affairs once widths are design parameters: it accepts the operation and states the relation it assumed as a static assertion in the generated design. Covers the three-way answer and what makes an undecidable relation load-bearing enough to state, the static assertion as a structurally derived species and what `ToED`, `OrderMembers` and the elaboration check do with it, the guard as its own record along with retraction and materialization, the minimization that reads the user's own assertions as facts, the checks that generate and the ones that deliberately do not, and the per-dialect printing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s, and never decides anything The width algebra substituted a non-top design parameter with its applied OR DEFAULT value. While a design elaborates its own body there is no instance to take an applied value from, so it took the default, and every width decision inside a parametric sub-design was made on a value the design may well not have. The elaboration root escaped only by being guarded out of the substitution entirely, which is why the same class elaborated cleanly standalone and failed the moment it was nested. It went wrong in both directions. An operation the applied value made perfectly legal was rejected, reporting the width symbolically while having decided it numerically, which is the shape of the report: nothing can prove `9 > W` for an opaque `W`. And an operation the applied value made illegal was accepted, stating no contract at all, so a module whose parameter is overridable truncated silently at every value its default did not cover. A parameter is now substituted only where an instantiation actually supplies a value. A design's own body never does, so its parameters stay the free variables they are and it states the same contract whether it is elaborated standalone or as a child. A decision made in the PARENT still resolves, and should: there the applied value is what the operation is about. Fixes #479 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`dout <> din` between mismatched widths reported ``Operation: `apply` ``, twice, once per direction. The name an error carries is the `CTName` of whichever `trydf` traps it, and `CTName` resolves to the enclosing method's name unless the site passes one. `<>` did pass one. It was never the trap that fired: `DFVal.TC.apply` wrapped `conv` in a `trydf` of its own, so the width mismatch was caught one level below the operator, stamped with that method's name, and logged as an error the `<>` trap could no longer rename. What reached the operator was a `DFError.Derived`, which the report filters out. The mirrored pair came from the same swallowing: the connect retries the flipped direction when the first throws, and both directions logged their own error. A conversion is not an operation. It is the receiving half of the `<>`, `:=`, `init` or `sel` the user wrote, and that operation's trap is the one that names it, so `TC.apply` no longer traps at all. Every path reaching it already sits under an operator's `trydf`, which the `:=` route, going through `Exact1.apply` straight to `conv`, had been demonstrating all along by reporting `:=` correctly. `Compare.apply` and its `DFXInt` override had the same shape and lose their traps for the same reason; dropping the override's also restores the flipped-direction retry a swallowed exception was suppressing. The same reasoning applies to the shared `DFDecimal` builder every integer and fixed-point constructor delegates to, which was where their width checks were being named; `DFSInt.apply` had been relying on it and now traps for itself. The rest is naming. Seven operator givens took the enclosing `apply` while holding the `ValueOf` of the very operator they implement, so `^`, `&`, `|`, `++`, `>>`, `<<` and `**` say so now. A type constructor names its type: `Bits constructor`, `UInt.to constructor`, `SFix constructor`. Domain blocks join ports and variables in naming what is being constructed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…, and the design says so Reading a parameter as a Scala value specializes the body to it: the `if` branch not taken and the `for` iteration that never ran leave nothing behind, so the design that comes out is the one for that value and no other. The generated module went on exposing the parameter as overridable all the same, and the width algebra went on treating it as a free variable, which is why a relation the branch itself guarantees could not be seen. The record is the purity marking, not anything kept where the reading happens. `PureCheckPhase` already names a read parameter on the design's own `@pure(true, ...)`, and re-attributes every application of a marked parameter at the call site, so a parameter read deep in a child marks the parameter of every design that feeds it. A parent that reads nothing itself is specialized just as surely, and states its own value. So a data-impure parameter folds to its value in `IntExprCalc` under `AppliedExpr` (the one case where a parameter with no instantiation site folds, the elaboration root included) and the design states `param == value` at materialization, where it joins the same minimization as every other constraint. Nothing is stated where an overriding instantiation cannot exist: a method design, a blackbox, or a parametrically-typed parameter, whose value has no literal of its own type to be compared against. Additive cancellation now walks both sides of the tree rather than the left spine, so `x + (y - x)` states `y`: a relative width adjustment written as the distance to another width is that width said the long way round. Fixes #480 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… declarations Advances the submodule to 7b658bf, which adds benchmarks/veer_eh1/ (provenance, upstream Apache-2.0 license, package clock/reset defaults, and the two constant headers) and gives the benchmarks repo its own .scalafmt.conf aligning `<>`, `=`, `=>` and `:=`. The constant headers take different Scala forms because the two Verilog headers they come from do not scope the same way: common_defines.vh is a global include, so its macros are top-level package definitions, while global.h is included inside 20 module bodies and is therefore an object that each design `export`s, which is what puts the names on the type as the baseline's localparams are. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
None of the three submodules recorded a branch, so a clone or `submodule update` checks each one out detached. That is normal git behaviour and harmless until you commit inside one: `git push origin main` from a detached HEAD resolves `main` to the stale local branch ref, pushes nothing, and reports success. It cost two silent no-op pushes in the benchmarks repo this week, and the parent pointer referenced commits the remote did not have. Recording `branch = main` lets `git submodule update --remote` track main and makes the intended branch explicit at the point someone reads .gitmodules. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Advances the submodule to 8efac57, porting veer_types.sv. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`&`, `|` and `^` each name two operations: the binary bitwise/logical one and the unary reduction. `MergeAssocFunc` keyed its associative merge on the `op` symbol alone, so it happily absorbed one form into the other and the reduction was simply lost: `a.^ ^ b.^` elaborated to `a ^ b.^`, and `(a ^ b).^` to `a ^ b`. Both backends then faithfully printed the corrupted IR, SystemVerilog silently (an 8-bit expression truncated into a 1-bit net) and VHDL as a type error. Only the multi-operand form of an associative op is associative at all, so the merge now requires both funcs to be in it. A chain of one form alone still merges, which is the whole of what the simplification was for. Fixes #483 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ations Banks the #483 species: an `op`-keyed predicate reasons about symbols, `&`/`|`/`^` name both a binary operation and a unary reduction, and arity is the only thing that tells them apart. Also records why the report arrives as a printer bug and why both nestings need probing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…at pins a parameter The skill covered the mechanics of translation but not the choices *between* equally-legal spellings, which is where the emitted HDL either tracks the gold or drifts from it. From the first two VeeR-EH1 helpers: A Verilog `assign` is a connection, so `<>` rather than `:=`. A named value beats a variable -- declare a VAR only where the baseline drives the bits separately, which is exactly when one named value cannot express it. Bit logic uses `&`, `|`, `~` for x-value equivalence, and the emitter picks `&` or `&&` from the operand types by itself. Concatenation is a tuple. `reduce`/`foldLeft` over DFHDL values need an explicit `[T <> VAL]`, and a seeded `foldLeft` emits the flat chain a `reduce` breaks into a paren group. Also a new subsection on parameters: what the elaboration *reads*, it pins. That is correct behaviour, but it turns one generic module into a specialised copy per instantiation, which is rarely what a port wants. `clog2` takes an `Int <> CONST` directly, slice bounds take one too, and a `generate`-style choice becomes a `.sel` on a constant condition rather than a Scala `if` -- so the design keeps both arms and stays generic. Plus the packed-struct section: field order is the baseline's, `: Int <> CONST` is what keeps a constant's name in the output, and an include's *scope* decides whether it maps to package-level definitions or an `export`ed object. Advances benchmarks to 9c52f7a. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Advances the submodule to 2e599af. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Advances the submodule to 27b42c5. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`cloneAnonValueAndDepsHere` reused the original's `DFType` instance, so the clone shared the original's `TypeRef` object. Type references are reference-counted before a patch purges them, but the count comes from the pre-patch member list and cannot see a member the same batch adds: a stage that clones a value and removes the original in one patch (`NameRegAliases` with a reg init) drops the count to zero and leaves the clone holding a dangling reference. Only parametric widths carry a type reference at all, which is why literal-width designs were unaffected. The clone now mints its own type references through the new `copyWithNewRefsHere`, so the added meta-design DB no longer depends on the original member surviving. Fixes DFHDL#485. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…al proof `.reg(n, init = ...)` says a delay chain more directly than declaring and chaining registers, but it names its flops after the source signal, so they stop matching the baseline's net names. equiv_make pairs by identical wire name, so the internal anchors are lost -- measured on rvsyncss, where the paired-cell count drops from 12 to 6. Free on two flops, since the proof closes on the outputs alone; not free on a module big enough that induction needs the anchors. Advances benchmarks to eac5952. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ecide, not an unknown The state analysis re-seeded a slice from `idxLowRef.getIntOpt`, so a bound that depends on a design parameter collapsed to `Slice.Unknown`, and the collapse was wrong in both directions at once. Reading, it was conservative to the point of being false: `Slice.Unknown` never proves containment, so `v(31, MB)` read out of a `v` assigned in full answered `Tri.Unknown` and `v` was reported as a latch under RT (issue #484). Writing, the same collapse over-claimed. The seeded slice of a parametrically-sized selection is `Slice.Full` (its `widthIntOpt` is `None`), and shifting `Full` leaves it `Full`, so `v(MB - 1, 0) := x` banked the WHOLE of `v` as written and a genuinely partial assignment passed the check. Both halves are the same missing composition. `departial`'s per-step slice calculus moves to `DFVal.Alias.Partial.composeSlice`, which maps a slice into the selected value's coordinates as a linear form, and the state analysis uses it for `ApplyRange` and `SelectField` in the read and the write direction (`ApplyIdx` keeps its whole-value approximation). `departial` itself is unchanged in behaviour, now expressed through the shared helper. `Coverage` then keeps the symbolic regions instead of degrading them to "touched", and decides a containment query over them: a slice lies within its own value's bounds, so a coverage spanning the whole value contains every slice of it whatever the endpoints are, and anything genuinely partial goes to a sweep that extends the covered prefix by a region provably starting at or before the cursor and ending after it. Two complementary parametric writes therefore cover the variable between them for every parameter assignment, while one that covers only part of it still reports the latch. The suite was green both before and after, which says the whole branch was untested rather than that the change is inert, so the tests pin the accepting shape and the rejecting one on each side: the elaboration check for RT and the `ExplicitState` output for DF. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…and a name or a function call in VHDL
The `ApplyRange` criteria of `NamedVerilogSelection` named the selected value
only when the selection was partial, but prefix legality has nothing to do
with the selection width: the printer emits `[hi:lo]` either way, so a
full-width `.bits(19, 0)` over an operation result printed
`(a + 20'd1)[19:0]`, a part-select on a parenthesized expression that no
strict frontend reads. The same guard also let a select over a select
(`a[15:0][15:0]`) and over a concatenation (`{...}[19:0]`) through. The width
guard is gone; a selected value without a Verilog name is named
unconditionally, while `hasVerilogName` keeps named prefixes exempt and
`isAllowedMultipleReferences` keeps the legal `vec[i][hi:lo]` / `s.f[hi:lo]`
chains inline.
The VHDL backend had the same genus a level wider, because
`NamedVHDLSelection` only ever ran for v93 pattern matching. A VHDL slice or
index prefix must be a name or a function call, so a selection over an
anonymous expression (`(unsigned(a) + 1)(19 downto 0)`, `(a or b)(3)`) and
over the TYPE conversions `unsigned(...)`/`signed(...)`, even of a named
value (`a.uint(15, 0)`), printed prefixes both GHDL and NVC reject. The stage
now runs for every VHDL dialect (the v93 match-selector rule is gated inside
the criteria), and a `hasVHDLName` predicate mirrors
`VHDLValPrinter.csDFValAliasAsIs`, form by form: conversions that print as
function calls are legal prefixes and stay inline (`to_slv(...)(19 downto 0)`
is untouched), the three type-conversion renderings are not, and a selection
chain is a name whenever its own root is, which the naming fixpoint repairs
independently.
Both backends also shared a consumer-side hole: the criteria scan visits
anonymous members only, so a NAMED selection (`val s = (a | b)(7, 0)`,
`val s = u.signed(20, 1)`) never surfaced its operand demand, and the very
shape the stage handles inline slipped through the moment the user bound it
to a val. The criteria entry point now derives the demand from the prefix
value's side, by re-asking the reading selection's own criteria; duplicate
demands merge in grouping, and named results fall to the existing filter.
Every previously-illegal probe output now analyzes clean under iverilog,
yosys, GHDL, and NVC, and the already-legal controls print byte-identically.
The suite was green with no reference output changed before the fix, which
says the whole branch was untested; the new `NamedSelectionSpec` tests pin
each shape and each was verified to fail with its guard reverted.
Fixes #486
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
IR: DFBits renamed to DFBitsWL with an added lowIdxRef; object DFBits remains as the zero-based apply/unapply view. Frontend: DFBits[W] = DFBitsWL[W, 0], user-facing BitsHL(hi, lo) constructor, width-only compatibility across low indices via the generalized Candidate/TC/Compare/ops machinery, and absolute index selection (with the new BitIndexLow/BitIndexHigh checks) on low-indexed values. Selection results always normalize to low 0; a nonzero low arises only from explicit BitsHL construction. Backends render [hi:lo] / (hi downto lo). Still open: DFacsimile nonzero-low data offsets, BitsHL selection tests and backend print-spec cases, testApps validation, struct/vector BitsHL cells. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DFacsimile translates absolute selection indices to relative data offsets (dynamic indexing of a low-indexed vector is refused), verified by a both-tiers simulation test. Match expressions rebase a nonzero-low selector to zero-based bits once at construction, so relative patterns and bind ranges hold across all backends; an equal-width low-drop cast prints as .bits and a cast into a nonzero-low type as .as(...). The remaining strict zero-based printer/stage arms are generalized, a DFBitsWL Singleton given supports type-only spellings (struct fields), superseding the removed DFBits given, and the natural-check message now says "natural". New pinned coverage: absolute selection and its diagnostics, backend [9:2] / (9 downto 2) rendering, a VHDL composite (record field, vector cells), and v2001 struct flattening offsets. User-guide BitsHL section (prefer Bits(width); no reversed direction) and IR reference updated. Known follow-ups (pre-existing, tracked separately): v2001 struct-field bit-select flattens to an illegal chained part-select; a literal range-select check over struct-field BitsHL values may fail to reduce at compile time. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The generalized selection givens move to an OpsLP low-priority trait (the CandidateLP/WidthLP idiom), dropping the NotGiven dispatch. Range selection now has two statically-checked forms: the W-form (HighIdx[W, L] bound) covers term-constructed receivers whose width is a reduced literal, and a new H-form (DFBitsHL[H, L] receiver) covers annotation-path receivers (e.g. a BitsHL[9, 2] <> VAL struct field) by binding H structurally from the unreduced RangeWidth application, checking bounds on H directly with nothing to collapse. Given-candidate backtracking on a failed using-clause is what lets the W-form's stuck check fall through to the H-form, so struct-field range selection compiles again (pinned in the VHDL composite test) while out-of-range literals still fail at compile time on both paths. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… into the chain DropStructsVecs stage 2 folded a partial chain into one range selection over the flattened declaration, but only for links present in the replacement map. A selection INTO a Bits-typed link (a bits field select or a vector bits-cell select, never themselves replaced) dangled and selected into the folded range, emitting an illegal chained select under v95/v2001 (`p[8:1][5]`), silently. The chain extractor now matches transitively through anonymous links (a named link still legally breaks the chain as its own net), the walk translates a Bits link's absolute indices by the link's low index (the BitsHL correction), and a single-bit result folds to a bit selection rather than a one-bit part-select, keeping runtime indices legal where part-select bounds must be constant. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… elaboration would The printer synthesizes the bound expressions `width - 1` and `width + low - 1`, which are never built as values, so elaboration's SimplifyFunc additive cancellation can never reach them: a `BitsHL(idxHigh, idxLow)` declaration (width stored as the cone `(idxHigh - idxLow) + 1`) emitted its high bound as the unreduced `((HI - LO) + 1) + LO - 1` instead of the `HI` the user wrote. The bound helpers now collect signed additive terms across anonymous DFInt32 `+`/`-` cones and cancel ident-transparent `=~`-equal opposite-sign pairs, the SimplifyFunc term calculus applied at print. Terms keep their references, so a surviving term renders with the plain spelling's relative naming; an anonymous constant folds into the offset while a named constant stays a symbolic term (IntExprCalc's linear form is unusable here: it folds named constants to their data, erasing the user's spelling). Non-cancelling cones print byte-identically to before; carry-widened widths improve from `(W + 1) - 1` to `W`. This also retires the associative-reduction TODO in `uboundCS`. Closes #489 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two lessons from the VeeR-EH1 beh_lib helpers, neither of which is a DFHDL fact. The transcription rule was too narrow. It said a conversion the compiler did not ask for is a smell, which caught half the cases; the actual cause was predicting that DFHDL would reject the obvious spelling and pre-emptively working around an error never raised. "I think it will not typecheck" is not a reason until the compiler says so, and the diagnostics name the fix when it does. Nine real corrections are tabulated, with the point that none was caught by compiling, by reading the emitted HDL, or by formal equivalence. Worst of the three smells is an operator the baseline did not use: `==` against XNOR on one bit is equivalent, so it passes every check and is visible only to a reader holding both files. And a verification-discipline note, because seven distinct green signals in one port meant nothing -- a sed that did not match, a sed -i that rewrote the gold filename, a semantically null mutation, a grep matching the failure line as well as the success line, head masking an exit code, probe classes sharing a file, and an ANSI prefix defeating a `^\[error\]` anchor. Assert the mutation applied and the control fails before believing a pass. Also records BitsHL's declaration-vs-expression rules, the carry operators, macro-modules as `@inline def`s, when a VAR is warranted, and that `.toScalaInt` neither causes nor cures parameter pinning. Advances benchmarks to f37d70e. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…, L] spelling A `BitsHL[H, L]` spelling with non-literal bounds collapses its width to `Int` at the spelling site (the guarded fold disproves const-ness of a constant singleton), erasing `H` from the type. Two consequences, each fixed at the one place the information still exists: - The type-only spelling in term position (`BitsHL[HI.type, LO.type] <> IN`) had no constructor at all and resolved to an unusable widened value. A no-arg `DFBitsHL.apply` now takes the bounds from the EXPLICIT type arguments via `ValueOf`, the apply site being the one place the high bound survives. - A constructor-form value (`BitsHL(HI, LO) <> IN`) could not convert to the collapsed parameter type `DFBitsWL[Int, LO.type]`: the candidate conversion targeted low-0 `DFBits[Int]` only, and the `fromTC` fallback needs a target type instance no given can produce once `H` is erased. The conversion target is generalized to `DFBitsWL[Int, L]`, the width staying pinned at `Int` so literal-width targets keep the width-checked TC route. The relabel is type-level only; the IR carries the true bounds and drives all checks. Fixes #490 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ummoning The collapse of non-literal width types to `Int` (987e0b0) left no type-position spelling from which a constant-bound width operation could be summoned back: a `Struct` field typed `BitsHL[H, L] <> VAL` with constant `Int <> CONST` bounds erased `H` at the spelling site, so the field's `DFType` had no given to serve it. The old Sig-based operations return, now under `IntP.Sig.Ops` (exported by the frontend), keeping TYPE-position arithmetic (`Bits[P1.type - P2.type]`) symbolic as `Sig1`/`Sig2` nodes instead of collapsing. The `Sig` given instances, until now placeholders, reconstruct the operation's `DFConstInt32` from the spelled operand types via `ValueOf`, reusing the value-level `IntParam` operators so the recovered constant matches the constructor-form width tree exactly. `DFBitsHL` is redefined over `Sig.Ops.RangeWidth`, and the `DFBitsWL` type-only given drops its `Singleton` width bound so a Sig-carried width resolves through the same path. Value-level operations stay on the collapsing operators (issue #431 remains fixed); only the type-position spellings are precise. Fixes #491 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… retires #491 is fixed, so a constant-bound BitsHL is usable as a Struct field. The note warning it was not now says what it does instead, with the emitted form. Advances benchmarks to b66331f, which converts the last three non-zero-base fields in the VeeR-EH1 type package. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A related domain may now declare its own input clock port: a clock fully synchronous with the clock of its related target (typically a gated version of it), while the reset is still shared through the relation. Identity is the dcl's design-relative name (domain `active` with dcl `clk` identifies as `active_clk`, independent of `@flatten`), so same-named derived clocks of the same origin refer to one clock across the hierarchy. - core: the related-domain check now permits `Clk <> IN` only, with dedicated errors for output/var clocks and any reset dcl. - AddClkRst: resolves derived-clock identities globally, mint-when-driven: if any same-identity dcl is explicitly connected (directly or via a parent's `child.active.clk` by-name selection), a distinct `Clk_<relName>` opaque is minted and the magnet flow threads the gated clock by type; otherwise the dcls retype to their origin's opaque and collapse onto the origin clock net (the ungated form). Nested gating chains through derived origins. - ToED: clock resolves to the nearest related-chain member with a clk dcl, reset still resolves through the full chain (honoring includeReset); no `@timing.clock` is moved onto derived clock ports (no create_clock). - DropDomains: by-name selection paths of domain-nested ports follow the port's flattened name. - SanityCheck/MagnetMap: ports nested in domain blocks are legal by-name selection targets and magnet points (Flattened collection), and domain-nested magnet points propagate under their design-relative name. - DB: device-top clock-location check exempts internally-driven clk dcls (generalizes the clk-VAR escape); pbnsToPort opened to the compiler. Tests: driven/collapse/nested/cross-design-unification and applied-twice in AddClkRstSpec; async shared reset, related-of-related, collapse, and includeReset=false in ToEDSpec; a four-level gated-clock threading test in ConnectMagnetsSpec; pass-through naming in AddMagnetsSpec; by-name flattened-path rewrite in DropDomainsSpec; error checks in ElaborationChecksSpec. Docs: "Derived Clocks (Gated Clocks)" in the design-domains guide; skill notes in verilog-to-dfhdl and new-stage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…thands Every RT container now provides three shorthand domain classes, each exactly equivalent to (and manifesting as) a plain RTDomain with the corresponding annotations: - RTRelatedDomain: `@timing.related(this)` of the enclosing container, injected at construction so it precedes any subclass body member. - RTDerivedClkDomain: RTRelatedDomain plus a `val clk = Clk <> IN` derived clock port (built via direct DFVal.Dcl, since core compiles pluginless). - RTTransparentDomain: RTRelatedDomain plus `@flattenMode.transparent`, for regrouping internal logic into related domains with zero naming impact. Being container members, the related target is selected by the instantiation path: `new gated.RTTransparentDomain` relates to `gated` rather than to the enclosing design. PrintCodeStringSpec pins the manifestation of all three and the path-prefixed form; the design-domains guide documents the shorthands and when to use each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ference `@timing.related(Foo)` did not re-elaborate: inside the design's own body the bare class name resolves to the companion, and a bare `this` would resolve to the annotated domain when the annotation sits inside a nested domain body. A design target now prints as `Foo.this` (from the design's `dclName`, since the annotation is printed within that class's body); domain targets keep their val-name reference. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A region is a scoping construct rather than a domain in its own right: it
places logic under a timing context with no observable footprint, neither a
clock identity nor a naming one. The name follows the construct's use site,
where its value lives: `new active.RTRegion: <logic>` opens a region of the
`active` domain. Dropping the `Domain` suffix is deliberate; the two
shorthands that create a grouping with a footprint keep it.
The docs section is reframed accordingly (two domain shorthands plus one
scoping construct) and gains "The Domain-and-Regions Pattern": declare a
timing context once (e.g. `new RTDerivedClkDomain {}`) and open sparse
regions of it wherever pieces of logic naturally live, none of them paying
a naming cost, so code order follows the dataflow rather than the clock
grouping.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… yosys equiv blind spot
Lessons from porting `dec_gpr_ctl` (bumped in the benchmarks submodule):
- The domain-and-regions composition for a derived clock. Registers placed
directly in an `RTDerivedClkDomain` take the domain-name prefix, and
marking that same domain transparent is worse: it strips the prefix from
the `clk` dcl too, which then collides with the design's own `clk` and the
pair emits as `clk_0`/`clk_1`, renaming the design's clock port. The clock
goes in a named domain and the logic in `RTRegion`s of it, placed where the
baseline declares those flops. Reach the members with `import`; `export` is
rejected outright because the region's type is anonymous.
- `BitsHL` covers a non-zero-base bit *range*, not an array: a `Vec` is
0-based, so a baseline `[31:1]` array is indexed shifted by one, and the
shift belongs at the `Vec` subscript alone.
- A `Bits` never compares against a Scala `Int`; the compare needs `.uint`.
- A parameter that is a pure function of another belongs in the body, where
it emits as a `localparam` in the parameter port list. Its derivation must
be transcribed rather than simplified, since guards like `(N == 1) ? 1 :
$clog2(N)` exist to avoid a zero width.
Plus a new section on proving a port against its baseline with yosys:
- `read_slang` instead of `read_verilog`, because yosys cannot parse the
`'{default: ...}` assignment pattern the emitter uses for a vector reset
(yosys#6120, filed upstream).
- A `Vec` and a Verilog packed array flatten in opposite order, so
`equiv_make` pairs those aggregates by name and mispairs them bit for bit;
rename them out of the way and pair the state through canonical-order taps
added to both wrappers.
- `equiv_simple`/`equiv_induct` ignore the CLK net. Verified rather than
assumed: tying a derived clock to `1'b0` in both wrappers still reports
"Equivalence successfully proven" against a gate whose flop was moved to
the root clock. Moving a flop between clocks is therefore not a valid
negative control.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The pre-SystemVerilog Verilog dialects have no unpacked-array assignment, so
a `Vec` reset emitted a PACKED replication into an UNPACKED array
(`mem <= {4{8'h00}}`). iverilog and yosys reject it outright; verilator reads
it as an assignment pattern initializing element 0 only, which would be
silent wrong hardware rather than a syntax error.
The new stage lowers a whole-vector drive into an element-wise one, in three
rules: a declaration's `init` becomes an `initial` block, a connection becomes
per-cell connections, and an assignment is unrolled where it stands. It runs
for verilog.v95/v2001, and for any backend under the new `dropWholeVecAssign`
option (`--drop-whole-vec-assign`), and sits before `DropStructsVecs` in
`BackendPrepStage` so the vector type is still there to unroll. That ordering
is deliberately NOT a dependency: it would drag the whole pre-backend pipeline
into every direct `.dropStructsVecs` invocation.
Only a declaration whose vector type actually reaches the backend is lowered,
which under the old dialects means a block-ram variable; anything else is
flattened to `Bits` and its whole-vector drive is already legal. An anonymous
composition (`all(x)`, a `Vector(a, b, c)` concatenation) is taken apart into
its own operands and needs no constant; any other source is taken apart by
selecting cell by cell, which does. A plain vector-to-vector drive is left
alone. Only the outermost dimension unrolls: below it the cell type is either
flattened to `Bits` or, in SystemVerilog, legal as an array literal.
A uniform source loops over the declaration's OWN element-count parameter, so
a parametric length stays parametric. Rule 2 emits connections rather than the
`process(all)` the issue's shape suggests: the only sources it lowers read
nothing, so that process would carry an empty sensitivity list and never
trigger ("@* found no sensitivities" under iverilog). Rule 1 is Verilog-only,
VHDL having no `initial` construct and no need of the lowering.
The Verilog printer's own vector-init workaround is removed rather than kept
as a fallback: a whole-vector init that reaches a dialect which cannot inline
it is now `unsupported`, since the stage owns the lowering.
Three latent backend bugs the stage exposed, each fixed and pinned:
- A `for` loop iterator was declared inside the unnamed procedural block
("Variable declaration in unnamed block requires SystemVerilog"). Iterators
now join the module's declaration region, like the process-local
declarations already there. This broke any user loop in a v95/v2001 process.
- A named process printed as `myblock : always_comb`. Verilog names a BLOCK,
so the label belongs on the `begin`: `initial begin : mem_init`.
- `DropProcessAll` derived its explicit sensitivity list by walking block
kinds it enumerates, and loop blocks were missing, so a signal read only
inside a `for` body never reached `always @(...)`. Loop bodies, `while`
guards and `for` ranges are now walked.
And one that the loop fix in turn exposed: an ARRAY cannot be named in a
Verilog event control (it takes expressions, and `@*` is undefined over arrays
besides being absent from v95), so `always @(mem)` was rejected by iverilog
and yosys alike. A constant-index read now contributes just that cell, which
is both precise and nameable; only a dynamic-index or whole read falls back to
listing every cell, `@(mem[0] or mem[1] or ...)`, the form synthesis has
always required here. VHDL names the array signal itself and is left alone.
Verified against iverilog, verilator and yosys on v95 and v2001 (and
verilator on sv2009 under the option), with the AES cipher simulation re-run
across every tool/dialect combination. No reference output changed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The manual-proving section already prescribes read_slang for DFHDL output; the training repo's cav now does this automatically for both sides when the plugin is present (CAV_FRONTEND=auto), so say so where the prescription lives. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Sr34sJ2wXWXZ9ENf817aT
Revised connection semantics for related-domain derived clocks, so that clocks connect automatically only in a predictable way: - A related domain may declare `Clk <> OUT`: the internal gating site, driven from its own design scope (`active.clk <> icgOut.as(active.Clk)`) and sourcing every same-named derived clock in scope through the magnet flow (the veer.sv structure, where the core creates its gated clocks). - Same-named derived clocks within a clock group always form one clock: AddClkRst mints the distinct `Clk_<relName>` type unconditionally, and the mint-when-driven detection and collapse-to-origin fallback are gone. A derived clock is never implicitly merged onto its origin clock; the ungated form is an explicit connection at a wrapper. - A derived clock group with no source anywhere surfaces as a top-level input port: sourceless Clk-kind magnet targets (magnetUnmatchedTargets, now exposed by MagnetMap in deterministic groupByOrdered order) climb the whole hierarchy in AddMagnets, so a forgotten gated-clock connection becomes a visible port instead of a silently dead or wrongly merged clock. ConnectMagnetsSpec pins the new behaviors (an output derived clock sourcing a sibling consumer; the sourceless bubble-to-top chain), AddClkRstSpec covers OUT minting, and the former collapse tests are re-harvested under always-mint semantics. Docs updated accordingly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Clk <> OUT counterpart of RTDerivedClkDomain: the internal gating site, whose design scope drives the derived clock (e.g. from an ICG output) and exports it to every same-named derived clock in scope. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nEqual Cache entries stay valid across formatting/doc edits (the code digest hashes typed trees), so stored members carry positions the live run no longer has, and value unification at adoption (globals, refTable re-uniting) broke on exactly the edits the digest is designed to survive. Meta now defines two named comparisons and derives no CanEqual, so a direct `meta == meta` does not compile and every site names which notion it means: - `sameIdentityAs` (name + annotations, precisely the digest-visible fields): implemented by equals/hashCode, so member case-class equality composes it implicitly and cached members unify with their live counterparts. - `sameDclAs` (all fields): "same declaration", anchored on position. Used by DesignLoadKey's intra-run gate equality (without it, same-named designs from different declarations unify into one) and UniqueDesigns' grouping (reachable via adopted cache children, which skip elaboration's dclName enumeration). The hashCode change flushed out a latent nondeterminism: magnetConnectionMap was a hash Map iterated by ConnectMagnets to emit connections, with only a by-name sort on top (which ties for same-named points). It is now an insertion-ordered ListMap, so connection order follows instantiation order; one ConnectMagnetsSpec expectation updates from the old hash-derived order. Tests: MetaSpec pins both notions; SubDesignCacheSpec adds the position-drift adoption regression (fails with "Failed reference check!" without the fix); UniqueDesignsSpec pins that same-named distinct declarations stay separate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Meta gains `namespace: String` (default ""), the Scala package path of the declaration it describes. It is digest-visible (a package clause is in the typed tree), so it joins `sameIdentityAs`/`hashCode`/`prot_=~`; regular values keep "" since their namespace is their design scope. The eventual packages feature places a named type by relating its namespace to the top design's. Capture, three paths: - plugin: `metaGen` takes a namespace argument; design classes and method declarations (`genDclMeta`) record their enclosing package via `mkNamespace`. Namespaces stop at the package level deliberately: enclosing objects are scoping, not namespacing. - macros: `TypeMetaGen` builds a full declaration Meta (name, namespace, position, doc) from the class symbol inside the struct and enum derivation macros, mirroring the plugin's `Position.fromAbsPath` convention. - runtime fallbacks: the product/reflection struct path, the enum-companion path, and the opaque `ClassEv` path record name + `getPackageName`, which agrees with the macro's package-level namespace (`SameFields.check` compares the two constructions by type equality). `NamedDFType` (DFStruct, DFEnum, DFOpaque, DFView) carries `meta: Meta` instead of a bare `name: String`: `name` reads `meta.name`, `updateName` goes through `meta.setName` (preserving namespace/position/doc under UniqueNames renames), and type identity becomes name + namespace + annotations + structure, composed through Meta's equality. Tuple structs are structural and get `Meta.named` (root namespace); DropRTProcess's synthesized state enum likewise. Five positional extractors that would have silently bound a Meta where a String stood (four `DFStruct(name, _)`, one `DFEnum(name, _, _)`) had dead binders, renamed to `_`. Zero output diff: the full suite passes unchanged from a cleared-cache state. MetaSpec extends to namespace identity and pins the captured type meta (struct/enum position + doc, opaque name + namespace, tuple root namespace). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…amed types
Closes the phase-2 loose ends ahead of the packages-feature emission phase:
- Global values: the plugin now passes the declaring package on every named
value's `setMeta` (MetaContext/DFC carry it), and `DFC.getMeta` keeps it only
at GLOBAL scope (no owner in context): a global constant records its package
(gold veer packages hold parameters too), while a design-scoped value's
namespace stays its design ("").
- Opaques: `ClassEv` captures the declaration's position, doc, and package at
materialization, so the `DFOpaque` given and every `ce`-holding `as`-op build
full meta; direct-instance callers (Clk()/Rst(), stage-minted magnets) keep
the runtime name+package fallback. The general-opaque FQN-hash id retires to
0 (identity is meta: name + namespace, like structs and enums); magnets keep
their per-instance id, and `prot_=~`/`isSimilarTo` compare meta identity.
The dead `opaqueType` extension (cast the Int id to TFE, zero callers) is
removed.
- Doc comments on named types now emit at the type declaration through the
shared `csNamedDFTypeDcl` choke point: `/** */` in DFHDL code, `/* */` above
SystemVerilog typedefs, `--` above VHDL type declarations. Macro-captured
docstrings include the raw comment markers, unlike the plugin's cooked form,
so `sanitizedDocstring` (internals) normalizes them; without it the printers
double-frame (`/**/** doc */*/`).
Tests: the SAME "Docstrings on named types" design (documented struct, enum,
and opaque) is pinned with exact expected output in PrintCodeStringSpec,
PrintVerilogCodeSpec, and PrintVHDLCodeSpec (appended at file end; earlier
tests there embed their own source positions). MetaSpec pins global-vs-local
value namespace, opaque position, and the inert general-opaque id. The backend
doc tests fail with the emission line reverted.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…printer Named types, global constants, and global static functions are placed by the namespace rules (printing.Namespacing): a namespace equal to or an ancestor of the TOP design's stays in the general global defs file; anything else lands in a dedicated package named by the namespace RELATIVE to the top's (dots to underscores). Two namespaces mapping to one package name, or a package name colliding with a design name, are hard errors. Clk/Rst/Magnet opaques are language-level and never packaged. Emission: shared partition in DFTypePrinter/Printer (packagedTypeDcls + packagedGlobalDecls + packagedContents) with cross-package topological ordering, sub-DB dedup, placement overriding design-locality, and hoisting of global-placed types referenced by packaged content into the global file (a package file cannot reference a type declared inside a design). Package files join printedDB ahead of designs and csDB after the globals section. References qualify instead of importing, so same-named declarations from different packages can never collide: SystemVerilog prints `pkg::name` (type names, enum entries, global constants, static-function calls), the DFHDL printer prints fully qualified `<namespace>.name` and renders each package as a real Scala `package <namespace>:` section. v95/v2001 keep merging everything into the single global header. Fixed along the way: DFC gains `getDclMeta` (design-block dclMeta keeps its namespace; the value-oriented `getMeta` owner gate was nulling def and child class design namespaces); global-scope method copies (the pre-existing non-unification mints one block per global call nest) dedup by `sameDclAs` at printing; DFSpec's mock top now carries the concrete spec's package namespace via `@metaContextIgnore` (the plugin's static injection would stamp `dfhdl`). The object-hierarchy question was settled back to packages-only: a Scala object is a value (aliasable, importable), so objects remain scoping. Pinned end-to-end in PrintCodeStringSpec and PrintVerilogCodeSpec over PkgFixtures.scala: sibling packages typespkg1/typespkg2 in one file, with typespkg2 referencing typespkg1 (struct field + qualified static call) and typespkg1 referencing uniquely-named general globals. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…SIWYG) A named type now emits under its own name in both backends, which is what the translation flows compare against (cav's interface_precheck string-compares port types: gold `veer_types::lsu_pkt_t` vs the prefixed form failed every struct-port module). The prefixes were quietly separating types from values, so UniqueNames now does it explicitly: type and value identifiers share one HDL namespace (SV in scope, VHDL case-insensitively), so the FINAL (post-rename) global type names are reserved against every value renamer, and each design's local type names against that design's values. Keyword avoidance for type names already came from the type renamers' reservedNames. Two collision renames this correctly produced are pinned in PrintVHDLCodeSpec (signal `state_0` vs enum `State_0` -> `state_0_0`; port `p` vs record `P` -> `p_0`). All 111 HDL reference files regenerate mechanically: prefix removal plus the column realignment that follows from shorter type names. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each namespace-derived package emits as a full VHDL package unit: the spec holds the type declarations with their conversion-function prototypes, then the constants and method prototypes in dependency order; the body holds the conversion-function and method bodies. Visibility is by `use` clauses (no qualification in VHDL): a package uses the general package plus every package preceding it in the cross-package topological order (dependencies are guaranteed to precede), and design files use all emitted packages. The general `<top>_pkg` now carries only global-placed content: packaged named types are excluded from its type collection, conversion functions, spec decls, and method bodies (which also pick up the same-declaration dedup), while design-local types hoisted by packaged references are included. Architecture declarative regions no longer re-declare packaged types (they arrive through the `use` clause; a local re-declaration would shadow and break type identity). The shared printing layer exposes the typed per-package entries (packagedGlobalDeclEntries + csPackagedGlobalDecl) so VHDL can assemble its spec/body split; Verilog keeps consuming the flat rendered form. Pinned end-to-end in PrintVHDLCodeSpec "Namespace-derived type packages" over the same PkgFixtures design as the Verilog and DFHDL pins: typespkg1/typespkg2 package units with records, enums, subtypes, conversion functions, constants, and the static function, cross-package use-visibility, and the entity ports typed by packaged records. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two backend-facing completions of the namespace-derived packages feature. `DropPackages` (new stage, last in BackendPrepStage) serves verilog.v95/v2001, which have no packages: it folds each packaged declaration's package name into its own name (`typespkg1_PkgEnum`, `typespkg1_pkgCalc`) and clears its namespace, so everything lands in the single global defs header under names that still say where they came from and cannot collide across packages. It covers exactly what the packaged emission places: named types, global constants, and the global HDL methods — whose placement analysis moves out of `Printer` into `analysis.HDLMethodAnalysis` so the stage and the printers cannot drift apart. `UniqueNames` now scopes the uniqueness of a global declaration by the package it is emitted into, rather than across all of them: every printer references a packaged declaration through its package, so two packages may hold the same simple name. The general defs group is uniquified first and reserved for every package (a package's content sits alongside the general globals), while two packages never see each other unqualified. It needs no backend flag — a backend without packages reaches this stage with the namespaces already flattened away by `DropPackages`, leaving the single general scope it wants. VHDL switches from `use work.<pkg>.all` to selected names (`work.<pkg>.<name>`) for packaged types, enum literals, conversion functions, constants and method calls — the same collision-proofing SystemVerilog gets from `pkg::`, and what makes package-scoped uniqueness safe there. Verified to analyze under ghdl (--std=93 and --std=08) and nvc, case choices included. Also fixes `ComposedDFTypeReplacement` dropping a struct's non-matching fields when rewriting the matching ones. Known residual: a global HDL method's name is still globally unique, since same-named design blocks are enumerated by elaboration, which is not package-aware.
…s wrong
The devdoc covers the Verilog-backend representation choice for a DFVector:
why packed is the default and unpacked the exception, the two halves of the
decision (supportsPackedVector is a TYPE property, hasMemAccessPattern a usage
one) and why that split is what makes it sound, the rendering consequences
(dimension placement, aggregate order, the streaming-reversal cast,
part-selects), and a standalone argument for why the two forms can never meet
in one operation. The user-facing rules stay in the type-system guide; this is
the "why it is shaped this way" companion.
Two things measured while writing it rather than taken from the source:
- An index-labeled aggregate (`'{0: e0, 1: e1, ...}`) IS legal on a packed
array and would have been representation-independent, needing no reversal
and no `unpackedOrder` flag. slang and verilator accept it; iverilog -g2012
and yosys's read_verilog reject it. So the positional-reversed form buys
portability across the partial frontends, which is the same trade as
avoiding `'{default:}` on unpacked arrays (yosys#6120). Recorded as the
reason, since the source does not say why.
- The namespace-derived package file is emitted with no `default_nettype /
`timescale header while every other file has both, and slang refuses the
mix, so any design using a type package fails to elaborate as a file set.
Listed under open issues; it is not this feature's, but it lands on the same
output and blocks the flow that consumes it.
Also corrects the verilog-to-dfhdl skill: it claimed a port literally named
`clk` collides with the magnet. It does not. `val clk = Clk <> IN` in a design
that also has registers emits one `clk` port, the registers still clock on it,
and `clk.actual` reads it as a Bit -- which is how lsu_clkdomain drives a
derived clock from the root clock the way an ungated ICG does. What collides is
a non-magnet port whose name shadows the magnet's.
The benchmarks bump carries lsu_clkdomain, lsu_trigger, the veer_types package
and the clock-domain traits.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ation
A DFHDL vector now prints as a packed descending array (element 0 at the
LSB end, DFHDL index i = SV index i) under the SystemVerilog dialects,
matching what hand-written baselines declare for bus-like array ports
(SystemVerilog cannot connect a packed port to an unpacked one, so the
old unpacked-only form was an incompatible interface).
Unpacked survives only where required: the pre-SV dialects, cell types
that cannot form a packed array (integer atoms, real, string, time, and
signed cells, whose element part-select would silently lose signedness),
and declarations whose usage follows the memory access pattern, so
block-RAM/ROM inference is preserved. The usage classification is
DFVal.hasMemAccessPattern in DFValAnalysis; the printer combines it with
the type-level packability into unpackedVectorDcls (init references are
representation-neutral on both sides).
Bits<->vector casts preserve the DFHDL element-0-at-MSB bit order via a
scalar-cell-granular streaming reversal ({<<W{...}}) or the element
concatenation; aggregates keep the order-free idx:value keys, listed
descending for packed targets. Verified against verilator and slang
(vanilla yosys's own parser accepts no assignment pattern on packed
targets; its slang frontend does).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`default_nettype` and `timescale` are compilation-unit state, not file state, so a file that omits them inherits whatever the previously compiled file left behind. The package files are compiled ahead of the designs and carried no header of their own, which left their compilation dependent on ordering. They now emit the same `csLibrary` header a design file does, which also subsumes the global defs include they were emitting by hand.
…ces it A global value's refTable bindings live in its own DesignContext, injected into the run's DB only at first reference (refTW -> injectGlobalCtx). The SimplifyFunc extractors run on the raw IR args before any refTW, so a never-yet-referenced global alias operand (an object-scoped Int <> CONST whose value is another const) crashed with `Missing ref` the moment an extractor stripped it, e.g. as the left operand of `-` (SelfCancelling) or as a max operand against a literal (MaxMinChainAbsorb). Injecting each operand's global context up front is exactly what refTW does moments later, idempotent, and covers every extractor including the global-scope ones. Fixes #494 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Aug 17, 2026
verilog.v2001: a Vec reset emits a whole-array assignment, which Verilog-2001 has no syntax for
#492
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.