Repository navigation
Inline fully packed child array outputs into split destinations #734
Description
Motivation
When the complete elements of a child's fully packed LogicArray output each drive a separate parent output, generated SystemVerilog can retain an intermediate array and one assignment per element. The equivalent connection from an ordinary packed Logic output can already be represented directly as a concatenation at the child port.
Supporting the array-source case would make generated wiring more concise and consistent without requiring users to flatten their array-based APIs. This is a synthesis-output optimization, not a report of incorrect simulation behavior. Explicit names on the destination ports should remain intact.
This is a follow-up to #726, which supports array destinations for split packed outputs but does not extend that transformation to array sources.
Desired solution
For a one-dimensional, fully packed, non-net child array output, connect the child port directly to an MSB-first concatenation of its whole-element destinations when every element is covered exactly once and the intermediate array can safely be removed.
For example, replace this representative parent wiring:
logic [2:0][4:0] words;
WordProducer producer(.data(data), .words(words));
assign first = words[0];
assign middle = words[1];
assign last = words[2];with:
WordProducer producer(.data(data), .words({last, middle, first}));The initial feature should be deliberately bounded:
- Require complete, non-overlapping whole-element coverage and matching destination widths.
- Preserve bit order, four-state behavior, destination port names, and repeated-synthesis stability.
- Retain existing wiring when the intermediate has a preserved name, additional consumers, partial coverage, overlapping destinations, or incompatible drivers.
- Exclude net/inout connections, unpacked arrays, nested arrays, and partial-element selections initially. These can be separate extensions.
- Keep destination declarations and reference/use accounting correct through subsequent synthesis passes.
Alternatives considered
- Keep the current assignments. This is functionally valid, but unnecessarily verbose for complete one-to-one connections.
- Flatten the child output to an ordinary packed
Logicbus. This can use the existing optimization, but changes the design's API and discards useful array structure merely to influence generated text. - Remove names from destination ports. This does not address the array-source restriction and should not be required for the optimization.
- Generalize all array layouts and fanout cases at once. That would increase implementation and verification scope; the fully packed whole-element case is a useful first step.
Additional details
Generic reproducer
This self-contained example uses only public ROHD APIs. Run it in a Dart project that depends on ROHD. The retained intermediate was observed on the PR 726 branch at commit 400d8baff; exact generated names and statement ordering may vary.
import 'package:rohd/rohd.dart';
class WordProducer extends Module {
LogicArray get words => output('words') as LogicArray;
WordProducer(Logic data) {
data = addInput('data', data, width: 15);
final result = addOutputArray('words',
dimensions: [3], elementWidth: 5);
for (var index = 0; index < 3; index++) {
result.elements[index] <= data.getRange(index * 5, (index + 1) * 5);
}
}
}
class SplitArrayTop extends Module {
SplitArrayTop() {
final data = addInput('data', Logic(width: 15), width: 15);
final producer = WordProducer(data);
addOutput('first', width: 5) <= producer.words.elements[0];
addOutput('middle', width: 5) <= producer.words.elements[1];
addOutput('last', width: 5) <= producer.words.elements[2];
}
}
Future<void> main() async {
final top = SplitArrayTop();
await top.build();
print(top.generateSynth());
}The parent outputs and the selected source elements are ordinary Logic objects, but the source elements belong to a LogicArray. Checking only an element's runtimeType therefore does not distinguish this case from an ordinary packed signal selection.
Implementation considerations
The existing SystemVerilogSynthModuleDefinition._inlinePackedRangesFromSubmoduleOutputs explicitly excludes array and structure-member sources. Supporting them requires discovering element connections and validating array layout, coverage, and ownership, not simply deleting that guard.
The existing _SynthLogicOutputConcat may be reusable for rendering the legal packed output concatenation. Its destination references must remain visible to any later pass that depends on signal use or declaration lifetime. The representation should remain confined to the SystemVerilog backend.
Suggested acceptance tests
- The reproducer emits a direct output concatenation with no intermediate array declaration or per-element parent assignments.
- Both natural and permuted element-to-destination ordering preserve values, including X/Z, in ROHD and Icarus.
- Standard and custom-SystemVerilog children retain the same external connection behavior.
- Named destination ports remain unchanged; explicitly preserved intermediate array names prevent removal.
- Partial coverage, extra consumers, overlapping destinations, and unsupported array/net shapes retain valid existing wiring.
- Destinations feeding another child remain declared and connected after later synthesis passes.
- Repeated SystemVerilog generation, including an intervening netlist generation, is stable.
Use a small set of targeted cases covering these boundaries rather than a large Cartesian-product test matrix.
Activity