Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide the ability to reserve instance names and provide + reserve definition names #86

Merged
merged 3 commits into from
Mar 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ For more information on Dart and tutorials, see https://dart.dev/ and https://da
- vscode: https://code.visualstudio.com/
- WSL: https://docs.microsoft.com/en-us/windows/wsl/install-win10
- Remote SSH: https://code.visualstudio.com/blogs/2019/07/25/remote-ssh
- Dart extension for vscode: https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code

## Getting started
Once you have Dart installed, if you don't already have a project, you can create one using `dart create`: https://dart.dev/tools/dart-tool
Expand Down
6 changes: 4 additions & 2 deletions lib/src/external.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ abstract class ExternalSystemVerilogModule extends Module
{required this.topModuleName,
this.parameters,
String name = 'external_module'})
: super(name: name);
: super(
name: name,
definitionName: topModuleName,
reserveDefinitionName: true);

@override
String instantiationVerilog(String instanceType, String instanceName,
Map<String, String> inputs, Map<String, String> outputs) {
//TODO: how to avoid module name conflicts with generated modules?
return SystemVerilogSynthesizer.instantiationVerilogWithParameters(
this, topModuleName, instanceName, inputs, outputs,
parameters: parameters, forceStandardInstantiation: true);
Expand Down
1 change: 1 addition & 0 deletions lib/src/logic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ class Logic {

/// Accesses the [index]th bit of this signal.
Logic operator [](int index) {
//TODO: support negative numbers to access relative from the end
return slice(index, index);
}

Expand Down
37 changes: 31 additions & 6 deletions lib/src/module.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ import 'package:rohd/src/utilities/uniquifier.dart';
/// This construct is similar to a SystemVerilog `module`.
abstract class Module {
/// The name of this [Module].
///
/// This is not necessarily the same as the instance name in generated code. For that,
/// see [uniqueInstanceName]. If you set [reserveName], then it is guaranteed to match
/// or else the [build] will fail.
final String name;

/// An internal list of sub-modules.
Expand Down Expand Up @@ -101,20 +105,41 @@ abstract class Module {
/// Returns true iff [net] is the same [Logic] as an input or output port of this [Module] with the same name.
bool isPort(Logic net) => isInput(net) || isOutput(net);

/// If this module has a [parent], after [build()] this will be a guaranteed unique name within its scope.
String get uniqueInstanceName => hasBuilt
/// If this module has a [parent], after [build] this will be a guaranteed unique name within its scope.
String get uniqueInstanceName => hasBuilt || reserveName
? _uniqueInstanceName
: throw Exception(
'Module must be built to access uniquified name. Call build() before accessing this.');
String _uniqueInstanceName;

Module({this.name = 'unnamed_module'}) : _uniqueInstanceName = name;
/// If true, guarantees [uniqueInstanceName] matches [name] or else the [build] will fail.
final bool reserveName;

/// The definition name of this [Module] used when instantiating instances in generated code.
///
/// By default, if none is provided at construction time, the definition name is the same
/// as the [runtimeType].
///
/// This could become uniquified by a [Synthesizer] unless [reserveDefinitionName] is set.
String get definitionName => _definitionName ?? runtimeType.toString();
final String? _definitionName;

/// If true, guarantees [definitionName] is maintained by a [Synthesizer], or else it will fail.
final bool reserveDefinitionName;

Module(
{this.name = 'unnamed_module',
this.reserveName = false,
String? definitionName,
this.reserveDefinitionName = false})
: _uniqueInstanceName = name,
_definitionName = definitionName;

/// Returns an [Iterable] of [Module]s representing the hierarchical path to this [Module].
///
/// The first element of the [Iterable] is the top-most hierarchy.
/// The last element of the [Iterable] is this [Module].
/// Only returns valid information after [build()].
/// Only returns valid information after [build].
Iterable<Module> hierarchy() {
if (!hasBuilt) {
throw Exception(
Expand Down Expand Up @@ -171,7 +196,8 @@ abstract class Module {
var uniquifier = Uniquifier();
for (var module in _modules) {
module._uniqueInstanceName = uniquifier.getUniqueName(
initialName: Sanitizer.sanitizeSV(module.name));
initialName: Sanitizer.sanitizeSV(module.name),
reserved: module.reserveName);
}

_hasBuilt = true;
Expand Down Expand Up @@ -204,7 +230,6 @@ abstract class Module {
/// signals as "unpreferred" can have the effect of making generated output easier to read.
@protected
static String unpreferredName(String name) {
//TODO: how to make sure there's no module name conflicts??
return _unpreferredPrefix + name;
}

Expand Down
5 changes: 3 additions & 2 deletions lib/src/synthesizers/synth_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class SynthBuilder {
if (_moduleToInstanceTypeMap.containsKey(module)) {
return _moduleToInstanceTypeMap[module]!;
}
var newName = module.runtimeType.toString();
var newName = module.definitionName;

var newSynthesisResult =
synthesizer.synthesize(module, _moduleToInstanceTypeMap);
Expand All @@ -74,7 +74,8 @@ class SynthBuilder {
_synthesisResults.lookup(newSynthesisResult)!.module]!;
} else {
_synthesisResults.add(newSynthesisResult);
newName = _instanceTypeUniquifier.getUniqueName(initialName: newName);
newName = _instanceTypeUniquifier.getUniqueName(
initialName: newName, reserved: module.reserveDefinitionName);
}

_moduleToInstanceTypeMap[module] = newName;
Expand Down
9 changes: 6 additions & 3 deletions lib/src/synthesizers/systemverilog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,9 @@ class _SynthModuleDefinition {
return moduleToSubModuleInstantiationMap[m]!;
} else {
var newSSMI = _SynthSubModuleInstantiation(
m, _getUniqueSynthSubModuleInstantiationName(m.uniqueInstanceName));
m,
_getUniqueSynthSubModuleInstantiationName(
m.uniqueInstanceName, m.reserveName));
moduleToSubModuleInstantiationMap[m] = newSSMI;
return newSSMI;
}
Expand All @@ -310,9 +312,10 @@ class _SynthModuleDefinition {
}

final Uniquifier _synthSubModuleInstantiationNameUniquifier = Uniquifier();
String _getUniqueSynthSubModuleInstantiationName(String? initialName) {
String _getUniqueSynthSubModuleInstantiationName(
String? initialName, bool reserved) {
return _synthSubModuleInstantiationNameUniquifier.getUniqueName(
initialName: initialName, nullStarter: 'm');
initialName: initialName, nullStarter: 'm', reserved: reserved);
}

_SynthLogic? _getSynthLogic(Logic? logic, bool allowPortName) {
Expand Down
4 changes: 3 additions & 1 deletion lib/src/values/logic_values.dart
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,8 @@ abstract class LogicValues {
/// Returns a new `LogicValues` with the order of all bits in the reverse order of this `LogicValues`
LogicValues get reversed;

//TODO: maybe getRange end should be optional, where it returns just bit of start if no end is provided

/// Returns a subset [LogicValues]. It is inclusive of [start], exclusive of [end].
LogicValues getRange(int start, int end) {
if (end < start) {
Expand Down Expand Up @@ -781,7 +783,7 @@ abstract class LogicValues {
/// True iff all bits are `z`.
bool get isFloating;

/// The current active value of this if it has width 1, as a [LogicValue].
/// The current active value of this, if it has width 1, as a [LogicValue].
///
/// Throws an Exception if width is not 1.
LogicValue get bit {
Expand Down
70 changes: 70 additions & 0 deletions test/name_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/// Copyright (C) 2022 Intel Corporation
/// SPDX-License-Identifier: BSD-3-Clause
///
/// definition_name_test.dart
/// Tests for definition names (including reserving them) of Modules.
///
/// 2022 March 7
/// Author: Max Korbel <max.korbel@intel.com>

import 'package:rohd/rohd.dart';
import 'package:test/test.dart';

class TopModule extends Module {
TopModule(Logic a, bool causeDefConflict, bool causeInstConflict)
: super(name: 'topModule') {
a = addInput('a', a);

// note: order matters
SpeciallyNamedModule([a, a].swizzle(), causeDefConflict, causeInstConflict);
SpeciallyNamedModule(a, true, causeInstConflict);
}
}

class SpeciallyNamedModule extends Module {
SpeciallyNamedModule(Logic a, bool reserveDefName, bool reserveInstanceName)
: super(
name: 'specialNameInstance',
reserveName: reserveInstanceName,
definitionName: 'specialName',
reserveDefinitionName: reserveDefName,
) {
addInput('a', a, width: a.width);
}
}

void main() {
group('definition name', () {
test('respected with no conflicts', () async {
var mod = SpeciallyNamedModule(Logic(), false, false);
await mod.build();
expect(mod.generateSynth(), contains('module specialName('));
});
test('uniquified with conflicts', () async {
var mod = TopModule(Logic(), false, false);
await mod.build();
var sv = mod.generateSynth();
expect(sv, contains('module specialName('));
expect(sv, contains('module specialName_0('));
});
test('reserved throws exception with conflicts', () async {
var mod = TopModule(Logic(), true, false);
await mod.build();
expect(() => mod.generateSynth(), throwsException);
});
});

group('instance name', () {
test('uniquified with conflicts', () async {
var mod = TopModule(Logic(), false, false);
await mod.build();
var sv = mod.generateSynth();
expect(sv, contains('specialNameInstance('));
expect(sv, contains('specialNameInstance_0('));
});
test('reserved throws exception with conflicts', () async {
var mod = TopModule(Logic(), false, true);
expect(() async => await mod.build(), throwsException);
});
});
}