Skip to content

Commit

Permalink
Add Logic.named and broaden clone (#550)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkorbel1 authored Jan 21, 2025
1 parent 10f213b commit f26de26
Show file tree
Hide file tree
Showing 7 changed files with 353 additions and 16 deletions.
29 changes: 29 additions & 0 deletions lib/src/signals/logic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,35 @@ class Logic {
naming: naming,
);

/// A cloning utility for [clone] and [named].
Logic _clone({String? name, Naming? naming}) =>
(isNet ? LogicNet.new : Logic.new)(
name: name ?? this.name,
naming: Naming.chooseCloneNaming(
originalName: this.name,
newName: name,
originalNaming: this.naming,
newNaming: naming),
width: width);

/// Makes a copy of `this`, optionally with the specified [name], but the same
/// [width].
Logic clone({String? name}) => _clone(name: name);

/// Makes a [clone] with the provided [name] and optionally [naming], then
/// assigns it to be driven by `this`.
///
/// This is a useful utility for naming the result of some hardware
/// construction without separately declaring a new named signal and then
/// assigning. For example:
///
/// ```dart
/// // named "myImportantNode" instead of a generated name like "a_xor_b"
/// final myImportantNode = (a ^ b).named('myImportantNode');
/// ```
Logic named(String name, {Naming? naming}) =>
_clone(name: name, naming: naming)..gets(this);

/// An internal constructor for [Logic] which additional provides access to
/// setting the [wire].
Logic._({
Expand Down
36 changes: 30 additions & 6 deletions lib/src/signals/logic_array.dart
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,7 @@ class LogicArray extends LogicStructure {
// calculate the next layer's dimensions
final nextDimensions = dimensions.length == 1
? null
: UnmodifiableListView(
dimensions.getRange(1, dimensions.length).toList(growable: false));
: List<int>.unmodifiable(dimensions.getRange(1, dimensions.length));

// if the total width will eventually be 0, then force element width to 0
if (elementWidth != 0 && dimensions.reduce((a, b) => a * b) == 0) {
Expand Down Expand Up @@ -168,7 +167,7 @@ class LogicArray extends LogicStructure {
))
.._arrayIndex = index,
growable: false),
dimensions: UnmodifiableListView(dimensions),
dimensions: List<int>.unmodifiable(dimensions),
elementWidth: elementWidth,
numUnpackedDimensions: numUnpackedDimensions,
name: name,
Expand All @@ -177,13 +176,38 @@ class LogicArray extends LogicStructure {
);
}

@override
LogicArray _clone({String? name, Naming? naming}) => LogicArray._factory(
dimensions,
elementWidth,
name: name ?? this.name,
numUnpackedDimensions: numUnpackedDimensions,
naming: Naming.chooseCloneNaming(
originalName: this.name,
newName: name,
originalNaming: this.naming,
newNaming: naming),
logicBuilder: isNet ? LogicNet.new : Logic.new,
logicArrayBuilder: isNet ? LogicArray.net : LogicArray.new,
isNet: isNet,
);

/// Creates a new [LogicArray] which has the same [dimensions],
/// [elementWidth], [numUnpackedDimensions] as `this`.
/// [elementWidth], [numUnpackedDimensions], and [isNet] as `this`.
///
/// If no new [name] is specified, then it will also have the same name.
@override
LogicArray clone({String? name}) => LogicArray(dimensions, elementWidth,
numUnpackedDimensions: numUnpackedDimensions, name: name ?? this.name);
LogicArray clone({String? name}) => _clone(name: name);

/// Makes a [clone] with the provided [name] and optionally [naming], then
/// assigns it to be driven by `this`.
///
/// This is a useful utility for naming the result of some hardware
/// construction without separately declaring a new named signal and then
/// assigning.
@override
LogicArray named(String name, {Naming? naming}) =>
_clone(name: name, naming: naming)..gets(this);

/// Private constructor for the factory [LogicArray] constructor.
///
Expand Down
29 changes: 23 additions & 6 deletions lib/src/signals/logic_structure.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,29 @@ class LogicStructure implements Logic {
});
}

