diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e4d10ff1..3d02b969a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,11 @@ ### Breaking Changes - `FiniteStateMachine` and `State` now require Dart enum state identifiers. Replace integer, string, or custom-object identifiers with enum members; update reset and transition targets accordingly. `currentState` and `nextState` are now `LogicEnum` signals (). -- Removed long-deprecated APIs, including `StateMachine`, `FF`, `IfBlock`, `Port`, `LogicValues`, `CustomSystemVerilog`, `Dumper`, legacy signal/value helpers, `PairInterface.modify` and its clone constructor, and old synthesis accessors. These were already deprecated before 0.6.10. See the [0.7.0 migration guide](doc/releases.md#core-api-migration-for-070) for replacements. +- Removed long-deprecated APIs, including `StateMachine`, `FF`, `IfBlock`, `Port`, `LogicValues`, `CustomSystemVerilog`, `Dumper`, legacy signal/value helpers, and old synthesis accessors. These were already deprecated before 0.6.10. See the [0.7.0 migration guide](doc/releases.md#core-api-migration-for-070) for replacements. ### Deprecated APIs retained for a later release +- `PairInterface.modify`, its constructor argument, and the `PairInterface.clone` constructor remain deprecated but available for downstream interface compatibility. Prefer `uniquify` when connecting or adding sub-interfaces, and `other.clone()`. - `WaveDumper`, `Module.generateSynth`, and `ModuleTree.hierarchyJSON` remain available with deprecation warnings. They were released APIs in 0.6.11, but their deprecations had not shipped in a tagged release. Use `Module.dumpWaves`, `Module.dumpSystemVerilog`, and `ModuleTree.hierarchyJson`, respectively. - `NetlistSynthesisResult.toFileContents()` remains deprecated but available because it first shipped in 0.6.11. Use `toSynthFileContents()` and read the returned artifacts' `contents`. - These APIs are not removed in 0.7.0; migrate callers now in preparation for removal in a later release. Compatibility retention follows the same approach as a 0.6.12 release, despite the version change. diff --git a/doc/releases.md b/doc/releases.md index 47a0e25a8..a44cbfda6 100644 --- a/doc/releases.md +++ b/doc/releases.md @@ -316,8 +316,6 @@ need breaking releases just because core removes these APIs. | Gate `y` output aliases | `out`, `Add.sum`, or `Mux.mux`, as appropriate | | `Add.out` | `Add.sum` | | `getReceivers()`, `getDrivers()`, `getConditionals()` | `receivers`, `drivers`, `conditionals` | -| `PairInterface.modify` and its constructor argument | Pass `uniquify` when connecting or adding sub-interfaces | -| `PairInterface.clone(other)` | `other.clone()` | | `Module.unpreferredName`, `Module.isUnpreferred` | `Naming.unpreferredName`, `Naming.isUnpreferred`, or `Logic.naming` | | `FiniteStateMachine.clk`, `Pipeline.clk` | Retain the clock supplied at construction; do not retrieve it from the module | | `Dumper` | `module.dumpWaves(outputPath: ...)` or `WaveformService` | @@ -332,6 +330,8 @@ change. Update callers now; do not add new uses outside compatibility tests. | Retained deprecated API | Replacement | Release history | | --- | --- | --- | +| `PairInterface.modify` and its constructor argument | Pass `uniquify` when connecting or adding sub-interfaces | Retained for downstream interface compatibility | +| `PairInterface.clone(other)` | `other.clone()` | Retained for downstream interface compatibility | | `Module.generateSynth` | `Module.dumpSystemVerilog` or `SystemVerilogService` | Released without deprecation in 0.6.11 | | `WaveDumper` | `module.dumpWaves(outputPath: ...)` or `WaveformService` | Released without deprecation in 0.6.11 | | `ModuleTree.hierarchyJSON` | `ModuleTree.hierarchyJson` | Released without deprecation in 0.6.11 | diff --git a/lib/src/interfaces/pair_interface.dart b/lib/src/interfaces/pair_interface.dart index f7f7c143f..3edfacc23 100644 --- a/lib/src/interfaces/pair_interface.dart +++ b/lib/src/interfaces/pair_interface.dart @@ -44,12 +44,23 @@ enum PairRole { /// It can be either directly used for simple scenarios, or extended for more /// complex situations. class PairInterface extends Interface { + /// A function that can be used to modify all port names in a certain way. + @Deprecated( + 'Use `uniquify` when connecting or adding sub interfaces instead.') + String Function(String original)? modify; + /// Constructs an instance of a [PairInterface] with the specified ports. + /// + /// The [modify] function will allow modification of all port names, in + /// addition to the usual uniquification that can occur during [connectIO]. PairInterface({ List? portsFromConsumer, List? portsFromProvider, List? sharedInputPorts, List? commonInOutPorts, + @Deprecated( + 'Use `uniquify` when connecting or adding sub interfaces instead.') + this.modify, }) { if (portsFromConsumer != null) { setPorts(portsFromConsumer, [PairDirection.fromConsumer]); @@ -65,6 +76,22 @@ class PairInterface extends Interface { } } + /// Creates a new instance of a [PairInterface] with the same ports and other + /// characteristics. + @Deprecated('Use `clone()` on an instance instead') + PairInterface.clone(PairInterface otherInterface) + : this( + portsFromConsumer: + _getMatchPorts(otherInterface, PairDirection.fromConsumer), + portsFromProvider: + _getMatchPorts(otherInterface, PairDirection.fromProvider), + sharedInputPorts: + _getMatchPorts(otherInterface, PairDirection.sharedInputs), + commonInOutPorts: + _getMatchPorts(otherInterface, PairDirection.commonInOuts), + modify: otherInterface.modify, + ); + /// Collects ports on a given [interface] tagged with [tag]. static List _getMatchPorts( Interface interface, PairDirection tag) => @@ -139,12 +166,16 @@ class PairInterface extends Interface { Iterable? inOutTags, String Function(String original)? uniquify}) { final nonNullUniquify = uniquify ?? (original) => original; + // ignore: deprecated_member_use_from_same_package - deprecated backwards compat + final nonNullModify = modify ?? (original) => original; + String newUniquify(String original) => + nonNullUniquify(nonNullModify(original)); super.connectIO(module, srcInterface, inputTags: inputTags, outputTags: outputTags, inOutTags: inOutTags, - uniquify: nonNullUniquify); + uniquify: newUniquify); if (subInterfaces.isNotEmpty) { if (srcInterface is! PairInterface) { @@ -162,7 +193,7 @@ class PairInterface extends Interface { subInterfaceEntry.value.uniquify ?? (original) => original; String newSubIntfUniquify(String original) => - nonNullUniquify(nonNullSubIntfUniquify(original)); + nonNullUniquify(nonNullSubIntfUniquify(nonNullModify(original))); if (!srcInterface._subInterfaces.containsKey(subInterfaceName)) { throw InterfaceTypeException( @@ -387,6 +418,8 @@ class PairInterface extends Interface { portsFromProvider: _getMatchPorts(this, PairDirection.fromProvider), sharedInputPorts: _getMatchPorts(this, PairDirection.sharedInputs), commonInOutPorts: _getMatchPorts(this, PairDirection.commonInOuts), + // ignore: deprecated_member_use_from_same_package - deprecated backwards compat + modify: modify, ); } diff --git a/test/benchmark_test.dart b/test/benchmark_test.dart index c660b505e..683c003e8 100644 --- a/test/benchmark_test.dart +++ b/test/benchmark_test.dart @@ -7,6 +7,7 @@ // 2022 September 28 // Author: Max Korbel +import 'package:benchmark_harness/benchmark_harness.dart'; import 'package:rohd/src/utilities/web.dart'; import 'package:test/test.dart'; @@ -19,44 +20,59 @@ import '../benchmark/pipeline_benchmark.dart'; import '../benchmark/ssa_driver_search_benchmark.dart'; import '../benchmark/wave_dump_benchmark.dart'; +/// Runs one pass of each phase. The timing loop in `measure()` runs for +/// seconds per benchmark, which is unnecessary for checking they still work. +void smokeRun(BenchmarkBase benchmark) { + benchmark + ..setup() + ..run() + ..teardown(); +} + +Future smokeRunAsync(AsyncBenchmarkBase benchmark) async { + await benchmark.setup(); + await benchmark.run(); + await benchmark.teardown(); +} + void main() { group('benchmark', tags: 'benchmark', () { test('pipeline', () async { - await PipelineBenchmark().measure(); + await smokeRunAsync(PipelineBenchmark()); }); test('logic value of', () { - LogicValueOfBenchmark().measure(); + smokeRun(LogicValueOfBenchmark()); }); test('byte enable', () { - ByteEnableBenchmark().measure(); + smokeRun(ByteEnableBenchmark()); }); if (!kIsWeb) { test('waveform', () async { - await WaveDumpBenchmark().measure(); + await smokeRunAsync(WaveDumpBenchmark()); }); } group('many seq and comb', () { for (final connectionType in ManySeqAndCombCombConnectionType.values) { test(connectionType.name, () async { - await ManySeqAndCombBenchmark(connectionType).measure(); + await smokeRunAsync(ManySeqAndCombBenchmark(connectionType)); }); } }); test('comb guard fanout', () async { - await CombGuardFanoutBenchmark().measure(); + await smokeRunAsync(CombGuardFanoutBenchmark()); }); test('ssa driver search', () { - SsaDriverSearchBenchmark().measure(); + smokeRun(SsaDriverSearchBenchmark()); }); test('many submodule', () async { - await ManySubmodulesBenchmark().measure(); + await smokeRunAsync(ManySubmodulesBenchmark()); }); }); } diff --git a/test/recently_deprecated_api_test.dart b/test/recently_deprecated_api_test.dart index b5aac2f5c..3fdd3d1df 100644 --- a/test/recently_deprecated_api_test.dart +++ b/test/recently_deprecated_api_test.dart @@ -21,6 +21,15 @@ class _CompatibilityModule extends Module { } } +class _CompatibilityPairModule extends Module { + _CompatibilityPairModule(PairInterface source, + {String Function(String original)? uniquify}) { + source + .clone() + .pairConnectIO(this, source, PairRole.provider, uniquify: uniquify); + } +} + void main() { tearDown(() async { await Simulator.reset(); @@ -63,6 +72,39 @@ void main() { result.toFileContents(), result.toSynthFileContents().single.contents); }); + test('pair interface modify and clone constructor remain compatible', () { + String prefixed(String original) => 'x_$original'; + final source = PairInterface( + portsFromConsumer: [Logic(name: 'a')], + portsFromProvider: [Logic(name: 'b')], + modify: prefixed, + ); + + final copy = PairInterface.clone(source); + + expect(copy.modify, same(prefixed)); + expect(copy.ports.keys, unorderedEquals(['a', 'b'])); + expect(source.clone().modify, same(prefixed)); + }); + + for (final uniquify in [false, true]) { + test('pair interface modify composes with uniquify=$uniquify', () async { + final source = PairInterface( + portsFromConsumer: [Logic(name: 'a')..put(1)], + portsFromProvider: [Logic(name: 'b')], + modify: (original) => 'legacy_$original', + ); + final module = _CompatibilityPairModule(source, + uniquify: uniquify ? (original) => 'outer_$original' : null); + await module.build(); + + final prefix = uniquify ? 'outer_legacy_' : 'legacy_'; + expect(module.inputs.keys, ['${prefix}a']); + expect(module.outputs.keys, ['${prefix}b']); + expect(module.input('${prefix}a').value, LogicValue.one); + }); + } + test('released waveform wrapper still captures VCD', () async { final directory = Directory.systemTemp.createTempSync('rohd_compatibility_'); diff --git a/test/submodule_output_mapping_test.dart b/test/submodule_output_mapping_test.dart index 59760dd0a..c03767527 100644 --- a/test/submodule_output_mapping_test.dart +++ b/test/submodule_output_mapping_test.dart @@ -228,8 +228,8 @@ class ConstantOutputTop extends Module { required bool namedConstant, required AssignmentOrder order, }) : super(name: 'constant_output_top') { - final data = addInput('data', Logic(width: 4), width: 4); - final observed = addOutput('observed', width: 5); + final data = addInput('data', Logic(width: 2), width: 2); + final observed = addOutput('observed', width: 3); final results = stageOutputs(data, ProducerKind.independent); Logic tie = Const(LogicValue.ofString(constant)); if (namedConstant) { @@ -508,7 +508,8 @@ void main() { group('independent outputs to packed parent bits', () { for (final producer in ProducerKind.values) { for (final order in AssignmentOrder.values) { - for (final width in [2, 5]) { + // Three bits distinguish both boundaries and an interior position. + for (final width in [2, 3]) { for (final aliasDepth in [0, 3]) { test( '${producer.name} ${order.name} ' @@ -741,7 +742,7 @@ void main() { group('constant position and four-state cross-products', () { for (final constant in ['0', '1', 'x', 'z']) { - for (final constantIndex in [0, 2, 4]) { + for (final constantIndex in [0, 1, 2]) { for (final namedConstant in [false, true]) { for (final order in [ AssignmentOrder.batch, @@ -762,7 +763,9 @@ void main() { final body = topBody(module); expect(body, isNot(contains('.result()')), reason: body); if (constant != 'z') { - for (var index = 0; index < 5; index++) { + for (var index = 0; + index < module.output('observed').width; + index++) { if (index != constantIndex) { expect(body, contains('.result(observed[$index])'), reason: body); @@ -772,12 +775,14 @@ void main() { NetlistSynthesizer().synthesizeToJson(module); expect(topBody(module), body); final vectors = [ - for (final input in inputPatterns(4)) + for (final input in inputPatterns(module.input('data').width)) Vector({ 'data': input }, { 'observed': LogicValue.ofString([ - for (var index = 4; index >= 0; index--) + for (var index = module.output('observed').width - 1; + index >= 0; + index--) if (index == constantIndex) constant else