Module services api#673
Conversation
…al/instance naming routine names
Introduces a singleton service registry (ModuleServices) that provides a unified query surface for DevTools and inspection tools. Module.build() now registers the root module with ModuleServices.instance. Also adds SvService which wraps SystemVerilog synthesis and registers with ModuleServices for DevTools access to SV metadata. This is a clean separation: no netlist code is included. The netlist branch will later extend ModuleServices with a netlistService field.
Directory.uri always appends a trailing slash, making
entity.uri.pathSegments.last == ""
so name.startsWith(_systemCTempPrefix) was never true — tmp_sc_*
directories were never deleted.
Switch to entity.path.split('/').last which gives the bare directory
name without the trailing-slash artefact.
pch/ is a persistent precompiled-header cache, pre-built by CI before tests run and reused across test files for speed. It is not test output pollution. Exclude it from the tmp_test emptiness check.
Re-add SystemCFfiCosim.cleanupCache() (lost in the SystemC service split) and its tearDownAll hook so check_tmp_test.sh passes. Bump actions/setup-node to node-version 24 (Node 20 is deprecated on GitHub Actions runners).
Introduces a singleton service registry (ModuleServices) that provides a unified query surface for DevTools and inspection tools. Module.build() now registers the root module with ModuleServices.instance. Also adds SvService which wraps SystemVerilog synthesis and registers with ModuleServices for DevTools access to SV metadata. This is a clean separation: no netlist code is included. The netlist branch will later extend ModuleServices with a netlistService field.
…ices_api # Conflicts: # lib/src/module.dart
Backed by _rootModule. Setter eagerly syncs ModuleTree.rootModuleInstance. Safe for module_services_api (no FLC dependencies).
| /// // Or get the concatenated output (like generateSynth): | ||
| /// print(sv.allContents); | ||
| /// ``` | ||
| class SystemVerilogService extends CodeGenService { |
There was a problem hiding this comment.
Something doesn't feel quite right to me in this API. Some thoughts:
- If I want to generate some SV (pretty common), why am I constructing a "service"? Though the abstraction levels as a service make sense to me.
- Why does the act of constructing said service immediately produce the output?
- In what cases do I want to run the SynthBuilder with a selected synthesizer instead of this service?
- When there are multiple synthesizers (e.g. SystemC), will I have multiple services or one service that takes multiple synthesizers?
The two APIs currently are basically:
- Turn this module into SV quickly (generateSynth function on module)
- Make a thing that builds an output based on this Synthesizer and give me outputs I can do more work with (SynthBuilder with Synthesizer arg)
This is deprecating the first, but keeping the second, and there's still some API overlap.
I think this is close to right, but something is a little off. Thoughts?
There was a problem hiding this comment.
Let me ponder this.
I have NetlistService, SystemVerilogService, SystemCService, TraceService, and WaveformService. So some of this is trying to go for least surprise even though they are not fully symmetric. For example, the outputing services need to be setup after the build. WaveformService tracks with the simulator running, so it is truly a live 'service', but the output services execute (though we could use an api that says 'generate' for those.
I never thought of SynthBuilder as a user API.
Here is a live usage on HCL:
final adder = FloatingPointAdderSinglePath(clk: clk, fp1, fp2);
await adder.build();
NetlistService(adder,
outputPath: '$buildDir/${adder.definitionName}.json');
final sv =
SystemVerilogService(adder, outputPath: '$buildDir/${adder.definitionName}.sv'
// embedSourceTraceComments: false,
);
final sc = SystemCService(adder,
outputPath: '$buildDir/${adder.definitionName}.sc', multiFile: false);
TraceService(adder, svService: sv, scService: sc).write('x.flc.json');
WaveformService(adder,
format: WaveOutputFormat.fst,
outputPath: '$buildDir/${adder.definitionName}.fst');There was a problem hiding this comment.
One additional thing: the synthesizers must run to store FLC info. Then the TraceService can access to generate FLC.
There was a problem hiding this comment.
I think of these services as constructing an in-memory view that can be further manipulated (written to file, written to string, perhaps searched, etc). The only challenge I face is that if we change any line numbers then trace gets broken.
Description & Motivation
This is a subset of the PR #669 that introduces the new API for Services attached to a module (outputters like wave dumpers, netlisters, translators, tracing services, etc).
Related Issue(s)
None
Testing
This runs a full dart test with the new API in place in the tests.
Backwards-compatibility
No, we retain the old interfaces but they should eventually be deprecated.
WaveDumper
generateSynth
...
Documentation
Some examples have been modified to match.