Skip to content

v0.20.0#406

Merged
soronpo merged 73 commits into
mainfrom
interfaces
Jun 18, 2026
Merged

v0.20.0#406
soronpo merged 73 commits into
mainfrom
interfaces

Conversation

@soronpo

@soronpo soronpo commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

Oron Port and others added 30 commits June 7, 2026 13:58
…` and `DropLocalBlocks` stages for backend dialects that do not support these constructs
…k-join-any, since dynamic span logic is currently not supported
Add hierarchical (root-DB) clones of the cross-design RT clk/rst analyses
that previously required the flat DB, computed via design-tree navigation
(subDBs.get(ownerRef) down, parentSubDBOpt up) with no global member index:
new_relatedAnnotMap, new_dependentRTDomainOwners, new_resolvedClkRstMap,
new_isDependentOn. Gated against the flat versions by new_clkRstEquivalenceCheck
(run from SanityCheck): flat XYZ == oldToNew.new_XYZ across the test corpus.

Migrate three stages off the flat DB using these clones:
- ExplicitClkRstCfg: drop the rebindGetSet=false hack
- AddClkRst: flat Stage -> GlobalStage (per-sub-DB patching; pre-pass for
  cross-design clk/rst OUT tracking)
- ToED: flat Stage -> HierarchyStage

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
MagnetMap.getHierarchical resolves each magnet ConnectPoint's design context
(owner + container design) once under its owning sub-DB getSet, then runs the
distance / inside-owner matching purely on the root-aware design tree
(designBlockOwnershipMap) — no flattening, no ref-resolving global getSet.

Rename the temporary equivalence gate new_clkRstEquivalenceCheck ->
new_hierEquivalenceCheck and extend it to also assert
magnetConnectionMap == oldToNew.new_magnetConnectionMap. Validated across the
full unit suite (incl. AES's cross-design magnet hierarchies).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Expose new_magnetPointInfo (each magnet ConnectPoint -> its owner design + name),
precomputed cross-design by MagnetMap.getHierarchical, so migrating consumers
never re-resolve a ConnectPoint that lives in another sub-DB.

- ConnectMagnets: drop the rebindGetSet=false hack; classify connections via
  new_magnetConnectionMap + new_magnetPointInfo and the root-aware design tree
  (parentOf via designBlockOwnershipMap), create connections per sub-DB.
- AddMagnets: flat Stage -> GlobalStage; accumulate missing magnets once over
  new_magnetConnectionMap (climbing designBlockOwnershipMap), then add each
  design's ports under its own sub-DB getSet.

No stage sets rebindGetSet=false anymore. Full unit suite green (incl. AES).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
No stage overrides rebindGetSet to false anymore (ExplicitClkRstCfg and
ConnectMagnets were migrated to read the new_* root-DB analyses), so drop the
knob and the outer-flat-getSet escape hatch: HierarchyStage.transformSubDB now
always sees the root DB and the current sub-DB's getSet. Full unit suite green.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…ndentOn

DropTimedRTWaits now reads the clk rate from the hierarchical
rootDB.new_resolvedClkRstMap (lookup hoisted out of the MetaDesign into the
sub-DB getSet context) instead of the flat resolvedClkRstMap. With that, the
flat isDependentOn extension in DFOwnerAnalysis has no remaining caller
(ExplicitClkRstCfg moved to new_isDependentOn earlier), so it is removed along
with its now-unused tailrec import.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
check is now a lazy val that dispatches on the DB representation:
  - hierarchical root: run each sub-DB's subDBCheck, then rootDBCheck once;
  - in-hierarchy sub-DB: subDBCheck only;
  - old-style flat DB: both groups on itself (preserves today's behavior).

subDBCheck holds the per-design structural checks (name, connectivity,
direct-ref); rootDBCheck holds the whole-tree checks (magnet connectivity,
dangling ports, derived-domain cycles, clk/rst rate, wait) plus the device-top
port location/direction checks. rootDBCheck still uses the flat implementations
and is dormant on the root for now — it will be rewired to the hierarchical
new_* analyses and the call sites switched to oldToNew.check in a follow-up.
The two callers (SanityCheck, Design) now read the lazy val.

directRefCheck runs slightly earlier (before the magnet/dangling checks); the
full suite confirms no check-ordering test depends on that. The device-top
location/direction checks stay after domainClkRateCheck so the reported error
is unchanged when a design lacks both a clock rate and a clock location.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
First rootDBCheck member rewired to run on the hierarchical root: same DFS as
the flat check but over new_dependentRTDomainOwners, naming cycle members via
new_fullNameViaInst routed through each owner's sub-DB (domainOwnerToSubDB).

Wired into new_hierEquivalenceCheck so it runs on every design that passes the
flat check, asserting it does not falsely flag a valid design. Error-message
exactness for the cycle case is validated by ElaborationChecksSpec once callers
switch to oldToNew.check (Step 2c). rootDBCheck itself is unchanged (still flat,
still the production path) until all members are rewired.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Second rootDBCheck member rewired for the root. The flat usesClk filter is
cross-design and flat-only, but is equivalent to "the resolver produced a
clock" — new_resolvedClkRstMap(owner)._1.isDefined — so the resolved-clock map
drives both the device-top filter and the explicit rate. Per-owner local reads
(getThisOrOwnerDesign.isDeviceTop, isTop position, getFullName, the clk timing
constraint) are routed through the owner's sub-DB getSet via atOwner, mirroring
new_resolvedClkRstMap. Device-top domains live in the toptop's sub-DB, so their
getFullName is the correct full path.

Wired into new_hierEquivalenceCheck so every passing design confirms it does not
falsely flag a valid design; the error message is validated by
ElaborationChecksSpec's "clock missing timing constraint check" at Step 2c.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Third rootDBCheck member rewired. Iterates each sub-DB's RT waits under that
sub-DB's getSet (the root has no members of its own) and reads the clock rate
from new_resolvedClkRstMap. The error hierarchy uses new_fullNameViaInst so a
wait in a nested design reports its full instance path (plain getFullName on a
sub-DB would be relative). Wired into new_hierEquivalenceCheck for passing-design
validation; the wait error messages are validated by ElaborationChecksSpec at
Step 2c.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…ep 2b)

Two device-top-only checks rewired for the root. Each iterates designMemberList
(root-aware), and for the device-top design resolves its members under that
design's sub-DB getSet via domainOwnerToSubDB(design).atGetSet. Device top ==
toptop, so getFullName yields the correct full path. Bodies are otherwise
identical to the flat checks. Wired into new_hierEquivalenceCheck (the Platform
device-top design exercises them); error messages validated at Step 2c.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Last rootDBCheck member rewired. Aggregates assignment coverage and the
connected-point set across all sub-DBs (each design's assignments/connections
live in its own sub-DB) plus the cross-design magnet connections. Input ports
are checked from each parent sub-DB (which owns the instance + its connections),
reading the child design's port list via the root-aware designMemberTable;
output ports per instantiated design (designBlockInstMap keys, matching the flat
version — the toptop's own ports are device IO, not dangling) under its own
sub-DB getSet. The dcl width is resolved inside the sub-DB getSet (a DFBits
width-param ref can't resolve against the root getSet).

Wired into new_hierEquivalenceCheck; no false positives across the suite. All
seven rootDBCheck members now have hierarchical clones (magnet is covered by the
existing map comparison). Error messages validated at Step 2c.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
check's isRoot branch now runs new_rootDBCheck (the new_* clones) instead of the
flat rootDBCheck, and both callers (SanityCheck.transform, Design post-elaboration)
call designDB.oldToNew.check, so the production connectivity/RT/device-top checks
run on the hierarchical root. The flat rootDBCheck remains only on the (now
unreached) isOldStyleFlatDB branch — dead, to be removed in Step 3.

Two fixes surfaced by the error tests once the hierarchical path went live:
- new_fullNameViaInst now mirrors the flat naming (design CLASS name, e.g.
  Internal2.dmn) by resolving the enclosing design in its own sub-DB rather than
  via the parent instance path.
- new_portLocationCheck iterates `members` directly: the root-aware
  designMemberList already includes the design block as the head, so the flat
  code's `design :: members` would double-count it (spurious Top-vs-Top
  collisions / doubled "missing" errors).

Removed the now-redundant new_* check calls from new_hierEquivalenceCheck (the
production root path exercises them); the gate keeps the analysis comparisons.
Full suite green (StagesSpec 434, CoreSpec 74, lib 152, platforms 1),
ElaborationChecksSpec error messages match.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Production runs the hierarchical new_rootDBCheck on the root via
oldToNew.check; the flat rootDBCheck and the six flat check methods
were only reachable through the now-unreached isOldStyleFlatDB branch
of `check`. Remove that branch and the dead flat checks
(checkDanglingPorts, circularDerivedDomainsCheck, domainClkRateCheck,
waitCheck, portLocationCheck, portResourceDirCheck). The flat clk/rst
analyses they consumed stay alive via the equivalence gate and the
DFMember CLK_FREQ folder until Steps 3b/3c.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
new_hierEquivalenceCheck was temporary scaffolding that asserted the
flat RT clk/rst + magnet analyses matched their hierarchical clones on
every design passing SanityCheck. The hierarchical path is now the sole
production path (Steps 2c/3a) and the equivalence is locked in by the
test suite, so drop the gate and its SanityCheck call site. The flat
magnetConnectionMap was only consumed by the gate and the (deleted)
flat checks, so remove it too. MagnetMap.get (flat) is now dead and
will be removed with the getHierarchical->get rename in Step 3d.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
… 3c)

resolvedClkRstMap is now a single representation-dispatched lazy val:
the hierarchical body runs on the root, a sub-DB delegates to its root,
and an old-style flat DB is converted via oldToNew first. The only flat
entry point is the CLK_FREQ const-folder (DFMember.Special), which only
runs on a complete immutable DB (disabled during elaboration), so the
oldToNew conversion is always valid there.

This lets the entire flat clk/rst chain go: relatedAnnotMap, pbnsToPort,
fullNameViaInst, dependentRTDomainOwners, reversedDependents, the
getResolvedClkRst/usesClkRst/usesClk/usesRst/relaxed extensions, and
fillDomainMap. hasClkAnnot/hasRstAnnot (representation-agnostic) move up
to the surviving constraints extension. new_resolvedClkRstMap loses its
prefix here (callers in ExplicitClkRstCfg/DropTimedRTWaits updated); the
remaining new_* analyses are renamed in Step 3d.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
With every flat counterpart deleted (Steps 3a-3c), the hierarchical
analyses are the sole implementations, so rename them to canonical
names: magnetConnectionMap, magnetPointInfo, magnetData, relatedAnnotMap,
pbnsToPort, designEnclosingDomain, getRTOwnerWithSub, fullNameViaInst,
dependentRTDomainOwners, isDependentOn, checkDanglingPorts,
circularDerivedDomainsCheck, domainClkRateCheck, waitCheck,
portLocationCheck, portResourceDirCheck, rootDBCheck. Callers in
ExplicitClkRstCfg/ConnectMagnets/AddMagnets updated; the stale
new-style/equivalence-gate comment block is rewritten.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
The flat MagnetMap.get (full-member-index magnet matching) lost its
only caller when the flat magnetConnectionMap was removed in Step 3b.
Drop it and rename the surviving hierarchical getHierarchical to the
canonical get (its sole call site is DB.magnetData).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
After dropping the new_ prefix, the doc comments that described each
method as a "Hierarchical clone of <X>" / "Routed clone of <X>" now name
the method itself, and a couple referenced deleted flat helpers
(getRTOwnerOption, the flat usesClk filter). Reword them to describe the
methods directly.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
SanityCheck used oldToNew twice per invocation (once for the checks,
once for the round-trip), and oldToNew is O(members) while SanityCheck
runs after every stage. Compute the hierarchical DB once and thread it
into both the check and hierarchicalDBRoundTripCheck. (The gate removed
in Step 3b had been a third oldToNew call.)

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
SanityCheck.transform now builds the hierarchical DB once, runs the four
structural checks (refCheck, memberExistenceCheck, ownershipCheck,
orderCheck) per sub-DB under each sub-DB's own getSet, then runs the
design checks via hierDB.check. The temporary hierarchicalDBRoundTripCheck
is removed.

Investigation (instrumenting each check independently across all 434
StagesSpec tests) showed refCheck was the only check that failed on
sub-DB scope; the other three are per-sub-DB-safe. refCheck had two
cross-design assumptions, both fixed by fetching the child design's
sub-DB via rootDB.subDBs and reading it under that sub-DB's getSet:

* instPortsByNameSet read child ports via
  inst.getDesignBlock.members(Folded), which threw NoSuchElementException
  because the child design isn't an owner in the parent sub-DB.
* the "removed member" refTable scan flagged inst.designRef -> childBlock
  (a legitimate cross-sub-DB reference); it now whitelists DFDesignBlock
  targets that are live subDBs keys.

Both carry a TODO for the future unification of DFDesignInst.designRef
with the child DFDesignBlock.ownerRef (the subDBs key), which collapses
the lookups to rootDB.subDBs.get(inst.designRef).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…apable

PrintCodeString was a no-op dependency anchor that happened to `extends Stage`;
it is now a `BundleStage`, removing it from the flat-DB stage list.

The printer layer (shared `Printer`/`AbstractPrinter` traits, inherited by
DFPrinter, VerilogPrinter and VHDLPrinter) is now able to render a hierarchical
root DB, not just a flat one:

* `withGetSet` constructs a same-typed sub-printer bound to a sub-DB's getSet;
  `csDB`/`printedDB` render each design under its owning sub-DB (the root's own
  getSet throws on ref resolution).
* designs are rendered in post-order DFS of the design tree, matching the flat
  `designMemberList` order (the `subDBs` ListMap is pre-order).
* const globals are aggregated across sub-DBs with first-occurrence dedup.
* `DB.hierGlobalNamedDFTypes` promotes a named type to global when any sub-DB
  marks it global (a sub-DB sees only one design, so a type used as IO in a
  child but a plain local in a parent would otherwise be printed twice); these
  are excluded from each design's local type declarations.
* `printerForDesign` routes a blackbox component/module declaration to the
  referenced blackbox design's own sub-DB (its port list lives there).

Validated byte-identical against the flat path across all of StagesSpec (DFHDL
434, Verilog+VHDL 78). HierarchicalPrintSpec is a permanent round-trip guard,
since the pipeline still feeds the printer a flat DB for now.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Oron Port and others added 29 commits June 15, 2026 21:15
The interfaces work will model interface declarations as
DFDesignBlock(InstMode.Interface) and views as new DFTypes, so the
placeholder DFInterfaceOwner owner is no longer needed. Drops it from the
DFMember hierarchy and ReadWriter, narrows DFNet.Ref to DFVal-only,
simplifies DFNet.Connection.unapply / FlatNet, and removes the per-stage
interface cases (AddClkRst, ToRT, ToED) and the <-> printer path. Updates
the IR reference docs to match.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
With DFInterfaceOwner gone, every DFOwner is a DFBlock and DFNet.Ref is
DFVal-only, so several fallback/recursion branches became unreachable
(E121 'unreachable case except for null' warnings). Removes the dead
branches in getOwnerBlock, getVeryLastMember, the ToED domain-owner and
GlobalizePortVectorParams ref matches, and simplifies the now-trivial
DFVal matches in Printer.csDFNet and FoldControlSteps.lhsDcl.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds two composite DFTypes for the interfaces/views feature:
- DFInterface: a bundle of named ports/fields, like DFStruct but structural
  (no bit representation; never lowered to Bits).
- DFView (sealed): a directed view over a DFInterface. DFView.AsIs carries a
  per-leaf-port direction overlay (dirMap); DFView.Flipped is the converse of
  another view (IR analog of VHDL 'converse). Both reuse the DFStruct/DFVector
  conventions for =~/isSimilarTo/getRefs/copyWithNewRefs; the bit-data methods
  throw, as these are not packable types.

Also adds InstMode.Interface so an interface declaration can be represented as
a DFDesignBlock(InstMode.Interface) (an 'empty design').

The new variants surface exhaustivity warnings in the type printers
(DFTypePrinter, VHDLTypePrinter); emitting interfaces/views to HDL is the
deferred backend phase.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds a basic Interface frontend (trait Interface + DFInterface/RTInterface/
EDInterface), mirroring Design's lifecycle but trimmed for a nested 'empty
design': scope is DFC.Scope.Interface, the owner is a Design.Block built with
InstMode.Interface, and param collection + instance creation reuse Design.Inst.
Drops the top-level/device-top/resource and top-check machinery (never applies
to a nested interface).

Adds HasConstParams as a common ancestor of Design and Interface so the
compiler plugin can key constant-parameter handling on it rather than on
Design specifically.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Teaches the compiler plugin about interfaces by generalizing the Design-keyed
constant-parameter machinery to the shared HasConstParams ancestor:
- MetaContextPlacerPhase now gates body-parameter generation on
  dfhdl.core.HasConstParams (was dfhdl.core.Design), so interfaces get the same
  '<> CONST' param-to-member generation. Renames genDesignBodyParams ->
  genContainerBodyParams.
- Renames the param-generation helpers for container generality:
  r__For_Plugin.genDesignParam -> genContainerParam, and the plugin's
  genDesignParamSym/genDesignParamValDef -> genContainerParam* .

PreTyperPhase recognizes interface parents (EDInterface/RTInterface/DFInterface)
and excludes them from auto-@top: an interface with '<> CONST' params would
otherwise match shouldAddTop and then be rejected by the Design-only
TopAnnotPhase. TopAnnotPhase is intentionally left Design-only (no @top for
interfaces).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
An interface is purely structural (ports + views, no behavioral statements), so
the DF/RT/ED distinction has nothing to act on. Replace the trait Interface +
DFInterface/RTInterface/EDInterface variants with a single domain-neutral
`Interface` based on the ED domain under the hood: ED is terminal in the
lowering pipeline (DF -> RT -> ED), so an ED interface is never transformed by
ToRT/ToED nor given clk/rst, and the same Interface is reusable inside a design
of any domain.

Updates the plugin's interface recognition to the single name (Set("Interface")).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Interface DFVal members (ports and const parameters) are internal wiring;
external code should reach them only through a view's `.VIEW`. Enforce this
at compile time in MetaContextPlacerPhase.prepareForValDef: any non-protected
(and non-private) DFVal declared in a class extending Interface is rejected.

The check runs post-typer so `<:< Interface` resolves transitively, catching
ports declared in indirect subclasses that a pre-typer syntactic check would
miss. Native `protected` access control then blocks `ifc.port` selections and
hides the members from IDE autocomplete.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Document how to define and use interfaces: declaration, protected
ports/params, views (in/out/flip/generic), instantiation and view
projection, nested interfaces (AXI4-Lite), vector views, HDL mapping,
and the restrictions summary. Wire the chapter into the nav.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
An interface is purely structural and carries no initial values. Add a
NotInsideInterface[A] compile-time constraint (AssertGiven over the
declaration scope encoded in the modifier's `A` type parameter) and
apply it to both `init` overloads and `initFile`, producing a clear
error message at the call site.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Fold the anchored-direction port and projection-terminal design into the
user-guide interfaces chapter: new terminology entries, an
Anchored-Direction Ports section (flippable vs anchored, the two view
rules, and sibling vs parent-to-child connection behavior), a
projection-terminals table (VIEW/FLIP/FLIPALL/MONITOR/DRIVER), and
updated restrictions.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Align the user-guide with the finalized projection-terminal family
(ASIS/FLIP/FLIPALL/MONITOR/DRIVER), renaming the plain `.VIEW` terminal to
`.ASIS` so every terminal is parallel and maps 1:1 to the IR Terminal enum.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Update the interface/view IR types to the finalized design:

- DFInterface.fieldMap now carries a per-field direction via a new
  DFInterface.Field(dfType, dir) (VAR = flippable, IN/OUT = anchored).
  Field extends HasRefCompare so the field map compares with `=~`.
- DFView collapses to a single concrete type (drop AsIs/Flipped). It
  stores only its overlay over the interface: a leaf `dirMap` plus a
  `nestedMap` of chosen sub-views, with a derived `projectedFieldMap`
  that merges in the field types on demand (no duplication). Adds the
  flip/flipAll/monitor/driver direction transforms; flip leaves anchored
  leaves untouched, flipAll is the true VHDL 'converse.
- Add Modifier.Terminal{AsIs,Flip,FlipAll,Monitor,Driver}, reshape
  ViewSite to Template | Projection(terminal), and add the IR-only
  Dir.VIEW(site) marker.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
- Treat INOUT as a first-class anchored direction; it is its own
  converse, so flip/flipAll leave it unchanged (matching SV inout
  modports and VHDL 'converse inout->inout). Documented on `invert`
  and DFInterface.Field.
- Add DFInterface.Field.isSimilarTo and leverage it from both
  DFInterface.isSimilarTo and DFView.isSimilarTo (with size checks),
  removing the duplicated per-field logic.
- DFView.prot_=~ now compares `projectedFieldMap =~` (a ListMap of
  HasRefCompare Fields), folding the leaf dirMap and nested sub-views
  into a single comparison.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Note that an anchored port may be <> INOUT (a bidirectional, self-
converse direction that flip/flipAll leave unchanged, mapping to SV
inout modports and VHDL inout mode-view elements), and show the
view.inout(...) builder form.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
The init/initFile interface restriction constrained the modifier's `A`
type parameter (`NotInsideInterface[A]`). That extra constraint
destabilized inference of the register/assignable markers carried in
`A` at call sites like `(Bit <> VAR.REG).init(1)`, so the resulting
register lost its assignable-REG marker and a later `.din` failed with
"This is not an assignable register" (e.g. in FoldControlSteps and
DropRTProcess).

Detect the interface scope via the ambient `DFC.Scope.Interface` given
(present inside an interface body) instead, so the constraint no longer
touches `A` and register inference is unaffected. The rejection behavior
is unchanged.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Add `opaque type StaticRef = DFRef.OneWay[DFDesignBlock]` (fully opaque,
no subtype bound) so the refs used as stable structural keys are not
treated as ordinary references by accident. It provides an explicit
`.asRef` unwrap, an `apply`/`Conversion` from `DFOwner.Ref` (covering
both `OneWay[DFDesignBlock]` and the unified `ownerRef` sub-DB keys),
and its own `CanEqual`/`ReadWriter`.

Retype the three structural-key sites to `StaticRef`: `DB.subDBs` keys,
`DFDesignInst.designRef`, and `DFInterface.interfaceRef`. Every site that
genuinely needs the underlying ref now unwraps via `.asRef` (resolution,
refTable/designBlockByKey lookups), and sub-DB keys built from `ownerRef`
wrap via `StaticRef(...)`, across the IR, core, and the hierarchy stages.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Previously, cancelling a running tool (simulator/builder) with Ctrl+C only
worked under foreground sbt, and could leave the tool (and its children)
running, stall on heavy tool output, continue past the cancelled step, or
print the notice twice.

- handle both delivery mechanisms: SIGINT (sbt/standalone) and the
  Thread.interrupt() the sbtn background-job thread receives.
- force-kill the whole process tree via the underlying java.lang.Process
  (descendants + destroyForcibly), so Windows tools that fork workers
  (gw_sh, vsim -> vsimk, ...) don't survive.
- wait on the underlying process rather than os-lib's waitFor(), which joins
  the output-pumper thread; under an output flood that join blocked on the
  back-pressured writer and made the abort feel slow.
- propagate a stack-trace-free ToolInterruptedException, caught at the DFApp
  boundary, so the run stops cleanly without a noisy trace and without
  sys.exit (which would tear down the reusable sbtn/sbt-shell server JVM).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@soronpo soronpo merged commit d37ec0d into main Jun 18, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant