Skip to content
Open
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
7 changes: 4 additions & 3 deletions lib/src/exceptions/logic/put_exception.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@

import 'package:rohd/rohd.dart';

/// An exception that thrown when a [Logic] signal fails to `put`.
/// An exception that thrown when a [Logic] signal fails to [Logic.put].
class PutException extends RohdException {
/// Creates an exception for when a `put` fails on a `Logic` with [context] as
/// to where the
/// Creates an exception for when a [Logic.put] fails on a [Logic] with
/// [context] as to where the failure occurred and [message] describing the
/// failure.
PutException(String context, String message)
: super('Failed to put value on signal ($context): $message');
}
16 changes: 14 additions & 2 deletions lib/src/module.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2021-2025 Intel Corporation
// Copyright (C) 2021-2026 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// module.dart
Expand All @@ -11,11 +11,11 @@ import 'dart:async';
import 'dart:collection';

import 'package:meta/meta.dart';

import 'package:rohd/rohd.dart';
import 'package:rohd/src/collections/traverseable_collection.dart';
import 'package:rohd/src/diagnostics/inspector_service.dart';
import 'package:rohd/src/utilities/config.dart';
import 'package:rohd/src/utilities/namer.dart';
import 'package:rohd/src/utilities/sanitizer.dart';
import 'package:rohd/src/utilities/timestamper.dart';
import 'package:rohd/src/utilities/uniquifier.dart';
Expand Down Expand Up @@ -52,6 +52,18 @@ abstract class Module {
/// An internal mapping of input names to their sources to this [Module].
late final Map<String, Logic> _inputSources = {};

// ─── Central naming (Namer) ─────────────────────────────────────

/// Central namer that owns both the signal and instance namespaces.
/// Initialized lazily on first access (after build).
@internal
late final Namer namer = _createNamer();

Namer _createNamer() {
assert(hasBuilt, 'Module must be built before canonical names are bound.');
return Namer.forModule(this);
}

/// An internal mapping of inOut names to their sources to this [Module].
late final Map<String, Logic> _inOutSources = {};

Expand Down
2 changes: 1 addition & 1 deletion lib/src/synthesizers/synth_builder.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2021-2025 Intel Corporation
// Copyright (C) 2021-2026 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// synth_builder.dart
Expand Down
3 changes: 1 addition & 2 deletions lib/src/synthesizers/synthesizer.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// Copyright (C) 2021-2023 Intel Corporation
// Copyright (C) 2021-2026 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// synthesizer.dart
// Generic definition for something that synthesizes output files
//
// 2021 August 26
// Author: Max Korbel <max.korbel@intel.com>
//

import 'package:rohd/rohd.dart';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2021-2025 Intel Corporation
// Copyright (C) 2021-2026 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// systemverilog_synthesizer.dart
Expand Down
107 changes: 13 additions & 94 deletions lib/src/synthesizers/utilities/synth_logic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import 'package:collection/collection.dart';
import 'package:meta/meta.dart';
import 'package:rohd/rohd.dart';
import 'package:rohd/src/synthesizers/utilities/utilities.dart';
import 'package:rohd/src/utilities/namer.dart';
import 'package:rohd/src/utilities/sanitizer.dart';
import 'package:rohd/src/utilities/uniquifier.dart';

/// Represents a logic signal in the generated code within a module.
@internal
Expand Down Expand Up @@ -212,92 +212,25 @@ class SynthLogic {
/// The name of this, if it has been picked.
String? _name;

/// Picks a [name].
/// Picks a [name] using the module's signal namer.
///
/// Must be called exactly once.
void pickName(Uniquifier uniquifier) {
void pickName() {
assert(_name == null, 'Should only pick a name once.');

_name = _findName(uniquifier);
_name = _findName();
}

/// Finds the best name from the collection of [Logic]s.
String _findName(Uniquifier uniquifier) {
// check for const
if (_constLogic != null) {
if (!_constNameDisallowed) {
return _constLogic!.value.toString();
} else {
assert(
logics.length > 1,
'If there is a constant, but the const name is not allowed, '
'there needs to be another option',
);
}
}

// check for reserved
if (_reservedLogic != null) {
return uniquifier.getUniqueName(
initialName: _reservedLogic!.name,
reserved: true,
);
}

// check for renameable
if (_renameableLogic != null) {
return uniquifier.getUniqueName(
initialName: _renameableLogic!.preferredSynthName,
);
}

// pick a preferred, available, mergeable name, if one exists
final unpreferredMergeableLogics = <Logic>[];
final uniquifiableMergeableLogics = <Logic>[];
for (final mergeableLogic in _mergeableLogics) {
if (Naming.isUnpreferred(mergeableLogic.preferredSynthName)) {
unpreferredMergeableLogics.add(mergeableLogic);
} else if (!uniquifier.isAvailable(mergeableLogic.preferredSynthName)) {
uniquifiableMergeableLogics.add(mergeableLogic);
} else {
return uniquifier.getUniqueName(
initialName: mergeableLogic.preferredSynthName,
);
}
}

// uniquify a preferred, mergeable name, if one exists
if (uniquifiableMergeableLogics.isNotEmpty) {
return uniquifier.getUniqueName(
initialName: uniquifiableMergeableLogics.first.preferredSynthName,
);
}

// pick an available unpreferred mergeable name, if one exists, otherwise
// uniquify an unpreferred mergeable name
if (unpreferredMergeableLogics.isNotEmpty) {
return uniquifier.getUniqueName(
initialName: unpreferredMergeableLogics
.firstWhereOrNull(
(element) =>
uniquifier.isAvailable(element.preferredSynthName),
)
?.preferredSynthName ??
unpreferredMergeableLogics.first.preferredSynthName,
///
/// Delegates to signal namer which handles constant value naming, priority
/// selection, and uniquification via the module's shared namespace.
String _findName() =>
parentSynthModuleDefinition.module.namer.signalNameOfBest(
logics,
constValue: _constLogic,
constNameDisallowed: _constNameDisallowed,
);
}

// pick anything (unnamed) and uniquify as necessary (considering preferred)
// no need to prefer an available one here, since it's all unnamed
return uniquifier.getUniqueName(
initialName: _unnamedLogics
.firstWhereOrNull(
(element) => !Naming.isUnpreferred(element.preferredSynthName),
)
?.preferredSynthName ??
_unnamedLogics.first.preferredSynthName,
);
}

/// Creates an instance to represent [initialLogic] and any that merge
/// into it.
Expand Down Expand Up @@ -404,7 +337,7 @@ class SynthLogic {

@override
String toString() => '${_name == null ? 'null' : '"$name"'}, '
'logics contained: ${logics.map((e) => e.preferredSynthName).toList()}';
'logics contained: ${logics.map(Namer.baseName).toList()}';

/// Provides a definition for a range in SV from a width.
static String _widthToRangeDef(int width, {bool forceRange = false}) {
Expand Down Expand Up @@ -551,17 +484,3 @@ class SynthLogicArrayElement extends SynthLogic {
' parentArray=($parentArray), element ${logic.arrayIndex}, logic: $logic'
' logics contained: ${logics.map((e) => e.name).toList()}';
}

extension on Logic {
/// Returns the preferred name for this [Logic] while generating in the synth
/// stack.
String get preferredSynthName => naming == Naming.reserved
// if reserved, keep the exact name
? name
: isArrayMember
// arrays nicely name their elements already
? name
// sanitize to remove any `.` in struct names
// the base `name` will be returned if not a structure.
: Sanitizer.sanitizeSV(structureName);
}
61 changes: 31 additions & 30 deletions lib/src/synthesizers/utilities/synth_module_definition.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import 'package:meta/meta.dart';
import 'package:rohd/rohd.dart';
import 'package:rohd/src/collections/traverseable_collection.dart';
import 'package:rohd/src/synthesizers/utilities/utilities.dart';
import 'package:rohd/src/utilities/uniquifier.dart';
import 'package:rohd/src/utilities/namer.dart';

/// A version of [BusSubset] that can be used for slicing on [LogicStructure]
/// ports.
Expand Down Expand Up @@ -110,10 +110,6 @@ class SynthModuleDefinition {
@override
String toString() => "module name: '${module.name}'";

/// Used to uniquify any identifiers, including signal names
/// and module instances.
final Uniquifier _synthInstantiationNameUniquifier;

/// Indicates whether [logic] has a corresponding present [SynthLogic] in
/// this definition.
@internal
Expand Down Expand Up @@ -289,14 +285,7 @@ class SynthModuleDefinition {

/// Creates a new definition representation for this [module].
SynthModuleDefinition(this.module)
: _synthInstantiationNameUniquifier = Uniquifier(
reservedNames: {
...module.inputs.keys,
...module.outputs.keys,
...module.inOuts.keys,
},
),
assert(
: assert(
!(module is SystemVerilog &&
module.generatedDefinitionType ==
DefinitionGenerationType.none),
Expand Down Expand Up @@ -465,6 +454,7 @@ class SynthModuleDefinition {

final receiverIsSubModuleOutput =
receiver.isOutput && (receiver.parentModule?.parent == module);

if (receiverIsSubModuleOutput) {
final subModule = receiver.parentModule!;

Expand Down Expand Up @@ -513,6 +503,7 @@ class SynthModuleDefinition {
_collapseArrays();
_collapseAssignments();
_assignSubmodulePortMapping();

_pruneUnused();
process();
_pickNames();
Expand Down Expand Up @@ -767,49 +758,59 @@ class SynthModuleDefinition {
}

/// Picks names of signals and sub-modules.
///
/// Signal names are read from [Namer.signalNameOf] for user-created
/// [Logic] objects) or kept as literal constants and are allocated from
/// [Namer.signalNameOf]. Submodule instance names are allocated
/// from [Namer.allocateName]. All names share a single
/// namespace managed by the module's [Namer].
void _pickNames() {
// first ports get priority
// Name allocation order matters — earlier claims get the unsuffixed name
// when there are collisions. This matches production ROHD priority:
// 1. Ports (reserved by _initNamespace, claimed via signalName)
// 2. Reserved submodule instances
// 3. Reserved internal signals
// 4. Non-reserved submodule instances
// 5. Non-reserved internal signals
for (final input in inputs) {
input.pickName(_synthInstantiationNameUniquifier);
input.pickName();
}
for (final output in outputs) {
output.pickName(_synthInstantiationNameUniquifier);
output.pickName();
}
for (final inOut in inOuts) {
inOut.pickName(_synthInstantiationNameUniquifier);
inOut.pickName();
}

// pick names of *reserved* submodule instances
final nonReservedSubmodules = <SynthSubModuleInstantiation>[];
// Reserved submodule instances first (they assert their exact name).
for (final submodule in subModuleInstantiations) {
if (submodule.module.reserveName) {
submodule.pickName(_synthInstantiationNameUniquifier);
submodule.pickName(module);
assert(submodule.module.name == submodule.name,
'Expect reserved names to retain their name.');
} else {
nonReservedSubmodules.add(submodule);
}
}

// then *reserved* internal signals get priority
// Reserved internal signals next.
final nonReservedSignals = <SynthLogic>[];
for (final signal in internalSignals) {
if (signal.isReserved) {
signal.pickName(_synthInstantiationNameUniquifier);
signal.pickName();
} else {
nonReservedSignals.add(signal);
}
}

// then submodule instances
for (final submodule in nonReservedSubmodules
.where((element) => element.needsInstantiation)) {
submodule.pickName(_synthInstantiationNameUniquifier);
// Then non-reserved submodule instances.
for (final submodule in subModuleInstantiations) {
if (!submodule.module.reserveName && submodule.needsInstantiation) {
submodule.pickName(module);
}
}

// then the rest of the internal signals
// Then the rest of the internal signals.
for (final signal in nonReservedSignals) {
signal.pickName(_synthInstantiationNameUniquifier);
signal.pickName();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2021-2025 Intel Corporation
// Copyright (C) 2021-2026 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// synth_sub_module_instantiation.dart
Expand All @@ -11,7 +11,7 @@ import 'dart:collection';

import 'package:rohd/rohd.dart';
import 'package:rohd/src/synthesizers/utilities/utilities.dart';
import 'package:rohd/src/utilities/uniquifier.dart';
import 'package:rohd/src/utilities/namer.dart';

/// Represents an instantiation of a module within another module.
class SynthSubModuleInstantiation {
Expand All @@ -25,13 +25,15 @@ class SynthSubModuleInstantiation {
String get name => _name!;

/// Selects a name for this module instance. Must be called exactly once.
void pickName(Uniquifier uniquifier) {
///
/// Names are allocated from [parentModule]'s [Namer]'s shared namespace
/// via [Namer.allocateName].
void pickName(Module parentModule) {
assert(_name == null, 'Should only pick a name once.');

_name = uniquifier.getUniqueName(
initialName: module.uniqueInstanceName,
_name = parentModule.namer.allocateName(
module.uniqueInstanceName,
reserved: module.reserveName,
nullStarter: 'm',
);
}

Expand Down
Loading
Loading