Repository navigation
gen/fhdl/verilog: Hierarchical Verilog fixes (dropped logic, undriven clocks, renamed domains). - #2765
Merged
Conversation
…shared submodule elsewhere. A module registered as a submodule of two parents is emitted by its first parent (owner), the other one gets a shared alias, not emitted. The alias carries the owner's statement ids so that the statements Migen merged into its parent aren't emitted twice, but these ids also counted as emitted by the alias' hierarchical ancestors: when the owner was inlined into a common ancestor, that ancestor dropped them and they were emitted nowhere. Seen with LitePCIe's soft PHY (LitePCIeEndpoint also registers its PHY): the PHY's clock assignments were missing, Vivado: no clock signal specified in event control. The ids emitted by a subtree now leave out those an alias takes from an owner outside the subtree. Test added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…riven by its parent. Such a signal is an input port of the child. Inlining it took the whole subtree into the parent: LiteX's add_pcie drives the PHY's MSI stream from the SoC, so LitePCIe's soft PHY (~185k LUTs at x8) was emitted in the top module and the hierarchical utilization report couldn't break it down. A child is still inlined when it also drives the signal. The test forcing an inlined child now has the child drive the signal too, test added for the input port. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ClockSignal (hierarchical). LiteICLink's transceivers set cd.clk = ClockSignal(...) for a shared transmit clock: the flat converter handles it, the hierarchical one put the ClockSignal in its signal sets (TypeError: unhashable type: 'ClockSignal'). The real clock is now resolved (as the namer does when printing) and is a port of the module. The namer looks the clock domains up by name in a dict or a clock domain list (the hierarchical one passed a list: no attribute 'get'), follows chained aliases, and names the domain in its reset-less error. Test added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ncestor (hierarchical). A ClockDomainsRenamer renames the domains of the merged fragment of the module it wraps, the fragments of the modules below keep the names they were built with. A descendant kept as a module (not inlined) had its sync logic in the old domain (sys_clk instead of the renamed one). Each module's sync statements are now grouped by their domain in the design's final fragment. Test added (renamed module with a ResetInserter child). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…een modules (hierarchical). The signals between children were all declared as wires in the parent: one never assigned (as a decoder's Signal(reset=1) clock enable) was an undriven wire instead of a constant, and the logic it enables was trimmed. Only the signals a child drives are wires now, the others regs with their reset value as in the flat netlist. Test added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Fixes to the hierarchical Verilog generation (
--hierarchical-verilog,Builder(hierarchical=True)), found while using it to break down the utilization of a large design: LitePCIe's soft PCIe PHY at 8 GT/s x4 and x8 (~60-190k LUTs) on a Kintex UltraScale+. Before these fixes the hierarchical netlist failed in Vivado (undriven clocks:no clock signal specified in event control). Once that was fixed, synthesis silently lost logic: 45k LUTs / 24k FFs against 62k / 39k for the flat netlist of the same design.One commit per issue, each with a test that fails without it:
LitePCIeEndpointalso registers its PHY as a submodule. The shared alias carried the owner's statement ids, so the alias' hierarchical ancestors counted them as emitted. When the owner was inlined into a common ancestor, that ancestor dropped them and they were emitted nowhere, starting with the PHY's clock assignments. The ids emitted by a subtree now leave out those an alias takes from an owner outside the subtree.add_pciedrives the PHY's MSI stream from the SoC, so the whole PHY (~185k LUTs at x8) ended up in the top module and the hierarchical utilization report couldn't break it down. A child is still inlined when it also drives the signal.ClockDomainwhoseclkis another domain'sClockSignal(LiteICLink's shared transmit clock,cd.clk = ClockSignal(...)) crashed the hierarchical lowering (unhashable type: 'ClockSignal'). It's now resolved to the real clock, as the namer does when printing. The namer looks the domains up by name in a dict or a clock domain list, follows chained aliases, and names the domain in its reset-less error (it readsig.cdaftersigbecameNone).ClockDomainsRenamerwas in the old domain when the module was kept hierarchical:sys_clkinstead of the renamed one. Each module's sync statements are now grouped by their domain in the design's final fragment.wirein the parent, instead of aregwith its reset value as in the flat netlist. A decoder'sce = Signal(reset=1), never assigned, became 0 and its logic was trimmed. Only the signals a child drives are forced to wires now.Tested:
python3 -m pytest test/hdl/: 63 passed. Fullpytest test: the same 15 failures as onmasterin this environment (test_integration.pyCPU boot tests andtest_serialboot_abort_recovers_and_loads, missing toolchains), no new one.xcku3p(Vivado 2024.1 synthesis, hierarchical against flat), 8 GT/s x4 endpoint: 67.5k LUTs / 39.2k FFs against 62.4k / 38.8k flat (a bit more from no cross-boundary optimization), no undriven net, 544 modules. The hardware builds stay flat; the hierarchical netlist was used for synthesis reports only.Written by an AI agent (Claude Code), supervised by Florent Kermarrec.
🤖 Generated with Claude Code