/// Creates a new [LogicStructure] with the same structure as `this`.
LogicStructure clone({String? name}) => LogicStructure(
elements.map((e) => e is LogicStructure
? e.clone()
: Logic(name: e.name, width: e.width, naming: e.naming)),
name: name ?? this.name);
@override
LogicStructure _clone({String? name, Naming? naming}) =>
// naming is not used for LogicStructure
LogicStructure(elements.map((e) => e.clone(name: e.name)),
name: name ?? this.name);

/// Creates a new [LogicStructure] with the same structure as `this` and
/// [clone]d [elements], optionally with the provided [name].
@override
LogicStructure clone({String? name}) => _clone(name: name);

/// Makes a [clone], optionally with the specified [name], then assigns it to
/// be driven by `this`.
///
/// The [naming] argument will not have any effect on a generic
/// [LogicStructure], but behavior may be overridden by implementers.
///
/// This is a useful utility for naming the result of some hardware
/// construction without separately declaring a new named signal and then
/// assigning.
@override
LogicStructure named(String name, {Naming? naming}) =>
clone(name: name)..gets(this);

@override
String get structureName {
Expand Down
23 changes: 23 additions & 0 deletions lib/src/utilities/naming.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,29 @@ enum Naming {
: Naming.renameable
: Naming.unnamed);

/// Picks a [Naming] for a clone based on its original conditions and
/// optionally provided new conditions.
static Naming chooseCloneNaming({
required String originalName,
required String? newName,
required Naming originalNaming,
required Naming? newNaming,
}) {
if (newNaming != null) {
// if provided, then use that
return newNaming;
}

if (newName == null && newNaming == null) {
// if not provided, we can default to mergeable, since we clone the old
// name and don't necessarily need the duplicate around
return Naming.mergeable;
}

// otherwise, use default
return Naming.chooseNaming(newName, newNaming);
}

/// Picks a [String] name based on an initial [name] and [naming].
///
/// If [name] is null, the name will be based on [nullStarter].
Expand Down
36 changes: 32 additions & 4 deletions test/logic_array_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -957,12 +957,11 @@ void main() {
final mod = WithSetArrayOffsetModule(LogicArray([2, 2], 8));
await testArrayPassthrough(mod, checkNoSwizzle: false);

final sv = mod.generateSynth();

// make sure we're reassigning both times it overlaps!
expect(
RegExp('assign laIn.*=.*swizzled')
.allMatches(mod.generateSynth())
.length,
2);
RegExp(r'assign laOut\[1\].*=.*swizzled').allMatches(sv).length, 2);
});
});

Expand Down Expand Up @@ -1078,4 +1077,33 @@ void main() {
SimCompare.checkIverilogVector(mod, vectors);
});
});

group('array clone', () {
for (final isNet in [true, false]) {
test('isNet = $isNet', () {
final la = (isNet ? LogicArray.net : LogicArray.new)(
[3, 2, 4],
8,
numUnpackedDimensions: 1,
name: 'myarray',
naming: Naming.reserved,
);
final clone = la.clone();
expect(la.dimensions, clone.dimensions);
expect(la.elementWidth, clone.elementWidth);
expect(la.numUnpackedDimensions, clone.numUnpackedDimensions);
expect(la.width, clone.width);
expect(la.elements.length, clone.elements.length);
for (var i = 0; i < la.elements.length; i++) {
expect(la.elements[i].width, clone.elements[i].width);
}
expect(la.name, clone.name);
expect(la.isNet, clone.isNet);
expect(clone.elements[0].elements[1].isNet, isNet);
expect(
clone.elements[1].elements[1].elements[1] is LogicArray, isFalse);
expect(clone.elements[1].elements[1].elements[1].isNet, isNet);
});
}
});
}
Loading

0 comments on commit f26de26

Please sign in to comment.