From f4e9d551be231edfb29f397c0d55d62beea9cbf1 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Mon, 1 May 2023 15:13:36 -0700 Subject: [PATCH 01/75] initial structure for logic structures, and refactoring logicvalue.of --- benchmark/logic_value_of_benchmark.dart | 2 +- .../logic_value_construction_exception.dart | 18 ++ .../logic_value/logic_value_exceptions.dart | 4 + lib/src/logic.dart | 65 +--- lib/src/logic_structure.dart | 286 ++++++++++++++++++ lib/src/modules/bus.dart | 3 +- lib/src/swizzle.dart | 4 +- lib/src/values/big_logic_value.dart | 2 +- lib/src/values/logic_value.dart | 124 +++++++- lib/src/values/small_logic_value.dart | 2 +- lib/src/values/values.dart | 1 + test/logic_value_test.dart | 8 +- test/swizzle_test.dart | 4 +- 13 files changed, 454 insertions(+), 69 deletions(-) create mode 100644 lib/src/exceptions/logic_value/logic_value_construction_exception.dart create mode 100644 lib/src/exceptions/logic_value/logic_value_exceptions.dart create mode 100644 lib/src/logic_structure.dart diff --git a/benchmark/logic_value_of_benchmark.dart b/benchmark/logic_value_of_benchmark.dart index 8041ce075..2c79d4cc3 100644 --- a/benchmark/logic_value_of_benchmark.dart +++ b/benchmark/logic_value_of_benchmark.dart @@ -33,7 +33,7 @@ class LogicValueOfBenchmark extends BenchmarkBase { @override void run() { - LogicValue.of(toOf); + LogicValue.ofIterable(toOf); } } diff --git a/lib/src/exceptions/logic_value/logic_value_construction_exception.dart b/lib/src/exceptions/logic_value/logic_value_construction_exception.dart new file mode 100644 index 000000000..3ad028d8f --- /dev/null +++ b/lib/src/exceptions/logic_value/logic_value_construction_exception.dart @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// logic_value_construction_exception.dart +// An exception that thrown when a signal failes to `put`. +// +// 2023 May 1 +// Author: Max Korbel + +import 'package:rohd/rohd.dart'; +import 'package:rohd/src/exceptions/rohd_exception.dart'; + +/// An exception that thrown when a [LogicValue] cannot be properly constructed. +class LogicValueConstructionException extends RohdException { + /// Creates an exception for when a construction of a `LogicValue` fails. + LogicValueConstructionException(String message) + : super('Failed to construct `LogicValue`: $message'); +} diff --git a/lib/src/exceptions/logic_value/logic_value_exceptions.dart b/lib/src/exceptions/logic_value/logic_value_exceptions.dart new file mode 100644 index 000000000..5007c4d6d --- /dev/null +++ b/lib/src/exceptions/logic_value/logic_value_exceptions.dart @@ -0,0 +1,4 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause + +export 'logic_value_construction_exception.dart'; diff --git a/lib/src/logic.dart b/lib/src/logic.dart index 0bdc182dc..116ccbfad 100644 --- a/lib/src/logic.dart +++ b/lib/src/logic.dart @@ -208,62 +208,18 @@ class _Wire { /// Keeps track of whether there is an active put, to detect reentrance. bool _isPutting = false; + //TODO: update to reference `LogicValue.of`. + /// Puts a value [val] onto this signal, which may or may not be picked up /// for [changed] in this [Simulator] tick. /// - /// The type of [val] should be an `int`, [BigInt], `bool`, or [LogicValue]. + /// The type of [val] and usage of [fill] should be supported by + /// [LogicValue.of]. /// /// This function is used for propogating glitches through connected signals. /// Use this function for custom definitions of [Module] behavior. - /// - /// If [fill] is set, all bits of the signal gets set to [val], similar - /// to `'` in SystemVerilog. void put(dynamic val, {required String signalName, bool fill = false}) { - LogicValue newValue; - if (val is int) { - if (fill) { - newValue = LogicValue.filled( - width, - val == 0 - ? LogicValue.zero - : val == 1 - ? LogicValue.one - : throw PutException( - signalName, 'Only can fill 0 or 1, but saw $val.')); - } else { - newValue = LogicValue.ofInt(val, width); - } - } else if (val is BigInt) { - if (fill) { - newValue = LogicValue.filled( - width, - val == BigInt.zero - ? LogicValue.zero - : val == BigInt.one - ? LogicValue.one - : throw PutException( - signalName, 'Only can fill 0 or 1, but saw $val.')); - } else { - newValue = LogicValue.ofBigInt(val, width); - } - } else if (val is bool) { - newValue = LogicValue.ofInt(val ? 1 : 0, width); - } else if (val is LogicValue) { - if (val.width == 1 && - (val == LogicValue.x || val == LogicValue.z || fill)) { - newValue = LogicValue.filled(width, val); - } else if (fill) { - throw PutException(signalName, - 'Failed to fill value with $val. To fill, it should be 1 bit.'); - } else { - newValue = val; - } - } else { - throw PutException( - signalName, - 'Unrecognized value "$val" to deposit on this signal. ' - 'Unknown type ${val.runtimeType} cannot be deposited.'); - } + var newValue = LogicValue.of(val, fill: fill, width: width); if (newValue.width != width) { throw PutException(signalName, @@ -598,7 +554,8 @@ class Logic { /// A function that returns whatever [Logic] is provided. /// /// Used as a default no-op for short-hands. - static Logic _nopS(Logic x) => x; + @protected + static Logic nopS(Logic x) => x; /// Shorthand for a [Conditional] which increments this by [val]. /// @@ -620,7 +577,7 @@ class Logic { /// ]); /// /// ``` - ConditionalAssign incr({Logic Function(Logic) s = _nopS, dynamic val = 1}) => + ConditionalAssign incr({Logic Function(Logic) s = nopS, dynamic val = 1}) => s(this) < s(this) + val; /// Shorthand for a [Conditional] which decrements this by [val]. @@ -643,7 +600,7 @@ class Logic { /// ]); /// /// ``` - ConditionalAssign decr({Logic Function(Logic) s = _nopS, dynamic val = 1}) => + ConditionalAssign decr({Logic Function(Logic) s = nopS, dynamic val = 1}) => s(this) < s(this) - val; /// Shorthand for a [Conditional] which increments this by [val]. @@ -665,7 +622,7 @@ class Logic { /// ]); /// /// ``` - ConditionalAssign mulAssign({Logic Function(Logic) s = _nopS, dynamic val}) => + ConditionalAssign mulAssign({Logic Function(Logic) s = nopS, dynamic val}) => s(this) < s(this) * val; /// Shorthand for a [Conditional] which increments this by [val]. @@ -687,7 +644,7 @@ class Logic { /// ]); /// /// ``` - ConditionalAssign divAssign({Logic Function(Logic) s = _nopS, dynamic val}) => + ConditionalAssign divAssign({Logic Function(Logic) s = nopS, dynamic val}) => s(this) < s(this) / val; /// Conditional assignment operator. diff --git a/lib/src/logic_structure.dart b/lib/src/logic_structure.dart new file mode 100644 index 000000000..70d9f4174 --- /dev/null +++ b/lib/src/logic_structure.dart @@ -0,0 +1,286 @@ +import 'dart:collection'; + +import 'package:rohd/rohd.dart'; +import 'package:rohd/src/utilities/synchronous_propagator.dart'; + +class LogicStructure implements Logic { + /// All components of this structure. + late final List components = UnmodifiableListView(_components); + final List _components = []; + + /// Packs all [components] into one flattened bus. + late final Logic packed = components + .map((e) { + if (e is LogicStructure) { + return e.packed; + } else { + return e; + } + }) + .toList() + .swizzle(); + + /// Creates a new [LogicStructure] with [components] as elements. + LogicStructure(List components) { + _components.addAll(components); + } + + //TODO: dimension List + + /////////////////////////////////////////////// + /////////////////////////////////////////////// + /////////////////////////////////////////////// + /////////////////////////////////////////////// + + @override + void put(dynamic val, {bool fill = false}) { + var index = 0; + for (final component in components) { + component.put(); + index += component.width; + } + } + + @override + Module? get parentModule => + throw Exception('Cannot access parent of a structure'); //TODO + @override + set parentModule(Module? newParentModule) => + throw Exception('Cannot access parent of a structure'); //TODO + + @override + ConditionalAssign operator <(other) { + // TODO: implement < + throw UnimplementedError(); + } + + @override + void operator <=(Logic other) { + // TODO: implement <= + } + + @override + Logic operator [](index) { + // TODO: implement [] + throw UnimplementedError(); + //TODO: should this still return just 1 bit or no? + } + + @override + Logic getRange(int startIndex, [int? endIndex]) { + // TODO: implement getRange + throw UnimplementedError(); + } + + @override + //TODO + Logic slice(int endIndex, int startIndex) => + packed.slice(endIndex, startIndex); + + @override + ConditionalAssign decr({Logic Function(Logic p1) s = Logic.nopS, val = 1}) { + // TODO: implement decr + throw UnimplementedError(); + } + + @override + ConditionalAssign divAssign({Logic Function(Logic p1) s = Logic.nopS, val}) { + // TODO: implement divAssign + throw UnimplementedError(); + } + + @override + ConditionalAssign mulAssign({Logic Function(Logic p1) s = Logic.nopS, val}) { + // TODO: implement mulAssign + throw UnimplementedError(); + } + + @override + ConditionalAssign incr({Logic Function(Logic p1) s = Logic.nopS, val = 1}) { + // TODO: implement incr + throw UnimplementedError(); + } + + @override + // TODO: implement dstConnections + Iterable get dstConnections => throw UnimplementedError(); + + @override + void gets(Logic other) { + // TODO: implement gets + } + + @override + void inject(val, {bool fill = false}) { + // TODO: implement inject + } + + @override + // TODO: implement isInput + bool get isInput => throw UnimplementedError(); + + @override + // TODO: implement isOutput + bool get isOutput => throw UnimplementedError(); + + @override + // TODO: implement isPort + bool get isPort => throw UnimplementedError(); + + @override + void makeUnassignable() { + // TODO: implement makeUnassignable + } + + @override + // TODO: implement name + String get name => throw UnimplementedError(); + + @override + // TODO: implement srcConnection + Logic? get srcConnection => throw UnimplementedError(); + + @override + // TODO: implement value + LogicValue get value => throw UnimplementedError(); + + @override + // TODO: implement width + int get width => throw UnimplementedError(); + + @override + Logic withSet(int startIndex, Logic update) { + // TODO: implement withSet + throw UnimplementedError(); + } + + ///////////////////////////////////////////////// + ///////////////////////////////////////////////// + ///////////////////////////////////////////////// + ///////////////////////////////////////////////// + + @override + Logic operator ~() => ~packed; + + @override + Logic operator &(Logic other) => packed & other; + + @override + Logic operator |(Logic other) => packed | other; + + @override + Logic operator ^(Logic other) => packed ^ other; + + @override + Logic operator *(dynamic other) => packed * other; + + @override + Logic operator +(dynamic other) => packed + other; + + @override + Logic operator -(dynamic other) => packed - other; + + @override + Logic operator /(dynamic other) => packed / other; + + @override + Logic operator %(dynamic other) => packed % other; + + @override + Logic operator <<(dynamic other) => packed << other; + + @override + Logic operator >(dynamic other) => packed > other; + + @override + Logic operator >=(dynamic other) => packed >= other; + + @override + Logic operator >>(dynamic other) => packed >> other; + + @override + Logic operator >>>(dynamic other) => packed >>> other; + + @override + Logic and() => packed.and(); + + @override + Logic or() => packed.or(); + + @override + Logic xor() => packed.xor(); + + @override + // ignore: deprecated_member_use_from_same_package + LogicValue get bit => packed.bit; + + @override + Stream get changed => packed.changed; + + @override + Logic eq(dynamic other) => packed.eq(other); + + @override + Logic neq(dynamic other) => packed.neq(other); + + @override + SynchronousEmitter get glitch => packed.glitch; + + @override + Logic gt(dynamic other) => packed.gt(other); + + @override + Logic gte(dynamic other) => packed.gte(other); + + @override + Logic lt(dynamic other) => packed.lt(other); + + @override + Logic lte(dynamic other) => packed.lte(other); + + @override + // ignore: deprecated_member_use_from_same_package + bool hasValidValue() => packed.hasValidValue(); + + @override + // ignore: deprecated_member_use_from_same_package + bool isFloating() => packed.isFloating(); + + @override + Logic isIn(List list) => packed.isIn(list); + + @override + Stream get negedge => packed.negedge; + + @override + Stream get posedge => packed.posedge; + + @override + Future get nextChanged => packed.nextChanged; + + @override + Future get nextNegedge => packed.nextNegedge; + + @override + Future get nextPosedge => packed.nextPosedge; + + @override + Logic replicate(int multiplier) => packed.replicate(multiplier); + + @override + Logic get reversed => packed.reversed; + + @override + Logic signExtend(int newWidth) => packed.signExtend(newWidth); + + @override + Logic zeroExtend(int newWidth) => packed.zeroExtend(newWidth); + + @override + // ignore: deprecated_member_use_from_same_package + BigInt get valueBigInt => packed.valueBigInt; + + @override + // ignore: deprecated_member_use_from_same_package + int get valueInt => packed.valueInt; +} diff --git a/lib/src/modules/bus.dart b/lib/src/modules/bus.dart index 315077759..b80ca6e65 100644 --- a/lib/src/modules/bus.dart +++ b/lib/src/modules/bus.dart @@ -159,7 +159,8 @@ class Swizzle extends Module with InlineSystemVerilog { /// Executes the functional behavior of this gate. void _execute() { - final updatedVal = LogicValue.of(_swizzleInputs.map((e) => e.value)); + final updatedVal = + LogicValue.ofIterable(_swizzleInputs.map((e) => e.value)); out.put(updatedVal); } diff --git a/lib/src/swizzle.dart b/lib/src/swizzle.dart index 4bd86872f..47a2781c6 100644 --- a/lib/src/swizzle.dart +++ b/lib/src/swizzle.dart @@ -46,7 +46,7 @@ extension LogicValueSwizzle on List { /// most significant (highest) bits. /// /// If you want the opposite, check out [rswizzle]. - LogicValue swizzle() => LogicValue.of(reversed); + LogicValue swizzle() => LogicValue.ofIterable(reversed); /// Performs a concatenation operation on the list of signals, where index 0 /// of this list is the *least* significant bit. @@ -57,7 +57,7 @@ extension LogicValueSwizzle on List { /// least significant (lowest) bits. /// /// If you want the opposite, check out [swizzle]. - LogicValue rswizzle() => LogicValue.of(this); + LogicValue rswizzle() => LogicValue.ofIterable(this); } /// Performs a concatenation operation on the list of signals, where index 0 of diff --git a/lib/src/values/big_logic_value.dart b/lib/src/values/big_logic_value.dart index 18d1f6c7f..43845638a 100644 --- a/lib/src/values/big_logic_value.dart +++ b/lib/src/values/big_logic_value.dart @@ -110,7 +110,7 @@ class _BigLogicValue extends LogicValue { } @override - LogicValue get reversed => LogicValue.of(toList().reversed); + LogicValue get reversed => LogicValue.ofIterable(toList().reversed); @override bool get isValid => _invalid.sign == 0; diff --git a/lib/src/values/logic_value.dart b/lib/src/values/logic_value.dart index 4fab13bb2..df862f7c4 100644 --- a/lib/src/values/logic_value.dart +++ b/lib/src/values/logic_value.dart @@ -115,6 +115,124 @@ abstract class LogicValue { : throw Exception('Failed to convert.'); } + /// Constructs a [LogicValue] from [val] which could be of a variety of types. + /// + /// Supported types include [String], [bool], [int], [BigInt], and + /// [LogicValue]. + /// + /// If [fill] is set, then all bits of the returne value will be set to [val]. + /// If the [val] is not representable as a single bit of information, then + /// setting [fill] will throw an [Exception]. + /// + /// If the [width] can be inferred from the type ([String] or [LogicValue]), + /// then [width] does not need to be provided. If [width] is provided and + /// does not match an inferred width, an [Exception] will be thrown. If a + /// [width] cannot be inferred, then it is required, or else it will throw + /// an [Exception]. If [val] does not fit in a specified [width] and [width] + /// cannot be inferred, then the returned value will be truncated. [bool]s + /// will infer a default width of `1`, but it can be overridden. Invalid + /// 1-bit [val]s will always be [fill]ed and must provide a [width], + /// even if [fill] is `false`. + static LogicValue of(dynamic val, {bool fill = false, int? width}) { + if (val is int) { + if (width == null) { + throw LogicValueConstructionException( + '`width` must be provided for `int`.'); + } + + if (fill) { + return LogicValue.filled( + width, + val == 0 + ? LogicValue.zero + : val == 1 + ? LogicValue.one + : throw LogicValueConstructionException( + '`int` can only can fill 0 or 1, but saw $val.')); + } else { + return LogicValue.ofInt(val, width); + } + } else if (val is BigInt) { + if (width == null) { + throw LogicValueConstructionException( + '`width` must be provided for `BigInt`.'); + } + + if (fill) { + return LogicValue.filled( + width, + val == BigInt.zero + ? LogicValue.zero + : val == BigInt.one + ? LogicValue.one + : throw LogicValueConstructionException( + '`BigInt` can only fill 0 or 1, but saw $val.')); + } else { + return LogicValue.ofBigInt(val, width); + } + } else if (val is bool) { + width ??= 1; + + if (fill) { + return LogicValue.filled(width, val ? LogicValue.one : LogicValue.zero); + } + return LogicValue.ofInt(val ? 1 : 0, width); + } else if (val is LogicValue) { + if (width != null && width != val.width) { + throw LogicValueConstructionException( + 'Inferred width of `val` (${val.width})' + ' is not equal to provided `width` ($width)'); + } + + if (fill && val.width != 1) { + throw LogicValueConstructionException( + 'Only 1-bit `LogicValue`s can be filled'); + } + + if (val.width == 1 && + (val == LogicValue.x || val == LogicValue.z || fill)) { + if (width == null) { + throw LogicValueConstructionException( + 'Filled `LogicValue` $val must have provided a width.'); + } + return LogicValue.filled(width, val); + } else if (fill) { + throw LogicValueConstructionException( + 'Failed to fill value with $val. To fill, it should be 1 bit.'); + } else { + return val; + } + } else if (val is String) { + if (width != null && width != val.length) { + throw LogicValueConstructionException( + 'Inferred width of `val` (${val.length})' + ' is not equal to provided `width` ($width)'); + } + + if (fill && val.length != 1) { + throw LogicValueConstructionException( + 'Only 1-bit values can be filled'); + } + + if (val.length == 1 && (val == 'x' || val == 'z' || fill)) { + if (width == null) { + throw LogicValueConstructionException( + 'Filled `String` $val must have provided a width.'); + } + return LogicValue.filled(width, LogicValue.ofString(val)); + } else if (fill) { + throw LogicValueConstructionException( + 'Failed to fill value with $val. To fill, it should be 1 bit.'); + } else { + return LogicValue.ofString(val); + } + } else { + throw LogicValueConstructionException('Unrecognized value type "$val" - ' + 'Unknown type ${val.runtimeType}' + ' cannot be converted to `LogicValue.'); + } + } + /// Constructs a [LogicValue] from [it]. /// /// The order of the created [LogicValue] will be such that the `i`th entry in @@ -129,7 +247,7 @@ abstract class LogicValue { /// var lv = LogicValue.of(it); /// print(lv); // This prints `6'b01xzx0` /// ``` - static LogicValue of(Iterable it) { + static LogicValue ofIterable(Iterable it) { var smallBuffer = LogicValue.empty; var fullResult = LogicValue.empty; @@ -212,7 +330,7 @@ abstract class LogicValue { /// print(lv); // This prints `3b'1x0` /// ``` @Deprecated('Use `of` instead.') - static LogicValue from(Iterable it) => of(it); + static LogicValue from(Iterable it) => ofIterable(it); /// Returns true if bits in [x] are all 0 static bool _bigIntIs0(BigInt x, int width) => @@ -984,7 +1102,7 @@ abstract class LogicValue { throw InvalidMultiplierException(multiplier); } - return LogicValue.of(List.filled(multiplier, this)); + return LogicValue.ofIterable(List.filled(multiplier, this)); } } diff --git a/lib/src/values/small_logic_value.dart b/lib/src/values/small_logic_value.dart index d461ff72a..b8c133579 100644 --- a/lib/src/values/small_logic_value.dart +++ b/lib/src/values/small_logic_value.dart @@ -94,7 +94,7 @@ class _SmallLogicValue extends LogicValue { } @override - LogicValue get reversed => LogicValue.of(toList().reversed); + LogicValue get reversed => LogicValue.ofIterable(toList().reversed); @override bool get isValid => _invalid == 0; diff --git a/lib/src/values/values.dart b/lib/src/values/values.dart index 1877b2770..478f705d1 100644 --- a/lib/src/values/values.dart +++ b/lib/src/values/values.dart @@ -6,6 +6,7 @@ library values; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd/src/exceptions/exceptions.dart'; +import 'package:rohd/src/exceptions/logic_value/logic_value_exceptions.dart'; part 'logic_value.dart'; part 'small_logic_value.dart'; diff --git a/test/logic_value_test.dart b/test/logic_value_test.dart index 5052809c7..ba0482957 100644 --- a/test/logic_value_test.dart +++ b/test/logic_value_test.dart @@ -262,15 +262,15 @@ void main() { equals(LogicValue.ofString('01xx10xxxxxxxxxx'))); expect( // test from Iterable - LogicValue.of([LogicValue.one, LogicValue.zero]) ^ - LogicValue.of([LogicValue.one, LogicValue.zero]), - equals(LogicValue.of([LogicValue.zero, LogicValue.zero]))); + LogicValue.ofIterable([LogicValue.one, LogicValue.zero]) ^ + LogicValue.ofIterable([LogicValue.one, LogicValue.zero]), + equals(LogicValue.ofIterable([LogicValue.zero, LogicValue.zero]))); }); }); test('LogicValue.of example', () { final it = [LogicValue.zero, LogicValue.x, LogicValue.ofString('01xz')]; - final lv = LogicValue.of(it); + final lv = LogicValue.ofIterable(it); expect(lv.toString(), equals("6'b01xzx0")); }); diff --git a/test/swizzle_test.dart b/test/swizzle_test.dart index 556dc36fd..af1cf6fd5 100644 --- a/test/swizzle_test.dart +++ b/test/swizzle_test.dart @@ -83,7 +83,7 @@ void main() { (index) => bits[index % bits.length] * (index % 17) + bits[(index + 1) % bits.length] * (index % 2)); - expect(LogicValue.of(swizzleStrings.map(LogicValue.ofString)), + expect(LogicValue.ofIterable(swizzleStrings.map(LogicValue.ofString)), equals(LogicValue.ofString(swizzleStrings.reversed.join()))); }); @@ -94,7 +94,7 @@ void main() { (index) => bits[index % bits.length] * (index % 71) + bits[(index + 1) % bits.length] * (index % 2)); - expect(LogicValue.of(swizzleStrings.map(LogicValue.ofString)), + expect(LogicValue.ofIterable(swizzleStrings.map(LogicValue.ofString)), equals(LogicValue.ofString(swizzleStrings.reversed.join()))); }); }); From a8949f158d97909d9eb46472e3d627f31eb27c0f Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Mon, 1 May 2023 16:21:40 -0700 Subject: [PATCH 02/75] mostly implemented, untested --- example/fir_filter.dart | 2 +- lib/src/logic.dart | 19 ++-- lib/src/logic_structure.dart | 165 +++++++++++++++++++------------ lib/src/modules/conditional.dart | 48 +++++++++ test/sequential_test.dart | 2 +- 5 files changed, 159 insertions(+), 77 deletions(-) diff --git a/example/fir_filter.dart b/example/fir_filter.dart index abbbfe7d9..1212b4eef 100644 --- a/example/fir_filter.dart +++ b/example/fir_filter.dart @@ -71,7 +71,7 @@ class FirFilter extends Module { ], orElse: [out < 0] + // Set all 'z' to zero. - List.generate(depth, (index) => z[index] < 0)) + List.generate(depth, (index) => z[index] < 0)) ]); } } diff --git a/lib/src/logic.dart b/lib/src/logic.dart index 116ccbfad..ed743d987 100644 --- a/lib/src/logic.dart +++ b/lib/src/logic.dart @@ -37,10 +37,7 @@ class LogicValueChanged { class Const extends Logic { /// Constructs a [Const] with the specified value. /// - /// If [val] is a [LogicValue], the [width] is inferred from it. - /// Otherwise, if [width] is not specified, the default [width] is 1. - /// If [fill] is set to `true`, the value is extended across - /// [width] (like `'` in SystemVerilog). + /// [val] should be processable by [LogicValue.of]. Const(dynamic val, {int? width, bool fill = false}) : super( name: 'const_$val', @@ -370,13 +367,13 @@ class Logic { /// /// Note: [parentModule] is not populated until after its parent [Module], /// if it exists, has been built. If no parent [Module] exists, returns false. - bool get isInput => _parentModule?.isInput(this) ?? false; + bool get isInput => parentModule?.isInput(this) ?? false; /// Returns true iff this signal is an output of its parent [Module]. /// /// Note: [parentModule] is not populated until after its parent [Module], /// if it exists, has been built. If no parent [Module] exists, returns false. - bool get isOutput => _parentModule?.isOutput(this) ?? false; + bool get isOutput => parentModule?.isOutput(this) ?? false; /// Returns true iff this signal is an input or output of its parent [Module]. /// @@ -577,7 +574,7 @@ class Logic { /// ]); /// /// ``` - ConditionalAssign incr({Logic Function(Logic) s = nopS, dynamic val = 1}) => + Conditional incr({Logic Function(Logic) s = nopS, dynamic val = 1}) => s(this) < s(this) + val; /// Shorthand for a [Conditional] which decrements this by [val]. @@ -600,7 +597,7 @@ class Logic { /// ]); /// /// ``` - ConditionalAssign decr({Logic Function(Logic) s = nopS, dynamic val = 1}) => + Conditional decr({Logic Function(Logic) s = nopS, dynamic val = 1}) => s(this) < s(this) - val; /// Shorthand for a [Conditional] which increments this by [val]. @@ -622,7 +619,7 @@ class Logic { /// ]); /// /// ``` - ConditionalAssign mulAssign({Logic Function(Logic) s = nopS, dynamic val}) => + Conditional mulAssign({Logic Function(Logic) s = nopS, dynamic val}) => s(this) < s(this) * val; /// Shorthand for a [Conditional] which increments this by [val]. @@ -644,7 +641,7 @@ class Logic { /// ]); /// /// ``` - ConditionalAssign divAssign({Logic Function(Logic) s = nopS, dynamic val}) => + Conditional divAssign({Logic Function(Logic) s = nopS, dynamic val}) => s(this) < s(this) / val; /// Conditional assignment operator. @@ -652,7 +649,7 @@ class Logic { /// Represents conditionally asigning the value of another signal to this. /// Returns an instance of [ConditionalAssign] to be be passed to a /// [Conditional]. - ConditionalAssign operator <(dynamic other) { + Conditional operator <(dynamic other) { if (_unassignable) { throw Exception('This signal "$this" has been marked as unassignable. ' 'It may be a constant expression or otherwise' diff --git a/lib/src/logic_structure.dart b/lib/src/logic_structure.dart index 70d9f4174..bce3e0bef 100644 --- a/lib/src/logic_structure.dart +++ b/lib/src/logic_structure.dart @@ -1,6 +1,9 @@ import 'dart:collection'; +import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; +import 'package:rohd/src/exceptions/module/module_exceptions.dart'; +import 'package:rohd/src/utilities/sanitizer.dart'; import 'package:rohd/src/utilities/synchronous_propagator.dart'; class LogicStructure implements Logic { @@ -20,12 +23,21 @@ class LogicStructure implements Logic { .toList() .swizzle(); + @override + final String name; + + /// An internal counter for encouraging unique naming of unnamed signals. + static int _structIdx = 0; + /// Creates a new [LogicStructure] with [components] as elements. - LogicStructure(List components) { + LogicStructure(List components, {String? name}) + : name = (name == null || name.isEmpty) + ? 'st${_structIdx++}' + : Sanitizer.sanitizeSV(name) { _components.addAll(components); } - //TODO: dimension List + //TODO: dimension List (only on array?) /////////////////////////////////////////////// /////////////////////////////////////////////// @@ -34,125 +46,150 @@ class LogicStructure implements Logic { @override void put(dynamic val, {bool fill = false}) { + final logicVal = LogicValue.of(val, fill: fill, width: width); + var index = 0; for (final component in components) { - component.put(); + component.put(logicVal.getRange(index, index + component.width)); index += component.width; } } @override - Module? get parentModule => - throw Exception('Cannot access parent of a structure'); //TODO - @override - set parentModule(Module? newParentModule) => - throw Exception('Cannot access parent of a structure'); //TODO + void inject(val, {bool fill = false}) { + final logicVal = LogicValue.of(val, fill: fill, width: width); - @override - ConditionalAssign operator <(other) { - // TODO: implement < - throw UnimplementedError(); + var index = 0; + for (final component in components) { + component.inject(logicVal.getRange(index, index + component.width)); + index += component.width; + } } @override - void operator <=(Logic other) { - // TODO: implement <= + Conditional operator <(dynamic other) { + final otherLogic = other is Logic ? other : Const(other, width: width); + + if (otherLogic.width != width) { + throw PortWidthMismatchException(otherLogic, width); + } + + final conditionalAssigns = []; + + var index = 0; + for (final component in components) { + conditionalAssigns + .add(component < otherLogic.getRange(index, index + component.width)); + index += component.width; + } + + return ConditionalGroup(conditionalAssigns); } @override - Logic operator [](index) { - // TODO: implement [] - throw UnimplementedError(); - //TODO: should this still return just 1 bit or no? + void gets(Logic other) { + if (other.width != width) { + throw PortWidthMismatchException(other, width); + } + + var index = 0; + for (final component in components) { + component <= other.getRange(index, index + component.width); + index += component.width; + } } @override - Logic getRange(int startIndex, [int? endIndex]) { - // TODO: implement getRange - throw UnimplementedError(); - } + void operator <=(Logic other) => gets(other); + + @override + Logic operator [](dynamic index) => packed[index]; + + @override + Logic getRange(int startIndex, [int? endIndex]) => + packed.getRange(startIndex, endIndex); @override - //TODO Logic slice(int endIndex, int startIndex) => packed.slice(endIndex, startIndex); + /// Increments each component of [components] using [Logic.incr]. @override - ConditionalAssign decr({Logic Function(Logic p1) s = Logic.nopS, val = 1}) { - // TODO: implement decr - throw UnimplementedError(); - } + Conditional incr( + {Logic Function(Logic p1) s = Logic.nopS, dynamic val = 1}) => + ConditionalGroup([ + for (final component in components) component.incr(s: s, val: val), + ]); + /// Decrements each component of [components] using [Logic.decr]. @override - ConditionalAssign divAssign({Logic Function(Logic p1) s = Logic.nopS, val}) { - // TODO: implement divAssign - throw UnimplementedError(); - } + Conditional decr( + {Logic Function(Logic p1) s = Logic.nopS, dynamic val = 1}) => + ConditionalGroup([ + for (final component in components) component.decr(s: s, val: val), + ]); + /// Divide-assigns each component of [components] using [Logic.divAssign]. @override - ConditionalAssign mulAssign({Logic Function(Logic p1) s = Logic.nopS, val}) { - // TODO: implement mulAssign - throw UnimplementedError(); - } + Conditional divAssign( + {Logic Function(Logic p1) s = Logic.nopS, dynamic val}) => + ConditionalGroup([ + for (final component in components) component.divAssign(s: s, val: val), + ]); + /// Multiply-assigns each component of [components] using [Logic.mulAssign]. @override - ConditionalAssign incr({Logic Function(Logic p1) s = Logic.nopS, val = 1}) { - // TODO: implement incr - throw UnimplementedError(); - } + Conditional mulAssign( + {Logic Function(Logic p1) s = Logic.nopS, dynamic val}) => + ConditionalGroup([ + for (final component in components) component.mulAssign(s: s, val: val), + ]); @override - // TODO: implement dstConnections - Iterable get dstConnections => throw UnimplementedError(); + late final Iterable dstConnections = [ + for (final component in components) ...component.dstConnections + ]; + + //TODO: is this safe to have a separate tracking here? + Module? _parentModule; @override - void gets(Logic other) { - // TODO: implement gets - } + Module? get parentModule => _parentModule; @override - void inject(val, {bool fill = false}) { - // TODO: implement inject - } + set parentModule(Module? newParentModule) => _parentModule = newParentModule; @override - // TODO: implement isInput - bool get isInput => throw UnimplementedError(); + bool get isInput => parentModule?.isInput(this) ?? false; @override // TODO: implement isOutput - bool get isOutput => throw UnimplementedError(); + bool get isOutput => parentModule?.isOutput(this) ?? false; @override // TODO: implement isPort - bool get isPort => throw UnimplementedError(); + bool get isPort => isInput || isOutput; @override void makeUnassignable() { - // TODO: implement makeUnassignable + for (final component in components) { + component.makeUnassignable(); + } } - @override - // TODO: implement name - String get name => throw UnimplementedError(); - @override // TODO: implement srcConnection Logic? get srcConnection => throw UnimplementedError(); @override - // TODO: implement value - LogicValue get value => throw UnimplementedError(); + LogicValue get value => packed.value; @override - // TODO: implement width - int get width => throw UnimplementedError(); + int get width => packed.width; @override - Logic withSet(int startIndex, Logic update) { - // TODO: implement withSet - throw UnimplementedError(); - } + Logic withSet(int startIndex, Logic update) => + packed.withSet(startIndex, update); ///////////////////////////////////////////////// ///////////////////////////////////////////////// diff --git a/lib/src/modules/conditional.dart b/lib/src/modules/conditional.dart index 47f4da62b..cefc5832b 100644 --- a/lib/src/modules/conditional.dart +++ b/lib/src/modules/conditional.dart @@ -642,6 +642,54 @@ abstract class Conditional { {required int context}); } +/// Represents a group of [Conditional]s to be executed. +class ConditionalGroup extends Conditional { + final List conditionals; + ConditionalGroup(this.conditionals); + + @override + Map _processSsa(Map currentMappings, + {required int context}) { + var mappings = currentMappings; + for (final conditional in conditionals) { + mappings = conditional._processSsa(mappings, context: context); + } + return mappings; + } + + @override + late final List drivers = [ + for (final conditional in conditionals) ...conditional.drivers + ]; + + @override + late final List receivers = [ + for (final conditional in conditionals) ...conditional.receivers + ]; + + @override + void execute(Set drivenSignals, void Function(Logic toGuard)? guard) { + for (final conditional in conditionals) { + conditional.execute(drivenSignals, guard); + } + } + + @override + String verilogContents(int indent, Map inputsNameMap, + Map outputsNameMap, String assignOperator) { + final verilog = StringBuffer(); + for (final conditional in conditionals) { + verilog.write(conditional.verilogContents( + indent, + inputsNameMap, + outputsNameMap, + assignOperator, + )); + } + return verilog.toString(); + } +} + /// An assignment that only happens under certain conditions. /// /// [Logic] has a short-hand for creating [ConditionalAssign] via the diff --git a/test/sequential_test.dart b/test/sequential_test.dart index 2cdb93191..a0a0ae348 100644 --- a/test/sequential_test.dart +++ b/test/sequential_test.dart @@ -28,7 +28,7 @@ class DelaySignal extends Module { final out = addOutput('out', width: bitWidth); - final zList = [z[0] < inputVal]; + final zList = [z[0] < inputVal]; for (var i = 0; i < z.length; i++) { if (i == z.length - 1) { zList.add(out < z[i]); From e7c215e7017f5eb5f052c84de562aa58ba65e90b Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Mon, 1 May 2023 16:37:17 -0700 Subject: [PATCH 03/75] fix some tests, allow put to truncate or extend --- lib/src/values/logic_value.dart | 34 ++++++++++++++++----------------- test/bus_test.dart | 17 ++++++++++------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/lib/src/values/logic_value.dart b/lib/src/values/logic_value.dart index df862f7c4..0a1d96039 100644 --- a/lib/src/values/logic_value.dart +++ b/lib/src/values/logic_value.dart @@ -126,10 +126,10 @@ abstract class LogicValue { /// /// If the [width] can be inferred from the type ([String] or [LogicValue]), /// then [width] does not need to be provided. If [width] is provided and - /// does not match an inferred width, an [Exception] will be thrown. If a + /// does not match an inferred width, then [width] is used. If a /// [width] cannot be inferred, then it is required, or else it will throw - /// an [Exception]. If [val] does not fit in a specified [width] and [width] - /// cannot be inferred, then the returned value will be truncated. [bool]s + /// an [Exception]. If [val] does not fit in a specified [width], + /// then the returned value will be truncated. [bool]s /// will infer a default width of `1`, but it can be overridden. Invalid /// 1-bit [val]s will always be [fill]ed and must provide a [width], /// even if [fill] is `false`. @@ -178,12 +178,6 @@ abstract class LogicValue { } return LogicValue.ofInt(val ? 1 : 0, width); } else if (val is LogicValue) { - if (width != null && width != val.width) { - throw LogicValueConstructionException( - 'Inferred width of `val` (${val.width})' - ' is not equal to provided `width` ($width)'); - } - if (fill && val.width != 1) { throw LogicValueConstructionException( 'Only 1-bit `LogicValue`s can be filled'); @@ -200,15 +194,15 @@ abstract class LogicValue { throw LogicValueConstructionException( 'Failed to fill value with $val. To fill, it should be 1 bit.'); } else { - return val; + if (val.width == width || width == null) { + return val; + } else if (width < val.width) { + return val.getRange(0, width); + } else { + return val.zeroExtend(width); + } } } else if (val is String) { - if (width != null && width != val.length) { - throw LogicValueConstructionException( - 'Inferred width of `val` (${val.length})' - ' is not equal to provided `width` ($width)'); - } - if (fill && val.length != 1) { throw LogicValueConstructionException( 'Only 1-bit values can be filled'); @@ -224,7 +218,13 @@ abstract class LogicValue { throw LogicValueConstructionException( 'Failed to fill value with $val. To fill, it should be 1 bit.'); } else { - return LogicValue.ofString(val); + if (val.length == width || width == null) { + return LogicValue.ofString(val); + } else if (width < val.length) { + return LogicValue.ofString(val.substring(0, width)); + } else { + return LogicValue.ofString(val).zeroExtend(width); + } } } else { throw LogicValueConstructionException('Unrecognized value type "$val" - ' diff --git a/test/bus_test.dart b/test/bus_test.dart index 8e5f7373c..c0d2da028 100644 --- a/test/bus_test.dart +++ b/test/bus_test.dart @@ -260,13 +260,16 @@ void main() { expect(out.value.toInt(), equals(0x55aa)); }); - group('put exceptions', () { - test('width mismatch', () { - expect( - () => Logic(name: 'byteSignal', width: 8) - .put(LogicValue.ofString('1010')), - throwsA(const TypeMatcher()), - ); + group('put width mismatches', () { + test('width too small', () { + final smallVal = LogicValue.ofString('1010'); + final byteSignal = Logic(name: 'byteSignal', width: 8)..put(smallVal); + expect(byteSignal.value, smallVal.zeroExtend(8)); + }); + test('width too big', () { + final bigVal = LogicValue.ofString('1010101010'); + final byteSignal = Logic(name: 'byteSignal', width: 8)..put(bigVal); + expect(byteSignal.value, bigVal.getRange(0, 8)); }); }); }); From cf5d9cd8051b71fc1b861fe4968c0f2173759e98 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 2 May 2023 08:34:33 -0700 Subject: [PATCH 04/75] started moving files around, started logic array --- lib/rohd.dart | 2 +- .../module/port_width_mismatch_exception.dart | 6 ++-- lib/src/{ => signals}/logic.dart | 0 lib/src/signals/logic_array.dart | 32 +++++++++++++++++++ lib/src/{ => signals}/logic_structure.dart | 11 ++++++- lib/src/signals/signals.dart | 6 ++++ 6 files changed, 53 insertions(+), 4 deletions(-) rename lib/src/{ => signals}/logic.dart (100%) create mode 100644 lib/src/signals/logic_array.dart rename lib/src/{ => signals}/logic_structure.dart (96%) create mode 100644 lib/src/signals/signals.dart diff --git a/lib/rohd.dart b/lib/rohd.dart index bee7965e8..fb8e40046 100644 --- a/lib/rohd.dart +++ b/lib/rohd.dart @@ -3,9 +3,9 @@ export 'src/external.dart'; export 'src/interface.dart'; -export 'src/logic.dart'; export 'src/module.dart'; export 'src/modules/modules.dart'; +export 'src/signals/signals.dart'; export 'src/simulator.dart'; export 'src/state_machine.dart'; export 'src/swizzle.dart'; diff --git a/lib/src/exceptions/module/port_width_mismatch_exception.dart b/lib/src/exceptions/module/port_width_mismatch_exception.dart index 710cf9acc..8ba427e6a 100644 --- a/lib/src/exceptions/module/port_width_mismatch_exception.dart +++ b/lib/src/exceptions/module/port_width_mismatch_exception.dart @@ -13,9 +13,11 @@ import 'package:rohd/src/exceptions/rohd_exception.dart'; /// An [Exception] thrown when a port has the wrong width. class PortWidthMismatchException extends RohdException { /// Constructs a new [Exception] for when a port has the wrong width. - PortWidthMismatchException(Logic port, int expectedWidth) + PortWidthMismatchException(Logic port, int expectedWidth, + {String additionalMessage = ''}) : super('Port ${port.name} has the wrong width.' - ' Expected $expectedWidth but found ${port.width}.'); + ' Expected $expectedWidth but found ${port.width}.' + ' $additionalMessage'); /// Constructs a new [Exception] for when two ports should have been the /// same width, but were not. diff --git a/lib/src/logic.dart b/lib/src/signals/logic.dart similarity index 100% rename from lib/src/logic.dart rename to lib/src/signals/logic.dart diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart new file mode 100644 index 000000000..0b371de65 --- /dev/null +++ b/lib/src/signals/logic_array.dart @@ -0,0 +1,32 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// logic_array.dart +// Definition of an array of `Logic`s. +// +// 2023 May 1 +// Author: Max Korbel + +import 'package:rohd/rohd.dart'; +import 'package:rohd/src/exceptions/module/module_exceptions.dart'; + +class LogicArray extends LogicStructure { + //TODO: calculate dimension + // Note: if any level of hierarchy has any elemnt *not* an array, then that's the end of it (flatten from that point down) + + ///TODO + LogicArray(super.components) { + if (components.isNotEmpty) { + final dim0 = components.first.width; + for (final component in components) { + if (component.width != dim0) { + throw PortWidthMismatchException(component, dim0, + additionalMessage: + 'All elements of a `LogicArray` must be equal width.'); + } + } + } + } + + //TODO: can we be stricter about assignments, etc. for arrays, only like-shaped arrays? +} diff --git a/lib/src/logic_structure.dart b/lib/src/signals/logic_structure.dart similarity index 96% rename from lib/src/logic_structure.dart rename to lib/src/signals/logic_structure.dart index bce3e0bef..7f746301e 100644 --- a/lib/src/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -1,6 +1,14 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// logic_structure.dart +// Definition of a structure containing multiple `Logic`s. +// +// 2023 May 1 +// Author: Max Korbel + import 'dart:collection'; -import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd/src/exceptions/module/module_exceptions.dart'; import 'package:rohd/src/utilities/sanitizer.dart'; @@ -94,6 +102,7 @@ class LogicStructure implements Logic { var index = 0; for (final component in components) { + //TODO: consider if other is a struct, and the ranges match component <= other.getRange(index, index + component.width); index += component.width; } diff --git a/lib/src/signals/signals.dart b/lib/src/signals/signals.dart new file mode 100644 index 000000000..46790efac --- /dev/null +++ b/lib/src/signals/signals.dart @@ -0,0 +1,6 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause + +export 'logic.dart'; +export 'logic_array.dart'; +export 'logic_structure.dart'; From 8cc4e50ab12c9a1c54689fadaef24c44a6eaf810 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 2 May 2023 09:28:13 -0700 Subject: [PATCH 05/75] fix comment --- lib/src/signals/logic.dart | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index ed743d987..381226314 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -1,13 +1,13 @@ -/// Copyright (C) 2021-2023 Intel Corporation -/// SPDX-License-Identifier: BSD-3-Clause -/// -/// logic.dart -/// Definition of basic signals, like Logic, and their behavior in the -/// simulator, as well as basic operations on them -/// -/// 2021 August 2 -/// Author: Max Korbel -/// +// Copyright (C) 2021-2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// logic.dart +// Definition of basic signals, like Logic, and their behavior in the +// simulator, as well as basic operations on them +// +// 2021 August 2 +// Author: Max Korbel + import 'dart:async'; import 'package:collection/collection.dart'; From 2a2b5fea6960a6226c90f46fc7256de12152d1f6 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 2 May 2023 11:57:06 -0700 Subject: [PATCH 06/75] first tests for logic array passing --- lib/src/exceptions/exceptions.dart | 1 + lib/src/modules/conditional.dart | 20 +++---- lib/src/modules/pipeline.dart | 2 +- lib/src/signals/logic.dart | 7 +++ lib/src/signals/logic_array.dart | 69 +++++++++++++++++++++++-- lib/src/signals/logic_structure.dart | 56 +++++++++++--------- lib/src/state_machine.dart | 4 +- lib/src/swizzle.dart | 2 +- lib/src/synthesizers/synth_builder.dart | 2 +- lib/src/synthesizers/systemverilog.dart | 4 +- lib/src/values/logic_value.dart | 3 +- test/logic_array_test.dart | 58 +++++++++++++++++++++ test/logic_structure_test.dart | 0 13 files changed, 184 insertions(+), 44 deletions(-) create mode 100644 test/logic_array_test.dart create mode 100644 test/logic_structure_test.dart diff --git a/lib/src/exceptions/exceptions.dart b/lib/src/exceptions/exceptions.dart index d9448341e..748637134 100644 --- a/lib/src/exceptions/exceptions.dart +++ b/lib/src/exceptions/exceptions.dart @@ -2,6 +2,7 @@ /// SPDX-License-Identifier: BSD-3-Clause export './conditionals/conditional_exceptions.dart'; export './logic/logic_exceptions.dart'; +export './logic_value/logic_value_exceptions.dart'; export './module/module_exceptions.dart'; export './name/name_exceptions.dart'; export './sim_compare/sim_compare_exceptions.dart'; diff --git a/lib/src/modules/conditional.dart b/lib/src/modules/conditional.dart index cefc5832b..aab02c1cb 100644 --- a/lib/src/modules/conditional.dart +++ b/lib/src/modules/conditional.dart @@ -629,7 +629,7 @@ abstract class Conditional { } } - return foundSsaLogics.toList(); + return foundSsaLogics.toList(growable: false); } /// Given existing [currentMappings], connects [drivers] and [receivers] @@ -921,13 +921,13 @@ class Case extends Conditional { ..addAll(item.then .map((conditional) => conditional.drivers) .expand((driver) => driver) - .toList()); + .toList(growable: false)); } if (defaultItem != null) { drivers.addAll(defaultItem! .map((conditional) => conditional.drivers) .expand((driver) => driver) - .toList()); + .toList(growable: false)); } return drivers; } @@ -942,13 +942,13 @@ class Case extends Conditional { receivers.addAll(item.then .map((conditional) => conditional.receivers) .expand((receiver) => receiver) - .toList()); + .toList(growable: false)); } if (defaultItem != null) { receivers.addAll(defaultItem! .map((conditional) => conditional.receivers) .expand((receiver) => receiver) - .toList()); + .toList(growable: false)); } return receivers; } @@ -1183,8 +1183,10 @@ class If extends Conditional { late final List conditionals = _getConditionals(); /// Calculates the set of conditionals directly within this. - List _getConditionals() => - iffs.map((iff) => iff.then).expand((conditional) => conditional).toList(); + List _getConditionals() => iffs + .map((iff) => iff.then) + .expand((conditional) => conditional) + .toList(growable: false); @override late final List drivers = _getDrivers(); @@ -1198,7 +1200,7 @@ class If extends Conditional { ..addAll(iff.then .map((conditional) => conditional.drivers) .expand((driver) => driver) - .toList()); + .toList(growable: false)); } return drivers; } @@ -1213,7 +1215,7 @@ class If extends Conditional { receivers.addAll(iff.then .map((conditional) => conditional.receivers) .expand((receiver) => receiver) - .toList()); + .toList(growable: false)); } return receivers; } diff --git a/lib/src/modules/pipeline.dart b/lib/src/modules/pipeline.dart index a0d9bf2ca..8fff663aa 100644 --- a/lib/src/modules/pipeline.dart +++ b/lib/src/modules/pipeline.dart @@ -219,7 +219,7 @@ class Pipeline { ffAssigns.map((conditional) { conditional as ConditionalAssign; return conditional.receiver < (resetValue ?? 0); - }).toList(), + }).toList(growable: false), ), Else(ffAssignsWithStall) ]) diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index 381226314..78560441f 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -356,6 +356,13 @@ class Logic { Module? get parentModule => _parentModule; Module? _parentModule; + LogicStructure? get parentStructure => _parentStructure; + LogicStructure? _parentStructure; + + @protected + set parentStructure(LogicStructure? newParentStructure) => + _parentStructure = newParentStructure; + /// Sets the value of [parentModule] to [newParentModule]. /// /// This should *only* be called by [Module.build()]. It is used to diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index 0b371de65..37dd92632 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -7,6 +7,7 @@ // 2023 May 1 // Author: Max Korbel +import 'package:collection/collection.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd/src/exceptions/module/module_exceptions.dart'; @@ -14,11 +15,52 @@ class LogicArray extends LogicStructure { //TODO: calculate dimension // Note: if any level of hierarchy has any elemnt *not* an array, then that's the end of it (flatten from that point down) + //TODO: if there's complex structure below an array, need to convey that it is flattened when instantiated somehow + // OR: just ban lists of anything other than vanilla logics? that's probably good enough? + + late final List dimensions = _calculateDimensions(); + + List _calculateDimensions() { + // current dimension is just the length + final currDim = elements.length; + + if (currDim == 0) { + return [currDim]; + } + + // check if all elements are: + // - LogicArrays + // - with the same dimensions + + final allElementsAreArray = + elements.firstWhereOrNull((element) => element is! LogicArray) == null; + + if (!allElementsAreArray) { + return [currDim]; + } + + final firstDim = (elements.first as LogicArray).dimensions; + + final listEq = const ListEquality().equals; + + for (final element in elements) { + element as LogicArray; + if (!listEq(firstDim, element.dimensions)) { + return [currDim]; + } + } + + // if they're all the same, return it back up + return [currDim, ...firstDim]; + } + + // _CastError (type 'List' is not a subtype of type 'List>>' in type cast) + ///TODO - LogicArray(super.components) { - if (components.isNotEmpty) { - final dim0 = components.first.width; - for (final component in components) { + LogicArray._(super.elements, {super.name}) { + if (elements.isNotEmpty) { + final dim0 = elements.first.width; + for (final component in elements) { if (component.width != dim0) { throw PortWidthMismatchException(component, dim0, additionalMessage: @@ -28,5 +70,24 @@ class LogicArray extends LogicStructure { } } + ///TODO + factory LogicArray(List dimensions, int width) { + if (dimensions.isEmpty) { + //TODO + throw Exception('Array must have at least 1 dimension'); + } + + return LogicArray._(List.generate( + dimensions[0], + (index) => dimensions.length == 1 + ? Logic(width: width) + : LogicArray( + dimensions + .getRange(1, dimensions.length) + .toList(growable: false), + width), + growable: false)); + } + //TODO: can we be stricter about assignments, etc. for arrays, only like-shaped arrays? } diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 7f746301e..18a9d8254 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -15,12 +15,12 @@ import 'package:rohd/src/utilities/sanitizer.dart'; import 'package:rohd/src/utilities/synchronous_propagator.dart'; class LogicStructure implements Logic { - /// All components of this structure. - late final List components = UnmodifiableListView(_components); - final List _components = []; + /// All elements of this structure. + late final List elements = UnmodifiableListView(_elements); + final List _elements = []; - /// Packs all [components] into one flattened bus. - late final Logic packed = components + /// Packs all [elements] into one flattened bus. + late final Logic packed = elements .map((e) { if (e is LogicStructure) { return e.packed; @@ -28,7 +28,7 @@ class LogicStructure implements Logic { return e; } }) - .toList() + .toList(growable: false) .swizzle(); @override @@ -37,12 +37,13 @@ class LogicStructure implements Logic { /// An internal counter for encouraging unique naming of unnamed signals. static int _structIdx = 0; - /// Creates a new [LogicStructure] with [components] as elements. - LogicStructure(List components, {String? name}) + /// Creates a new [LogicStructure] with [elements] as elements. + LogicStructure(List elements, {String? name}) : name = (name == null || name.isEmpty) ? 'st${_structIdx++}' : Sanitizer.sanitizeSV(name) { - _components.addAll(components); + //TODO: make sure no components already have a parentComponent + _elements.addAll(elements); } //TODO: dimension List (only on array?) @@ -57,7 +58,7 @@ class LogicStructure implements Logic { final logicVal = LogicValue.of(val, fill: fill, width: width); var index = 0; - for (final component in components) { + for (final component in elements) { component.put(logicVal.getRange(index, index + component.width)); index += component.width; } @@ -68,7 +69,7 @@ class LogicStructure implements Logic { final logicVal = LogicValue.of(val, fill: fill, width: width); var index = 0; - for (final component in components) { + for (final component in elements) { component.inject(logicVal.getRange(index, index + component.width)); index += component.width; } @@ -85,7 +86,7 @@ class LogicStructure implements Logic { final conditionalAssigns = []; var index = 0; - for (final component in components) { + for (final component in elements) { conditionalAssigns .add(component < otherLogic.getRange(index, index + component.width)); index += component.width; @@ -101,7 +102,7 @@ class LogicStructure implements Logic { } var index = 0; - for (final component in components) { + for (final component in elements) { //TODO: consider if other is a struct, and the ranges match component <= other.getRange(index, index + component.width); index += component.width; @@ -122,41 +123,41 @@ class LogicStructure implements Logic { Logic slice(int endIndex, int startIndex) => packed.slice(endIndex, startIndex); - /// Increments each component of [components] using [Logic.incr]. + /// Increments each component of [elements] using [Logic.incr]. @override Conditional incr( {Logic Function(Logic p1) s = Logic.nopS, dynamic val = 1}) => ConditionalGroup([ - for (final component in components) component.incr(s: s, val: val), + for (final component in elements) component.incr(s: s, val: val), ]); - /// Decrements each component of [components] using [Logic.decr]. + /// Decrements each component of [elements] using [Logic.decr]. @override Conditional decr( {Logic Function(Logic p1) s = Logic.nopS, dynamic val = 1}) => ConditionalGroup([ - for (final component in components) component.decr(s: s, val: val), + for (final component in elements) component.decr(s: s, val: val), ]); - /// Divide-assigns each component of [components] using [Logic.divAssign]. + /// Divide-assigns each component of [elements] using [Logic.divAssign]. @override Conditional divAssign( {Logic Function(Logic p1) s = Logic.nopS, dynamic val}) => ConditionalGroup([ - for (final component in components) component.divAssign(s: s, val: val), + for (final component in elements) component.divAssign(s: s, val: val), ]); - /// Multiply-assigns each component of [components] using [Logic.mulAssign]. + /// Multiply-assigns each component of [elements] using [Logic.mulAssign]. @override Conditional mulAssign( {Logic Function(Logic p1) s = Logic.nopS, dynamic val}) => ConditionalGroup([ - for (final component in components) component.mulAssign(s: s, val: val), + for (final component in elements) component.mulAssign(s: s, val: val), ]); @override late final Iterable dstConnections = [ - for (final component in components) ...component.dstConnections + for (final component in elements) ...component.dstConnections ]; //TODO: is this safe to have a separate tracking here? @@ -168,6 +169,15 @@ class LogicStructure implements Logic { @override set parentModule(Module? newParentModule) => _parentModule = newParentModule; + //TODO: to track naming + @override + LogicStructure? get parentStructure => _parentStructure; + LogicStructure? _parentStructure; + + @override + set parentStructure(LogicStructure? newParentStructure) => + _parentStructure = newParentStructure; + @override bool get isInput => parentModule?.isInput(this) ?? false; @@ -181,7 +191,7 @@ class LogicStructure implements Logic { @override void makeUnassignable() { - for (final component in components) { + for (final component in elements) { component.makeUnassignable(); } } diff --git a/lib/src/state_machine.dart b/lib/src/state_machine.dart index c8282c97c..d805a42ee 100644 --- a/lib/src/state_machine.dart +++ b/lib/src/state_machine.dart @@ -85,11 +85,11 @@ class StateMachine { _stateValueLookup[ _stateLookup[entry.value]] ])) - .toList(), + .toList(growable: false), conditionalType: ConditionalType.unique, defaultItem: [nextState < currentState]) ])) - .toList(), + .toList(growable: false), conditionalType: ConditionalType.unique, defaultItem: [nextState < currentState]) ]); diff --git a/lib/src/swizzle.dart b/lib/src/swizzle.dart index 47a2781c6..2aa4cff25 100644 --- a/lib/src/swizzle.dart +++ b/lib/src/swizzle.dart @@ -32,7 +32,7 @@ extension LogicSwizzle on List { /// significant (lowest) bits. /// /// If you want the opposite, check out [swizzle]. - Logic rswizzle() => Swizzle(reversed.toList()).out; + Logic rswizzle() => Swizzle(reversed.toList(growable: false)).out; } /// Allows lists of [LogicValue]s to be swizzled. diff --git a/lib/src/synthesizers/synth_builder.dart b/lib/src/synthesizers/synth_builder.dart index ef953f5d1..2f8ba03d3 100644 --- a/lib/src/synthesizers/synth_builder.dart +++ b/lib/src/synthesizers/synth_builder.dart @@ -65,7 +65,7 @@ class SynthBuilder { /// the [synthesizer]. List getFileContents() => synthesisResults .map((synthesisResult) => synthesisResult.toFileContents()) - .toList(); + .toList(growable: false); /// Provides an instance type name for [module]. /// diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart index addf18f43..a3434c6e9 100644 --- a/lib/src/synthesizers/systemverilog.dart +++ b/lib/src/synthesizers/systemverilog.dart @@ -150,14 +150,14 @@ class _SystemVerilogSynthesisResult extends SynthesisResult { List _verilogInputs() { final declarations = _synthModuleDefinition.inputs .map((sig) => 'input logic ${sig.definitionName()}') - .toList(); + .toList(growable: false); return declarations; } List _verilogOutputs() { final declarations = _synthModuleDefinition.outputs .map((sig) => 'output logic ${sig.definitionName()}') - .toList(); + .toList(growable: false); return declarations; } diff --git a/lib/src/values/logic_value.dart b/lib/src/values/logic_value.dart index 0a1d96039..2a73b2226 100644 --- a/lib/src/values/logic_value.dart +++ b/lib/src/values/logic_value.dart @@ -503,7 +503,8 @@ abstract class LogicValue { /// print(lv); // This prints `[1'h0, 1'bx, 1'h1]` /// ``` List toList() => - List.generate(width, (index) => this[index]).toList(); + List.generate(width, (index) => this[index]) + .toList(growable: false); /// Converts this [LogicValue] to a binary [String], including a decorator at /// the front in SystemVerilog style. diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart new file mode 100644 index 000000000..0144654db --- /dev/null +++ b/test/logic_array_test.dart @@ -0,0 +1,58 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// logic_array_test.dart +// Tests for LogicArray +// +// 2023 May 2 +// Author: Max Korbel + +import 'package:collection/collection.dart'; +import 'package:rohd/rohd.dart'; +import 'package:test/test.dart'; + +void main() { + group('construct LogicArray', () { + final listEq = const ListEquality().equals; + + test('empty array', () { + final arr = LogicArray([0], 20); + expect(arr.width, 0); + expect(arr.elements.isEmpty, true); + }); + test('single-dim array', () { + final dim = [5]; + const w = 16; + final arr = LogicArray(dim, w); + + expect(listEq(arr.dimensions, dim), true); + + for (final element in arr.elements) { + expect(element.width, w); + } + + expect(arr.width, w * dim[0]); + }); + test('many-dim array', () { + final dim = [5, 8, 3]; + const w = 32; + final arr = LogicArray(dim, w); + + expect(listEq(arr.dimensions, dim), true); + + for (final element0 in arr.elements) { + element0 as LogicArray; + for (final element1 in element0.elements) { + element1 as LogicArray; + for (final element2 in element1.elements) { + expect(element2.width, w); + } + } + } + expect(arr.width, w * dim.reduce((a, b) => a * b)); + }); + test('no dim exception', () { + //TODO + }); + }); +} diff --git a/test/logic_structure_test.dart b/test/logic_structure_test.dart new file mode 100644 index 000000000..e69de29bb From 9310430867af04d60822ffc5b9588939a8ec08ea Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 2 May 2023 12:51:21 -0700 Subject: [PATCH 07/75] make element access easier --- lib/src/signals/logic.dart | 4 ++++ lib/src/signals/logic_structure.dart | 1 + test/logic_array_test.dart | 9 +++++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index 78560441f..5cb252d90 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -708,6 +708,10 @@ class Logic { throw Exception('Expected `int` or `Logic`'); } + //TODO: test this + late final List elements = + List.generate(width, (index) => this[index], growable: false); + /// Accesses a subset of this signal from [startIndex] to [endIndex], /// both inclusive. /// diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 18a9d8254..c1ae9d98c 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -16,6 +16,7 @@ import 'package:rohd/src/utilities/synchronous_propagator.dart'; class LogicStructure implements Logic { /// All elements of this structure. + @override late final List elements = UnmodifiableListView(_elements); final List _elements = []; diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 0144654db..d6bd0af40 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -40,16 +40,21 @@ void main() { expect(listEq(arr.dimensions, dim), true); + // make sure we can access elements + arr.elements[0].elements[2].elements[1]; + for (final element0 in arr.elements) { - element0 as LogicArray; for (final element1 in element0.elements) { - element1 as LogicArray; for (final element2 in element1.elements) { expect(element2.width, w); } } } expect(arr.width, w * dim.reduce((a, b) => a * b)); + expect( + listEq((arr.elements[0] as LogicArray).dimensions, + dim.getRange(1, dim.length).toList()), + true); }); test('no dim exception', () { //TODO From 3a7943a78fa7d3d35cfd3eb5940d0ef25b49fae3 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 2 May 2023 15:03:44 -0700 Subject: [PATCH 08/75] initial module creationg with arrays --- lib/src/module.dart | 69 +++++++++++++++++++++---- lib/src/signals/logic.dart | 3 ++ lib/src/signals/logic_array.dart | 44 +++++++++++----- lib/src/signals/logic_structure.dart | 14 ++++- lib/src/synthesizers/systemverilog.dart | 36 +++++++++++-- test/logic_array_test.dart | 24 +++++++++ 6 files changed, 163 insertions(+), 27 deletions(-) diff --git a/lib/src/module.dart b/lib/src/module.dart index 26a06191e..70daf38a6 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -421,8 +421,8 @@ abstract class Module { await _traceInputForModuleContents(dstConnection); } } - if (signal.srcConnection != null) { - await _traceOutputForModuleContents(signal.srcConnection!); + for (final srcConnection in signal.srcConnections) { + await _traceOutputForModuleContents(srcConnection); } } } @@ -458,12 +458,42 @@ abstract class Module { throw Exception('Port width mismatch, signal "$x" does not' ' have specified width "$width".'); } - _inputs[name] = Logic(name: name, width: width)..gets(x); + final inPort = Logic(name: name, width: width) + ..gets(x) + // ignore: invalid_use_of_protected_member + ..parentModule = this; - // ignore: invalid_use_of_protected_member - _inputs[name]!.parentModule = this; + _inputs[name] = inPort; - return _inputs[name]!; + return inPort; + } + + LogicArray addInputArray( + String name, + LogicArray x, { + List dimensions = const [1], + int elementWidth = 1, + int numDimensionsPacked = 0, //TODO + }) { + _checkForSafePortName(name); + + final listEq = const ListEquality().equals; + + if (!listEq(x.dimensions, dimensions)) { + //TODO + } + if (x.elementWidth != elementWidth) { + //TODO + } + + final inArr = LogicArray(dimensions, elementWidth, name: name) + ..gets(x) + // ignore: invalid_use_of_protected_member + ..parentModule = this; + + _inputs[name] = inArr; + + return inArr; } /// Registers an output to this [Module] and returns an output port that @@ -473,12 +503,31 @@ abstract class Module { @protected Logic addOutput(String name, {int width = 1}) { _checkForSafePortName(name); - _outputs[name] = Logic(name: name, width: width); - // ignore: invalid_use_of_protected_member - _outputs[name]!.parentModule = this; + final outPort = Logic(name: name, width: width) + // ignore: invalid_use_of_protected_member + ..parentModule = this; + + _outputs[name] = outPort; + + return outPort; + } + + LogicArray addOutputArray( + String name, { + List dimensions = const [1], + int elementWidth = 1, + int numDimensionsPacked = 0, //TODO + }) { + _checkForSafePortName(name); + + final outArr = LogicArray(dimensions, elementWidth, name: name) + // ignore: invalid_use_of_protected_member + ..parentModule = this; + + _outputs[name] = outArr; - return _outputs[name]!; + return outArr; } @override diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index 5cb252d90..96f91acd8 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -299,6 +299,9 @@ class Logic { Logic? get srcConnection => _srcConnection; Logic? _srcConnection; + Iterable get srcConnections => + [if (srcConnection != null) srcConnection!]; + /// An [Iterable] of all [Logic]s that are being directly driven by `this`. late final Iterable dstConnections = UnmodifiableListView(_dstConnections); diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index 37dd92632..39e308641 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -20,6 +20,8 @@ class LogicArray extends LogicStructure { late final List dimensions = _calculateDimensions(); + //TODO: support ports that are packed, unpacked, or a mix + List _calculateDimensions() { // current dimension is just the length final currDim = elements.length; @@ -31,7 +33,9 @@ class LogicArray extends LogicStructure { // check if all elements are: // - LogicArrays // - with the same dimensions + // - with same element widths + //TODO: if we always construct ourselves, then this is safer? final allElementsAreArray = elements.firstWhereOrNull((element) => element is! LogicArray) == null; @@ -54,7 +58,21 @@ class LogicArray extends LogicStructure { return [currDim, ...firstDim]; } - // _CastError (type 'List' is not a subtype of type 'List>>' in type cast) + late final int elementWidth = _calculateElementWidth(); + + int _calculateElementWidth() { + if (width == 0) { + return 0; + } + + // assume all elements are the same width + + Logic arr = this; + while (arr.elements.first is LogicArray) { + arr = arr.elements.first; + } + return arr.elements.first.width; + } ///TODO LogicArray._(super.elements, {super.name}) { @@ -71,22 +89,24 @@ class LogicArray extends LogicStructure { } ///TODO - factory LogicArray(List dimensions, int width) { + factory LogicArray(List dimensions, int elementWidth, {String? name}) { if (dimensions.isEmpty) { //TODO throw Exception('Array must have at least 1 dimension'); } - return LogicArray._(List.generate( - dimensions[0], - (index) => dimensions.length == 1 - ? Logic(width: width) - : LogicArray( - dimensions - .getRange(1, dimensions.length) - .toList(growable: false), - width), - growable: false)); + return LogicArray._( + List.generate( + dimensions[0], + (index) => dimensions.length == 1 + ? Logic(width: elementWidth) + : LogicArray( + dimensions + .getRange(1, dimensions.length) + .toList(growable: false), + elementWidth), + growable: false), + name: name); } //TODO: can we be stricter about assignments, etc. for arrays, only like-shaped arrays? diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index c1ae9d98c..dd1a2dec3 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -9,6 +9,8 @@ import 'dart:collection'; +import 'package:collection/collection.dart'; +import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd/src/exceptions/module/module_exceptions.dart'; import 'package:rohd/src/utilities/sanitizer.dart'; @@ -44,7 +46,11 @@ class LogicStructure implements Logic { ? 'st${_structIdx++}' : Sanitizer.sanitizeSV(name) { //TODO: make sure no components already have a parentComponent - _elements.addAll(elements); + _elements + ..addAll(elements) + ..forEach((element) { + element.parentStructure = this; + }); } //TODO: dimension List (only on array?) @@ -167,6 +173,7 @@ class LogicStructure implements Logic { @override Module? get parentModule => _parentModule; + @protected @override set parentModule(Module? newParentModule) => _parentModule = newParentModule; @@ -175,6 +182,7 @@ class LogicStructure implements Logic { LogicStructure? get parentStructure => _parentStructure; LogicStructure? _parentStructure; + @protected @override set parentStructure(LogicStructure? newParentStructure) => _parentStructure = newParentStructure; @@ -201,6 +209,10 @@ class LogicStructure implements Logic { // TODO: implement srcConnection Logic? get srcConnection => throw UnimplementedError(); + @override + Iterable get srcConnections => + [for (final element in elements) ...element.srcConnections]; + @override LogicValue get value => packed.value; diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart index a3434c6e9..308b137c2 100644 --- a/lib/src/synthesizers/systemverilog.dart +++ b/lib/src/synthesizers/systemverilog.dart @@ -357,6 +357,11 @@ class _SynthModuleDefinition { for (var i = 0; i < logicsToTraverse.length; i++) { final receiver = logicsToTraverse[i]; + if (receiver is LogicArray) { + //TODO: is this right? + logicsToTraverse.addAll(receiver.srcConnections); + continue; + } final driver = receiver.srcConnection; final receiverIsModuleInput = module.isInput(receiver); @@ -385,7 +390,7 @@ class _SynthModuleDefinition { _getSynthSubModuleInstantiation(subModule); subModuleInstantiation.outputMapping[synthReceiver] = receiver; - subModule.inputs.values.forEach(logicsToTraverse.add); + logicsToTraverse.addAll(subModule.inputs.values); } else if (driver != null) { if (!module.isInput(receiver)) { // stop at the input to this module @@ -583,12 +588,35 @@ class _SynthLogic { _needsDeclaration = false; } + static String _widthToRangeDef(int width) { + if (width > 1) { + return '[${width - 1}:0]'; + } else { + return ''; + } + } + String definitionName() { - if (logic.width > 1) { - return '[${logic.width - 1}:0] $name'; + String packedDims; + + if (logic is LogicArray) { + final logicArr = logic as LogicArray; + + final packedDimsBuf = + StringBuffer(_widthToRangeDef(logicArr.elementWidth)); + + final dims = logicArr.dimensions; + for (final dim in dims.reversed) { + packedDimsBuf.write(_widthToRangeDef(dim)); + } + packedDims = packedDimsBuf.toString(); } else { - return name; + packedDims = _widthToRangeDef(logic.width); } + + //TODO: unpackedDims + + return '$packedDims $name'; } } diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index d6bd0af40..5293d9f20 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -7,10 +7,25 @@ // 2023 May 2 // Author: Max Korbel +import 'dart:io'; + import 'package:collection/collection.dart'; import 'package:rohd/rohd.dart'; import 'package:test/test.dart'; +class SimpleLAModule extends Module { + SimpleLAModule(LogicArray laIn) { + laIn = addInputArray('laIn', laIn, + dimensions: laIn.dimensions, elementWidth: laIn.elementWidth); + + addOutputArray('laOut', + dimensions: laIn.dimensions, elementWidth: laIn.elementWidth) <= + ~laIn; + } +} + +//TODO: test internal array signals as well + void main() { group('construct LogicArray', () { final listEq = const ListEquality().equals; @@ -19,6 +34,7 @@ void main() { final arr = LogicArray([0], 20); expect(arr.width, 0); expect(arr.elements.isEmpty, true); + expect(arr.elementWidth, 0); }); test('single-dim array', () { final dim = [5]; @@ -32,6 +48,7 @@ void main() { } expect(arr.width, w * dim[0]); + expect(arr.elementWidth, w); }); test('many-dim array', () { final dim = [5, 8, 3]; @@ -55,9 +72,16 @@ void main() { listEq((arr.elements[0] as LogicArray).dimensions, dim.getRange(1, dim.length).toList()), true); + expect(arr.elementWidth, w); }); test('no dim exception', () { //TODO }); }); + + test('simple logic array ports module', () async { + final mod = SimpleLAModule(LogicArray([3, 4, 5], 8)); + await mod.build(); + File('tmp_simple_la_mod.sv').writeAsStringSync(mod.generateSynth()); + }); } From 2cd66d115a07f7578e9305d14e76458e60a4ead2 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 2 May 2023 16:18:37 -0700 Subject: [PATCH 09/75] simpler generated verilog works sorta --- lib/src/module.dart | 6 ++ lib/src/signals/logic.dart | 4 +- lib/src/signals/logic_structure.dart | 84 +++++++++++++++++++--------- lib/src/swizzle.dart | 9 +-- test/logic_array_test.dart | 12 ++-- 5 files changed, 77 insertions(+), 38 deletions(-) diff --git a/lib/src/module.dart b/lib/src/module.dart index 70daf38a6..7b0113202 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -458,6 +458,12 @@ abstract class Module { throw Exception('Port width mismatch, signal "$x" does not' ' have specified width "$width".'); } + + if (x is LogicArray) { + // ignore: parameter_assignments + x = x.packed; + } + final inPort = Logic(name: name, width: width) ..gets(x) // ignore: invalid_use_of_protected_member diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index 96f91acd8..aa807441b 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -740,9 +740,7 @@ class Logic { (startIndex < 0) ? width + startIndex : startIndex; final modifiedEndIndex = (endIndex < 0) ? width + endIndex : endIndex; - if (width == 1 && - modifiedEndIndex == 0 && - modifiedEndIndex == modifiedStartIndex) { + if (modifiedStartIndex == 0 && modifiedEndIndex == width - 1) { // ignore: avoid_returning_this return this; } diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index dd1a2dec3..3ac55a338 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -16,6 +16,8 @@ import 'package:rohd/src/exceptions/module/module_exceptions.dart'; import 'package:rohd/src/utilities/sanitizer.dart'; import 'package:rohd/src/utilities/synchronous_propagator.dart'; +//TODO: how to deal with LogicStructure as an input/output? + class LogicStructure implements Logic { /// All elements of this structure. @override @@ -65,20 +67,20 @@ class LogicStructure implements Logic { final logicVal = LogicValue.of(val, fill: fill, width: width); var index = 0; - for (final component in elements) { - component.put(logicVal.getRange(index, index + component.width)); - index += component.width; + for (final element in elements) { + element.put(logicVal.getRange(index, index + element.width)); + index += element.width; } } @override - void inject(val, {bool fill = false}) { + void inject(dynamic val, {bool fill = false}) { final logicVal = LogicValue.of(val, fill: fill, width: width); var index = 0; - for (final component in elements) { - component.inject(logicVal.getRange(index, index + component.width)); - index += component.width; + for (final element in elements) { + element.inject(logicVal.getRange(index, index + element.width)); + index += element.width; } } @@ -93,10 +95,10 @@ class LogicStructure implements Logic { final conditionalAssigns = []; var index = 0; - for (final component in elements) { + for (final element in elements) { conditionalAssigns - .add(component < otherLogic.getRange(index, index + component.width)); - index += component.width; + .add(element < otherLogic.getRange(index, index + element.width)); + index += element.width; } return ConditionalGroup(conditionalAssigns); @@ -109,10 +111,10 @@ class LogicStructure implements Logic { } var index = 0; - for (final component in elements) { + for (final element in elements) { //TODO: consider if other is a struct, and the ranges match - component <= other.getRange(index, index + component.width); - index += component.width; + element <= other.getRange(index, index + element.width); + index += element.width; } } @@ -123,48 +125,75 @@ class LogicStructure implements Logic { Logic operator [](dynamic index) => packed[index]; @override - Logic getRange(int startIndex, [int? endIndex]) => - packed.getRange(startIndex, endIndex); + Logic getRange(int startIndex, [int? endIndex]) { + endIndex ??= width; + + //TODO: do math for modified indices! + + //TODO: do range checks + + //TODO: test edge cases here + + // grab all elements that fall in this range, keeping track of the offset + int? offset; + final matchingElements = []; + + final requestedWidth = endIndex - startIndex; + + var index = 0; + for (final element in elements) { + final elementInRange = (index >= startIndex) && (index < endIndex); + if (elementInRange) { + matchingElements.add(element); + offset ??= index - startIndex; + } + index += element.width; + } + + return matchingElements + .swizzle() + .getRange(offset!, offset + requestedWidth); + } @override Logic slice(int endIndex, int startIndex) => packed.slice(endIndex, startIndex); - /// Increments each component of [elements] using [Logic.incr]. + /// Increments each element of [elements] using [Logic.incr]. @override Conditional incr( {Logic Function(Logic p1) s = Logic.nopS, dynamic val = 1}) => ConditionalGroup([ - for (final component in elements) component.incr(s: s, val: val), + for (final element in elements) element.incr(s: s, val: val), ]); - /// Decrements each component of [elements] using [Logic.decr]. + /// Decrements each element of [elements] using [Logic.decr]. @override Conditional decr( {Logic Function(Logic p1) s = Logic.nopS, dynamic val = 1}) => ConditionalGroup([ - for (final component in elements) component.decr(s: s, val: val), + for (final element in elements) element.decr(s: s, val: val), ]); - /// Divide-assigns each component of [elements] using [Logic.divAssign]. + /// Divide-assigns each element of [elements] using [Logic.divAssign]. @override Conditional divAssign( {Logic Function(Logic p1) s = Logic.nopS, dynamic val}) => ConditionalGroup([ - for (final component in elements) component.divAssign(s: s, val: val), + for (final element in elements) element.divAssign(s: s, val: val), ]); - /// Multiply-assigns each component of [elements] using [Logic.mulAssign]. + /// Multiply-assigns each element of [elements] using [Logic.mulAssign]. @override Conditional mulAssign( {Logic Function(Logic p1) s = Logic.nopS, dynamic val}) => ConditionalGroup([ - for (final component in elements) component.mulAssign(s: s, val: val), + for (final element in elements) element.mulAssign(s: s, val: val), ]); @override late final Iterable dstConnections = [ - for (final component in elements) ...component.dstConnections + for (final element in elements) ...element.dstConnections ]; //TODO: is this safe to have a separate tracking here? @@ -200,8 +229,8 @@ class LogicStructure implements Logic { @override void makeUnassignable() { - for (final component in elements) { - component.makeUnassignable(); + for (final element in elements) { + element.makeUnassignable(); } } @@ -217,7 +246,8 @@ class LogicStructure implements Logic { LogicValue get value => packed.value; @override - int get width => packed.width; + late final int width = + elements.map((e) => e.width).reduce((w1, w2) => w1 + w2); @override Logic withSet(int startIndex, Logic update) => diff --git a/lib/src/swizzle.dart b/lib/src/swizzle.dart index 2aa4cff25..c11bf9a3e 100644 --- a/lib/src/swizzle.dart +++ b/lib/src/swizzle.dart @@ -21,7 +21,7 @@ extension LogicSwizzle on List { /// significant (highest) bits. /// /// If you want the opposite, check out [rswizzle]. - Logic swizzle() => Swizzle(this).out; + Logic swizzle() => length == 1 ? first : Swizzle(this).out; /// Performs a concatenation operation on the list of signals, where index 0 /// of this list is the *least* significant bit(s). @@ -32,7 +32,8 @@ extension LogicSwizzle on List { /// significant (lowest) bits. /// /// If you want the opposite, check out [swizzle]. - Logic rswizzle() => Swizzle(reversed.toList(growable: false)).out; + Logic rswizzle() => + length == 1 ? first : Swizzle(reversed.toList(growable: false)).out; } /// Allows lists of [LogicValue]s to be swizzled. @@ -46,7 +47,7 @@ extension LogicValueSwizzle on List { /// most significant (highest) bits. /// /// If you want the opposite, check out [rswizzle]. - LogicValue swizzle() => LogicValue.ofIterable(reversed); + LogicValue swizzle() => length == 1 ? first : LogicValue.ofIterable(reversed); /// Performs a concatenation operation on the list of signals, where index 0 /// of this list is the *least* significant bit. @@ -57,7 +58,7 @@ extension LogicValueSwizzle on List { /// least significant (lowest) bits. /// /// If you want the opposite, check out [swizzle]. - LogicValue rswizzle() => LogicValue.ofIterable(this); + LogicValue rswizzle() => length == 1 ? first : LogicValue.ofIterable(this); } /// Performs a concatenation operation on the list of signals, where index 0 of diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 5293d9f20..50b0c480d 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -13,14 +13,16 @@ import 'package:collection/collection.dart'; import 'package:rohd/rohd.dart'; import 'package:test/test.dart'; -class SimpleLAModule extends Module { - SimpleLAModule(LogicArray laIn) { +class SimpleLAPassthrough extends Module { + SimpleLAPassthrough(LogicArray laIn) { laIn = addInputArray('laIn', laIn, dimensions: laIn.dimensions, elementWidth: laIn.elementWidth); addOutputArray('laOut', dimensions: laIn.dimensions, elementWidth: laIn.elementWidth) <= - ~laIn; + laIn; + + //TODO: add some more interesting logic } } @@ -80,8 +82,10 @@ void main() { }); test('simple logic array ports module', () async { - final mod = SimpleLAModule(LogicArray([3, 4, 5], 8)); + final mod = SimpleLAPassthrough(LogicArray([3], 8)); await mod.build(); + //TODO: test we don't generate extraneous packed things + File('tmp_simple_la_mod.sv').writeAsStringSync(mod.generateSynth()); }); } From 5976e089f7f25439564937dbe2c6d18c527e0d06 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Wed, 3 May 2023 10:51:49 -0700 Subject: [PATCH 10/75] almost got right assignments in passthrough, but extra non-existent intermediate --- lib/src/interface.dart | 2 ++ lib/src/module.dart | 2 +- lib/src/signals/logic.dart | 34 +++++++++++++++++++++++++ lib/src/signals/logic_array.dart | 5 ++-- lib/src/signals/logic_structure.dart | 29 ++++++++++++++++++++- lib/src/synthesizers/systemverilog.dart | 21 ++++++++++----- 6 files changed, 83 insertions(+), 10 deletions(-) diff --git a/lib/src/interface.dart b/lib/src/interface.dart index 2433d5c50..3b08ede2c 100644 --- a/lib/src/interface.dart +++ b/lib/src/interface.dart @@ -25,6 +25,8 @@ class Port extends Logic { } } +//TODO: how to support ports that are arrays? + /// Represents a logical interface to a [Module]. /// /// Interfaces make it easier to define port connections of a [Module] diff --git a/lib/src/module.dart b/lib/src/module.dart index 7b0113202..f4fb8d3cd 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -459,7 +459,7 @@ abstract class Module { ' have specified width "$width".'); } - if (x is LogicArray) { + if (x is LogicStructure) { // ignore: parameter_assignments x = x.packed; } diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index aa807441b..d271a8e15 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -362,6 +362,40 @@ class Logic { LogicStructure? get parentStructure => _parentStructure; LogicStructure? _parentStructure; + LogicStructure? get rootStructure { + //TODO: do we even need this? + LogicStructure? root; + while (parentStructure != null) { + root = parentStructure; + } + return root; + } + + /// True if this is a member of a [LogicArray]. + bool get isArrayMember => parentStructure is LogicArray; + + /// TODO + /// Returns the name relative to the [parentStructure]-defined hierarchy, if + /// one exists. Otherwise, this is the same as [name]. + /// + /// This is useful for finding the name of a signal as an element of a + /// [LogicArray]. + String get structureName { + if (parentStructure != null) { + if (parentStructure is LogicArray) { + return '${parentStructure!.structureName}[${arrayIndex!}]'; + } else { + return '${parentStructure!.structureName}.$name'; + } + } else { + return name; + } + } + + //TODO: protect this properly + @protected + int? arrayIndex; + @protected set parentStructure(LogicStructure? newParentStructure) => _parentStructure = newParentStructure; diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index 39e308641..b63b6f04c 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -98,13 +98,14 @@ class LogicArray extends LogicStructure { return LogicArray._( List.generate( dimensions[0], - (index) => dimensions.length == 1 + (index) => (dimensions.length == 1 ? Logic(width: elementWidth) : LogicArray( dimensions .getRange(1, dimensions.length) .toList(growable: false), - elementWidth), + elementWidth)) + ..arrayIndex = index, growable: false), name: name); } diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 3ac55a338..72e2d3477 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -55,7 +55,34 @@ class LogicStructure implements Logic { }); } - //TODO: dimension List (only on array?) + @override + LogicStructure get rootStructure { + var root = this; + while (parentStructure != null) { + root = parentStructure!; + } + return root; + } + + //TODO + String get structureName { + if (parentStructure != null) { + if (isArrayMember) { + return '${parentStructure!.structureName}[${arrayIndex!}]'; + } else { + return '${parentStructure!.structureName}.$name'; + } + } else { + return name; + } + } + + //TODO: protect this properly + @protected + int? arrayIndex; + + @override + bool get isArrayMember => parentStructure is LogicArray; /////////////////////////////////////////////// /////////////////////////////////////////////// diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart index 308b137c2..afe71bcca 100644 --- a/lib/src/synthesizers/systemverilog.dart +++ b/lib/src/synthesizers/systemverilog.dart @@ -321,9 +321,14 @@ class _SynthModuleDefinition { } else if (logicToSynthMap.containsKey(logic)) { return logicToSynthMap[logic]!; } else { + final synthLogicName = logic.parentStructure is LogicArray + ? logic.structureName + : _getUniqueSynthLogicName(logic.name, allowPortName); final newSynth = _SynthLogic( - logic, _getUniqueSynthLogicName(logic.name, allowPortName), - renameable: !allowPortName); + logic, + synthLogicName, + renameable: !allowPortName, + ); logicToSynthMap[logic] = newSynth; return newSynth; } @@ -359,7 +364,9 @@ class _SynthModuleDefinition { final receiver = logicsToTraverse[i]; if (receiver is LogicArray) { //TODO: is this right? - logicsToTraverse.addAll(receiver.srcConnections); + logicsToTraverse + ..addAll(receiver.srcConnections) + ..addAll(receiver.elements); continue; } final driver = receiver.srcConnection; @@ -414,7 +421,8 @@ class _SynthModuleDefinition { } } - _collapseAssignments(); + //TODO: TEMP TEMP TEMP + // _collapseAssignments(); _collapseChainableModules(); } @@ -546,14 +554,15 @@ class _SynthLogic { final String _name; final bool _renameable; bool get renameable => _mergedNameSynthLogic?.renameable ?? _renameable; - bool _needsDeclaration = true; + bool _needsDeclaration; _SynthLogic? _mergedNameSynthLogic; String? _mergedConst; bool get needsDeclaration => _needsDeclaration; String get name => _mergedNameSynthLogic?.name ?? _mergedConst ?? _name; _SynthLogic(this.logic, this._name, {bool renameable = true}) - : _renameable = renameable; + : _renameable = renameable & !logic.isArrayMember, + _needsDeclaration = !logic.isArrayMember; @override String toString() => "'$name', logic name: '${logic.name}'"; From 278032861a61e92a58bb3e4f8f92604aab43e7dd Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Wed, 3 May 2023 11:04:11 -0700 Subject: [PATCH 11/75] first correct sv generated for simple passthrough --- lib/src/module.dart | 2 ++ lib/src/signals/logic.dart | 6 +++--- lib/src/signals/logic_structure.dart | 4 ++-- lib/src/synthesizers/systemverilog.dart | 10 ++++++---- test/logic_array_test.dart | 1 + 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/src/module.dart b/lib/src/module.dart index f4fb8d3cd..2eda686c2 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -112,10 +112,12 @@ abstract class Module { /// Returns true iff [net] is the same [Logic] as the input port of this /// [Module] with the same name. bool isInput(Logic net) => _inputs[net.name] == net; + //TODO? || (net.isArrayMember && isInput(net.rootStructure!)); /// Returns true iff [net] is the same [Logic] as the output port of this /// [Module] with the same name. bool isOutput(Logic net) => _outputs[net.name] == net; + //TODO? ||(net.isArrayMember && isOutput(net.rootStructure!)); /// Returns true iff [net] is the same [Logic] as an input or output port of /// this [Module] with the same name. diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index d271a8e15..a21e74654 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -364,9 +364,9 @@ class Logic { LogicStructure? get rootStructure { //TODO: do we even need this? - LogicStructure? root; - while (parentStructure != null) { - root = parentStructure; + LogicStructure? root = parentStructure; + while (root?.parentStructure != null) { + root = root?.parentStructure; } return root; } diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 72e2d3477..1304546bf 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -58,8 +58,8 @@ class LogicStructure implements Logic { @override LogicStructure get rootStructure { var root = this; - while (parentStructure != null) { - root = parentStructure!; + while (root.parentStructure != null) { + root = root.parentStructure!; } return root; } diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart index afe71bcca..7f8ca4c80 100644 --- a/lib/src/synthesizers/systemverilog.dart +++ b/lib/src/synthesizers/systemverilog.dart @@ -365,8 +365,9 @@ class _SynthModuleDefinition { if (receiver is LogicArray) { //TODO: is this right? logicsToTraverse - ..addAll(receiver.srcConnections) + // ..addAll(receiver.srcConnections) ..addAll(receiver.elements); + continue; } final driver = receiver.srcConnection; @@ -399,7 +400,9 @@ class _SynthModuleDefinition { logicsToTraverse.addAll(subModule.inputs.values); } else if (driver != null) { - if (!module.isInput(receiver)) { + if (!module.isInput(receiver) && + !(receiver.isArrayMember && + module.isInput(receiver.rootStructure!))) { // stop at the input to this module logicsToTraverse.add(driver); assignments.add(_SynthAssignment(synthDriver, synthReceiver)); @@ -421,8 +424,7 @@ class _SynthModuleDefinition { } } - //TODO: TEMP TEMP TEMP - // _collapseAssignments(); + _collapseAssignments(); _collapseChainableModules(); } diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 50b0c480d..df0985813 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -27,6 +27,7 @@ class SimpleLAPassthrough extends Module { } //TODO: test internal array signals as well +//TODO: test module hierarchy void main() { group('construct LogicArray', () { From c4e0ac756ab363f1a6610a554f3d36f6d6a6c7fa Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Wed, 3 May 2023 11:44:47 -0700 Subject: [PATCH 12/75] got 2d passthrough working --- lib/src/signals/logic_structure.dart | 41 ++++++++++++++++++++++------ test/logic_array_test.dart | 20 ++++++++++---- 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 1304546bf..91b140fb6 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -8,6 +8,7 @@ // Author: Max Korbel import 'dart:collection'; +import 'dart:math'; import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; @@ -123,6 +124,7 @@ class LogicStructure implements Logic { var index = 0; for (final element in elements) { + //TODO: same as `gets`, iterate if element is array? conditionalAssigns .add(element < otherLogic.getRange(index, index + element.width)); index += element.width; @@ -140,7 +142,17 @@ class LogicStructure implements Logic { var index = 0; for (final element in elements) { //TODO: consider if other is a struct, and the ranges match - element <= other.getRange(index, index + element.width); + if (element is LogicStructure) { + var subIndex = 0; + for (final subElement in element.elements) { + subElement <= + other.getRange( + index + subIndex, index + subIndex + subElement.width); + subIndex += subElement.width; + } + } else { + element <= other.getRange(index, index + element.width); + } index += element.width; } } @@ -162,24 +174,37 @@ class LogicStructure implements Logic { //TODO: test edge cases here // grab all elements that fall in this range, keeping track of the offset - int? offset; final matchingElements = []; final requestedWidth = endIndex - startIndex; var index = 0; for (final element in elements) { - final elementInRange = (index >= startIndex) && (index < endIndex); + // if the *start* or *end* of `element` is within [startIndex, endIndex], + // then we have to include it in `matchingElements` + final elementStart = index; + final elementEnd = index + element.width; + + final elementInRange = + ((elementStart >= startIndex) && (elementStart < endIndex)) || + ((elementEnd > startIndex) && (elementEnd <= endIndex)); + if (elementInRange) { - matchingElements.add(element); - offset ??= index - startIndex; + // figure out the subset of `element` that needs to be included + final elementStartGrab = max(elementStart, startIndex) - index; + final elementEndGrab = min(elementEnd, endIndex) - index; + + matchingElements + .add(element.getRange(elementStartGrab, elementEndGrab)); } + index += element.width; } - return matchingElements - .swizzle() - .getRange(offset!, offset + requestedWidth); + assert(!(matchingElements.isEmpty && requestedWidth != 0), + 'If the requested width is not 0, expect to get some matches.'); + + return matchingElements.swizzle(); } @override diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index df0985813..5a60c4bb4 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -82,11 +82,21 @@ void main() { }); }); - test('simple logic array ports module', () async { - final mod = SimpleLAPassthrough(LogicArray([3], 8)); - await mod.build(); - //TODO: test we don't generate extraneous packed things + group('simple logicarray passthrough module', () { + test('single dimension', () async { + final mod = SimpleLAPassthrough(LogicArray([3], 8)); + await mod.build(); + //TODO: test we don't generate extraneous packed things - File('tmp_simple_la_mod.sv').writeAsStringSync(mod.generateSynth()); + File('tmp_simple_la_mod.sv').writeAsStringSync(mod.generateSynth()); + }); + + test('2 dimensions', () async { + final mod = SimpleLAPassthrough(LogicArray([3, 2], 8)); + await mod.build(); + //TODO: test we don't generate extraneous packed things + + File('tmp_simple_la_mod_2dim.sv').writeAsStringSync(mod.generateSynth()); + }); }); } From 32fde744627ef3aeef32fb3a7183a645fa6028e3 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Wed, 3 May 2023 12:41:07 -0700 Subject: [PATCH 13/75] got 3d passthrough working --- lib/src/signals/logic_structure.dart | 43 +++++++++++++++++++--------- test/logic_array_test.dart | 8 ++++++ 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 91b140fb6..f71d1d18d 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -133,26 +133,41 @@ class LogicStructure implements Logic { return ConditionalGroup(conditionalAssigns); } + //TODO: cache this + List _leafElements() { + final leaves = []; + for (final element in elements) { + if (element is LogicStructure) { + leaves.addAll(element._leafElements()); + } else { + leaves.add(element); + } + } + return leaves; + } + @override void gets(Logic other) { if (other.width != width) { throw PortWidthMismatchException(other, width); } + final leafElements = _leafElements(); + var index = 0; - for (final element in elements) { + for (final element in leafElements) { //TODO: consider if other is a struct, and the ranges match - if (element is LogicStructure) { - var subIndex = 0; - for (final subElement in element.elements) { - subElement <= - other.getRange( - index + subIndex, index + subIndex + subElement.width); - subIndex += subElement.width; - } - } else { - element <= other.getRange(index, index + element.width); - } + // if (element is LogicStructure) { + // var subIndex = 0; + // for (final subElement in element.elements) { + // subElement <= + // other.getRange( + // index + subIndex, index + subIndex + subElement.width); + // subIndex += subElement.width; + // } + // } else { + element <= other.getRange(index, index + element.width); + // } index += element.width; } } @@ -178,8 +193,10 @@ class LogicStructure implements Logic { final requestedWidth = endIndex - startIndex; + final leafElements = _leafElements(); + var index = 0; - for (final element in elements) { + for (final element in leafElements) { // if the *start* or *end* of `element` is within [startIndex, endIndex], // then we have to include it in `matchingElements` final elementStart = index; diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 5a60c4bb4..12f781d9f 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -98,5 +98,13 @@ void main() { File('tmp_simple_la_mod_2dim.sv').writeAsStringSync(mod.generateSynth()); }); + + test('3 dimensions', () async { + final mod = SimpleLAPassthrough(LogicArray([3, 2, 3], 8)); + await mod.build(); + //TODO: test we don't generate extraneous packed things + + File('tmp_simple_la_mod_3dim.sv').writeAsStringSync(mod.generateSynth()); + }); }); } From 4569e175dffd571557840f0a9f630010bb2ca9ed Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Wed, 3 May 2023 15:51:07 -0700 Subject: [PATCH 14/75] got sv test passing, fix order of dims bug --- lib/src/signals/logic_structure.dart | 26 +++++----------- lib/src/synthesizers/systemverilog.dart | 10 ++++-- lib/src/utilities/simcompare.dart | 41 +++++++++++++++++++------ lib/src/values/logic_value.dart | 2 +- test/logic_array_test.dart | 31 +++++++++++++++++-- 5 files changed, 75 insertions(+), 35 deletions(-) diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index f71d1d18d..3abb84519 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -35,7 +35,7 @@ class LogicStructure implements Logic { } }) .toList(growable: false) - .swizzle(); + .rswizzle(); @override final String name; @@ -133,12 +133,16 @@ class LogicStructure implements Logic { return ConditionalGroup(conditionalAssigns); } + //TODO + late final List leafElements = + UnmodifiableListView(_calculateLeafElements()); + //TODO: cache this - List _leafElements() { + List _calculateLeafElements() { final leaves = []; for (final element in elements) { if (element is LogicStructure) { - leaves.addAll(element._leafElements()); + leaves.addAll(element._calculateLeafElements()); } else { leaves.add(element); } @@ -152,22 +156,10 @@ class LogicStructure implements Logic { throw PortWidthMismatchException(other, width); } - final leafElements = _leafElements(); - var index = 0; for (final element in leafElements) { - //TODO: consider if other is a struct, and the ranges match - // if (element is LogicStructure) { - // var subIndex = 0; - // for (final subElement in element.elements) { - // subElement <= - // other.getRange( - // index + subIndex, index + subIndex + subElement.width); - // subIndex += subElement.width; - // } - // } else { element <= other.getRange(index, index + element.width); - // } + index += element.width; } } @@ -193,8 +185,6 @@ class LogicStructure implements Logic { final requestedWidth = endIndex - startIndex; - final leafElements = _leafElements(); - var index = 0; for (final element in leafElements) { // if the *start* or *end* of `element` is within [startIndex, endIndex], diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart index 7f8ca4c80..336cab1fd 100644 --- a/lib/src/synthesizers/systemverilog.dart +++ b/lib/src/synthesizers/systemverilog.dart @@ -607,19 +607,23 @@ class _SynthLogic { } } + //TODO: how to uniquify names of `LogicArray`? + String definitionName() { String packedDims; if (logic is LogicArray) { final logicArr = logic as LogicArray; - final packedDimsBuf = - StringBuffer(_widthToRangeDef(logicArr.elementWidth)); + final packedDimsBuf = StringBuffer(); final dims = logicArr.dimensions; - for (final dim in dims.reversed) { + for (final dim in dims) { packedDimsBuf.write(_widthToRangeDef(dim)); } + + packedDimsBuf.write(_widthToRangeDef(logicArr.elementWidth)); + packedDims = packedDimsBuf.toString(); } else { packedDims = _widthToRangeDef(logic.width); diff --git a/lib/src/utilities/simcompare.dart b/lib/src/utilities/simcompare.dart index 0db5655b4..6977230e9 100644 --- a/lib/src/utilities/simcompare.dart +++ b/lib/src/utilities/simcompare.dart @@ -66,10 +66,24 @@ class Vector { } /// Converts this vector into a SystemVerilog check. - String toTbVerilog() { - final assignments = inputValues.keys - .map((signalName) => '$signalName = ${inputValues[signalName]};') - .join('\n'); + String toTbVerilog(Module module) { + final assignments = inputValues.keys.map((signalName) { + final signal = module.signals.firstWhere((e) => e.name == signalName); + + if (signal is LogicArray) { + final arrAssigns = StringBuffer(); + var index = 0; + final fulLVal = LogicValue.of(inputValues[signalName]); + for (final leaf in signal.leafElements) { + final subVal = fulLVal.getRange(index, index + leaf.width); + arrAssigns.writeln('${leaf.structureName} = $subVal;'); + index += leaf.width; + } + return arrAssigns.toString(); + } else { + return '$signalName = ${inputValues[signalName]};'; + } + }).join('\n'); final checks = expectedOutputValues.keys .map((signalName) => _errorCheckString(signalName, expectedOutputValues[signalName], inputValues.toString())) @@ -123,7 +137,7 @@ abstract class SimCompare { expect(oBit, equals(value), reason: errorReason); } } else { - expect(o.value, equals(value)); + expect(o.value, equals(value), reason: errorReason); } } else { throw NonSupportedTypeException(value.runtimeType.toString()); @@ -192,7 +206,13 @@ abstract class SimCompare { }) { String signalDeclaration(String signalName) { final signal = module.signals.firstWhere((e) => e.name == signalName); - if (signal.width != 1) { + + if (signal is LogicArray) { + //TODO: handle packed vs unpacked!! + // ignore: parameter_assignments, prefer_interpolation_to_compose_strings + return signal.dimensions.map((d) => '[${d - 1}:0]').join() + + ' [${signal.elementWidth - 1}:0] $signalName'; + } else if (signal.width != 1) { return '[${signal.width - 1}:0] $signalName'; } else { return signalName; @@ -201,14 +221,15 @@ abstract class SimCompare { final topModule = moduleName ?? module.definitionName; final allSignals = { - for (final e in vectors) ...e.inputValues.keys, - for (final e in vectors) ...e.expectedOutputValues.keys, + for (final v in vectors) ...v.inputValues.keys, + for (final v in vectors) ...v.expectedOutputValues.keys, }; final localDeclarations = allSignals.map((e) => 'logic ${signalDeclaration(e)};').join('\n'); final moduleConnections = allSignals.map((e) => '.$e($e)').join(', '); final moduleInstance = '$topModule dut($moduleConnections);'; - final stimulus = vectors.map((e) => e.toTbVerilog()).join('\n'); + final stimulus = + vectors.map((e) => e.toTbVerilog(module)).join('\n'); //TODO for arrays final generatedVerilog = module.generateSynth(); // so that when they run in parallel, they dont step on each other @@ -258,7 +279,7 @@ abstract class SimCompare { return line; }) .whereNotNull() - .join(); + .join('\n'); if (maskedOutput.isNotEmpty) { print(maskedOutput); } diff --git a/lib/src/values/logic_value.dart b/lib/src/values/logic_value.dart index 2a73b2226..7f8fed749 100644 --- a/lib/src/values/logic_value.dart +++ b/lib/src/values/logic_value.dart @@ -95,7 +95,7 @@ abstract class LogicValue { /// Constructs a [LogicValue] with the [width] number of bits, where every /// bit has the same value of [fill]. /// - /// [width] must be greater than or equal to 0. + /// [width] must be greater than or equal to 0. [fill] must be 1 bit. static LogicValue filled(int width, LogicValue fill) => _FilledLogicValue(fill._enum, width); diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 12f781d9f..1f881e9ad 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -8,12 +8,15 @@ // Author: Max Korbel import 'dart:io'; +import 'dart:math'; import 'package:collection/collection.dart'; import 'package:rohd/rohd.dart'; +import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; class SimpleLAPassthrough extends Module { + Logic get laOut => output('laOut'); SimpleLAPassthrough(LogicArray laIn) { laIn = addInputArray('laIn', laIn, dimensions: laIn.dimensions, elementWidth: laIn.elementWidth); @@ -83,12 +86,34 @@ void main() { }); group('simple logicarray passthrough module', () { + Future testArrayPassthrough(SimpleLAPassthrough mod) async { + await mod.build(); + + const randWidth = 23; + final rand = Random(1234); + final values = List.generate( + 10, + (index) => LogicValue.ofInt(rand.nextInt(1 << randWidth), randWidth) + .replicate(mod.laOut.width ~/ randWidth + 1) + .getRange(0, mod.laOut.width)); + + final vectors = [ + for (final value in values) Vector({'laIn': value}, {'laOut': value}) + ]; + + //TODO: test we don't generate extraneous packed things in verilog + + await SimCompare.checkFunctionalVector(mod, vectors); + SimCompare.checkIverilogVector(mod, vectors, dontDeleteTmpFiles: true); + } + test('single dimension', () async { final mod = SimpleLAPassthrough(LogicArray([3], 8)); - await mod.build(); - //TODO: test we don't generate extraneous packed things + await testArrayPassthrough(mod); + // await mod.build(); + // - File('tmp_simple_la_mod.sv').writeAsStringSync(mod.generateSynth()); + // File('tmp_simple_la_mod.sv').writeAsStringSync(mod.generateSynth()); }); test('2 dimensions', () async { From 457a3b047ce733a39ee19c084f809ec7be13f78f Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Wed, 3 May 2023 15:52:26 -0700 Subject: [PATCH 15/75] got it working for up through 3 dims --- test/logic_array_test.dart | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 1f881e9ad..8e2c20043 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -101,7 +101,7 @@ void main() { for (final value in values) Vector({'laIn': value}, {'laOut': value}) ]; - //TODO: test we don't generate extraneous packed things in verilog + expect(mod.generateSynth().contains('swizzle'), false); await SimCompare.checkFunctionalVector(mod, vectors); SimCompare.checkIverilogVector(mod, vectors, dontDeleteTmpFiles: true); @@ -110,26 +110,16 @@ void main() { test('single dimension', () async { final mod = SimpleLAPassthrough(LogicArray([3], 8)); await testArrayPassthrough(mod); - // await mod.build(); - // - - // File('tmp_simple_la_mod.sv').writeAsStringSync(mod.generateSynth()); }); test('2 dimensions', () async { final mod = SimpleLAPassthrough(LogicArray([3, 2], 8)); - await mod.build(); - //TODO: test we don't generate extraneous packed things - - File('tmp_simple_la_mod_2dim.sv').writeAsStringSync(mod.generateSynth()); + await testArrayPassthrough(mod); }); test('3 dimensions', () async { final mod = SimpleLAPassthrough(LogicArray([3, 2, 3], 8)); - await mod.build(); - //TODO: test we don't generate extraneous packed things - - File('tmp_simple_la_mod_3dim.sv').writeAsStringSync(mod.generateSynth()); + await testArrayPassthrough(mod); }); }); } From 7bbe227846bc4079db9e9458613b0734a8a68b0d Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Wed, 3 May 2023 16:07:10 -0700 Subject: [PATCH 16/75] add test for 4d --- test/logic_array_test.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 8e2c20043..fbcf7cc9b 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -104,7 +104,7 @@ void main() { expect(mod.generateSynth().contains('swizzle'), false); await SimCompare.checkFunctionalVector(mod, vectors); - SimCompare.checkIverilogVector(mod, vectors, dontDeleteTmpFiles: true); + SimCompare.checkIverilogVector(mod, vectors); } test('single dimension', () async { @@ -121,5 +121,10 @@ void main() { final mod = SimpleLAPassthrough(LogicArray([3, 2, 3], 8)); await testArrayPassthrough(mod); }); + + test('4 dimensions', () async { + final mod = SimpleLAPassthrough(LogicArray([5, 4, 3, 2], 8)); + await testArrayPassthrough(mod); + }); }); } From 0acbb01a4993ca6de7510671db203ffce79516f7 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Wed, 3 May 2023 16:20:31 -0700 Subject: [PATCH 17/75] packing and unpacking works --- lib/src/signals/logic.dart | 7 +++++++ test/logic_array_test.dart | 30 +++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index a21e74654..4fba4c6e9 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -482,6 +482,13 @@ class Logic { void gets(Logic other) { _assertConnectable(other); + // If we are connecting a `LogicStructure` to this simple `Logic`, + // then pack it first. + if (other is LogicStructure) { + // ignore: parameter_assignments + other = other.packed; + } + _connect(other); _srcConnection = other; diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index fbcf7cc9b..da6e22620 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -29,6 +29,22 @@ class SimpleLAPassthrough extends Module { } } +class PackAndUnpackPassthrough extends Module implements SimpleLAPassthrough { + Logic get laOut => output('laOut'); + PackAndUnpackPassthrough(LogicArray laIn) { + laIn = addInputArray('laIn', laIn, + dimensions: laIn.dimensions, elementWidth: laIn.elementWidth); + + final intermediate = Logic(name: 'intermediate', width: laIn.width); + + intermediate <= laIn; + + addOutputArray('laOut', + dimensions: laIn.dimensions, elementWidth: laIn.elementWidth) <= + intermediate; + } +} + //TODO: test internal array signals as well //TODO: test module hierarchy @@ -85,8 +101,9 @@ void main() { }); }); - group('simple logicarray passthrough module', () { - Future testArrayPassthrough(SimpleLAPassthrough mod) async { + group('logicarray passthrough', () { + Future testArrayPassthrough(SimpleLAPassthrough mod, + {bool checkNoSwizzle = true}) async { await mod.build(); const randWidth = 23; @@ -101,7 +118,9 @@ void main() { for (final value in values) Vector({'laIn': value}, {'laOut': value}) ]; - expect(mod.generateSynth().contains('swizzle'), false); + if (checkNoSwizzle) { + expect(mod.generateSynth().contains('swizzle'), false); + } await SimCompare.checkFunctionalVector(mod, vectors); SimCompare.checkIverilogVector(mod, vectors); @@ -126,5 +145,10 @@ void main() { final mod = SimpleLAPassthrough(LogicArray([5, 4, 3, 2], 8)); await testArrayPassthrough(mod); }); + + test('pack and unpack', () async { + final mod = PackAndUnpackPassthrough(LogicArray([3], 8)); + await testArrayPassthrough(mod, checkNoSwizzle: false); + }); }); } From 1f4fc58c1e5bea4bc6141f27e528b0fbcd29fc72 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Wed, 3 May 2023 16:21:54 -0700 Subject: [PATCH 18/75] regroup tests --- test/logic_array_test.dart | 50 ++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index da6e22620..451e4ee85 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -126,29 +126,37 @@ void main() { SimCompare.checkIverilogVector(mod, vectors); } - test('single dimension', () async { - final mod = SimpleLAPassthrough(LogicArray([3], 8)); - await testArrayPassthrough(mod); + group('simple', () { + test('single dimension', () async { + final mod = SimpleLAPassthrough(LogicArray([3], 8)); + await testArrayPassthrough(mod); + }); + + test('2 dimensions', () async { + final mod = SimpleLAPassthrough(LogicArray([3, 2], 8)); + await testArrayPassthrough(mod); + }); + + test('3 dimensions', () async { + final mod = SimpleLAPassthrough(LogicArray([3, 2, 3], 8)); + await testArrayPassthrough(mod); + }); + + test('4 dimensions', () async { + final mod = SimpleLAPassthrough(LogicArray([5, 4, 3, 2], 8)); + await testArrayPassthrough(mod); + }); }); - test('2 dimensions', () async { - final mod = SimpleLAPassthrough(LogicArray([3, 2], 8)); - await testArrayPassthrough(mod); - }); - - test('3 dimensions', () async { - final mod = SimpleLAPassthrough(LogicArray([3, 2, 3], 8)); - await testArrayPassthrough(mod); - }); - - test('4 dimensions', () async { - final mod = SimpleLAPassthrough(LogicArray([5, 4, 3, 2], 8)); - await testArrayPassthrough(mod); - }); - - test('pack and unpack', () async { - final mod = PackAndUnpackPassthrough(LogicArray([3], 8)); - await testArrayPassthrough(mod, checkNoSwizzle: false); + group('pack and unpack', () { + test('1d', () async { + final mod = PackAndUnpackPassthrough(LogicArray([3], 8)); + await testArrayPassthrough(mod, checkNoSwizzle: false); + }); + test('3d', () async { + final mod = PackAndUnpackPassthrough(LogicArray([5, 3, 2], 8)); + await testArrayPassthrough(mod, checkNoSwizzle: false); + }); }); }); } From 999610515f1dd10bebc0ad320739928cbc100bf1 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Thu, 4 May 2023 09:18:44 -0700 Subject: [PATCH 19/75] got intermediate arrays instantiating --- lib/src/signals/logic.dart | 17 +++++++- lib/src/signals/logic_array.dart | 15 +++++++ lib/src/signals/logic_structure.dart | 16 +++++++- lib/src/synthesizers/systemverilog.dart | 54 +++++++++++++++++++------ test/logic_array_test.dart | 49 ++++++++++++++++++++++ 5 files changed, 136 insertions(+), 15 deletions(-) diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index 4fba4c6e9..d54b1f280 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -47,6 +47,9 @@ class Const extends Logic { } } +//TODO: move _Wire and all of the "signals" to their own files, use "part" +// so that we can keep things safer instead of using @protected + /// Represents a physical wire which shares a common value with one or /// more [Logic]s. class _Wire { @@ -378,7 +381,7 @@ class Logic { /// Returns the name relative to the [parentStructure]-defined hierarchy, if /// one exists. Otherwise, this is the same as [name]. /// - /// This is useful for finding the name of a signal as an element of a + /// This is useful for finding the name of a signal as an element of a root /// [LogicArray]. String get structureName { if (parentStructure != null) { @@ -396,6 +399,18 @@ class Logic { @protected int? arrayIndex; + //TODO + List? get arrayLocationFromRoot { + if (!isArrayMember) { + return null; + } + + return [ + ...parentStructure!.arrayLocationFromRoot!, + arrayIndex!, + ]; + } + @protected set parentStructure(LogicStructure? newParentStructure) => _parentStructure = newParentStructure; diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index b63b6f04c..44cc76920 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -74,6 +74,9 @@ class LogicArray extends LogicStructure { return arr.elements.first.width; } + @override + String toString() => 'LogicArray($dimensions, $elementWidth): $name'; + ///TODO LogicArray._(super.elements, {super.name}) { if (elements.isNotEmpty) { @@ -110,5 +113,17 @@ class LogicArray extends LogicStructure { name: name); } + //TODO + List? get arrayLocationFromRoot { + if (!isArrayMember) { + return []; + } + + return [ + ...parentStructure!.arrayLocationFromRoot!, + arrayIndex!, + ]; + } + //TODO: can we be stricter about assignments, etc. for arrays, only like-shaped arrays? } diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 3abb84519..9375f748a 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -82,6 +82,18 @@ class LogicStructure implements Logic { @protected int? arrayIndex; + //TODO + List? get arrayLocationFromRoot { + if (!isArrayMember) { + return null; + } + + return [ + ...parentStructure!.arrayLocationFromRoot!, + arrayIndex!, + ]; + } + @override bool get isArrayMember => parentStructure is LogicArray; @@ -294,8 +306,8 @@ class LogicStructure implements Logic { } @override - // TODO: implement srcConnection - Logic? get srcConnection => throw UnimplementedError(); + // TODO: implement srcConnection, should it be exception or null? + Logic? get srcConnection => null; @override Iterable get srcConnections => diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart index 336cab1fd..24543f51e 100644 --- a/lib/src/synthesizers/systemverilog.dart +++ b/lib/src/synthesizers/systemverilog.dart @@ -315,20 +315,31 @@ class _SynthModuleDefinition { _synthInstantiationNameUniquifier.getUniqueName( initialName: initialName, nullStarter: 'm', reserved: reserved); + //TODO: why does this ever need to return null? _SynthLogic? _getSynthLogic(Logic? logic, bool allowPortName) { if (logic == null) { return null; } else if (logicToSynthMap.containsKey(logic)) { return logicToSynthMap[logic]!; } else { - final synthLogicName = logic.parentStructure is LogicArray - ? logic.structureName - : _getUniqueSynthLogicName(logic.name, allowPortName); - final newSynth = _SynthLogic( - logic, - synthLogicName, - renameable: !allowPortName, - ); + _SynthLogic newSynth; + if (logic.isArrayMember) { + // grab the parent array (potentially recursively) + final parentArraySynthLogic = + _getSynthLogic(logic.parentStructure, allowPortName); + + newSynth = _SynthLogicArrayElement(logic, parentArraySynthLogic!); + } else { + final synthLogicName = + _getUniqueSynthLogicName(logic.name, allowPortName); + + newSynth = _SynthLogic( + logic, + synthLogicName, + renameable: !allowPortName, + ); + } + logicToSynthMap[logic] = newSynth; return newSynth; } @@ -367,9 +378,15 @@ class _SynthModuleDefinition { logicsToTraverse // ..addAll(receiver.srcConnections) ..addAll(receiver.elements); + //TODO: what about if it's an array inside a struct? + // ..add(receiver.rootStructure); //TODO: delete this? - continue; + // continue; } + if (receiver.isArrayMember) { + logicsToTraverse.add(receiver.parentStructure!); + } + final driver = receiver.srcConnection; final receiverIsModuleInput = module.isInput(receiver); @@ -550,21 +567,34 @@ class _SynthModuleDefinition { } } +class _SynthLogicArrayElement extends _SynthLogic { + /// The [_SynthLogic] tracking the name of the direct parent array. + final _SynthLogic parentArray; + + @override + bool get _needsDeclaration => false; + + @override + String get name => '${parentArray.name}[${logic.arrayIndex!}]'; + + _SynthLogicArrayElement(Logic logic, this.parentArray) + : super(logic, '**ARRAY_ELEMENT**', renameable: false); +} + /// Represents a logic signal in the generated code within a module. class _SynthLogic { final Logic logic; final String _name; final bool _renameable; bool get renameable => _mergedNameSynthLogic?.renameable ?? _renameable; - bool _needsDeclaration; + bool _needsDeclaration = true; _SynthLogic? _mergedNameSynthLogic; String? _mergedConst; bool get needsDeclaration => _needsDeclaration; String get name => _mergedNameSynthLogic?.name ?? _mergedConst ?? _name; _SynthLogic(this.logic, this._name, {bool renameable = true}) - : _renameable = renameable & !logic.isArrayMember, - _needsDeclaration = !logic.isArrayMember; + : _renameable = renameable; @override String toString() => "'$name', logic name: '${logic.name}'"; diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 451e4ee85..3fa3d01de 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -45,6 +45,31 @@ class PackAndUnpackPassthrough extends Module implements SimpleLAPassthrough { } } +class PackAndUnpackWithArraysPassthrough extends Module + implements SimpleLAPassthrough { + Logic get laOut => output('laOut'); + PackAndUnpackWithArraysPassthrough(LogicArray laIn) { + laIn = addInputArray('laIn', laIn, + dimensions: laIn.dimensions, elementWidth: laIn.elementWidth); + + final intermediate1 = Logic(name: 'intermediate1', width: laIn.width); + final intermediate3 = Logic(name: 'intermediate3', width: laIn.width); + + // unpack with reversed dimensions + final intermediate2 = LogicArray( + laIn.dimensions.reversed.toList(), laIn.elementWidth, + name: 'intermediate2'); + + intermediate1 <= laIn; + intermediate2 <= intermediate1; + intermediate3 <= intermediate2; + + addOutputArray('laOut', + dimensions: laIn.dimensions, elementWidth: laIn.elementWidth) <= + intermediate3; + } +} + //TODO: test internal array signals as well //TODO: test module hierarchy @@ -153,10 +178,34 @@ void main() { final mod = PackAndUnpackPassthrough(LogicArray([3], 8)); await testArrayPassthrough(mod, checkNoSwizzle: false); }); + test('3d', () async { final mod = PackAndUnpackPassthrough(LogicArray([5, 3, 2], 8)); await testArrayPassthrough(mod, checkNoSwizzle: false); }); }); + + group('pack and unpack with arrays', () { + test('1d', () async { + final mod = PackAndUnpackWithArraysPassthrough(LogicArray([3], 8)); + + await mod.build(); + File('tmp_pack_and_unpack_arr.sv') + .writeAsStringSync(mod.generateSynth()); + + // await testArrayPassthrough(mod, checkNoSwizzle: false); + }); + + test('2d', () async { + final mod = PackAndUnpackWithArraysPassthrough(LogicArray([3, 2], 8)); + await testArrayPassthrough(mod, checkNoSwizzle: false); + }); + + test('3d', () async { + final mod = + PackAndUnpackWithArraysPassthrough(LogicArray([4, 3, 2], 8)); + await testArrayPassthrough(mod, checkNoSwizzle: false); + }); + }); }); } From a805276f55613c492de7f3353d062cc8e4e04ec1 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Thu, 4 May 2023 09:42:16 -0700 Subject: [PATCH 20/75] got test working with pack and unpack in sv --- lib/src/synthesizers/systemverilog.dart | 13 ++++++++++--- test/logic_array_test.dart | 7 +------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart index 24543f51e..3a49715b8 100644 --- a/lib/src/synthesizers/systemverilog.dart +++ b/lib/src/synthesizers/systemverilog.dart @@ -389,6 +389,9 @@ class _SynthModuleDefinition { final driver = receiver.srcConnection; + // TODO: is this fixing https://github.com/intel/rohd/issues/254? test? + final receiverIsConstant = driver == null && receiver is Const; + final receiverIsModuleInput = module.isInput(receiver); final receiverIsModuleOutput = module.isOutput(receiver); final driverIsModuleInput = driver != null && module.isInput(driver); @@ -420,13 +423,15 @@ class _SynthModuleDefinition { if (!module.isInput(receiver) && !(receiver.isArrayMember && module.isInput(receiver.rootStructure!))) { + //TODO: This is suspicious, doesn't seem right + // stop at the input to this module logicsToTraverse.add(driver); assignments.add(_SynthAssignment(synthDriver, synthReceiver)); } - } else if (driver == null && receiver.value.isValid) { + } else if (receiverIsConstant && receiver.value.isValid) { assignments.add(_SynthAssignment(receiver.value, synthReceiver)); - } else if (driver == null && !receiver.value.isFloating) { + } else if (receiverIsConstant && !receiver.value.isFloating) { // this is a signal that is *partially* invalid (e.g. 0b1z1x0) assignments.add(_SynthAssignment(receiver.value, synthReceiver)); } @@ -594,7 +599,9 @@ class _SynthLogic { String get name => _mergedNameSynthLogic?.name ?? _mergedConst ?? _name; _SynthLogic(this.logic, this._name, {bool renameable = true}) - : _renameable = renameable; + : _renameable = renameable && + //TODO: should we never rename arrays? + logic is! LogicArray; @override String toString() => "'$name', logic name: '${logic.name}'"; diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 3fa3d01de..c4d584f65 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -188,12 +188,7 @@ void main() { group('pack and unpack with arrays', () { test('1d', () async { final mod = PackAndUnpackWithArraysPassthrough(LogicArray([3], 8)); - - await mod.build(); - File('tmp_pack_and_unpack_arr.sv') - .writeAsStringSync(mod.generateSynth()); - - // await testArrayPassthrough(mod, checkNoSwizzle: false); + await testArrayPassthrough(mod, checkNoSwizzle: false); }); test('2d', () async { From 7698623c6ff18d657b8332fe962fdd6fe108f0b1 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Thu, 4 May 2023 09:51:26 -0700 Subject: [PATCH 21/75] more tests with rearranging --- test/logic_array_test.dart | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index c4d584f65..68e75c4d5 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -70,6 +70,25 @@ class PackAndUnpackWithArraysPassthrough extends Module } } +class RearrangeArraysPassthrough extends Module implements SimpleLAPassthrough { + Logic get laOut => output('laOut'); + RearrangeArraysPassthrough(LogicArray laIn) { + laIn = addInputArray('laIn', laIn, + dimensions: laIn.dimensions, elementWidth: laIn.elementWidth); + + // rearrange with reversed dimensions + final intermediate = LogicArray( + laIn.dimensions.reversed.toList(), laIn.elementWidth, + name: 'intermediate'); + + intermediate <= laIn; + + addOutputArray('laOut', + dimensions: laIn.dimensions, elementWidth: laIn.elementWidth) <= + intermediate; + } +} + //TODO: test internal array signals as well //TODO: test module hierarchy @@ -202,5 +221,10 @@ void main() { await testArrayPassthrough(mod, checkNoSwizzle: false); }); }); + + test('change array dimensions around and back 3d', () async { + final mod = RearrangeArraysPassthrough(LogicArray([4, 3, 2], 8)); + await testArrayPassthrough(mod); + }); }); } From fa4b4652c55326ece024991bbef775fe62e08c3c Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Thu, 4 May 2023 09:53:52 -0700 Subject: [PATCH 22/75] fix empty array test and teardown --- lib/src/signals/logic_structure.dart | 5 +++-- test/logic_array_test.dart | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 9375f748a..d928cdca0 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -317,8 +317,9 @@ class LogicStructure implements Logic { LogicValue get value => packed.value; @override - late final int width = - elements.map((e) => e.width).reduce((w1, w2) => w1 + w2); + late final int width = elements.isEmpty + ? 0 + : elements.map((e) => e.width).reduce((w1, w2) => w1 + w2); @override Logic withSet(int startIndex, Logic update) => diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 68e75c4d5..401b91a3a 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -93,6 +93,10 @@ class RearrangeArraysPassthrough extends Module implements SimpleLAPassthrough { //TODO: test module hierarchy void main() { + tearDown(() async { + await Simulator.reset(); + }); + group('construct LogicArray', () { final listEq = const ListEquality().equals; From 1d946e0a50dd247f061b258b3e7dd95578e35362 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Thu, 4 May 2023 10:52:08 -0700 Subject: [PATCH 23/75] add unpacked arrays, but simcompare needs fixing --- lib/src/module.dart | 13 +++++-- lib/src/signals/logic.dart | 3 +- lib/src/signals/logic_array.dart | 41 ++++++++++++------- lib/src/synthesizers/systemverilog.dart | 24 +++++++++--- lib/src/utilities/simcompare.dart | 13 +++++-- test/logic_array_test.dart | 52 ++++++++++++++++++------- 6 files changed, 103 insertions(+), 43 deletions(-) diff --git a/lib/src/module.dart b/lib/src/module.dart index 2eda686c2..ced3a307f 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -481,12 +481,13 @@ abstract class Module { LogicArray x, { List dimensions = const [1], int elementWidth = 1, - int numDimensionsPacked = 0, //TODO + int numDimensionsUnpacked = 0, //TODO }) { _checkForSafePortName(name); final listEq = const ListEquality().equals; + //TODO: do we care if dimensions match or just width here? if (!listEq(x.dimensions, dimensions)) { //TODO } @@ -494,7 +495,8 @@ abstract class Module { //TODO } - final inArr = LogicArray(dimensions, elementWidth, name: name) + final inArr = LogicArray(dimensions, elementWidth, + name: name, numDimensionsUnpacked: numDimensionsUnpacked) ..gets(x) // ignore: invalid_use_of_protected_member ..parentModule = this; @@ -525,11 +527,14 @@ abstract class Module { String name, { List dimensions = const [1], int elementWidth = 1, - int numDimensionsPacked = 0, //TODO + int numDimensionsUnpacked = 0, //TODO }) { _checkForSafePortName(name); - final outArr = LogicArray(dimensions, elementWidth, name: name) + //TODO: checks like are in `addInputArray` + + final outArr = LogicArray(dimensions, elementWidth, + name: name, numDimensionsUnpacked: numDimensionsUnpacked) // ignore: invalid_use_of_protected_member ..parentModule = this; diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index d54b1f280..74518eb9f 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -396,9 +396,8 @@ class Logic { } //TODO: protect this properly - @protected - int? arrayIndex; + int? arrayIndex; //TODO List? get arrayLocationFromRoot { if (!isArrayMember) { diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index 44cc76920..4b4d5cee6 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -78,7 +78,7 @@ class LogicArray extends LogicStructure { String toString() => 'LogicArray($dimensions, $elementWidth): $name'; ///TODO - LogicArray._(super.elements, {super.name}) { + LogicArray._(super.elements, {super.name, this.numDimensionsUnpacked = 0}) { if (elements.isNotEmpty) { final dim0 = elements.first.width; for (final component in elements) { @@ -91,26 +91,39 @@ class LogicArray extends LogicStructure { } } + final int numDimensionsUnpacked; + ///TODO - factory LogicArray(List dimensions, int elementWidth, {String? name}) { + /// + /// Setting the [numDimensionsUnpacked] gives a hint to [Synthesizer]s about + /// the intent for declaration of signals. By default, all dimensions are + /// packed, but if the value is set to more than `0`, then the outer-most + /// dimensions (first in [dimensions]) will become unpacked. It must be less + /// than or equal to the length of [dimensions]. Modifying it will have no + /// impact on simulation functionality or behavior. In SystemVerilog, there + /// are some differences in access patterns for packed vs. unpacked arrays. + factory LogicArray(List dimensions, int elementWidth, + {String? name, int numDimensionsUnpacked = 0}) { if (dimensions.isEmpty) { //TODO throw Exception('Array must have at least 1 dimension'); } return LogicArray._( - List.generate( - dimensions[0], - (index) => (dimensions.length == 1 - ? Logic(width: elementWidth) - : LogicArray( - dimensions - .getRange(1, dimensions.length) - .toList(growable: false), - elementWidth)) - ..arrayIndex = index, - growable: false), - name: name); + List.generate( + dimensions[0], + (index) => (dimensions.length == 1 + ? Logic(width: elementWidth) + : LogicArray( + dimensions + .getRange(1, dimensions.length) + .toList(growable: false), + elementWidth)) + ..arrayIndex = index, + growable: false), + name: name, + numDimensionsUnpacked: numDimensionsUnpacked, + ); } //TODO diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart index 3a49715b8..499e9dcb7 100644 --- a/lib/src/synthesizers/systemverilog.dart +++ b/lib/src/synthesizers/systemverilog.dart @@ -583,7 +583,9 @@ class _SynthLogicArrayElement extends _SynthLogic { String get name => '${parentArray.name}[${logic.arrayIndex!}]'; _SynthLogicArrayElement(Logic logic, this.parentArray) - : super(logic, '**ARRAY_ELEMENT**', renameable: false); + : assert(logic.isArrayMember, + 'Should only be used for elements in a LogicArray'), + super(logic, '**ARRAY_ELEMENT**', renameable: false); } /// Represents a logic signal in the generated code within a module. @@ -648,27 +650,37 @@ class _SynthLogic { String definitionName() { String packedDims; + String unpackedDims; if (logic is LogicArray) { final logicArr = logic as LogicArray; final packedDimsBuf = StringBuffer(); + final unpackedDimsBuf = StringBuffer(); final dims = logicArr.dimensions; - for (final dim in dims) { - packedDimsBuf.write(_widthToRangeDef(dim)); + for (var i = 0; i < dims.length; i++) { + final dim = dims[i]; + final dimStr = _widthToRangeDef(dim); + if (i < logicArr.numDimensionsUnpacked) { + unpackedDimsBuf.write(dimStr); + } else { + packedDimsBuf.write(dimStr); + } } packedDimsBuf.write(_widthToRangeDef(logicArr.elementWidth)); packedDims = packedDimsBuf.toString(); + unpackedDims = unpackedDimsBuf.toString(); } else { packedDims = _widthToRangeDef(logic.width); + unpackedDims = ''; } - //TODO: unpackedDims - - return '$packedDims $name'; + return [packedDims, name, unpackedDims] + .where((e) => e.isNotEmpty) + .join(' '); } } diff --git a/lib/src/utilities/simcompare.dart b/lib/src/utilities/simcompare.dart index 6977230e9..4067bb685 100644 --- a/lib/src/utilities/simcompare.dart +++ b/lib/src/utilities/simcompare.dart @@ -48,6 +48,7 @@ class Vector { /// the [inputValues]. String _errorCheckString( String sigName, dynamic expected, String inputValues) { + //TODO: make this work so that it checks every array element instead final expectedHexStr = expected is int ? '0x${expected.toRadixString(16)}' : expected.toString(); @@ -209,9 +210,14 @@ abstract class SimCompare { if (signal is LogicArray) { //TODO: handle packed vs unpacked!! + final unpackedDims = + signal.dimensions.getRange(0, signal.numDimensionsUnpacked); + final packedDims = signal.dimensions + .getRange(signal.numDimensionsUnpacked, signal.dimensions.length); // ignore: parameter_assignments, prefer_interpolation_to_compose_strings - return signal.dimensions.map((d) => '[${d - 1}:0]').join() + - ' [${signal.elementWidth - 1}:0] $signalName'; + return packedDims.map((d) => '[${d - 1}:0]').join() + + ' [${signal.elementWidth - 1}:0] $signalName' + + unpackedDims.map((d) => '[${d - 1}:0]').join(); } else if (signal.width != 1) { return '[${signal.width - 1}:0] $signalName'; } else { @@ -228,8 +234,7 @@ abstract class SimCompare { allSignals.map((e) => 'logic ${signalDeclaration(e)};').join('\n'); final moduleConnections = allSignals.map((e) => '.$e($e)').join(', '); final moduleInstance = '$topModule dut($moduleConnections);'; - final stimulus = - vectors.map((e) => e.toTbVerilog(module)).join('\n'); //TODO for arrays + final stimulus = vectors.map((e) => e.toTbVerilog(module)).join('\n'); final generatedVerilog = module.generateSynth(); // so that when they run in parallel, they dont step on each other diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 401b91a3a..c219a757d 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -19,10 +19,14 @@ class SimpleLAPassthrough extends Module { Logic get laOut => output('laOut'); SimpleLAPassthrough(LogicArray laIn) { laIn = addInputArray('laIn', laIn, - dimensions: laIn.dimensions, elementWidth: laIn.elementWidth); + dimensions: laIn.dimensions, + elementWidth: laIn.elementWidth, + numDimensionsUnpacked: laIn.numDimensionsUnpacked); addOutputArray('laOut', - dimensions: laIn.dimensions, elementWidth: laIn.elementWidth) <= + dimensions: laIn.dimensions, + elementWidth: laIn.elementWidth, + numDimensionsUnpacked: laIn.numDimensionsUnpacked) <= laIn; //TODO: add some more interesting logic @@ -33,14 +37,18 @@ class PackAndUnpackPassthrough extends Module implements SimpleLAPassthrough { Logic get laOut => output('laOut'); PackAndUnpackPassthrough(LogicArray laIn) { laIn = addInputArray('laIn', laIn, - dimensions: laIn.dimensions, elementWidth: laIn.elementWidth); + dimensions: laIn.dimensions, + elementWidth: laIn.elementWidth, + numDimensionsUnpacked: laIn.numDimensionsUnpacked); final intermediate = Logic(name: 'intermediate', width: laIn.width); intermediate <= laIn; addOutputArray('laOut', - dimensions: laIn.dimensions, elementWidth: laIn.elementWidth) <= + dimensions: laIn.dimensions, + elementWidth: laIn.elementWidth, + numDimensionsUnpacked: laIn.numDimensionsUnpacked) <= intermediate; } } @@ -48,9 +56,12 @@ class PackAndUnpackPassthrough extends Module implements SimpleLAPassthrough { class PackAndUnpackWithArraysPassthrough extends Module implements SimpleLAPassthrough { Logic get laOut => output('laOut'); - PackAndUnpackWithArraysPassthrough(LogicArray laIn) { + PackAndUnpackWithArraysPassthrough(LogicArray laIn, + {int intermediateUnpacked = 0}) { laIn = addInputArray('laIn', laIn, - dimensions: laIn.dimensions, elementWidth: laIn.elementWidth); + dimensions: laIn.dimensions, + elementWidth: laIn.elementWidth, + numDimensionsUnpacked: laIn.numDimensionsUnpacked); final intermediate1 = Logic(name: 'intermediate1', width: laIn.width); final intermediate3 = Logic(name: 'intermediate3', width: laIn.width); @@ -58,39 +69,48 @@ class PackAndUnpackWithArraysPassthrough extends Module // unpack with reversed dimensions final intermediate2 = LogicArray( laIn.dimensions.reversed.toList(), laIn.elementWidth, - name: 'intermediate2'); + name: 'intermediate2', numDimensionsUnpacked: intermediateUnpacked); intermediate1 <= laIn; intermediate2 <= intermediate1; intermediate3 <= intermediate2; addOutputArray('laOut', - dimensions: laIn.dimensions, elementWidth: laIn.elementWidth) <= + dimensions: laIn.dimensions, + elementWidth: laIn.elementWidth, + numDimensionsUnpacked: laIn.numDimensionsUnpacked) <= intermediate3; } } class RearrangeArraysPassthrough extends Module implements SimpleLAPassthrough { Logic get laOut => output('laOut'); - RearrangeArraysPassthrough(LogicArray laIn) { + RearrangeArraysPassthrough(LogicArray laIn, {int intermediateUnpacked = 0}) { laIn = addInputArray('laIn', laIn, - dimensions: laIn.dimensions, elementWidth: laIn.elementWidth); + dimensions: laIn.dimensions, + elementWidth: laIn.elementWidth, + numDimensionsUnpacked: laIn.numDimensionsUnpacked); // rearrange with reversed dimensions final intermediate = LogicArray( laIn.dimensions.reversed.toList(), laIn.elementWidth, - name: 'intermediate'); + name: 'intermediate', numDimensionsUnpacked: intermediateUnpacked); intermediate <= laIn; addOutputArray('laOut', - dimensions: laIn.dimensions, elementWidth: laIn.elementWidth) <= + dimensions: laIn.dimensions, + elementWidth: laIn.elementWidth, + numDimensionsUnpacked: laIn.numDimensionsUnpacked) <= intermediate; } } //TODO: test internal array signals as well //TODO: test module hierarchy +//TODO: test constant assignments (to part and all of array) +//TODO: Test packed and unpacked arrays both +//TODO: test passing packed into unpacked, unpacked into packed void main() { tearDown(() async { @@ -171,7 +191,7 @@ void main() { } await SimCompare.checkFunctionalVector(mod, vectors); - SimCompare.checkIverilogVector(mod, vectors); + SimCompare.checkIverilogVector(mod, vectors, dontDeleteTmpFiles: true); } group('simple', () { @@ -194,6 +214,12 @@ void main() { final mod = SimpleLAPassthrough(LogicArray([5, 4, 3, 2], 8)); await testArrayPassthrough(mod); }); + + test('4d, half packed', () async { + final mod = SimpleLAPassthrough( + LogicArray([5, 4, 3, 2], 8, numDimensionsUnpacked: 2)); + await testArrayPassthrough(mod); + }); }); group('pack and unpack', () { From 2a9ed8fc3c203f67e54ecb44503ad47557dadb88 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Fri, 5 May 2023 08:17:25 -0700 Subject: [PATCH 24/75] got unpacked test working, but not with iverilog --- lib/src/utilities/simcompare.dart | 43 +++++++++++++++++++++++-------- lib/src/values/logic_value.dart | 3 +++ test/logic_array_test.dart | 7 +++++ 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/lib/src/utilities/simcompare.dart b/lib/src/utilities/simcompare.dart index 4067bb685..09c5e6422 100644 --- a/lib/src/utilities/simcompare.dart +++ b/lib/src/utilities/simcompare.dart @@ -46,15 +46,12 @@ class Vector { /// Computes a SystemVerilog code string that checks in a SystemVerilog /// simulation whether a signal [sigName] has the [expected] value given /// the [inputValues]. - String _errorCheckString( - String sigName, dynamic expected, String inputValues) { - //TODO: make this work so that it checks every array element instead + String _errorCheckString(String sigName, dynamic expected, + LogicValue expectedVal, String inputValues) { final expectedHexStr = expected is int ? '0x${expected.toRadixString(16)}' : expected.toString(); - final expectedValStr = (expected is LogicValue && expected.width == 1) - ? "'${expected.toString(includeWidth: false)}" - : expected.toString(); + final expectedValStr = expectedVal.toString(); if (expected is! int && expected is! LogicValue) { throw Exception( @@ -69,7 +66,8 @@ class Vector { /// Converts this vector into a SystemVerilog check. String toTbVerilog(Module module) { final assignments = inputValues.keys.map((signalName) { - final signal = module.signals.firstWhere((e) => e.name == signalName); + // ignore: invalid_use_of_protected_member + final signal = module.input(signalName); if (signal is LogicArray) { final arrAssigns = StringBuffer(); @@ -85,10 +83,33 @@ class Vector { return '$signalName = ${inputValues[signalName]};'; } }).join('\n'); - final checks = expectedOutputValues.keys - .map((signalName) => _errorCheckString(signalName, - expectedOutputValues[signalName], inputValues.toString())) - .join('\n'); + + final checksList = []; + for (final expectedOutput in expectedOutputValues.entries) { + final outputName = expectedOutput.key; + final outputPort = module.output(outputName); + final expected = expectedOutput.value; + final expectedValue = LogicValue.of( + expected, + width: outputPort.width, + ); + final inputStimulus = inputValues.toString(); + + if (outputPort is LogicArray) { + var index = 0; + for (final leaf in outputPort.leafElements) { + final subVal = expectedValue.getRange(index, index + leaf.width); + checksList.add(_errorCheckString( + leaf.structureName, subVal, subVal, inputStimulus)); + index += leaf.width; + } + } else { + checksList.add(_errorCheckString( + outputName, expected, expectedValue, inputStimulus)); + } + } + final checks = checksList.join('\n'); + final tbVerilog = [ assignments, '#$_offset', diff --git a/lib/src/values/logic_value.dart b/lib/src/values/logic_value.dart index 7f8fed749..3ed9d3fb3 100644 --- a/lib/src/values/logic_value.dart +++ b/lib/src/values/logic_value.dart @@ -226,6 +226,9 @@ abstract class LogicValue { return LogicValue.ofString(val).zeroExtend(width); } } + } else if (val is Iterable) { + // TODO: handle if fill + return ofIterable(val); } else { throw LogicValueConstructionException('Unrecognized value type "$val" - ' 'Unknown type ${val.runtimeType}' diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index c219a757d..d5dd13c15 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -111,6 +111,7 @@ class RearrangeArraysPassthrough extends Module implements SimpleLAPassthrough { //TODO: test constant assignments (to part and all of array) //TODO: Test packed and unpacked arrays both //TODO: test passing packed into unpacked, unpacked into packed +//TODO: test that unpacked and packed are properly instantiated in SV void main() { tearDown(() async { @@ -215,6 +216,12 @@ void main() { await testArrayPassthrough(mod); }); + test('1d, unpacked', () async { + final mod = + SimpleLAPassthrough(LogicArray([3], 8, numDimensionsUnpacked: 1)); + await testArrayPassthrough(mod); + }); + test('4d, half packed', () async { final mod = SimpleLAPassthrough( LogicArray([5, 4, 3, 2], 8, numDimensionsUnpacked: 2)); From a9dbef1671dc90f08ec2b1f475fe45c921cb97f6 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Fri, 5 May 2023 09:53:04 -0700 Subject: [PATCH 25/75] test unpacked, but no sv sims --- lib/src/utilities/simcompare.dart | 15 +++++++++++---- test/logic_array_test.dart | 30 ++++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/lib/src/utilities/simcompare.dart b/lib/src/utilities/simcompare.dart index 09c5e6422..2bdbea718 100644 --- a/lib/src/utilities/simcompare.dart +++ b/lib/src/utilities/simcompare.dart @@ -46,7 +46,7 @@ class Vector { /// Computes a SystemVerilog code string that checks in a SystemVerilog /// simulation whether a signal [sigName] has the [expected] value given /// the [inputValues]. - String _errorCheckString(String sigName, dynamic expected, + static String _errorCheckString(String sigName, dynamic expected, LogicValue expectedVal, String inputValues) { final expectedHexStr = expected is int ? '0x${expected.toRadixString(16)}' @@ -72,9 +72,9 @@ class Vector { if (signal is LogicArray) { final arrAssigns = StringBuffer(); var index = 0; - final fulLVal = LogicValue.of(inputValues[signalName]); + final fullVal = LogicValue.of(inputValues[signalName]); for (final leaf in signal.leafElements) { - final subVal = fulLVal.getRange(index, index + leaf.width); + final subVal = fullVal.getRange(index, index + leaf.width); arrAssigns.writeln('${leaf.structureName} = $subVal;'); index += leaf.width; } @@ -202,6 +202,7 @@ abstract class SimCompare { bool allowWarnings = false, bool maskKnownWarnings = true, bool enableChecking = true, + bool buildOnly = false, }) { final result = iverilogVector(module, vectors, moduleName: moduleName, @@ -209,7 +210,8 @@ abstract class SimCompare { dumpWaves: dumpWaves, iverilogExtraArgs: iverilogExtraArgs, allowWarnings: allowWarnings, - maskKnownWarnings: maskKnownWarnings); + maskKnownWarnings: maskKnownWarnings, + buildOnly: buildOnly); if (enableChecking) { expect(result, true); } @@ -225,6 +227,7 @@ abstract class SimCompare { List iverilogExtraArgs = const [], bool allowWarnings = false, bool maskKnownWarnings = true, + bool buildOnly = false, }) { String signalDeclaration(String signalName) { final signal = module.signals.firstWhere((e) => e.name == signalName); @@ -326,6 +329,10 @@ abstract class SimCompare { return false; } + if (buildOnly) { + return true; + } + final simResult = Process.runSync('vvp', [tmpOutput]); if (printIfContentsAndCheckError(simResult.stdout)) { return false; diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index d5dd13c15..7e14ad884 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -113,6 +113,10 @@ class RearrangeArraysPassthrough extends Module implements SimpleLAPassthrough { //TODO: test passing packed into unpacked, unpacked into packed //TODO: test that unpacked and packed are properly instantiated in SV +//TODO: file issue tracking that unpacked arrays not fully tested +// https://github.com/steveicarus/iverilog/issues/482 +// https://github.com/verilator/verilator/issues/2907 + void main() { tearDown(() async { await Simulator.reset(); @@ -172,7 +176,7 @@ void main() { group('logicarray passthrough', () { Future testArrayPassthrough(SimpleLAPassthrough mod, - {bool checkNoSwizzle = true}) async { + {bool checkNoSwizzle = true, bool noSvSim = false}) async { await mod.build(); const randWidth = 23; @@ -192,7 +196,7 @@ void main() { } await SimCompare.checkFunctionalVector(mod, vectors); - SimCompare.checkIverilogVector(mod, vectors, dontDeleteTmpFiles: true); + SimCompare.checkIverilogVector(mod, vectors, buildOnly: noSvSim); } group('simple', () { @@ -219,13 +223,31 @@ void main() { test('1d, unpacked', () async { final mod = SimpleLAPassthrough(LogicArray([3], 8, numDimensionsUnpacked: 1)); - await testArrayPassthrough(mod); + + // unpacked array assignment not fully supported in iverilog + await testArrayPassthrough(mod, noSvSim: true); + + final sv = mod.generateSynth(); + expect(sv.contains(RegExp(r'\[7:0\]\s*laIn\s*\[2:0\]')), true); + expect(sv.contains(RegExp(r'\[7:0\]\s*laOut\s*\[2:0\]')), true); }); test('4d, half packed', () async { final mod = SimpleLAPassthrough( LogicArray([5, 4, 3, 2], 8, numDimensionsUnpacked: 2)); - await testArrayPassthrough(mod); + + // unpacked array assignment not fully supported in iverilog + await testArrayPassthrough(mod, noSvSim: true); + + final sv = mod.generateSynth(); + expect( + sv.contains(RegExp( + r'\[2:0\]\s*\[1:0\]\s*\[7:0\]\s*laIn\s*\[4:0\]\s*\[3:0\]')), + true); + expect( + sv.contains(RegExp( + r'\[2:0\]\s*\[1:0\]\s*\[7:0\]\s*laOut\s*\[4:0\]\s*\[3:0\]')), + true); }); }); From 56ba4d32699dbc09c1439c78f03d0c6404a2d26c Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Fri, 5 May 2023 09:58:45 -0700 Subject: [PATCH 26/75] add more unpacked tests, no sv sim --- test/logic_array_test.dart | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 7e14ad884..2408cb2da 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -106,7 +106,6 @@ class RearrangeArraysPassthrough extends Module implements SimpleLAPassthrough { } } -//TODO: test internal array signals as well //TODO: test module hierarchy //TODO: test constant assignments (to part and all of array) //TODO: Test packed and unpacked arrays both @@ -261,6 +260,14 @@ void main() { final mod = PackAndUnpackPassthrough(LogicArray([5, 3, 2], 8)); await testArrayPassthrough(mod, checkNoSwizzle: false); }); + + test('3d unpacked', () async { + final mod = PackAndUnpackPassthrough( + LogicArray([5, 3, 2], 8, numDimensionsUnpacked: 2)); + + // unpacked array assignment not fully supported in iverilog + await testArrayPassthrough(mod, checkNoSwizzle: false, noSvSim: true); + }); }); group('pack and unpack with arrays', () { @@ -279,11 +286,31 @@ void main() { PackAndUnpackWithArraysPassthrough(LogicArray([4, 3, 2], 8)); await testArrayPassthrough(mod, checkNoSwizzle: false); }); + + test('3d unpacked', () async { + final mod = PackAndUnpackWithArraysPassthrough( + LogicArray([4, 3, 2], 8, numDimensionsUnpacked: 2), + intermediateUnpacked: 1); + + // unpacked array assignment not fully supported in iverilog + await testArrayPassthrough(mod, checkNoSwizzle: false, noSvSim: true); + }); }); - test('change array dimensions around and back 3d', () async { - final mod = RearrangeArraysPassthrough(LogicArray([4, 3, 2], 8)); - await testArrayPassthrough(mod); + group('change array dimensions around and back', () { + test('3d', () async { + final mod = RearrangeArraysPassthrough(LogicArray([4, 3, 2], 8)); + await testArrayPassthrough(mod); + }); + + test('3d unpacked', () async { + final mod = RearrangeArraysPassthrough( + LogicArray([4, 3, 2], 8, numDimensionsUnpacked: 2), + intermediateUnpacked: 1); + + // unpacked array assignment not fully supported in iverilog + await testArrayPassthrough(mod, noSvSim: true); + }); }); }); } From 682579fb0eaa5becdadcd22e250af760a85f7c5f Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Fri, 5 May 2023 10:00:21 -0700 Subject: [PATCH 27/75] sv check for intermediate unpacked --- test/logic_array_test.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 2408cb2da..192d535a7 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -310,6 +310,9 @@ void main() { // unpacked array assignment not fully supported in iverilog await testArrayPassthrough(mod, noSvSim: true); + + final sv = mod.generateSynth(); + expect(sv.contains('logic [2:0][3:0][7:0] intermediate [1:0]'), true); }); }); }); From d6539220965d08d485a93579fcbfbfacd160b4a5 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Fri, 5 May 2023 11:35:18 -0700 Subject: [PATCH 28/75] tests for name collisions with arrays --- test/logic_array_test.dart | 53 +++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 192d535a7..d6a1d1793 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -64,7 +64,7 @@ class PackAndUnpackWithArraysPassthrough extends Module numDimensionsUnpacked: laIn.numDimensionsUnpacked); final intermediate1 = Logic(name: 'intermediate1', width: laIn.width); - final intermediate3 = Logic(name: 'intermediate3', width: laIn.width); + final intermediate3 = Logic(name: 'intermediate2', width: laIn.width); // unpack with reversed dimensions final intermediate2 = LogicArray( @@ -106,6 +106,41 @@ class RearrangeArraysPassthrough extends Module implements SimpleLAPassthrough { } } +class ArrayNameConflicts extends Module implements SimpleLAPassthrough { + Logic get laOut => output('laOut'); + ArrayNameConflicts(LogicArray laIn, {int intermediateUnpacked = 0}) { + laIn = addInputArray('laIn', laIn, + dimensions: laIn.dimensions, + elementWidth: laIn.elementWidth, + numDimensionsUnpacked: laIn.numDimensionsUnpacked); + + final intermediate1 = Logic(name: 'intermediate', width: laIn.width); + final intermediate3 = Logic(name: 'intermediate', width: laIn.width); + final intermediate5 = Logic(name: 'intermediate', width: laIn.width); + + // unpack with reversed dimensions + final intermediate2 = LogicArray( + laIn.dimensions.reversed.toList(), laIn.elementWidth, + name: 'intermediate', numDimensionsUnpacked: intermediateUnpacked); + + final intermediate4 = LogicArray( + laIn.dimensions.reversed.toList(), laIn.elementWidth, + name: 'intermediate', numDimensionsUnpacked: intermediateUnpacked); + + intermediate1 <= laIn; + intermediate2 <= intermediate1; + intermediate3 <= intermediate2; + intermediate4 <= intermediate3; + intermediate5 <= intermediate4; + + addOutputArray('laOut', + dimensions: laIn.dimensions, + elementWidth: laIn.elementWidth, + numDimensionsUnpacked: laIn.numDimensionsUnpacked) <= + intermediate5; + } +} + //TODO: test module hierarchy //TODO: test constant assignments (to part and all of array) //TODO: Test packed and unpacked arrays both @@ -315,5 +350,21 @@ void main() { expect(sv.contains('logic [2:0][3:0][7:0] intermediate [1:0]'), true); }); }); + + group('name collisions', () { + test('3d', () async { + final mod = ArrayNameConflicts(LogicArray([4, 3, 2], 8)); + await testArrayPassthrough(mod, checkNoSwizzle: false); + }); + + test('3d unpacked', () async { + final mod = ArrayNameConflicts( + LogicArray([4, 3, 2], 8, numDimensionsUnpacked: 2), + intermediateUnpacked: 1); + + // unpacked array assignment not fully supported in iverilog + await testArrayPassthrough(mod, checkNoSwizzle: false, noSvSim: true); + }); + }); }); } From 4b12d7d12a16f3924b8d2230bf6f728568878b62 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Fri, 5 May 2023 12:38:11 -0700 Subject: [PATCH 29/75] got array-based instantiation working --- lib/src/module.dart | 8 ++- lib/src/signals/logic_structure.dart | 7 +- lib/src/synthesizers/systemverilog.dart | 6 +- test/logic_array_test.dart | 93 ++++++++++++++++++++++++- 4 files changed, 107 insertions(+), 7 deletions(-) diff --git a/lib/src/module.dart b/lib/src/module.dart index ced3a307f..3c27ad47a 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -111,12 +111,16 @@ abstract class Module { /// Returns true iff [net] is the same [Logic] as the input port of this /// [Module] with the same name. - bool isInput(Logic net) => _inputs[net.name] == net; + bool isInput(Logic net) => + _inputs[net.name] == net || + (net.isArrayMember && isInput(net.parentStructure!)); //TODO? || (net.isArrayMember && isInput(net.rootStructure!)); /// Returns true iff [net] is the same [Logic] as the output port of this /// [Module] with the same name. - bool isOutput(Logic net) => _outputs[net.name] == net; + bool isOutput(Logic net) => + _outputs[net.name] == net || + (net.isArrayMember && isOutput(net.parentStructure!)); //TODO? ||(net.isArrayMember && isOutput(net.rootStructure!)); /// Returns true iff [net] is the same [Logic] as an input or output port of diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index d928cdca0..68b0ee9ef 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -275,7 +275,12 @@ class LogicStructure implements Logic { @protected @override - set parentModule(Module? newParentModule) => _parentModule = newParentModule; + set parentModule(Module? newParentModule) { + _parentModule = newParentModule; + for (final element in elements) { + element.parentModule = newParentModule; + } + } //TODO: to track naming @override diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart index 499e9dcb7..5c170baaf 100644 --- a/lib/src/synthesizers/systemverilog.dart +++ b/lib/src/synthesizers/systemverilog.dart @@ -392,8 +392,10 @@ class _SynthModuleDefinition { // TODO: is this fixing https://github.com/intel/rohd/issues/254? test? final receiverIsConstant = driver == null && receiver is Const; - final receiverIsModuleInput = module.isInput(receiver); - final receiverIsModuleOutput = module.isOutput(receiver); + final receiverIsModuleInput = + module.isInput(receiver) && !receiver.isArrayMember; + final receiverIsModuleOutput = + module.isOutput(receiver) && !receiver.isArrayMember; final driverIsModuleInput = driver != null && module.isInput(driver); final driverIsModuleOutput = driver != null && module.isOutput(driver); diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index d6a1d1793..3a7c42178 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -141,11 +141,64 @@ class ArrayNameConflicts extends Module implements SimpleLAPassthrough { } } +class SimpleArraysAndHierarchy extends Module implements SimpleLAPassthrough { + Logic get laOut => output('laOut'); + SimpleArraysAndHierarchy(LogicArray laIn) { + laIn = addInputArray('laIn', laIn, + dimensions: laIn.dimensions, + elementWidth: laIn.elementWidth, + numDimensionsUnpacked: laIn.numDimensionsUnpacked); + + final intermediate = SimpleLAPassthrough(laIn).laOut; + + addOutputArray('laOut', + dimensions: laIn.dimensions, + elementWidth: laIn.elementWidth, + numDimensionsUnpacked: laIn.numDimensionsUnpacked) <= + intermediate; + } +} + +class FancyArraysAndHierarchy extends Module implements SimpleLAPassthrough { + Logic get laOut => output('laOut'); + FancyArraysAndHierarchy(LogicArray laIn, {int intermediateUnpacked = 0}) { + laIn = addInputArray('laIn', laIn, + dimensions: laIn.dimensions, + elementWidth: laIn.elementWidth, + numDimensionsUnpacked: laIn.numDimensionsUnpacked); + + final invertedLaIn = LogicArray(laIn.dimensions, laIn.elementWidth, + numDimensionsUnpacked: intermediateUnpacked) + ..gets(~laIn); + + final x1 = SimpleLAPassthrough(laIn).laOut; + final x2 = SimpleLAPassthrough(laIn).laOut; + final x3 = SimpleLAPassthrough(invertedLaIn).laOut; + final x4 = SimpleLAPassthrough(invertedLaIn).laOut; + + final y1 = ~(x1 ^ x3); + final y2 = ~(x2 ^ x4); + + final z1 = laIn ^ y1; + final z2 = y2 ^ laIn; + + final same = z1 & z2; + + addOutputArray('laOut', + dimensions: laIn.dimensions, + elementWidth: laIn.elementWidth, + numDimensionsUnpacked: laIn.numDimensionsUnpacked) <= + same; + } +} + //TODO: test module hierarchy //TODO: test constant assignments (to part and all of array) -//TODO: Test packed and unpacked arrays both //TODO: test passing packed into unpacked, unpacked into packed //TODO: test that unpacked and packed are properly instantiated in SV +//TODO: test passing Logic into addInput/OutputArray works +//TODO: test empty array +//TODO: test array with 1 element (KNOWN BUG) //TODO: file issue tracking that unpacked arrays not fully tested // https://github.com/steveicarus/iverilog/issues/482 @@ -230,7 +283,8 @@ void main() { } await SimCompare.checkFunctionalVector(mod, vectors); - SimCompare.checkIverilogVector(mod, vectors, buildOnly: noSvSim); + SimCompare.checkIverilogVector(mod, vectors, + buildOnly: noSvSim, dontDeleteTmpFiles: true); } group('simple', () { @@ -366,5 +420,40 @@ void main() { await testArrayPassthrough(mod, checkNoSwizzle: false, noSvSim: true); }); }); + + group('simple hierarchy', () { + test('3d', () async { + final mod = SimpleArraysAndHierarchy(LogicArray([2], 8)); + await testArrayPassthrough(mod); + //TODO: BAD! where's the sub-module instatiation!! + + expect(mod.generateSynth(), contains('SimpleLAPassthrough')); + }); + + test('3d unpacked', () async { + final mod = SimpleArraysAndHierarchy( + LogicArray([4, 3, 2], 8, numDimensionsUnpacked: 2)); + + // unpacked array assignment not fully supported in iverilog + await testArrayPassthrough(mod, noSvSim: true); + }); + }); + + group('fancy hierarchy', () { + test('3d', () async { + final mod = FancyArraysAndHierarchy(LogicArray([4, 3, 2], 8)); + await testArrayPassthrough(mod, checkNoSwizzle: false); + //TODO: BAD! where's the sub-module instatiation!! + }); + + test('3d unpacked', () async { + final mod = FancyArraysAndHierarchy( + LogicArray([4, 3, 2], 8, numDimensionsUnpacked: 2), + intermediateUnpacked: 1); + + // unpacked array assignment not fully supported in iverilog + await testArrayPassthrough(mod, checkNoSwizzle: false, noSvSim: true); + }); + }); }); } From 9332224baf619fbacae2854feaab255c51eab645 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Fri, 5 May 2023 12:44:46 -0700 Subject: [PATCH 30/75] all hier tests passing --- test/logic_array_test.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 3a7c42178..4230c9ffc 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -436,6 +436,8 @@ void main() { // unpacked array assignment not fully supported in iverilog await testArrayPassthrough(mod, noSvSim: true); + + expect(mod.generateSynth(), contains('SimpleLAPassthrough')); }); }); From 410cb4fbfe208b8311d48de45e25a679d5530308 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Fri, 5 May 2023 12:50:46 -0700 Subject: [PATCH 31/75] add LogicArray.of --- lib/src/signals/logic_array.dart | 10 ++++++++++ test/logic_array_test.dart | 3 +-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index 4b4d5cee6..c072c7694 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -138,5 +138,15 @@ class LogicArray extends LogicStructure { ]; } + //TODO: doc and test + factory LogicArray.of(Logic other, + {required List dimensions, + required int elementWidth, + String? name, + int numDimensionsUnpacked = 0}) => + LogicArray(dimensions, elementWidth, + name: name, numDimensionsUnpacked: numDimensionsUnpacked) + ..gets(other); + //TODO: can we be stricter about assignments, etc. for arrays, only like-shaped arrays? } diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 4230c9ffc..390be162f 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -283,8 +283,7 @@ void main() { } await SimCompare.checkFunctionalVector(mod, vectors); - SimCompare.checkIverilogVector(mod, vectors, - buildOnly: noSvSim, dontDeleteTmpFiles: true); + SimCompare.checkIverilogVector(mod, vectors, buildOnly: noSvSim); } group('simple', () { From 12f84771d98618a81ce6cfaa8440e2f6eca2db71 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Fri, 5 May 2023 16:34:46 -0700 Subject: [PATCH 32/75] add range fix and hier dimension fix --- lib/src/signals/logic_array.dart | 9 ++++++++- lib/src/synthesizers/systemverilog.dart | 6 +++--- test/logic_array_test.dart | 15 ++++++++++++++- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index c072c7694..c03d748fd 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -7,6 +7,8 @@ // 2023 May 1 // Author: Max Korbel +import 'dart:math'; + import 'package:collection/collection.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd/src/exceptions/module/module_exceptions.dart'; @@ -109,6 +111,8 @@ class LogicArray extends LogicStructure { throw Exception('Array must have at least 1 dimension'); } + //TODO: check that numDimensionsUnpacked <= dimensions.length + return LogicArray._( List.generate( dimensions[0], @@ -118,7 +122,10 @@ class LogicArray extends LogicStructure { dimensions .getRange(1, dimensions.length) .toList(growable: false), - elementWidth)) + elementWidth, + //TODO: test that this gets propagated down properly + numDimensionsUnpacked: max(0, numDimensionsUnpacked - 1), + )) ..arrayIndex = index, growable: false), name: name, diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart index 5c170baaf..f7f022134 100644 --- a/lib/src/synthesizers/systemverilog.dart +++ b/lib/src/synthesizers/systemverilog.dart @@ -640,8 +640,8 @@ class _SynthLogic { _needsDeclaration = false; } - static String _widthToRangeDef(int width) { - if (width > 1) { + static String _widthToRangeDef(int width, {bool forceRange = false}) { + if (width > 1 || forceRange) { return '[${width - 1}:0]'; } else { return ''; @@ -663,7 +663,7 @@ class _SynthLogic { final dims = logicArr.dimensions; for (var i = 0; i < dims.length; i++) { final dim = dims[i]; - final dimStr = _widthToRangeDef(dim); + final dimStr = _widthToRangeDef(dim, forceRange: true); if (i < logicArr.numDimensionsUnpacked) { unpackedDimsBuf.write(dimStr); } else { diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 390be162f..a36b20cd6 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -283,7 +283,8 @@ void main() { } await SimCompare.checkFunctionalVector(mod, vectors); - SimCompare.checkIverilogVector(mod, vectors, buildOnly: noSvSim); + SimCompare.checkIverilogVector(mod, vectors, + buildOnly: noSvSim, dontDeleteTmpFiles: true); } group('simple', () { @@ -292,6 +293,11 @@ void main() { await testArrayPassthrough(mod); }); + test('single element', () async { + final mod = SimpleLAPassthrough(LogicArray([1], 8)); + await testArrayPassthrough(mod); + }); + test('2 dimensions', () async { final mod = SimpleLAPassthrough(LogicArray([3, 2], 8)); await testArrayPassthrough(mod); @@ -319,6 +325,13 @@ void main() { expect(sv.contains(RegExp(r'\[7:0\]\s*laOut\s*\[2:0\]')), true); }); + test('single element, unpacked', () async { + final mod = + SimpleLAPassthrough(LogicArray([1], 8, numDimensionsUnpacked: 1)); + await testArrayPassthrough(mod, noSvSim: true); + //TODO: bug in iverilog? https://github.com/steveicarus/iverilog/issues/915 + }); + test('4d, half packed', () async { final mod = SimpleLAPassthrough( LogicArray([5, 4, 3, 2], 8, numDimensionsUnpacked: 2)); From 83b5e0eeaf79f272ee62db68ac1b2b558faef79e Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Wed, 10 May 2023 10:10:45 -0700 Subject: [PATCH 33/75] logicvalue of improvements, and structure test starting --- lib/src/module.dart | 5 ++ lib/src/signals/logic_array.dart | 2 +- lib/src/signals/logic_structure.dart | 7 ++- lib/src/values/logic_value.dart | 77 ++++++++++++++++++---------- test/logic_array_test.dart | 15 ++++-- test/logic_structure_test.dart | 37 +++++++++++++ 6 files changed, 107 insertions(+), 36 deletions(-) diff --git a/lib/src/module.dart b/lib/src/module.dart index 3c27ad47a..4d7079d02 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -123,6 +123,11 @@ abstract class Module { (net.isArrayMember && isOutput(net.parentStructure!)); //TODO? ||(net.isArrayMember && isOutput(net.rootStructure!)); + //TODO: what if inputs/outputs are added multiple times? + // what if elements of arrays are added again? is that ok? + // what if an array is added which had elements previously? is that ok? + // what if in general input is fed into input? + /// 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); diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index c03d748fd..4e4e22f1f 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -29,7 +29,7 @@ class LogicArray extends LogicStructure { final currDim = elements.length; if (currDim == 0) { - return [currDim]; + return const [0]; } // check if all elements are: diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 68b0ee9ef..c85bab81b 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -44,7 +44,7 @@ class LogicStructure implements Logic { static int _structIdx = 0; /// Creates a new [LogicStructure] with [elements] as elements. - LogicStructure(List elements, {String? name}) + LogicStructure(Iterable elements, {String? name}) : name = (name == null || name.isEmpty) ? 'st${_structIdx++}' : Sanitizer.sanitizeSV(name) { @@ -386,8 +386,9 @@ class LogicStructure implements Logic { @override Logic xor() => packed.xor(); + @Deprecated('Use `value` instead.' + ' Check `width` separately to confirm single-bit.') @override - // ignore: deprecated_member_use_from_same_package LogicValue get bit => packed.bit; @override @@ -415,11 +416,9 @@ class LogicStructure implements Logic { Logic lte(dynamic other) => packed.lte(other); @override - // ignore: deprecated_member_use_from_same_package bool hasValidValue() => packed.hasValidValue(); @override - // ignore: deprecated_member_use_from_same_package bool isFloating() => packed.isFloating(); @override diff --git a/lib/src/values/logic_value.dart b/lib/src/values/logic_value.dart index 3ed9d3fb3..3607d24b0 100644 --- a/lib/src/values/logic_value.dart +++ b/lib/src/values/logic_value.dart @@ -117,22 +117,24 @@ abstract class LogicValue { /// Constructs a [LogicValue] from [val] which could be of a variety of types. /// - /// Supported types include [String], [bool], [int], [BigInt], and - /// [LogicValue]. - /// - /// If [fill] is set, then all bits of the returne value will be set to [val]. - /// If the [val] is not representable as a single bit of information, then - /// setting [fill] will throw an [Exception]. - /// - /// If the [width] can be inferred from the type ([String] or [LogicValue]), - /// then [width] does not need to be provided. If [width] is provided and - /// does not match an inferred width, then [width] is used. If a - /// [width] cannot be inferred, then it is required, or else it will throw - /// an [Exception]. If [val] does not fit in a specified [width], - /// then the returned value will be truncated. [bool]s - /// will infer a default width of `1`, but it can be overridden. Invalid - /// 1-bit [val]s will always be [fill]ed and must provide a [width], - /// even if [fill] is `false`. + /// Supported types include [String], [bool], [int], [BigInt], [LogicValue], + /// and [Iterable]. + /// + /// If [fill] is set, then all bits of the returned value will be set to + /// [val]. If the [val] is not representable as a single bit of information, + /// then setting [fill] will throw an [Exception]. + /// + /// If the [width] can be inferred from the type (e.g. [String], [LogicValue], + /// [Iterable]), then [width] does not need to be provided. + /// If [width] is provided and does not match an inferred width, then [width] + /// is used. + /// If a [width] cannot be inferred, then it is required, or else it will + /// throw an [Exception]. + /// If [val] does not fit in a specified [width], then the returned value will + /// be truncated. + /// [bool]s will infer a default width of `1`, but it can be overridden. + /// Invalid 1-bit [val]s will always be [fill]ed even if [fill] is `false`, + /// but will default to a width of 1 unless [width] is specified. static LogicValue of(dynamic val, {bool fill = false, int? width}) { if (val is int) { if (width == null) { @@ -183,16 +185,16 @@ abstract class LogicValue { 'Only 1-bit `LogicValue`s can be filled'); } - if (val.width == 1 && - (val == LogicValue.x || val == LogicValue.z || fill)) { + if (val.width == 1 && (!val.isValid || fill)) { + if (!val.isValid) { + // ignore: parameter_assignments + width ??= 1; + } if (width == null) { throw LogicValueConstructionException( 'Filled `LogicValue` $val must have provided a width.'); } return LogicValue.filled(width, val); - } else if (fill) { - throw LogicValueConstructionException( - 'Failed to fill value with $val. To fill, it should be 1 bit.'); } else { if (val.width == width || width == null) { return val; @@ -209,14 +211,15 @@ abstract class LogicValue { } if (val.length == 1 && (val == 'x' || val == 'z' || fill)) { + if (val == 'x' || val == 'z') { + // ignore: parameter_assignments + width ??= 1; + } if (width == null) { throw LogicValueConstructionException( 'Filled `String` $val must have provided a width.'); } return LogicValue.filled(width, LogicValue.ofString(val)); - } else if (fill) { - throw LogicValueConstructionException( - 'Failed to fill value with $val. To fill, it should be 1 bit.'); } else { if (val.length == width || width == null) { return LogicValue.ofString(val); @@ -227,8 +230,30 @@ abstract class LogicValue { } } } else if (val is Iterable) { - // TODO: handle if fill - return ofIterable(val); + if (fill && val.length != 1) { + throw LogicValueConstructionException( + 'Only 1-bit values can be filled'); + } + + if (val.length == 1 && + (val.first == LogicValue.x || val.first == LogicValue.z || fill)) { + if (!val.first.isValid) { + width ??= 1; + } + if (width == null) { + throw LogicValueConstructionException( + 'Filled `Iterable` $val must have provided a width.'); + } + return LogicValue.filled(width, val.first); + } else { + if (val.length == width || width == null) { + return LogicValue.ofIterable(val); + } else if (width < val.length) { + return LogicValue.ofIterable(val).getRange(0, width); + } else { + return LogicValue.ofIterable(val).zeroExtend(width); + } + } } else { throw LogicValueConstructionException('Unrecognized value type "$val" - ' 'Unknown type ${val.runtimeType}' diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index a36b20cd6..5e6137e4b 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -192,13 +192,15 @@ class FancyArraysAndHierarchy extends Module implements SimpleLAPassthrough { } } -//TODO: test module hierarchy //TODO: test constant assignments (to part and all of array) //TODO: test passing packed into unpacked, unpacked into packed //TODO: test that unpacked and packed are properly instantiated in SV //TODO: test passing Logic into addInput/OutputArray works //TODO: test empty array //TODO: test array with 1 element (KNOWN BUG) +//TODO: test sub-array as a port +//TODO: test arrays in conditional assignments +//TODO: test arrays in If/Case expressions //TODO: file issue tracking that unpacked arrays not fully tested // https://github.com/steveicarus/iverilog/issues/482 @@ -263,7 +265,9 @@ void main() { group('logicarray passthrough', () { Future testArrayPassthrough(SimpleLAPassthrough mod, - {bool checkNoSwizzle = true, bool noSvSim = false}) async { + {bool checkNoSwizzle = true, + bool noSvSim = false, + bool noIverilog = false}) async { await mod.build(); const randWidth = 23; @@ -283,8 +287,9 @@ void main() { } await SimCompare.checkFunctionalVector(mod, vectors); - SimCompare.checkIverilogVector(mod, vectors, - buildOnly: noSvSim, dontDeleteTmpFiles: true); + if (!noIverilog) { + SimCompare.checkIverilogVector(mod, vectors, buildOnly: noSvSim); + } } group('simple', () { @@ -328,7 +333,7 @@ void main() { test('single element, unpacked', () async { final mod = SimpleLAPassthrough(LogicArray([1], 8, numDimensionsUnpacked: 1)); - await testArrayPassthrough(mod, noSvSim: true); + await testArrayPassthrough(mod, noSvSim: true, noIverilog: true); //TODO: bug in iverilog? https://github.com/steveicarus/iverilog/issues/915 }); diff --git a/test/logic_structure_test.dart b/test/logic_structure_test.dart index e69de29bb..b1e8df54b 100644 --- a/test/logic_structure_test.dart +++ b/test/logic_structure_test.dart @@ -0,0 +1,37 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// logic_structure_test.dart +// Tests for LogicStructure +// +// 2023 May 5 +// Author: Max Korbel + +import 'dart:io'; +import 'dart:math'; + +import 'package:collection/collection.dart'; +import 'package:rohd/rohd.dart'; +import 'package:rohd/src/utilities/simcompare.dart'; +import 'package:test/test.dart'; + +//TODO: test port is a structure +//TODO: test port is a structure with an array in it +//TODO: test ports are components of a structure +//TODO: check coverage +//TODO: Test making a structure that extends LogicStructure +//TODO: test structures in conditional assignments +//TODO: test structures in If/Case expressions + +void main() { + group('LogicStructure construction', () { + test('simple construction', () { + final s = LogicStructure([ + Logic(), + Logic(), + ], name: 'structure'); + + expect(s.name, 'structure'); + }); + }); +} From 369309dd138c2a2513f22e073ff7534cf3980d21 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Fri, 19 May 2023 10:00:06 -0700 Subject: [PATCH 34/75] some more progress and todos --- lib/src/modules/bus.dart | 2 ++ lib/src/signals/logic_structure.dart | 2 ++ test/logic_array_test.dart | 10 ++++++++- test/logic_structure_test.dart | 33 ++++++++++++++++++++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/lib/src/modules/bus.dart b/lib/src/modules/bus.dart index b80ca6e65..bff043361 100644 --- a/lib/src/modules/bus.dart +++ b/lib/src/modules/bus.dart @@ -164,6 +164,8 @@ class Swizzle extends Module with InlineSystemVerilog { out.put(updatedVal); } + //TODO: what happens when nothing in the swizzle? this is like a 0-width thing bug + @override String inlineVerilog(Map inputs) { if (inputs.length != _swizzleInputs.length) { diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index c85bab81b..0eed33b8f 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -230,6 +230,8 @@ class LogicStructure implements Logic { Logic slice(int endIndex, int startIndex) => packed.slice(endIndex, startIndex); + //TODO: don't make these operate on per-element, just pack the whole thing and do it? + /// Increments each element of [elements] using [Logic.incr]. @override Conditional incr( diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 5e6137e4b..ff3daf87c 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -288,7 +288,8 @@ void main() { await SimCompare.checkFunctionalVector(mod, vectors); if (!noIverilog) { - SimCompare.checkIverilogVector(mod, vectors, buildOnly: noSvSim); + SimCompare.checkIverilogVector(mod, vectors, + buildOnly: noSvSim, dontDeleteTmpFiles: true); } } @@ -405,6 +406,13 @@ void main() { group('change array dimensions around and back', () { test('3d', () async { + // final x = LogicArray([3], 8); + // final y = LogicArray([3], 8); + // //TODO: write a part-assign automation + // for (var i = 0; i < 2; i++) { + // x.elements[i] <= y.elements[i]; + // } + final mod = RearrangeArraysPassthrough(LogicArray([4, 3, 2], 8)); await testArrayPassthrough(mod); }); diff --git a/test/logic_structure_test.dart b/test/logic_structure_test.dart index b1e8df54b..7a33c8f01 100644 --- a/test/logic_structure_test.dart +++ b/test/logic_structure_test.dart @@ -23,9 +23,33 @@ import 'package:test/test.dart'; //TODO: test structures in conditional assignments //TODO: test structures in If/Case expressions +class MyStruct extends LogicStructure { + final Logic ready; + final Logic valid; + + factory MyStruct() => MyStruct._( + Logic(name: 'ready'), + Logic(name: 'valid'), + ); + + MyStruct._(this.ready, this.valid) : super([ready, valid]); +} + +class ModStructPort extends Module { + ModStructPort(MyStruct struct) { + final ready = addInput('ready', struct.ready); + final valid = addOutput('valid'); + struct.valid <= valid; + + valid <= ready; + } +} + void main() { group('LogicStructure construction', () { test('simple construction', () { + LogicValue.ofBigInt(BigInt.zero, 128) + + (LogicValue.ofBigInt(BigInt.zero, 128)); final s = LogicStructure([ Logic(), Logic(), @@ -34,4 +58,13 @@ void main() { expect(s.name, 'structure'); }); }); + + group('LogicStructures with modules', () { + test('simple struct bi-directional', () async { + final struct = MyStruct(); + final mod = ModStructPort(struct); + await mod.build(); + print(mod.generateSynth()); + }); + }); } From 681661efb745cad402e13e9f254c8db16e3fda3e Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Fri, 26 May 2023 09:07:11 -0700 Subject: [PATCH 35/75] add test for constant assignment, not passing --- README.md | 4 +- lib/src/module.dart | 6 ++- lib/src/modules/conditional.dart | 3 ++ lib/src/utilities/simcompare.dart | 3 +- lib/src/values/values.dart | 1 - test/bus_test.dart | 1 - test/conditionals_test.dart | 2 +- test/logic_array_test.dart | 73 +++++++++++++++++++++++++++++-- 8 files changed, 83 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 839d25c72..6baa59696 100644 --- a/README.md +++ b/README.md @@ -486,7 +486,7 @@ Combinational([ c < 0, d < 1, ], - conditionalType: ConditionalType.Unique + conditionalType: ConditionalType.unique ), CaseZ([b,a].swizzle(),[ CaseItem(Const(LogicValue.ofString('z1')), [ @@ -495,7 +495,7 @@ Combinational([ ], defaultItem: [ e < 0, ], - conditionalType: ConditionalType.Priority + conditionalType: ConditionalType.priority ) ]); ``` diff --git a/lib/src/module.dart b/lib/src/module.dart index 4d7079d02..fea9e2a44 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -496,13 +496,17 @@ abstract class Module { final listEq = const ListEquality().equals; - //TODO: do we care if dimensions match or just width here? + //TODO: do we care if dimensions match or just width here? if so, test! + // think of consistency: logic ports can accept and flatten arrays, so why + // not let array ports re-shuffle logic? then people can add their own + // validation if necessary if (!listEq(x.dimensions, dimensions)) { //TODO } if (x.elementWidth != elementWidth) { //TODO } + //TODO: also check on the num dimensions unpacked! final inArr = LogicArray(dimensions, elementWidth, name: name, numDimensionsUnpacked: numDimensionsUnpacked) diff --git a/lib/src/modules/conditional.dart b/lib/src/modules/conditional.dart index aab02c1cb..6815dcfd1 100644 --- a/lib/src/modules/conditional.dart +++ b/lib/src/modules/conditional.dart @@ -644,6 +644,9 @@ abstract class Conditional { /// Represents a group of [Conditional]s to be executed. class ConditionalGroup extends Conditional { + //TODO: Tests for conditional groups + //TODO: add an ability to add comments to these groups + final List conditionals; ConditionalGroup(this.conditionals); diff --git a/lib/src/utilities/simcompare.dart b/lib/src/utilities/simcompare.dart index 2bdbea718..1cbfa2205 100644 --- a/lib/src/utilities/simcompare.dart +++ b/lib/src/utilities/simcompare.dart @@ -72,7 +72,8 @@ class Vector { if (signal is LogicArray) { final arrAssigns = StringBuffer(); var index = 0; - final fullVal = LogicValue.of(inputValues[signalName]); + final fullVal = + LogicValue.of(inputValues[signalName], width: signal.width); for (final leaf in signal.leafElements) { final subVal = fullVal.getRange(index, index + leaf.width); arrAssigns.writeln('${leaf.structureName} = $subVal;'); diff --git a/lib/src/values/values.dart b/lib/src/values/values.dart index 478f705d1..1877b2770 100644 --- a/lib/src/values/values.dart +++ b/lib/src/values/values.dart @@ -6,7 +6,6 @@ library values; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd/src/exceptions/exceptions.dart'; -import 'package:rohd/src/exceptions/logic_value/logic_value_exceptions.dart'; part 'logic_value.dart'; part 'small_logic_value.dart'; diff --git a/test/bus_test.dart b/test/bus_test.dart index c0d2da028..f192c2642 100644 --- a/test/bus_test.dart +++ b/test/bus_test.dart @@ -9,7 +9,6 @@ /// import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/logic/logic_exceptions.dart'; import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; diff --git a/test/conditionals_test.dart b/test/conditionals_test.dart index b1f143a90..5b5a06fdb 100644 --- a/test/conditionals_test.dart +++ b/test/conditionals_test.dart @@ -366,7 +366,7 @@ class MultipleConditionalModule extends Module { b = addInput('b', b); final c = addOutput('c'); - final Conditional condOne = c < 1; + final condOne = c < 1; Combinational([ If.block([ElseIf.s(a, condOne), ElseIf.s(b, condOne)]) diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index ff3daf87c..1faa4cfaa 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -28,13 +28,13 @@ class SimpleLAPassthrough extends Module { elementWidth: laIn.elementWidth, numDimensionsUnpacked: laIn.numDimensionsUnpacked) <= laIn; - - //TODO: add some more interesting logic } } class PackAndUnpackPassthrough extends Module implements SimpleLAPassthrough { + @override Logic get laOut => output('laOut'); + PackAndUnpackPassthrough(LogicArray laIn) { laIn = addInputArray('laIn', laIn, dimensions: laIn.dimensions, @@ -55,7 +55,9 @@ class PackAndUnpackPassthrough extends Module implements SimpleLAPassthrough { class PackAndUnpackWithArraysPassthrough extends Module implements SimpleLAPassthrough { + @override Logic get laOut => output('laOut'); + PackAndUnpackWithArraysPassthrough(LogicArray laIn, {int intermediateUnpacked = 0}) { laIn = addInputArray('laIn', laIn, @@ -84,7 +86,9 @@ class PackAndUnpackWithArraysPassthrough extends Module } class RearrangeArraysPassthrough extends Module implements SimpleLAPassthrough { + @override Logic get laOut => output('laOut'); + RearrangeArraysPassthrough(LogicArray laIn, {int intermediateUnpacked = 0}) { laIn = addInputArray('laIn', laIn, dimensions: laIn.dimensions, @@ -107,7 +111,9 @@ class RearrangeArraysPassthrough extends Module implements SimpleLAPassthrough { } class ArrayNameConflicts extends Module implements SimpleLAPassthrough { + @override Logic get laOut => output('laOut'); + ArrayNameConflicts(LogicArray laIn, {int intermediateUnpacked = 0}) { laIn = addInputArray('laIn', laIn, dimensions: laIn.dimensions, @@ -142,7 +148,9 @@ class ArrayNameConflicts extends Module implements SimpleLAPassthrough { } class SimpleArraysAndHierarchy extends Module implements SimpleLAPassthrough { + @override Logic get laOut => output('laOut'); + SimpleArraysAndHierarchy(LogicArray laIn) { laIn = addInputArray('laIn', laIn, dimensions: laIn.dimensions, @@ -160,7 +168,9 @@ class SimpleArraysAndHierarchy extends Module implements SimpleLAPassthrough { } class FancyArraysAndHierarchy extends Module implements SimpleLAPassthrough { + @override Logic get laOut => output('laOut'); + FancyArraysAndHierarchy(LogicArray laIn, {int intermediateUnpacked = 0}) { laIn = addInputArray('laIn', laIn, dimensions: laIn.dimensions, @@ -192,7 +202,27 @@ class FancyArraysAndHierarchy extends Module implements SimpleLAPassthrough { } } -//TODO: test constant assignments (to part and all of array) +class ConstantAssignmentArrayModule extends Module { + Logic get laOut => output('laOut'); + + ConstantAssignmentArrayModule(LogicArray laIn) { + laIn = addInputArray('laIn', laIn, + dimensions: [3, 3, 3, 3], numDimensionsUnpacked: 2, elementWidth: 8); + + addOutputArray('laOut', + dimensions: laIn.dimensions, + numDimensionsUnpacked: laIn.numDimensionsUnpacked, + elementWidth: laIn.elementWidth); + + laOut.elements[1] <= Const(1, width: 3 * 3 * 3 * 8, fill: true); + laOut.elements[2].elements[1] <= Const(0, width: 3 * 3 * 8); + laOut.elements[2].elements[2].elements[1] <= + Const(1, width: 3 * 8, fill: true); + laOut.elements[2].elements[2].elements[2].elements[1] <= + Const(0, width: 8, fill: true); + } +} + //TODO: test passing packed into unpacked, unpacked into packed //TODO: test that unpacked and packed are properly instantiated in SV //TODO: test passing Logic into addInput/OutputArray works @@ -220,6 +250,7 @@ void main() { expect(arr.elements.isEmpty, true); expect(arr.elementWidth, 0); }); + test('single-dim array', () { final dim = [5]; const w = 16; @@ -234,6 +265,7 @@ void main() { expect(arr.width, w * dim[0]); expect(arr.elementWidth, w); }); + test('many-dim array', () { final dim = [5, 8, 3]; const w = 32; @@ -258,6 +290,7 @@ void main() { true); expect(arr.elementWidth, w); }); + test('no dim exception', () { //TODO }); @@ -483,4 +516,38 @@ void main() { }); }); }); + + test('array constant assignments', () async { + //TODO: test constant assignments (to part and all of array) + final mod = ConstantAssignmentArrayModule( + LogicArray([3, 3, 3, 3], 8, numDimensionsUnpacked: 2)); + await mod.build(); + + final a = []; + for (var i = 0; i < 3; i++) { + for (var j = 0; j < 3; j++) { + for (var k = 0; k < 3; k++) { + for (var l = 0; l < 3; l++) { + if (i == 1) { + a.add(LogicValue.filled(8, LogicValue.one)); + } else if (i == 2 && j == 1) { + a.add(LogicValue.filled(8, LogicValue.zero)); + } else if (i == 2 && j == 2 && k == 1) { + a.add(LogicValue.filled(8, LogicValue.one)); + } else if (i == 2 && j == 2 && k == 2 && l == 1) { + a.add(LogicValue.filled(8, LogicValue.zero)); + } else { + a.add(LogicValue.filled(8, LogicValue.z)); + } + } + } + } + } + final vectors = [ + Vector({'laIn': 0}, {'laOut': a.rswizzle()}) + ]; + + await SimCompare.checkFunctionalVector(mod, vectors); + SimCompare.checkIverilogVector(mod, vectors, dontDeleteTmpFiles: true); + }); } From 40ea8e05fe06a28942c3be4c71690945d19dbeb7 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Fri, 26 May 2023 14:19:10 -0700 Subject: [PATCH 36/75] fix bug with constant assignment with bus subset and array --- lib/src/signals/logic.dart | 3 +- lib/src/synthesizers/systemverilog.dart | 4 +- test/bus_test.dart | 35 +++++++++++ test/logic_array_test.dart | 78 ++++++++++++++++--------- test/logic_structure_test.dart | 4 +- 5 files changed, 90 insertions(+), 34 deletions(-) diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index 74518eb9f..5553eea55 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -40,7 +40,8 @@ class Const extends Logic { /// [val] should be processable by [LogicValue.of]. Const(dynamic val, {int? width, bool fill = false}) : super( - name: 'const_$val', + // ignore: invalid_use_of_protected_member + name: Module.unpreferredName('const_$val'), width: val is LogicValue ? val.width : width ?? 1) { put(val, fill: fill); _unassignable = true; diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart index f7f022134..6e9f5d2a2 100644 --- a/lib/src/synthesizers/systemverilog.dart +++ b/lib/src/synthesizers/systemverilog.dart @@ -374,6 +374,7 @@ class _SynthModuleDefinition { for (var i = 0; i < logicsToTraverse.length; i++) { final receiver = logicsToTraverse[i]; if (receiver is LogicArray) { + //TODO: should this be structure instead of array? maybe not? //TODO: is this right? logicsToTraverse // ..addAll(receiver.srcConnections) @@ -558,8 +559,9 @@ class _SynthModuleDefinition { } else { reducedAssignments.add(assignment); } - } else if (dst.renameable) { + } else if (dst.renameable && Module.isUnpreferred(dst.name)) { // src is a constant, feed that string directly in + // but only if this isn't a preferred signal (e.g. bus subset) dst.mergeConst(assignment.srcName()); } else { // nothing can be done here, keep it as-is diff --git a/test/bus_test.dart b/test/bus_test.dart index f192c2642..8b2b6850b 100644 --- a/test/bus_test.dart +++ b/test/bus_test.dart @@ -153,6 +153,14 @@ class BusTestModule extends Module { } } +class ConstBusModule extends Module { + ConstBusModule(int c, {required bool subset}) { + final outWidth = subset ? 8 : 16; + addOutput('const_subset', width: outWidth) <= + Const(c, width: 16).getRange(0, outWidth); + } +} + void main() { tearDown(() async { await Simulator.reset(); @@ -274,6 +282,33 @@ void main() { }); group('simcompare', () { + group('const sv gen', () { + test('Subset of a const', () async { + final mod = ConstBusModule(0xabcd, subset: true); + await mod.build(); + final vectors = [ + Vector({}, {'const_subset': 0xcd}), + ]; + + await SimCompare.checkFunctionalVector(mod, vectors); + SimCompare.checkIverilogVector(mod, vectors); + }); + + test('Assignment of a const', () async { + final mod = ConstBusModule(0xabcd, subset: false); + await mod.build(); + final vectors = [ + Vector({}, {'const_subset': 0xabcd}), + ]; + + await SimCompare.checkFunctionalVector(mod, vectors); + SimCompare.checkIverilogVector(mod, vectors); + + final sv = mod.generateSynth(); + expect(sv.contains("assign const_subset = 16'habcd;"), true); + }); + }); + test('NotGate bus', () async { final gtm = BusTestModule(Logic(width: 8), Logic(width: 8)); await gtm.build(); diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 1faa4cfaa..01468d2e0 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -207,15 +207,20 @@ class ConstantAssignmentArrayModule extends Module { ConstantAssignmentArrayModule(LogicArray laIn) { laIn = addInputArray('laIn', laIn, - dimensions: [3, 3, 3, 3], numDimensionsUnpacked: 2, elementWidth: 8); + dimensions: [3, 3, 3, 3], + numDimensionsUnpacked: laIn.numDimensionsUnpacked, + elementWidth: 8); addOutputArray('laOut', dimensions: laIn.dimensions, numDimensionsUnpacked: laIn.numDimensionsUnpacked, elementWidth: laIn.elementWidth); - laOut.elements[1] <= Const(1, width: 3 * 3 * 3 * 8, fill: true); - laOut.elements[2].elements[1] <= Const(0, width: 3 * 3 * 8); + laOut.elements[1] <= + Const([for (var i = 0; i < 3 * 3 * 3; i++) LogicValue.ofInt(i, 8)] + .rswizzle()); + laOut.elements[2].elements[1] <= + (Logic(width: 3 * 3 * 8)..gets(Const(0, width: 3 * 3 * 8))); laOut.elements[2].elements[2].elements[1] <= Const(1, width: 3 * 8, fill: true); laOut.elements[2].elements[2].elements[2].elements[1] <= @@ -517,37 +522,52 @@ void main() { }); }); - test('array constant assignments', () async { - //TODO: test constant assignments (to part and all of array) - final mod = ConstantAssignmentArrayModule( - LogicArray([3, 3, 3, 3], 8, numDimensionsUnpacked: 2)); - await mod.build(); - - final a = []; - for (var i = 0; i < 3; i++) { - for (var j = 0; j < 3; j++) { - for (var k = 0; k < 3; k++) { - for (var l = 0; l < 3; l++) { - if (i == 1) { - a.add(LogicValue.filled(8, LogicValue.one)); - } else if (i == 2 && j == 1) { - a.add(LogicValue.filled(8, LogicValue.zero)); - } else if (i == 2 && j == 2 && k == 1) { - a.add(LogicValue.filled(8, LogicValue.one)); - } else if (i == 2 && j == 2 && k == 2 && l == 1) { - a.add(LogicValue.filled(8, LogicValue.zero)); - } else { - a.add(LogicValue.filled(8, LogicValue.z)); + group('array constant assignments', () { + Future test_array_constant_assignments( + {required int numDimensionsUnpacked, bool doSvSim = true}) async { + final mod = ConstantAssignmentArrayModule(LogicArray([3, 3, 3, 3], 8, + numDimensionsUnpacked: numDimensionsUnpacked)); + await mod.build(); + + final a = []; + var iIdx = 0; + for (var i = 0; i < 3; i++) { + for (var j = 0; j < 3; j++) { + for (var k = 0; k < 3; k++) { + for (var l = 0; l < 3; l++) { + if (i == 1) { + a.add(LogicValue.ofInt(iIdx, 8)); + iIdx++; + } else if (i == 2 && j == 1) { + a.add(LogicValue.filled(8, LogicValue.zero)); + } else if (i == 2 && j == 2 && k == 1) { + a.add(LogicValue.filled(8, LogicValue.one)); + } else if (i == 2 && j == 2 && k == 2 && l == 1) { + a.add(LogicValue.filled(8, LogicValue.zero)); + } else { + a.add(LogicValue.filled(8, LogicValue.z)); + } } } } } + final vectors = [ + Vector({'laIn': 0}, {'laOut': a.rswizzle()}) + ]; + + await SimCompare.checkFunctionalVector(mod, vectors); + SimCompare.checkIverilogVector(mod, vectors, + dontDeleteTmpFiles: true, buildOnly: !doSvSim); } - final vectors = [ - Vector({'laIn': 0}, {'laOut': a.rswizzle()}) - ]; - await SimCompare.checkFunctionalVector(mod, vectors); - SimCompare.checkIverilogVector(mod, vectors, dontDeleteTmpFiles: true); + test('with packed only', () async { + await test_array_constant_assignments(numDimensionsUnpacked: 0); + }); + + test('with unpacked also', () async { + // unpacked array assignment not fully supported in iverilog + await test_array_constant_assignments( + numDimensionsUnpacked: 2, doSvSim: false); + }); }); } diff --git a/test/logic_structure_test.dart b/test/logic_structure_test.dart index 7a33c8f01..94e028a76 100644 --- a/test/logic_structure_test.dart +++ b/test/logic_structure_test.dart @@ -48,8 +48,6 @@ class ModStructPort extends Module { void main() { group('LogicStructure construction', () { test('simple construction', () { - LogicValue.ofBigInt(BigInt.zero, 128) + - (LogicValue.ofBigInt(BigInt.zero, 128)); final s = LogicStructure([ Logic(), Logic(), @@ -64,7 +62,7 @@ void main() { final struct = MyStruct(); final mod = ModStructPort(struct); await mod.build(); - print(mod.generateSynth()); + print(mod.generateSynth()); //TODO }); }); } From 6bd6bedc20f4d763656b322b298fe393b9fefe3e Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Fri, 26 May 2023 14:42:40 -0700 Subject: [PATCH 37/75] tested struct ports --- test/logic_array_test.dart | 6 ++---- test/logic_structure_test.dart | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 01468d2e0..bb073eb93 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -326,8 +326,7 @@ void main() { await SimCompare.checkFunctionalVector(mod, vectors); if (!noIverilog) { - SimCompare.checkIverilogVector(mod, vectors, - buildOnly: noSvSim, dontDeleteTmpFiles: true); + SimCompare.checkIverilogVector(mod, vectors, buildOnly: noSvSim); } } @@ -556,8 +555,7 @@ void main() { ]; await SimCompare.checkFunctionalVector(mod, vectors); - SimCompare.checkIverilogVector(mod, vectors, - dontDeleteTmpFiles: true, buildOnly: !doSvSim); + SimCompare.checkIverilogVector(mod, vectors, buildOnly: !doSvSim); } test('with packed only', () async { diff --git a/test/logic_structure_test.dart b/test/logic_structure_test.dart index 94e028a76..9f6839fbd 100644 --- a/test/logic_structure_test.dart +++ b/test/logic_structure_test.dart @@ -15,7 +15,6 @@ import 'package:rohd/rohd.dart'; import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; -//TODO: test port is a structure //TODO: test port is a structure with an array in it //TODO: test ports are components of a structure //TODO: check coverage @@ -45,6 +44,14 @@ class ModStructPort extends Module { } } +class ModStructPassthrough extends Module { + MyStruct get sOut => MyStruct()..gets(output('sOut')); + ModStructPassthrough(MyStruct struct) { + struct = MyStruct()..gets(addInput('sIn', struct, width: struct.width)); + addOutput('sOut', width: struct.width) <= struct; + } +} + void main() { group('LogicStructure construction', () { test('simple construction', () { @@ -62,7 +69,29 @@ void main() { final struct = MyStruct(); final mod = ModStructPort(struct); await mod.build(); - print(mod.generateSynth()); //TODO + + final vectors = [ + Vector({'ready': 0}, {'valid': 0}), + Vector({'ready': 1}, {'valid': 1}), + ]; + + await SimCompare.checkFunctionalVector(mod, vectors); + SimCompare.checkIverilogVector(mod, vectors); + }); + + test('simple passthrough struct', () async { + final struct = MyStruct(); + final mod = ModStructPassthrough(struct); + await mod.build(); + + final vectors = [ + Vector({'sIn': 0}, {'sOut': 0}), + Vector({'sIn': LogicValue.ofString('10')}, + {'sOut': LogicValue.ofString('10')}), + ]; + + await SimCompare.checkFunctionalVector(mod, vectors); + SimCompare.checkIverilogVector(mod, vectors); }); }); } From 0c7dc99d762eea20bae9715cd87021405263f3b6 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Fri, 26 May 2023 15:04:03 -0700 Subject: [PATCH 38/75] fancy struct inverter --- lib/src/signals/logic_structure.dart | 1 + test/logic_structure_test.dart | 54 +++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 0eed33b8f..22c476336 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -97,6 +97,7 @@ class LogicStructure implements Logic { @override bool get isArrayMember => parentStructure is LogicArray; + //TODO: delete this crap /////////////////////////////////////////////// /////////////////////////////////////////////// /////////////////////////////////////////////// diff --git a/test/logic_structure_test.dart b/test/logic_structure_test.dart index 9f6839fbd..0731879e0 100644 --- a/test/logic_structure_test.dart +++ b/test/logic_structure_test.dart @@ -31,7 +31,22 @@ class MyStruct extends LogicStructure { Logic(name: 'valid'), ); - MyStruct._(this.ready, this.valid) : super([ready, valid]); + MyStruct._(this.ready, this.valid) : super([ready, valid], name: 'myStruct'); +} + +class MyFancyStruct extends LogicStructure { + final LogicArray arr; + final Logic bus; + final LogicStructure subStruct; + + factory MyFancyStruct({int busWidth = 12}) => MyFancyStruct._( + LogicArray([3, 3], 8, name: 'arr'), + Logic(name: 'bus', width: busWidth), + MyStruct(), + ); + + MyFancyStruct._(this.arr, this.bus, this.subStruct) + : super([arr, bus, subStruct], name: 'myFancyStruct'); } class ModStructPort extends Module { @@ -46,13 +61,28 @@ class ModStructPort extends Module { class ModStructPassthrough extends Module { MyStruct get sOut => MyStruct()..gets(output('sOut')); + ModStructPassthrough(MyStruct struct) { struct = MyStruct()..gets(addInput('sIn', struct, width: struct.width)); addOutput('sOut', width: struct.width) <= struct; } } +class FancyStructInverter extends Module { + MyFancyStruct get sOut => MyFancyStruct()..gets(output('sOut')); + + FancyStructInverter(MyFancyStruct struct) { + struct = MyFancyStruct() + ..gets(addInput('sIn', struct, width: struct.width)); + addOutput('sOut', width: struct.width) <= ~struct; + } +} + void main() { + tearDown(() async { + await Simulator.reset(); + }); + group('LogicStructure construction', () { test('simple construction', () { final s = LogicStructure([ @@ -93,5 +123,27 @@ void main() { await SimCompare.checkFunctionalVector(mod, vectors); SimCompare.checkIverilogVector(mod, vectors); }); + + test('fancy struct inverter', () async { + final struct = MyFancyStruct(); + final mod = FancyStructInverter(struct); + await mod.build(); + + struct.arr.elements[2].elements[1].put(0x55); + expect(mod.sOut.arr.elements[2].elements[1].value.toInt(), 0xaa); + + struct.bus.put(0x0f0); + expect(mod.sOut.bus.value.toInt(), 0xf0f); + + final vectors = [ + Vector({'sIn': 0}, + {'sOut': LogicValue.filled(struct.width, LogicValue.one)}), + Vector({'sIn': LogicValue.ofString('10' * (struct.width ~/ 2))}, + {'sOut': LogicValue.ofString('01' * (struct.width ~/ 2))}), + ]; + + await SimCompare.checkFunctionalVector(mod, vectors); + SimCompare.checkIverilogVector(mod, vectors); + }); }); } From 12dbab923751e8debb3f2b7cdbf805db9f170131 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Fri, 26 May 2023 15:25:04 -0700 Subject: [PATCH 39/75] reorganize signals directory --- lib/src/signals/const.dart | 25 +++ lib/src/signals/logic.dart | 233 +---------------------- lib/src/signals/logic_value_changed.dart | 26 +++ lib/src/signals/signals.dart | 17 +- lib/src/signals/wire.dart | 203 ++++++++++++++++++++ lib/src/values/values.dart | 4 +- test/logic_structure_test.dart | 3 - 7 files changed, 273 insertions(+), 238 deletions(-) create mode 100644 lib/src/signals/const.dart create mode 100644 lib/src/signals/logic_value_changed.dart create mode 100644 lib/src/signals/wire.dart diff --git a/lib/src/signals/const.dart b/lib/src/signals/const.dart new file mode 100644 index 000000000..05efee902 --- /dev/null +++ b/lib/src/signals/const.dart @@ -0,0 +1,25 @@ +// Copyright (C) 2021-2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// const.dart +// Definition of signals with constant values. +// +// 2023 May 26 +// Author: Max Korbel + +part of signals; + +/// Represents a [Logic] that never changes value. +class Const extends Logic { + /// Constructs a [Const] with the specified value. + /// + /// [val] should be processable by [LogicValue.of]. + Const(dynamic val, {int? width, bool fill = false}) + : super( + // ignore: invalid_use_of_protected_member + name: Module.unpreferredName('const_$val'), + width: val is LogicValue ? val.width : width ?? 1) { + put(val, fill: fill); + _unassignable = true; + } +} diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index 5553eea55..c3677ca6e 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -8,242 +8,11 @@ // 2021 August 2 // Author: Max Korbel -import 'dart:async'; - -import 'package:collection/collection.dart'; -import 'package:meta/meta.dart'; -import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/logic/logic_exceptions.dart'; -import 'package:rohd/src/utilities/sanitizer.dart'; -import 'package:rohd/src/utilities/synchronous_propagator.dart'; - -/// Represents the event of a [Logic] changing value. -class LogicValueChanged { - /// The newly updated value of the [Logic]. - final LogicValue newValue; - - /// The previous value of the [Logic]. - final LogicValue previousValue; - - /// Represents the event of a [Logic] changing value from [previousValue] - /// to [newValue]. - const LogicValueChanged(this.newValue, this.previousValue); - - @override - String toString() => '$previousValue --> $newValue'; -} - -/// Represents a [Logic] that never changes value. -class Const extends Logic { - /// Constructs a [Const] with the specified value. - /// - /// [val] should be processable by [LogicValue.of]. - Const(dynamic val, {int? width, bool fill = false}) - : super( - // ignore: invalid_use_of_protected_member - name: Module.unpreferredName('const_$val'), - width: val is LogicValue ? val.width : width ?? 1) { - put(val, fill: fill); - _unassignable = true; - } -} +part of signals; //TODO: move _Wire and all of the "signals" to their own files, use "part" // so that we can keep things safer instead of using @protected -/// Represents a physical wire which shares a common value with one or -/// more [Logic]s. -class _Wire { - _Wire({required this.width}) - : _currentValue = LogicValue.filled(width, LogicValue.z); - - /// The current active value of this signal. - LogicValue get value => _currentValue; - - /// The number of bits in this signal. - final int width; - - /// The current active value of this signal. - LogicValue _currentValue; - - /// The last value of this signal before the [Simulator] tick. - /// - /// This is useful for detecting when to trigger an edge. - LogicValue? _preTickValue; - - /// A stream of [LogicValueChanged] events for every time the signal - /// transitions at any time during a [Simulator] tick. - /// - /// This event can occur more than once per edge, or even if there is no edge. - SynchronousEmitter get glitch => _glitchController.emitter; - final SynchronousPropagator _glitchController = - SynchronousPropagator(); - - /// Controller for stable events that can be safely consumed at the - /// end of a [Simulator] tick. - final StreamController _changedController = - StreamController.broadcast(sync: true); - - /// Tracks whether is being subscribed to by anything/anyone. - bool _changedBeingWatched = false; - - /// A [Stream] of [LogicValueChanged] events which triggers at most once - /// per [Simulator] tick, iff the value of the [Logic] has changed. - Stream get changed { - if (!_changedBeingWatched) { - // only do these simulator subscriptions if someone has asked for - // them! saves performance! - _changedBeingWatched = true; - - _preTickSubscription = Simulator.preTick.listen((event) { - _preTickValue = value; - }); - _postTickSubscription = Simulator.postTick.listen((event) { - if (value != _preTickValue && _preTickValue != null) { - _changedController.add(LogicValueChanged(value, _preTickValue!)); - } - }); - } - return _changedController.stream; - } - - /// The subscription to the [Simulator]'s `preTick`. - /// - /// Only non-null if [_changedBeingWatched] is true. - late final StreamSubscription _preTickSubscription; - - /// The subscription to the [Simulator]'s `postTick`. - /// - /// Only non-null if [_changedBeingWatched] is true. - late final StreamSubscription _postTickSubscription; - - /// Cancels all [Simulator] subscriptions and uses [newChanged] as the - /// source to replace all [changed] events for this [_Wire]. - void _migrateChangedTriggers(Stream newChanged) { - if (_changedBeingWatched) { - unawaited(_preTickSubscription.cancel()); - unawaited(_postTickSubscription.cancel()); - newChanged.listen(_changedController.add); - _changedBeingWatched = false; - } - } - - /// Tells this [_Wire] to adopt all the behavior of [other] so that - /// it can replace [other]. - void _adopt(_Wire other) { - _glitchController.emitter.adopt(other._glitchController.emitter); - other._migrateChangedTriggers(changed); - } - - /// Store the [negedge] stream to avoid creating multiple copies - /// of streams. - Stream? _negedge; - - /// Store the [posedge] stream to avoid creating multiple copies - /// of streams. - Stream? _posedge; - - /// A [Stream] of [LogicValueChanged] events which triggers at most once - /// per [Simulator] tick, iff the value of the [Logic] has changed - /// from `1` to `0`. - /// - /// Throws an exception if [width] is not `1`. - Stream get negedge { - if (width != 1) { - throw Exception( - 'Can only detect negedge when width is 1, but was $width'); - } - - _negedge ??= changed.where((args) => LogicValue.isNegedge( - args.previousValue, - args.newValue, - ignoreInvalid: true, - )); - - return _negedge!; - } - - /// A [Stream] of [LogicValueChanged] events which triggers at most once - /// per [Simulator] tick, iff the value of the [Logic] has changed - /// from `0` to `1`. - /// - /// Throws an exception if [width] is not `1`. - Stream get posedge { - if (width != 1) { - throw Exception( - 'Can only detect posedge when width is 1, but was $width'); - } - - _posedge ??= changed.where((args) => LogicValue.isPosedge( - args.previousValue, - args.newValue, - ignoreInvalid: true, - )); - - return _posedge!; - } - - /// Triggers at most once, the next time that this [Logic] changes - /// value at the end of a [Simulator] tick. - Future get nextChanged => changed.first; - - /// Triggers at most once, the next time that this [Logic] changes - /// value at the end of a [Simulator] tick from `0` to `1`. - /// - /// Throws an exception if [width] is not `1`. - Future get nextPosedge => posedge.first; - - /// Triggers at most once, the next time that this [Logic] changes - /// value at the end of a [Simulator] tick from `1` to `0`. - /// - /// Throws an exception if [width] is not `1`. - Future get nextNegedge => negedge.first; - - /// Injects a value onto this signal in the current [Simulator] tick. - /// - /// This function calls [put()] in [Simulator.injectAction()]. - void inject(dynamic val, {required String signalName, bool fill = false}) { - Simulator.injectAction(() => put(val, signalName: signalName, fill: fill)); - } - - /// Keeps track of whether there is an active put, to detect reentrance. - bool _isPutting = false; - - //TODO: update to reference `LogicValue.of`. - - /// Puts a value [val] onto this signal, which may or may not be picked up - /// for [changed] in this [Simulator] tick. - /// - /// The type of [val] and usage of [fill] should be supported by - /// [LogicValue.of]. - /// - /// This function is used for propogating glitches through connected signals. - /// Use this function for custom definitions of [Module] behavior. - void put(dynamic val, {required String signalName, bool fill = false}) { - var newValue = LogicValue.of(val, fill: fill, width: width); - - if (newValue.width != width) { - throw PutException(signalName, - 'Updated value width mismatch. The width of $val should be $width.'); - } - - if (_isPutting) { - // if this is the result of a cycle, then contention! - newValue = LogicValue.filled(width, LogicValue.x); - } - - final prevValue = _currentValue; - _currentValue = newValue; - - // sends out a glitch if the value deposited has changed - if (_currentValue != prevValue) { - _isPutting = true; - _glitchController.add(LogicValueChanged(_currentValue, prevValue)); - _isPutting = false; - } - } -} - /// Represents a logical signal of any width which can change values. class Logic { /// An internal counter for encouraging unique naming of unnamed signals. diff --git a/lib/src/signals/logic_value_changed.dart b/lib/src/signals/logic_value_changed.dart new file mode 100644 index 000000000..ccf0fb2e5 --- /dev/null +++ b/lib/src/signals/logic_value_changed.dart @@ -0,0 +1,26 @@ +// Copyright (C) 2021-2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// logic_value_changed.dart +// Definition of an event when a signal value changes. +// +// 2023 May 26 +// Author: Max Korbel + +import 'package:rohd/rohd.dart'; + +/// Represents the event of a [Logic] changing value. +class LogicValueChanged { + /// The newly updated value of the [Logic]. + final LogicValue newValue; + + /// The previous value of the [Logic]. + final LogicValue previousValue; + + /// Represents the event of a [Logic] changing value from [previousValue] + /// to [newValue]. + const LogicValueChanged(this.newValue, this.previousValue); + + @override + String toString() => '$previousValue --> $newValue'; +} diff --git a/lib/src/signals/signals.dart b/lib/src/signals/signals.dart index 46790efac..b7315d497 100644 --- a/lib/src/signals/signals.dart +++ b/lib/src/signals/signals.dart @@ -1,6 +1,21 @@ // Copyright (C) 2023 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause -export 'logic.dart'; +library signals; + +import 'dart:async'; +import 'dart:collection'; + +import 'package:meta/meta.dart'; +import 'package:rohd/rohd.dart'; +import 'package:rohd/src/exceptions/exceptions.dart'; +import 'package:rohd/src/utilities/sanitizer.dart'; +import 'package:rohd/src/utilities/synchronous_propagator.dart'; + export 'logic_array.dart'; export 'logic_structure.dart'; +export 'logic_value_changed.dart'; + +part 'const.dart'; +part 'logic.dart'; +part 'wire.dart'; diff --git a/lib/src/signals/wire.dart b/lib/src/signals/wire.dart new file mode 100644 index 000000000..6c309c82d --- /dev/null +++ b/lib/src/signals/wire.dart @@ -0,0 +1,203 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// wire.dart +// Definition of underlying structure for storing information on a signal. +// +// 2023 May 26 +// Author: Max Korbel + +part of signals; + +/// Represents a physical wire which shares a common value with one or +/// more [Logic]s. +class _Wire { + _Wire({required this.width}) + : _currentValue = LogicValue.filled(width, LogicValue.z); + + /// The current active value of this signal. + LogicValue get value => _currentValue; + + /// The number of bits in this signal. + final int width; + + /// The current active value of this signal. + LogicValue _currentValue; + + /// The last value of this signal before the [Simulator] tick. + /// + /// This is useful for detecting when to trigger an edge. + LogicValue? _preTickValue; + + /// A stream of [LogicValueChanged] events for every time the signal + /// transitions at any time during a [Simulator] tick. + /// + /// This event can occur more than once per edge, or even if there is no edge. + SynchronousEmitter get glitch => _glitchController.emitter; + final SynchronousPropagator _glitchController = + SynchronousPropagator(); + + /// Controller for stable events that can be safely consumed at the + /// end of a [Simulator] tick. + final StreamController _changedController = + StreamController.broadcast(sync: true); + + /// Tracks whether is being subscribed to by anything/anyone. + bool _changedBeingWatched = false; + + /// A [Stream] of [LogicValueChanged] events which triggers at most once + /// per [Simulator] tick, iff the value of the [Logic] has changed. + Stream get changed { + if (!_changedBeingWatched) { + // only do these simulator subscriptions if someone has asked for + // them! saves performance! + _changedBeingWatched = true; + + _preTickSubscription = Simulator.preTick.listen((event) { + _preTickValue = value; + }); + _postTickSubscription = Simulator.postTick.listen((event) { + if (value != _preTickValue && _preTickValue != null) { + _changedController.add(LogicValueChanged(value, _preTickValue!)); + } + }); + } + return _changedController.stream; + } + + /// The subscription to the [Simulator]'s `preTick`. + /// + /// Only non-null if [_changedBeingWatched] is true. + late final StreamSubscription _preTickSubscription; + + /// The subscription to the [Simulator]'s `postTick`. + /// + /// Only non-null if [_changedBeingWatched] is true. + late final StreamSubscription _postTickSubscription; + + /// Cancels all [Simulator] subscriptions and uses [newChanged] as the + /// source to replace all [changed] events for this [_Wire]. + void _migrateChangedTriggers(Stream newChanged) { + if (_changedBeingWatched) { + unawaited(_preTickSubscription.cancel()); + unawaited(_postTickSubscription.cancel()); + newChanged.listen(_changedController.add); + _changedBeingWatched = false; + } + } + + /// Tells this [_Wire] to adopt all the behavior of [other] so that + /// it can replace [other]. + void _adopt(_Wire other) { + _glitchController.emitter.adopt(other._glitchController.emitter); + other._migrateChangedTriggers(changed); + } + + /// Store the [negedge] stream to avoid creating multiple copies + /// of streams. + Stream? _negedge; + + /// Store the [posedge] stream to avoid creating multiple copies + /// of streams. + Stream? _posedge; + + /// A [Stream] of [LogicValueChanged] events which triggers at most once + /// per [Simulator] tick, iff the value of the [Logic] has changed + /// from `1` to `0`. + /// + /// Throws an exception if [width] is not `1`. + Stream get negedge { + if (width != 1) { + throw Exception( + 'Can only detect negedge when width is 1, but was $width'); + } + + _negedge ??= changed.where((args) => LogicValue.isNegedge( + args.previousValue, + args.newValue, + ignoreInvalid: true, + )); + + return _negedge!; + } + + /// A [Stream] of [LogicValueChanged] events which triggers at most once + /// per [Simulator] tick, iff the value of the [Logic] has changed + /// from `0` to `1`. + /// + /// Throws an exception if [width] is not `1`. + Stream get posedge { + if (width != 1) { + throw Exception( + 'Can only detect posedge when width is 1, but was $width'); + } + + _posedge ??= changed.where((args) => LogicValue.isPosedge( + args.previousValue, + args.newValue, + ignoreInvalid: true, + )); + + return _posedge!; + } + + /// Triggers at most once, the next time that this [Logic] changes + /// value at the end of a [Simulator] tick. + Future get nextChanged => changed.first; + + /// Triggers at most once, the next time that this [Logic] changes + /// value at the end of a [Simulator] tick from `0` to `1`. + /// + /// Throws an exception if [width] is not `1`. + Future get nextPosedge => posedge.first; + + /// Triggers at most once, the next time that this [Logic] changes + /// value at the end of a [Simulator] tick from `1` to `0`. + /// + /// Throws an exception if [width] is not `1`. + Future get nextNegedge => negedge.first; + + /// Injects a value onto this signal in the current [Simulator] tick. + /// + /// This function calls [put()] in [Simulator.injectAction()]. + void inject(dynamic val, {required String signalName, bool fill = false}) { + Simulator.injectAction(() => put(val, signalName: signalName, fill: fill)); + } + + /// Keeps track of whether there is an active put, to detect reentrance. + bool _isPutting = false; + + //TODO: update to reference `LogicValue.of`. + + /// Puts a value [val] onto this signal, which may or may not be picked up + /// for [changed] in this [Simulator] tick. + /// + /// The type of [val] and usage of [fill] should be supported by + /// [LogicValue.of]. + /// + /// This function is used for propogating glitches through connected signals. + /// Use this function for custom definitions of [Module] behavior. + void put(dynamic val, {required String signalName, bool fill = false}) { + var newValue = LogicValue.of(val, fill: fill, width: width); + + if (newValue.width != width) { + throw PutException(signalName, + 'Updated value width mismatch. The width of $val should be $width.'); + } + + if (_isPutting) { + // if this is the result of a cycle, then contention! + newValue = LogicValue.filled(width, LogicValue.x); + } + + final prevValue = _currentValue; + _currentValue = newValue; + + // sends out a glitch if the value deposited has changed + if (_currentValue != prevValue) { + _isPutting = true; + _glitchController.add(LogicValueChanged(_currentValue, prevValue)); + _isPutting = false; + } + } +} diff --git a/lib/src/values/values.dart b/lib/src/values/values.dart index 1877b2770..d3cc2abe1 100644 --- a/lib/src/values/values.dart +++ b/lib/src/values/values.dart @@ -1,5 +1,5 @@ -/// Copyright (C) 2021-2022 Intel Corporation -/// SPDX-License-Identifier: BSD-3-Clause +// Copyright (C) 2021-2022 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause library values; diff --git a/test/logic_structure_test.dart b/test/logic_structure_test.dart index 0731879e0..7e528e5cc 100644 --- a/test/logic_structure_test.dart +++ b/test/logic_structure_test.dart @@ -15,10 +15,7 @@ import 'package:rohd/rohd.dart'; import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; -//TODO: test port is a structure with an array in it -//TODO: test ports are components of a structure //TODO: check coverage -//TODO: Test making a structure that extends LogicStructure //TODO: test structures in conditional assignments //TODO: test structures in If/Case expressions From 44e6d066d4e18c3d39d78c1b831a6df787efcd74 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Fri, 26 May 2023 15:50:15 -0700 Subject: [PATCH 40/75] move array and struct into the signals lib --- lib/src/signals/logic_array.dart | 6 +-- lib/src/signals/logic_structure.dart | 62 ++++++++++++++++++++++++---- lib/src/signals/signals.dart | 4 +- 3 files changed, 56 insertions(+), 16 deletions(-) diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index 4e4e22f1f..13058f419 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -7,11 +7,7 @@ // 2023 May 1 // Author: Max Korbel -import 'dart:math'; - -import 'package:collection/collection.dart'; -import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/module/module_exceptions.dart'; +part of signals; class LogicArray extends LogicStructure { //TODO: calculate dimension diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 22c476336..e92a689fd 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -7,15 +7,7 @@ // 2023 May 1 // Author: Max Korbel -import 'dart:collection'; -import 'dart:math'; - -import 'package:collection/collection.dart'; -import 'package:meta/meta.dart'; -import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/module/module_exceptions.dart'; -import 'package:rohd/src/utilities/sanitizer.dart'; -import 'package:rohd/src/utilities/synchronous_propagator.dart'; +part of signals; //TODO: how to deal with LogicStructure as an input/output? @@ -461,4 +453,56 @@ class LogicStructure implements Logic { @override // ignore: deprecated_member_use_from_same_package int get valueInt => packed.valueInt; + + ///////////////////////////////////////////// + ///////////////////////////////////////////// + ///////////////////////////////////////////// + + @override + Logic? get _srcConnection => throw UnsupportedError('Delegated to elements'); + + @override + set _srcConnection(Logic? srcConnection) { + throw UnsupportedError('Delegated to elements'); + } + + @override + bool get _unassignable => throw UnsupportedError('Delegated to elements'); + + @override + set _unassignable(bool unassignable) { + throw UnsupportedError('Delegated to elements'); + } + + @override + _Wire get _wire => throw UnsupportedError('Delegated to elements'); + + @override + set _wire(_Wire wire) { + throw UnsupportedError('Delegated to elements'); + } + + @override + void _assertConnectable(Logic other) { + throw UnsupportedError('Delegated to elements'); + } + + @override + void _connect(Logic other) { + throw UnsupportedError('Delegated to elements'); + } + + @override + Set get _dstConnections => + throw UnsupportedError('Delegated to elements'); + + @override + void _registerConnection(Logic dstConnection) { + throw UnsupportedError('Delegated to elements'); + } + + @override + void _updateWire(_Wire newWire) { + throw UnsupportedError('Delegated to elements'); + } } diff --git a/lib/src/signals/signals.dart b/lib/src/signals/signals.dart index b7315d497..0d332b42f 100644 --- a/lib/src/signals/signals.dart +++ b/lib/src/signals/signals.dart @@ -12,10 +12,10 @@ import 'package:rohd/src/exceptions/exceptions.dart'; import 'package:rohd/src/utilities/sanitizer.dart'; import 'package:rohd/src/utilities/synchronous_propagator.dart'; -export 'logic_array.dart'; -export 'logic_structure.dart'; export 'logic_value_changed.dart'; part 'const.dart'; part 'logic.dart'; part 'wire.dart'; +part 'logic_structure.dart'; +part 'logic_array.dart'; From d9dce828007316e90d400c691fdddc366f290cf7 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 30 May 2023 11:35:36 -0700 Subject: [PATCH 41/75] get rid of rootstructure stuff, not needed --- lib/src/module.dart | 2 -- lib/src/signals/logic.dart | 9 --------- lib/src/signals/logic_structure.dart | 12 +----------- lib/src/signals/signals.dart | 2 ++ lib/src/synthesizers/systemverilog.dart | 5 +---- 5 files changed, 4 insertions(+), 26 deletions(-) diff --git a/lib/src/module.dart b/lib/src/module.dart index fea9e2a44..316cd7726 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -114,14 +114,12 @@ abstract class Module { bool isInput(Logic net) => _inputs[net.name] == net || (net.isArrayMember && isInput(net.parentStructure!)); - //TODO? || (net.isArrayMember && isInput(net.rootStructure!)); /// Returns true iff [net] is the same [Logic] as the output port of this /// [Module] with the same name. bool isOutput(Logic net) => _outputs[net.name] == net || (net.isArrayMember && isOutput(net.parentStructure!)); - //TODO? ||(net.isArrayMember && isOutput(net.rootStructure!)); //TODO: what if inputs/outputs are added multiple times? // what if elements of arrays are added again? is that ok? diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index c3677ca6e..98866a99a 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -135,15 +135,6 @@ class Logic { LogicStructure? get parentStructure => _parentStructure; LogicStructure? _parentStructure; - LogicStructure? get rootStructure { - //TODO: do we even need this? - LogicStructure? root = parentStructure; - while (root?.parentStructure != null) { - root = root?.parentStructure; - } - return root; - } - /// True if this is a member of a [LogicArray]. bool get isArrayMember => parentStructure is LogicArray; diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index e92a689fd..5456e05c1 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -9,8 +9,6 @@ part of signals; -//TODO: how to deal with LogicStructure as an input/output? - class LogicStructure implements Logic { /// All elements of this structure. @override @@ -41,6 +39,7 @@ class LogicStructure implements Logic { ? 'st${_structIdx++}' : Sanitizer.sanitizeSV(name) { //TODO: make sure no components already have a parentComponent + // but do we care? _elements ..addAll(elements) ..forEach((element) { @@ -48,15 +47,6 @@ class LogicStructure implements Logic { }); } - @override - LogicStructure get rootStructure { - var root = this; - while (root.parentStructure != null) { - root = root.parentStructure!; - } - return root; - } - //TODO String get structureName { if (parentStructure != null) { diff --git a/lib/src/signals/signals.dart b/lib/src/signals/signals.dart index 0d332b42f..592fdab34 100644 --- a/lib/src/signals/signals.dart +++ b/lib/src/signals/signals.dart @@ -5,7 +5,9 @@ library signals; import 'dart:async'; import 'dart:collection'; +import 'dart:math'; +import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd/src/exceptions/exceptions.dart'; diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart index 6e9f5d2a2..75f4b2816 100644 --- a/lib/src/synthesizers/systemverilog.dart +++ b/lib/src/synthesizers/systemverilog.dart @@ -380,7 +380,6 @@ class _SynthModuleDefinition { // ..addAll(receiver.srcConnections) ..addAll(receiver.elements); //TODO: what about if it's an array inside a struct? - // ..add(receiver.rootStructure); //TODO: delete this? // continue; } @@ -423,9 +422,7 @@ class _SynthModuleDefinition { logicsToTraverse.addAll(subModule.inputs.values); } else if (driver != null) { - if (!module.isInput(receiver) && - !(receiver.isArrayMember && - module.isInput(receiver.rootStructure!))) { + if (!module.isInput(receiver)) { //TODO: This is suspicious, doesn't seem right // stop at the input to this module From 4aba1277005735cecc125e2bc40c3d8666581042 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 30 May 2023 11:41:15 -0700 Subject: [PATCH 42/75] remove arrayLocationFromRoot --- lib/src/signals/logic.dart | 11 ----------- lib/src/signals/logic_array.dart | 12 ------------ lib/src/signals/logic_structure.dart | 12 ------------ 3 files changed, 35 deletions(-) diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index 98866a99a..8c1327e4a 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -159,17 +159,6 @@ class Logic { //TODO: protect this properly int? arrayIndex; - //TODO - List? get arrayLocationFromRoot { - if (!isArrayMember) { - return null; - } - - return [ - ...parentStructure!.arrayLocationFromRoot!, - arrayIndex!, - ]; - } @protected set parentStructure(LogicStructure? newParentStructure) => diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index 13058f419..7bfc79edb 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -129,18 +129,6 @@ class LogicArray extends LogicStructure { ); } - //TODO - List? get arrayLocationFromRoot { - if (!isArrayMember) { - return []; - } - - return [ - ...parentStructure!.arrayLocationFromRoot!, - arrayIndex!, - ]; - } - //TODO: doc and test factory LogicArray.of(Logic other, {required List dimensions, diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 5456e05c1..0d73b178f 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -64,18 +64,6 @@ class LogicStructure implements Logic { @protected int? arrayIndex; - //TODO - List? get arrayLocationFromRoot { - if (!isArrayMember) { - return null; - } - - return [ - ...parentStructure!.arrayLocationFromRoot!, - arrayIndex!, - ]; - } - @override bool get isArrayMember => parentStructure is LogicArray; From b65f68b837a834c61240a2c9db17b77eabaedc08 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 30 May 2023 11:52:52 -0700 Subject: [PATCH 43/75] make arrayIndex privately settable --- lib/src/signals/logic.dart | 17 ++++++++++------- lib/src/signals/logic_array.dart | 2 +- lib/src/signals/logic_structure.dart | 5 ++--- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index 8c1327e4a..acfa233af 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -135,6 +135,10 @@ class Logic { LogicStructure? get parentStructure => _parentStructure; LogicStructure? _parentStructure; + @protected + set parentStructure(LogicStructure? newParentStructure) => + _parentStructure = newParentStructure; + /// True if this is a member of a [LogicArray]. bool get isArrayMember => parentStructure is LogicArray; @@ -156,13 +160,12 @@ class Logic { } } - //TODO: protect this properly - - int? arrayIndex; - - @protected - set parentStructure(LogicStructure? newParentStructure) => - _parentStructure = newParentStructure; + /// If this is a part of a [LogicArray], the index within that array. + /// Othwerise, returns `null`. + /// + /// If [isArrayMember] is true, this will be non-`null`. + int? get arrayIndex => _arrayIndex; + int? _arrayIndex; /// Sets the value of [parentModule] to [newParentModule]. /// diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index 7bfc79edb..2d07524e1 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -122,7 +122,7 @@ class LogicArray extends LogicStructure { //TODO: test that this gets propagated down properly numDimensionsUnpacked: max(0, numDimensionsUnpacked - 1), )) - ..arrayIndex = index, + .._arrayIndex = index, growable: false), name: name, numDimensionsUnpacked: numDimensionsUnpacked, diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 0d73b178f..f36ae6253 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -60,9 +60,8 @@ class LogicStructure implements Logic { } } - //TODO: protect this properly - @protected - int? arrayIndex; + int? _arrayIndex; + int? get arrayIndex => _arrayIndex; @override bool get isArrayMember => parentStructure is LogicArray; From ad97344d54053cc291e557a5110ad8a1d46e8aa2 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 30 May 2023 11:57:24 -0700 Subject: [PATCH 44/75] adjust parentstructure --- lib/src/signals/logic.dart | 11 +++++------ lib/src/signals/logic_structure.dart | 7 +------ test/logic_array_test.dart | 1 - test/logic_structure_test.dart | 4 ---- 4 files changed, 6 insertions(+), 17 deletions(-) diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index acfa233af..5c11d6988 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -132,22 +132,21 @@ class Logic { Module? get parentModule => _parentModule; Module? _parentModule; + /// If this is a part of a [LogicStructure], the structure which this is + /// a part of. Otherwise, `null`. + /// + /// This is usually either a [LogicStructure] or [LogicArray]. LogicStructure? get parentStructure => _parentStructure; LogicStructure? _parentStructure; - @protected - set parentStructure(LogicStructure? newParentStructure) => - _parentStructure = newParentStructure; - /// True if this is a member of a [LogicArray]. bool get isArrayMember => parentStructure is LogicArray; - /// TODO /// Returns the name relative to the [parentStructure]-defined hierarchy, if /// one exists. Otherwise, this is the same as [name]. /// /// This is useful for finding the name of a signal as an element of a root - /// [LogicArray]. + /// [LogicArray] or [LogicStructure]. String get structureName { if (parentStructure != null) { if (parentStructure is LogicArray) { diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index f36ae6253..678d3d6ef 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -43,7 +43,7 @@ class LogicStructure implements Logic { _elements ..addAll(elements) ..forEach((element) { - element.parentStructure = this; + element._parentStructure = this; }); } @@ -259,11 +259,6 @@ class LogicStructure implements Logic { LogicStructure? get parentStructure => _parentStructure; LogicStructure? _parentStructure; - @protected - @override - set parentStructure(LogicStructure? newParentStructure) => - _parentStructure = newParentStructure; - @override bool get isInput => parentModule?.isInput(this) ?? false; diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index bb073eb93..b33fb36ed 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -7,7 +7,6 @@ // 2023 May 2 // Author: Max Korbel -import 'dart:io'; import 'dart:math'; import 'package:collection/collection.dart'; diff --git a/test/logic_structure_test.dart b/test/logic_structure_test.dart index 7e528e5cc..043650bce 100644 --- a/test/logic_structure_test.dart +++ b/test/logic_structure_test.dart @@ -7,10 +7,6 @@ // 2023 May 5 // Author: Max Korbel -import 'dart:io'; -import 'dart:math'; - -import 'package:collection/collection.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; From 0b05ff198a9fb3086913ca894c8b16cc343ca0da Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 30 May 2023 12:55:18 -0700 Subject: [PATCH 45/75] move srcConnections to only exist in module --- lib/src/module.dart | 23 +++++++++++++++++++++-- lib/src/signals/logic.dart | 8 +++----- lib/src/signals/logic_structure.dart | 7 ++----- lib/src/synthesizers/systemverilog.dart | 5 +---- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/lib/src/module.dart b/lib/src/module.dart index 316cd7726..d5b646649 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -430,8 +430,13 @@ abstract class Module { await _traceInputForModuleContents(dstConnection); } } - for (final srcConnection in signal.srcConnections) { - await _traceOutputForModuleContents(srcConnection); + + if (signal is LogicStructure) { + for (final srcConnection in signal.srcConnections) { + await _traceOutputForModuleContents(srcConnection); + } + } else if (signal.srcConnection != null) { + await _traceOutputForModuleContents(signal.srcConnection!); } } } @@ -592,3 +597,17 @@ abstract class Module { .join('\n\n////////////////////\n\n'); } } + +extension _ModuleLogicStructureUtils on LogicStructure { + /// Provides a list of all source connections of all elements within + /// this structure, recursively. + /// + /// Useful for searching during [Module] build. + Iterable get srcConnections => [ + for (final element in elements) + if (element is LogicStructure) + ...element.srcConnections + else if (element.srcConnection != null) + element.srcConnection! + ]; +} diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index 5c11d6988..069731c46 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -72,9 +72,6 @@ class Logic { Logic? get srcConnection => _srcConnection; Logic? _srcConnection; - Iterable get srcConnections => - [if (srcConnection != null) srcConnection!]; - /// An [Iterable] of all [Logic]s that are being directly driven by `this`. late final Iterable dstConnections = UnmodifiableListView(_dstConnections); @@ -127,8 +124,9 @@ class Logic { /// The [Module] that this [Logic] exists within. /// - /// This only gets populated after its parent [Module], if it exists, - /// has been built. + /// For internal signals, this only gets populated after its parent [Module], + /// if it exists, has been built. Ports (both input and output) have this + /// populated at the time of creation. Module? get parentModule => _parentModule; Module? _parentModule; diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 678d3d6ef..b86c5d698 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -277,14 +277,11 @@ class LogicStructure implements Logic { } } + /// A [LogicStructure] never has a direct source driving it, only its + /// [elements] do, so always returns `null`. @override - // TODO: implement srcConnection, should it be exception or null? Logic? get srcConnection => null; - @override - Iterable get srcConnections => - [for (final element in elements) ...element.srcConnections]; - @override LogicValue get value => packed.value; diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart index 75f4b2816..7ce9d9d10 100644 --- a/lib/src/synthesizers/systemverilog.dart +++ b/lib/src/synthesizers/systemverilog.dart @@ -375,10 +375,7 @@ class _SynthModuleDefinition { final receiver = logicsToTraverse[i]; if (receiver is LogicArray) { //TODO: should this be structure instead of array? maybe not? - //TODO: is this right? - logicsToTraverse - // ..addAll(receiver.srcConnections) - ..addAll(receiver.elements); + logicsToTraverse.addAll(receiver.elements); //TODO: what about if it's an array inside a struct? // continue; From 8a327a1a850333f8bccdfcf30a7413e378ec073f Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 30 May 2023 13:43:29 -0700 Subject: [PATCH 46/75] test sub array --- lib/src/synthesizers/systemverilog.dart | 5 +---- test/logic_array_test.dart | 11 ++++++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart index 7ce9d9d10..3ba2ec169 100644 --- a/lib/src/synthesizers/systemverilog.dart +++ b/lib/src/synthesizers/systemverilog.dart @@ -374,12 +374,9 @@ class _SynthModuleDefinition { for (var i = 0; i < logicsToTraverse.length; i++) { final receiver = logicsToTraverse[i]; if (receiver is LogicArray) { - //TODO: should this be structure instead of array? maybe not? logicsToTraverse.addAll(receiver.elements); - //TODO: what about if it's an array inside a struct? - - // continue; } + if (receiver.isArrayMember) { logicsToTraverse.add(receiver.parentStructure!); } diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index b33fb36ed..e11969bcf 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -230,9 +230,7 @@ class ConstantAssignmentArrayModule extends Module { //TODO: test passing packed into unpacked, unpacked into packed //TODO: test that unpacked and packed are properly instantiated in SV //TODO: test passing Logic into addInput/OutputArray works -//TODO: test empty array -//TODO: test array with 1 element (KNOWN BUG) -//TODO: test sub-array as a port +//TODO: test empty array (various empty dimensions) //TODO: test arrays in conditional assignments //TODO: test arrays in If/Case expressions @@ -391,6 +389,13 @@ void main() { r'\[2:0\]\s*\[1:0\]\s*\[7:0\]\s*laOut\s*\[4:0\]\s*\[3:0\]')), true); }); + + test('sub-array', () async { + final superArray = LogicArray([4, 3, 2], 8); + final subArray = superArray.elements[0] as LogicArray; + final mod = SimpleLAPassthrough(subArray); + await testArrayPassthrough(mod); + }); }); group('pack and unpack', () { From ae57d582dbf03273e384aa2d3e2df807e86fe75f Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 30 May 2023 15:42:17 -0700 Subject: [PATCH 47/75] empty multi-dim --- lib/src/signals/logic.dart | 4 ++-- lib/src/signals/logic_array.dart | 4 ++++ test/logic_array_test.dart | 7 ++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index 069731c46..a2346e42a 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -517,8 +517,8 @@ class Logic { } //TODO: test this - late final List elements = - List.generate(width, (index) => this[index], growable: false); + late final List elements = UnmodifiableListView( + List.generate(width, (index) => this[index], growable: false)); /// Accesses a subset of this signal from [startIndex] to [endIndex], /// both inclusive. diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index 2d07524e1..50957b903 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -56,6 +56,10 @@ class LogicArray extends LogicStructure { return [currDim, ...firstDim]; } + /// The width of leaf elements in this array. + /// + /// If the array has no leaf elements and/or the [width] is 0, then the + /// [elementWidth] is always 0. late final int elementWidth = _calculateElementWidth(); int _calculateElementWidth() { diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index e11969bcf..b956ba7e4 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -230,7 +230,6 @@ class ConstantAssignmentArrayModule extends Module { //TODO: test passing packed into unpacked, unpacked into packed //TODO: test that unpacked and packed are properly instantiated in SV //TODO: test passing Logic into addInput/OutputArray works -//TODO: test empty array (various empty dimensions) //TODO: test arrays in conditional assignments //TODO: test arrays in If/Case expressions @@ -253,6 +252,12 @@ void main() { expect(arr.elementWidth, 0); }); + test('empty multi-dim array', () { + final arr = LogicArray([5, 2, 0, 3], 6); + expect(arr.width, 0); + expect(arr.elementWidth, 0); + }); + test('single-dim array', () { final dim = [5]; const w = 16; From 6925e9f97a8a3fd3d884aed3484d67c0ede9ee0b Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 30 May 2023 16:08:50 -0700 Subject: [PATCH 48/75] test input and output ports with arrays and normal logics --- lib/src/module.dart | 35 +++++++------- test/logic_array_test.dart | 94 ++++++++++++++++++++++++++++++++------ 2 files changed, 96 insertions(+), 33 deletions(-) diff --git a/lib/src/module.dart b/lib/src/module.dart index d5b646649..6e0908b7d 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -488,29 +488,23 @@ abstract class Module { return inPort; } + /// Registers and returns an input [LogicArray] port to this [Module] with + /// the specified [dimensions], [elementWidth], and [numDimensionsUnpacked] + /// named [name]. + /// + /// This is very similar to [addInput], except for [LogicArray]s. + /// + /// Performs validation on overall width matching for [x], but not on + /// [dimensions], [elementWidth], or [numDimensionsUnpacked]. LogicArray addInputArray( String name, - LogicArray x, { + Logic x, { List dimensions = const [1], int elementWidth = 1, - int numDimensionsUnpacked = 0, //TODO + int numDimensionsUnpacked = 0, }) { _checkForSafePortName(name); - final listEq = const ListEquality().equals; - - //TODO: do we care if dimensions match or just width here? if so, test! - // think of consistency: logic ports can accept and flatten arrays, so why - // not let array ports re-shuffle logic? then people can add their own - // validation if necessary - if (!listEq(x.dimensions, dimensions)) { - //TODO - } - if (x.elementWidth != elementWidth) { - //TODO - } - //TODO: also check on the num dimensions unpacked! - final inArr = LogicArray(dimensions, elementWidth, name: name, numDimensionsUnpacked: numDimensionsUnpacked) ..gets(x) @@ -539,16 +533,19 @@ abstract class Module { return outPort; } + /// Registers and returns an output [LogicArray] port to this [Module] with + /// the specified [dimensions], [elementWidth], and [numDimensionsUnpacked] + /// named [name]. + /// + /// This is very similar to [addOutput], except for [LogicArray]s. LogicArray addOutputArray( String name, { List dimensions = const [1], int elementWidth = 1, - int numDimensionsUnpacked = 0, //TODO + int numDimensionsUnpacked = 0, }) { _checkForSafePortName(name); - //TODO: checks like are in `addInputArray` - final outArr = LogicArray(dimensions, elementWidth, name: name, numDimensionsUnpacked: numDimensionsUnpacked) // ignore: invalid_use_of_protected_member diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index b956ba7e4..01e1927a2 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -16,16 +16,54 @@ import 'package:test/test.dart'; class SimpleLAPassthrough extends Module { Logic get laOut => output('laOut'); - SimpleLAPassthrough(LogicArray laIn) { - laIn = addInputArray('laIn', laIn, - dimensions: laIn.dimensions, - elementWidth: laIn.elementWidth, - numDimensionsUnpacked: laIn.numDimensionsUnpacked); + SimpleLAPassthrough( + LogicArray laIn, { + List? dimOverride, + int? elemWidthOverride, + int? numUnpackedOverride, + }) { + laIn = addInputArray( + 'laIn', + laIn, + dimensions: dimOverride ?? laIn.dimensions, + elementWidth: elemWidthOverride ?? laIn.elementWidth, + numDimensionsUnpacked: numUnpackedOverride ?? laIn.numDimensionsUnpacked, + ); + + addOutputArray( + 'laOut', + dimensions: dimOverride ?? laIn.dimensions, + elementWidth: elemWidthOverride ?? laIn.elementWidth, + numDimensionsUnpacked: + numUnpackedOverride ?? laIn.numDimensionsUnpacked, + ) <= + laIn; + } +} - addOutputArray('laOut', - dimensions: laIn.dimensions, - elementWidth: laIn.elementWidth, - numDimensionsUnpacked: laIn.numDimensionsUnpacked) <= +class SimpleLAPassthroughLogic extends Module implements SimpleLAPassthrough { + @override + Logic get laOut => output('laOut'); + SimpleLAPassthroughLogic( + Logic laIn, { + required List dimensions, + required int elementWidth, + required int numDimensionsUnpacked, + }) { + laIn = addInputArray( + 'laIn', + laIn, + dimensions: dimensions, + elementWidth: elementWidth, + numDimensionsUnpacked: numDimensionsUnpacked, + ); + + addOutputArray( + 'laOut', + dimensions: dimensions, + elementWidth: elementWidth, + numDimensionsUnpacked: numDimensionsUnpacked, + ) <= laIn; } } @@ -229,7 +267,6 @@ class ConstantAssignmentArrayModule extends Module { //TODO: test passing packed into unpacked, unpacked into packed //TODO: test that unpacked and packed are properly instantiated in SV -//TODO: test passing Logic into addInput/OutputArray works //TODO: test arrays in conditional assignments //TODO: test arrays in If/Case expressions @@ -328,7 +365,8 @@ void main() { await SimCompare.checkFunctionalVector(mod, vectors); if (!noIverilog) { - SimCompare.checkIverilogVector(mod, vectors, buildOnly: noSvSim); + SimCompare.checkIverilogVector(mod, vectors, + buildOnly: noSvSim, dontDeleteTmpFiles: true); } } @@ -476,6 +514,34 @@ void main() { }); }); + group('different port and input widths', () { + test('array param mismatch', () async { + final i = LogicArray([3, 2], 8, numDimensionsUnpacked: 1); + final o = LogicArray([3, 2], 8, numDimensionsUnpacked: 1); + final mod = SimpleLAPassthrough( + i, + dimOverride: [1, 3], + elemWidthOverride: 16, + numUnpackedOverride: 0, + ); + o <= mod.laOut; + await testArrayPassthrough(mod); + }); + + test('logic into array', () async { + final i = Logic(width: 3 * 2 * 8); + final o = Logic(width: 3 * 2 * 8); + final mod = SimpleLAPassthroughLogic( + i, + dimensions: [1, 3], + elementWidth: 16, + numDimensionsUnpacked: 0, + ); + o <= mod.laOut; + await testArrayPassthrough(mod); + }); + }); + group('name collisions', () { test('3d', () async { final mod = ArrayNameConflicts(LogicArray([4, 3, 2], 8)); @@ -531,7 +597,7 @@ void main() { }); group('array constant assignments', () { - Future test_array_constant_assignments( + Future testArrayConstantAssignments( {required int numDimensionsUnpacked, bool doSvSim = true}) async { final mod = ConstantAssignmentArrayModule(LogicArray([3, 3, 3, 3], 8, numDimensionsUnpacked: numDimensionsUnpacked)); @@ -568,12 +634,12 @@ void main() { } test('with packed only', () async { - await test_array_constant_assignments(numDimensionsUnpacked: 0); + await testArrayConstantAssignments(numDimensionsUnpacked: 0); }); test('with unpacked also', () async { // unpacked array assignment not fully supported in iverilog - await test_array_constant_assignments( + await testArrayConstantAssignments( numDimensionsUnpacked: 2, doSvSim: false); }); }); From 65bff6c72617d20d62263300dcf5b0b58aa48666 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 30 May 2023 16:20:51 -0700 Subject: [PATCH 49/75] some paranoia about swizzle testing --- lib/src/modules/bus.dart | 2 -- test/logic_array_test.dart | 2 +- test/swizzle_test.dart | 25 +++++++++++++++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/lib/src/modules/bus.dart b/lib/src/modules/bus.dart index bff043361..b80ca6e65 100644 --- a/lib/src/modules/bus.dart +++ b/lib/src/modules/bus.dart @@ -164,8 +164,6 @@ class Swizzle extends Module with InlineSystemVerilog { out.put(updatedVal); } - //TODO: what happens when nothing in the swizzle? this is like a 0-width thing bug - @override String inlineVerilog(Map inputs) { if (inputs.length != _swizzleInputs.length) { diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 01e1927a2..aff837391 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -366,7 +366,7 @@ void main() { await SimCompare.checkFunctionalVector(mod, vectors); if (!noIverilog) { SimCompare.checkIverilogVector(mod, vectors, - buildOnly: noSvSim, dontDeleteTmpFiles: true); + buildOnly: noSvSim); } } diff --git a/test/swizzle_test.dart b/test/swizzle_test.dart index af1cf6fd5..13c167620 100644 --- a/test/swizzle_test.dart +++ b/test/swizzle_test.dart @@ -20,6 +20,15 @@ class SwizzlyModule extends Module { } } +class SwizzlyEmpty extends Module { + SwizzlyEmpty(Logic a) { + a = addInput('a', a, width: a.width); + addOutput('b', width: a.width) <= + [a, [].swizzle()].swizzle() + + [].swizzle().zeroExtend(a.width); + } +} + void main() { tearDown(() async { await Simulator.reset(); @@ -165,4 +174,20 @@ void main() { expect(simResult, equals(true)); }); }); + + test('zero-width swizzle', () { + [].swizzle(); + }); + + test('zero-width swizzle module', () async { + final mod = SwizzlyEmpty(Logic()); + await mod.build(); + final vectors = [ + Vector({'a': 0}, {'b': bin('0')}), + Vector({'a': 1}, {'b': bin('1')}), + ]; + await SimCompare.checkFunctionalVector(mod, vectors); + final simResult = SimCompare.iverilogVector(mod, vectors); + expect(simResult, equals(true)); + }); } From 6cfa91a8b1620e6faf8c8bbe587e3eab01ebd7aa Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 30 May 2023 16:22:38 -0700 Subject: [PATCH 50/75] make it so that oven_fsm is not generated all the time --- example/oven_fsm.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/example/oven_fsm.dart b/example/oven_fsm.dart index 7bee071bd..679e3ec80 100644 --- a/example/oven_fsm.dart +++ b/example/oven_fsm.dart @@ -200,7 +200,9 @@ Future main({bool noPrint = false}) async { // // Check on https://mermaid.js.org/intro/ to view the diagram generated. // If you are using vscode, you can download the mermaid extension. - oven.ovenStateMachine.generateDiagram(outputPath: 'oven_fsm.md'); + if (!noPrint) { + oven.ovenStateMachine.generateDiagram(outputPath: 'oven_fsm.md'); + } // Before we can simulate or generate code with the counter, we need // to build it. From f4eb858bd7a08bc9faa186ab7f2b37452c2af529 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 30 May 2023 16:33:30 -0700 Subject: [PATCH 51/75] add description to conditionalgroup --- lib/src/modules/conditional.dart | 15 +++++++++++++-- lib/src/signals/logic_structure.dart | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/src/modules/conditional.dart b/lib/src/modules/conditional.dart index 6815dcfd1..94255c12c 100644 --- a/lib/src/modules/conditional.dart +++ b/lib/src/modules/conditional.dart @@ -648,7 +648,10 @@ class ConditionalGroup extends Conditional { //TODO: add an ability to add comments to these groups final List conditionals; - ConditionalGroup(this.conditionals); + + final String? description; + + ConditionalGroup(this.conditionals, {this.description}); @override Map _processSsa(Map currentMappings, @@ -680,7 +683,12 @@ class ConditionalGroup extends Conditional { @override String verilogContents(int indent, Map inputsNameMap, Map outputsNameMap, String assignOperator) { - final verilog = StringBuffer(); + final verilog = StringBuffer()..writeln(); + + if (description != null) { + verilog.writeln('// $description'); + } + for (final conditional in conditionals) { verilog.write(conditional.verilogContents( indent, @@ -689,6 +697,9 @@ class ConditionalGroup extends Conditional { assignOperator, )); } + + verilog.writeln(); + return verilog.toString(); } } diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index b86c5d698..225e2f764 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -198,7 +198,7 @@ class LogicStructure implements Logic { @override Logic slice(int endIndex, int startIndex) => - packed.slice(endIndex, startIndex); + packed.slice(endIndex, startIndex); //TODO: this should use getRange //TODO: don't make these operate on per-element, just pack the whole thing and do it? From 4bede120b5424540562c7b515cfa941b215a0a9a Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 30 May 2023 16:42:11 -0700 Subject: [PATCH 52/75] resolve merge issues and fix tmp file thing --- lib/src/signals/logic.dart | 2 +- lib/src/signals/logic_structure.dart | 3 +++ lib/src/utilities/simcompare.dart | 18 ++++++++---------- test/bus_test.dart | 1 + 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index 55d26df9f..2497e11ea 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -302,7 +302,7 @@ class Logic { Logic operator ^(Logic other) => Xor2Gate(this, other).out; /// Power operation - Logic pow(dynamic other) => Power(this, other).out; + Logic pow(dynamic exponent) => Power(this, exponent).out; /// Addition. /// diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 225e2f764..d232aaa14 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -326,6 +326,9 @@ class LogicStructure implements Logic { @override Logic operator %(dynamic other) => packed % other; + @override + Logic pow(dynamic exponent) => packed.pow(exponent); + @override Logic operator <<(dynamic other) => packed << other; diff --git a/lib/src/utilities/simcompare.dart b/lib/src/utilities/simcompare.dart index 1cbfa2205..16c6636dd 100644 --- a/lib/src/utilities/simcompare.dart +++ b/lib/src/utilities/simcompare.dart @@ -330,16 +330,14 @@ abstract class SimCompare { return false; } - if (buildOnly) { - return true; - } - - final simResult = Process.runSync('vvp', [tmpOutput]); - if (printIfContentsAndCheckError(simResult.stdout)) { - return false; - } - if (printIfContentsAndCheckError(simResult.stderr)) { - return false; + if (!buildOnly) { + final simResult = Process.runSync('vvp', [tmpOutput]); + if (printIfContentsAndCheckError(simResult.stdout)) { + return false; + } + if (printIfContentsAndCheckError(simResult.stderr)) { + return false; + } } if (!dontDeleteTmpFiles) { diff --git a/test/bus_test.dart b/test/bus_test.dart index 30665244f..22cb5c607 100644 --- a/test/bus_test.dart +++ b/test/bus_test.dart @@ -9,6 +9,7 @@ /// import 'package:rohd/rohd.dart'; +import 'package:rohd/src/exceptions/exceptions.dart'; import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; From 421632f819e69db2d744938694b8e54a0b06e472 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Wed, 31 May 2023 10:03:54 -0700 Subject: [PATCH 53/75] added logic array ports --- lib/src/interface.dart | 12 +----------- lib/src/module.dart | 8 ++++---- lib/src/signals/logic_array.dart | 33 +++++++++++++++++++------------- lib/src/signals/port.dart | 25 ++++++++++++++++++++++++ lib/src/signals/signals.dart | 1 + 5 files changed, 51 insertions(+), 28 deletions(-) create mode 100644 lib/src/signals/port.dart diff --git a/lib/src/interface.dart b/lib/src/interface.dart index 3b08ede2c..05507b99e 100644 --- a/lib/src/interface.dart +++ b/lib/src/interface.dart @@ -15,17 +15,7 @@ import 'package:rohd/rohd.dart'; import 'package:rohd/src/exceptions/name/name_exceptions.dart'; import 'package:rohd/src/utilities/sanitizer.dart'; -/// An extension of [Logic] useful for [Interface] definitions. -class Port extends Logic { - /// Constructs a [Logic] intended to be used for ports in an [Interface]. - Port(String name, [int width = 1]) : super(name: name, width: width) { - if (!Sanitizer.isSanitary(name)) { - throw InvalidPortNameException(name); - } - } -} - -//TODO: how to support ports that are arrays? +//TODO: test interfaces with arrays /// Represents a logical interface to a [Module]. /// diff --git a/lib/src/module.dart b/lib/src/module.dart index 6e0908b7d..a01b730ab 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -456,6 +456,7 @@ abstract class Module { if (!Sanitizer.isSanitary(name)) { throw InvalidPortNameException(name); } + if (outputs.containsKey(name) || inputs.containsKey(name)) { throw Exception('Already defined a port with name "$name".'); } @@ -469,8 +470,7 @@ abstract class Module { Logic addInput(String name, Logic x, {int width = 1}) { _checkForSafePortName(name); if (x.width != width) { - throw Exception('Port width mismatch, signal "$x" does not' - ' have specified width "$width".'); + throw PortWidthMismatchException(x, width); } if (x is LogicStructure) { @@ -478,7 +478,7 @@ abstract class Module { x = x.packed; } - final inPort = Logic(name: name, width: width) + final inPort = Port(name, width) ..gets(x) // ignore: invalid_use_of_protected_member ..parentModule = this; @@ -524,7 +524,7 @@ abstract class Module { Logic addOutput(String name, {int width = 1}) { _checkForSafePortName(name); - final outPort = Logic(name: name, width: width) + final outPort = Port(name, width) // ignore: invalid_use_of_protected_member ..parentModule = this; diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index 50957b903..358807e8a 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -107,11 +107,16 @@ class LogicArray extends LogicStructure { factory LogicArray(List dimensions, int elementWidth, {String? name, int numDimensionsUnpacked = 0}) { if (dimensions.isEmpty) { - //TODO + //TODO: fix exception + //TODO: test throw Exception('Array must have at least 1 dimension'); } - //TODO: check that numDimensionsUnpacked <= dimensions.length + if (numDimensionsUnpacked > dimensions.length) { + //TODO: fix exception + //TODO: test + throw Exception('Cannot unpack more than dim length'); + } return LogicArray._( List.generate( @@ -133,15 +138,17 @@ class LogicArray extends LogicStructure { ); } - //TODO: doc and test - factory LogicArray.of(Logic other, - {required List dimensions, - required int elementWidth, - String? name, - int numDimensionsUnpacked = 0}) => - LogicArray(dimensions, elementWidth, - name: name, numDimensionsUnpacked: numDimensionsUnpacked) - ..gets(other); - - //TODO: can we be stricter about assignments, etc. for arrays, only like-shaped arrays? + //TODO: test rohd cosim with array ports + + factory LogicArray.port(String name, + [List dimensions = const [1], + int elementWidth = 1, + int numDimensionsUnpacked = 0]) { + if (!Sanitizer.isSanitary(name)) { + throw InvalidPortNameException(name); + } + + return LogicArray(dimensions, elementWidth, + numDimensionsUnpacked: numDimensionsUnpacked, name: name); + } } diff --git a/lib/src/signals/port.dart b/lib/src/signals/port.dart new file mode 100644 index 000000000..74b8fc3ee --- /dev/null +++ b/lib/src/signals/port.dart @@ -0,0 +1,25 @@ +// Copyright (C) 2021-2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// port.dart +// Definition of Port. +// +// 2023 May 30 +// Author: Max Korbel + +import 'package:rohd/rohd.dart'; +import 'package:rohd/src/exceptions/name/invalid_portname_exceptions.dart'; +import 'package:rohd/src/utilities/sanitizer.dart'; + +/// An extension of [Logic] which performs some additional validation for +/// inputs and outputs of [Module]s. +/// +/// Useful for [Interface] definitions. +class Port extends Logic { + /// Constructs a [Logic] intended to be used for ports in an [Interface]. + Port(String name, [int width = 1]) : super(name: name, width: width) { + if (!Sanitizer.isSanitary(name)) { + throw InvalidPortNameException(name); + } + } +} diff --git a/lib/src/signals/signals.dart b/lib/src/signals/signals.dart index 592fdab34..9cf087d69 100644 --- a/lib/src/signals/signals.dart +++ b/lib/src/signals/signals.dart @@ -15,6 +15,7 @@ import 'package:rohd/src/utilities/sanitizer.dart'; import 'package:rohd/src/utilities/synchronous_propagator.dart'; export 'logic_value_changed.dart'; +export 'port.dart'; part 'const.dart'; part 'logic.dart'; From 57586d24a4ad2151e12894c606c219188a892573 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Wed, 31 May 2023 13:36:48 -0700 Subject: [PATCH 54/75] got conditional assignments tested and working --- lib/src/modules/conditional.dart | 22 +++++++------- lib/src/signals/logic_structure.dart | 35 +++++++++++----------- test/logic_array_test.dart | 45 ++++++++++++++++++++++++---- 3 files changed, 67 insertions(+), 35 deletions(-) diff --git a/lib/src/modules/conditional.dart b/lib/src/modules/conditional.dart index 9dace061f..247d88fb3 100644 --- a/lib/src/modules/conditional.dart +++ b/lib/src/modules/conditional.dart @@ -721,22 +721,20 @@ class ConditionalGroup extends Conditional { @override String verilogContents(int indent, Map inputsNameMap, Map outputsNameMap, String assignOperator) { - final verilog = StringBuffer()..writeln(); + final verilog = StringBuffer(); if (description != null) { verilog.writeln('// $description'); } - for (final conditional in conditionals) { - verilog.write(conditional.verilogContents( - indent, - inputsNameMap, - outputsNameMap, - assignOperator, - )); - } - - verilog.writeln(); + verilog.write(conditionals + .map((c) => c.verilogContents( + indent, + inputsNameMap, + outputsNameMap, + assignOperator, + )) + .join('\n')); return verilog.toString(); } @@ -756,7 +754,7 @@ class ConditionalAssign extends Conditional { /// Conditionally assigns [receiver] to the value of [driver]. ConditionalAssign(this.receiver, this.driver) { if (driver.width != receiver.width) { - throw Exception('Width for $receiver and $driver must match but do not.'); + throw PortWidthMismatchException.equalWidth(receiver, driver); } } diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index d232aaa14..c83187328 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -77,7 +77,7 @@ class LogicStructure implements Logic { final logicVal = LogicValue.of(val, fill: fill, width: width); var index = 0; - for (final element in elements) { + for (final element in leafElements) { element.put(logicVal.getRange(index, index + element.width)); index += element.width; } @@ -88,12 +88,26 @@ class LogicStructure implements Logic { final logicVal = LogicValue.of(val, fill: fill, width: width); var index = 0; - for (final element in elements) { + for (final element in leafElements) { element.inject(logicVal.getRange(index, index + element.width)); index += element.width; } } + @override + void gets(Logic other) { + if (other.width != width) { + throw PortWidthMismatchException(other, width); + } + + var index = 0; + for (final element in leafElements) { + element <= other.getRange(index, index + element.width); + + index += element.width; + } + } + @override Conditional operator <(dynamic other) { final otherLogic = other is Logic ? other : Const(other, width: width); @@ -105,7 +119,7 @@ class LogicStructure implements Logic { final conditionalAssigns = []; var index = 0; - for (final element in elements) { + for (final element in leafElements) { //TODO: same as `gets`, iterate if element is array? conditionalAssigns .add(element < otherLogic.getRange(index, index + element.width)); @@ -116,6 +130,7 @@ class LogicStructure implements Logic { } //TODO + // mention they are in order! late final List leafElements = UnmodifiableListView(_calculateLeafElements()); @@ -132,20 +147,6 @@ class LogicStructure implements Logic { return leaves; } - @override - void gets(Logic other) { - if (other.width != width) { - throw PortWidthMismatchException(other, width); - } - - var index = 0; - for (final element in leafElements) { - element <= other.getRange(index, index + element.width); - - index += element.width; - } - } - @override void operator <=(Logic other) => gets(other); diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index aff837391..e80b54813 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -260,16 +260,43 @@ class ConstantAssignmentArrayModule extends Module { (Logic(width: 3 * 3 * 8)..gets(Const(0, width: 3 * 3 * 8))); laOut.elements[2].elements[2].elements[1] <= Const(1, width: 3 * 8, fill: true); - laOut.elements[2].elements[2].elements[2].elements[1] <= - Const(0, width: 8, fill: true); + laOut.elements[2].elements[2].elements[2].elements[1] <= Const(0, width: 8); + } +} + +class CondAssignArray extends Module implements SimpleLAPassthrough { + @override + Logic get laOut => output('laOut'); + CondAssignArray( + LogicArray laIn, { + List? dimOverride, + int? elemWidthOverride, + int? numUnpackedOverride, + }) { + laIn = addInputArray( + 'laIn', + laIn, + dimensions: dimOverride ?? laIn.dimensions, + elementWidth: elemWidthOverride ?? laIn.elementWidth, + numDimensionsUnpacked: numUnpackedOverride ?? laIn.numDimensionsUnpacked, + ); + + final laOut = addOutputArray( + 'laOut', + dimensions: dimOverride ?? laIn.dimensions, + elementWidth: elemWidthOverride ?? laIn.elementWidth, + numDimensionsUnpacked: numUnpackedOverride ?? laIn.numDimensionsUnpacked, + ); + + Combinational([laOut < laIn]); } } -//TODO: test passing packed into unpacked, unpacked into packed -//TODO: test that unpacked and packed are properly instantiated in SV //TODO: test arrays in conditional assignments //TODO: test arrays in If/Case expressions +//TODO: test single-dimension, one-bit elements (looks like normal logic) + //TODO: file issue tracking that unpacked arrays not fully tested // https://github.com/steveicarus/iverilog/issues/482 // https://github.com/verilator/verilator/issues/2907 @@ -365,8 +392,7 @@ void main() { await SimCompare.checkFunctionalVector(mod, vectors); if (!noIverilog) { - SimCompare.checkIverilogVector(mod, vectors, - buildOnly: noSvSim); + SimCompare.checkIverilogVector(mod, vectors, buildOnly: noSvSim); } } @@ -594,6 +620,13 @@ void main() { await testArrayPassthrough(mod, checkNoSwizzle: false, noSvSim: true); }); }); + + group('conditionals', () { + test('3 dimensions conditional assignment', () async { + final mod = CondAssignArray(LogicArray([3, 2, 3], 8)); + await testArrayPassthrough(mod); + }); + }); }); group('array constant assignments', () { From ac9d3e8f885d23ee45f554f139c6c9b0721257b1 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Wed, 31 May 2023 16:16:49 -0700 Subject: [PATCH 55/75] test array of single bit like a bus --- test/logic_array_test.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index e80b54813..c3af76337 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -295,8 +295,6 @@ class CondAssignArray extends Module implements SimpleLAPassthrough { //TODO: test arrays in conditional assignments //TODO: test arrays in If/Case expressions -//TODO: test single-dimension, one-bit elements (looks like normal logic) - //TODO: file issue tracking that unpacked arrays not fully tested // https://github.com/steveicarus/iverilog/issues/482 // https://github.com/verilator/verilator/issues/2907 @@ -407,6 +405,11 @@ void main() { await testArrayPassthrough(mod); }); + test('array of bits', () async { + final mod = SimpleLAPassthrough(LogicArray([8], 1)); + await testArrayPassthrough(mod); + }); + test('2 dimensions', () async { final mod = SimpleLAPassthrough(LogicArray([3, 2], 8)); await testArrayPassthrough(mod); From ee929b15d1f48f75391a6ba2ada38bbff35f39e9 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Wed, 31 May 2023 16:32:58 -0700 Subject: [PATCH 56/75] arrays as expressions --- test/logic_array_test.dart | 46 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index c3af76337..03c79cac6 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -292,8 +292,45 @@ class CondAssignArray extends Module implements SimpleLAPassthrough { } } -//TODO: test arrays in conditional assignments -//TODO: test arrays in If/Case expressions +class CondCompArray extends Module implements SimpleLAPassthrough { + @override + Logic get laOut => output('laOut'); + CondCompArray( + LogicArray laIn, { + List? dimOverride, + int? elemWidthOverride, + int? numUnpackedOverride, + }) : assert(laIn.dimensions.length == 1, 'test assumes 1x1 array'), + assert(laIn.width == 1, 'test assumes 1x1 array') { + laIn = addInputArray( + 'laIn', + laIn, + dimensions: dimOverride ?? laIn.dimensions, + elementWidth: elemWidthOverride ?? laIn.elementWidth, + numDimensionsUnpacked: numUnpackedOverride ?? laIn.numDimensionsUnpacked, + ); + + final laOut = addOutputArray( + 'laOut', + dimensions: dimOverride ?? laIn.dimensions, + elementWidth: elemWidthOverride ?? laIn.elementWidth, + numDimensionsUnpacked: numUnpackedOverride ?? laIn.numDimensionsUnpacked, + ); + + Combinational([ + If( + laIn, + then: [laOut < laIn], + orElse: [ + Case(laIn, [ + CaseItem(Const(0), [laOut < laIn]), + CaseItem(Const(1), [laOut < ~laIn]), + ]) + ], + ), + ]); + } +} //TODO: file issue tracking that unpacked arrays not fully tested // https://github.com/steveicarus/iverilog/issues/482 @@ -629,6 +666,11 @@ void main() { final mod = CondAssignArray(LogicArray([3, 2, 3], 8)); await testArrayPassthrough(mod); }); + + test('1x1 expressions in if and case', () async { + final mod = CondCompArray(LogicArray([1], 1)); + await testArrayPassthrough(mod); + }); }); }); From 493d872657e96a26fbe24d641eea39131ab2220b Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Thu, 1 Jun 2023 10:30:15 -0700 Subject: [PATCH 57/75] test with interfaces --- lib/src/interface.dart | 4 -- lib/src/values/logic_value.dart | 3 ++ test/logic_array_test.dart | 70 +++++++++++++++++++++++++++------ 3 files changed, 62 insertions(+), 15 deletions(-) diff --git a/lib/src/interface.dart b/lib/src/interface.dart index 05507b99e..5c1b109e2 100644 --- a/lib/src/interface.dart +++ b/lib/src/interface.dart @@ -12,10 +12,6 @@ import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/name/name_exceptions.dart'; -import 'package:rohd/src/utilities/sanitizer.dart'; - -//TODO: test interfaces with arrays /// Represents a logical interface to a [Module]. /// diff --git a/lib/src/values/logic_value.dart b/lib/src/values/logic_value.dart index 7843aabe0..f53a61faf 100644 --- a/lib/src/values/logic_value.dart +++ b/lib/src/values/logic_value.dart @@ -114,6 +114,8 @@ abstract class LogicValue { : throw Exception('Failed to convert.'); } + // complete test suite for LogicValue.of + /// Constructs a [LogicValue] from [val] which could be of a variety of types. /// /// Supported types include [String], [bool], [int], [BigInt], [LogicValue], @@ -237,6 +239,7 @@ abstract class LogicValue { if (val.length == 1 && (val.first == LogicValue.x || val.first == LogicValue.z || fill)) { if (!val.first.isValid) { + // ignore: parameter_assignments width ??= 1; } if (width == null) { diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 03c79cac6..9c6e51058 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -41,6 +41,56 @@ class SimpleLAPassthrough extends Module { } } +enum LADir { laIn, laOut } + +class LAPassthroughIntf extends Interface { + final List dimensions; + final int elementWidth; + final int numDimensionsUnpacked; + + Logic get laIn => port('laIn'); + Logic get laOut => port('laOut'); + + LAPassthroughIntf({ + required this.dimensions, + required this.elementWidth, + required this.numDimensionsUnpacked, + }) { + setPorts([ + LogicArray.port('laIn', dimensions, elementWidth, numDimensionsUnpacked) + ], [ + LADir.laIn + ]); + + setPorts([ + LogicArray.port('laOut', dimensions, elementWidth, numDimensionsUnpacked) + ], [ + LADir.laOut + ]); + } + + LAPassthroughIntf.clone(LAPassthroughIntf other) + : this( + dimensions: other.dimensions, + elementWidth: other.elementWidth, + numDimensionsUnpacked: other.numDimensionsUnpacked, + ); +} + +class LAPassthroughWithIntf extends Module implements SimpleLAPassthrough { + @override + Logic get laOut => output('laOut'); + LAPassthroughWithIntf( + LAPassthroughIntf intf, + ) { + intf = LAPassthroughIntf.clone(intf) + ..connectIO(this, intf, + inputTags: {LADir.laIn}, outputTags: {LADir.laOut}); + + intf.laOut <= intf.laIn; + } +} + class SimpleLAPassthroughLogic extends Module implements SimpleLAPassthrough { @override Logic get laOut => output('laOut'); @@ -332,10 +382,6 @@ class CondCompArray extends Module implements SimpleLAPassthrough { } } -//TODO: file issue tracking that unpacked arrays not fully tested -// https://github.com/steveicarus/iverilog/issues/482 -// https://github.com/verilator/verilator/issues/2907 - void main() { tearDown(() async { await Simulator.reset(); @@ -505,6 +551,15 @@ void main() { final mod = SimpleLAPassthrough(subArray); await testArrayPassthrough(mod); }); + + test('3 dimensions with interface', () async { + final mod = LAPassthroughWithIntf(LAPassthroughIntf( + dimensions: [3, 2, 3], + elementWidth: 8, + numDimensionsUnpacked: 0, + )); + await testArrayPassthrough(mod); + }); }); group('pack and unpack', () { @@ -556,13 +611,6 @@ void main() { group('change array dimensions around and back', () { test('3d', () async { - // final x = LogicArray([3], 8); - // final y = LogicArray([3], 8); - // //TODO: write a part-assign automation - // for (var i = 0; i < 2; i++) { - // x.elements[i] <= y.elements[i]; - // } - final mod = RearrangeArraysPassthrough(LogicArray([4, 3, 2], 8)); await testArrayPassthrough(mod); }); From 6378b3b17a061baccab0ff7e848f36062fea0d34 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Thu, 1 Jun 2023 10:39:00 -0700 Subject: [PATCH 58/75] some cleanup of conditionalgroup --- lib/src/modules/conditional.dart | 43 +++++++++++++------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/lib/src/modules/conditional.dart b/lib/src/modules/conditional.dart index 247d88fb3..be396026d 100644 --- a/lib/src/modules/conditional.dart +++ b/lib/src/modules/conditional.dart @@ -682,14 +682,12 @@ abstract class Conditional { /// Represents a group of [Conditional]s to be executed. class ConditionalGroup extends Conditional { - //TODO: Tests for conditional groups - //TODO: add an ability to add comments to these groups - + @override final List conditionals; - final String? description; - - ConditionalGroup(this.conditionals, {this.description}); + /// Creates a group of [conditionals] to be executed in order and bundles + /// them into a single [Conditional]. + ConditionalGroup(this.conditionals); @override Map _processSsa(Map currentMappings, @@ -720,24 +718,15 @@ class ConditionalGroup extends Conditional { @override String verilogContents(int indent, Map inputsNameMap, - Map outputsNameMap, String assignOperator) { - final verilog = StringBuffer(); - - if (description != null) { - verilog.writeln('// $description'); - } - - verilog.write(conditionals - .map((c) => c.verilogContents( - indent, - inputsNameMap, - outputsNameMap, - assignOperator, - )) - .join('\n')); - - return verilog.toString(); - } + Map outputsNameMap, String assignOperator) => + conditionals + .map((c) => c.verilogContents( + indent, + inputsNameMap, + outputsNameMap, + assignOperator, + )) + .join('\n'); } /// An assignment that only happens under certain conditions. @@ -951,7 +940,8 @@ class Case extends Conditional { } @override - late final List conditionals = _getConditionals(); + late final List conditionals = + UnmodifiableListView(_getConditionals()); /// Calculates the set of conditionals directly within this. List _getConditionals() => [ @@ -1230,7 +1220,8 @@ class If extends Conditional { } @override - late final List conditionals = _getConditionals(); + late final List conditionals = + UnmodifiableListView(_getConditionals()); /// Calculates the set of conditionals directly within this. List _getConditionals() => iffs From e44580f9d6ab6fbd27c7e501a0ddce103b98e82c Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Thu, 1 Jun 2023 10:56:25 -0700 Subject: [PATCH 59/75] cleanup based on api design changes for arrays --- lib/src/module.dart | 5 -- lib/src/signals/logic_array.dart | 99 +++++++------------------------- 2 files changed, 21 insertions(+), 83 deletions(-) diff --git a/lib/src/module.dart b/lib/src/module.dart index a01b730ab..6adc81a1d 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -121,11 +121,6 @@ abstract class Module { _outputs[net.name] == net || (net.isArrayMember && isOutput(net.parentStructure!)); - //TODO: what if inputs/outputs are added multiple times? - // what if elements of arrays are added again? is that ok? - // what if an array is added which had elements previously? is that ok? - // what if in general input is fed into input? - /// 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); diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index 358807e8a..5cdecf442 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -10,88 +10,23 @@ part of signals; class LogicArray extends LogicStructure { - //TODO: calculate dimension - // Note: if any level of hierarchy has any elemnt *not* an array, then that's the end of it (flatten from that point down) - - //TODO: if there's complex structure below an array, need to convey that it is flattened when instantiated somehow - // OR: just ban lists of anything other than vanilla logics? that's probably good enough? - - late final List dimensions = _calculateDimensions(); - - //TODO: support ports that are packed, unpacked, or a mix - - List _calculateDimensions() { - // current dimension is just the length - final currDim = elements.length; - - if (currDim == 0) { - return const [0]; - } - - // check if all elements are: - // - LogicArrays - // - with the same dimensions - // - with same element widths - - //TODO: if we always construct ourselves, then this is safer? - final allElementsAreArray = - elements.firstWhereOrNull((element) => element is! LogicArray) == null; - - if (!allElementsAreArray) { - return [currDim]; - } - - final firstDim = (elements.first as LogicArray).dimensions; - - final listEq = const ListEquality().equals; - - for (final element in elements) { - element as LogicArray; - if (!listEq(firstDim, element.dimensions)) { - return [currDim]; - } - } - - // if they're all the same, return it back up - return [currDim, ...firstDim]; - } + final List dimensions; /// The width of leaf elements in this array. /// /// If the array has no leaf elements and/or the [width] is 0, then the /// [elementWidth] is always 0. - late final int elementWidth = _calculateElementWidth(); - - int _calculateElementWidth() { - if (width == 0) { - return 0; - } - - // assume all elements are the same width - - Logic arr = this; - while (arr.elements.first is LogicArray) { - arr = arr.elements.first; - } - return arr.elements.first.width; - } + final int elementWidth; @override String toString() => 'LogicArray($dimensions, $elementWidth): $name'; ///TODO - LogicArray._(super.elements, {super.name, this.numDimensionsUnpacked = 0}) { - if (elements.isNotEmpty) { - final dim0 = elements.first.width; - for (final component in elements) { - if (component.width != dim0) { - throw PortWidthMismatchException(component, dim0, - additionalMessage: - 'All elements of a `LogicArray` must be equal width.'); - } - } - } - } + LogicArray._(super.elements, + {required this.dimensions, + required this.elementWidth, + super.name, + this.numDimensionsUnpacked = 0}); final int numDimensionsUnpacked; @@ -118,15 +53,23 @@ class LogicArray extends LogicStructure { throw Exception('Cannot unpack more than dim length'); } + // calculate the next layer's dimensions + final nextDimensions = dimensions.length == 1 + ? null + : dimensions.getRange(1, dimensions.length).toList(growable: false); + + // if the total width will eventually be 0, then force element width to 0 + if (elementWidth != 0 && dimensions.reduce((a, b) => a * b) == 0) { + elementWidth = 0; + } + return LogicArray._( List.generate( - dimensions[0], + dimensions.first, (index) => (dimensions.length == 1 ? Logic(width: elementWidth) : LogicArray( - dimensions - .getRange(1, dimensions.length) - .toList(growable: false), + nextDimensions!, elementWidth, //TODO: test that this gets propagated down properly numDimensionsUnpacked: max(0, numDimensionsUnpacked - 1), @@ -134,12 +77,12 @@ class LogicArray extends LogicStructure { .._arrayIndex = index, growable: false), name: name, + dimensions: dimensions, + elementWidth: elementWidth, numDimensionsUnpacked: numDimensionsUnpacked, ); } - //TODO: test rohd cosim with array ports - factory LogicArray.port(String name, [List dimensions = const [1], int elementWidth = 1, From 2bb7eebd1051cbf231d50fae0255004d94d64285 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Thu, 1 Jun 2023 11:25:00 -0700 Subject: [PATCH 60/75] Improved error checking for construction of arrays --- .../logic/logic_construction_exception.dart | 21 +++++++++++ .../exceptions/logic/logic_exceptions.dart | 1 + lib/src/signals/logic_array.dart | 37 ++++++++++--------- lib/src/signals/logic_structure.dart | 7 +++- test/logic_array_test.dart | 9 ++++- 5 files changed, 55 insertions(+), 20 deletions(-) create mode 100644 lib/src/exceptions/logic/logic_construction_exception.dart diff --git a/lib/src/exceptions/logic/logic_construction_exception.dart b/lib/src/exceptions/logic/logic_construction_exception.dart new file mode 100644 index 000000000..bb6c2dc1e --- /dev/null +++ b/lib/src/exceptions/logic/logic_construction_exception.dart @@ -0,0 +1,21 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// logic_construction_exception.dart +// An exception thrown when a logical signal fails to construct. +// +// 2023 June 1 +// Author: Max Korbel + +import 'package:rohd/rohd.dart'; +import 'package:rohd/src/exceptions/rohd_exception.dart'; + +/// An exception that thrown when a [Logic] is connecting to itself. +class LogicConstructionException extends RohdException { + /// A message describing why the construction failed. + final String reason; + + /// Creates an exception when a [Logic] is trying to connect itself. + LogicConstructionException(this.reason) + : super('Failed to construct signal: $reason'); +} diff --git a/lib/src/exceptions/logic/logic_exceptions.dart b/lib/src/exceptions/logic/logic_exceptions.dart index ae89ff1a9..ab7c343b0 100644 --- a/lib/src/exceptions/logic/logic_exceptions.dart +++ b/lib/src/exceptions/logic/logic_exceptions.dart @@ -2,5 +2,6 @@ /// SPDX-License-Identifier: BSD-3-Clause export 'invalid_multiplier_exception.dart'; +export 'logic_construction_exception.dart'; export 'put_exception.dart'; export 'self_connecting_logic_exception.dart'; diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index 5cdecf442..1820a3975 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -21,13 +21,6 @@ class LogicArray extends LogicStructure { @override String toString() => 'LogicArray($dimensions, $elementWidth): $name'; - ///TODO - LogicArray._(super.elements, - {required this.dimensions, - required this.elementWidth, - super.name, - this.numDimensionsUnpacked = 0}); - final int numDimensionsUnpacked; ///TODO @@ -42,21 +35,20 @@ class LogicArray extends LogicStructure { factory LogicArray(List dimensions, int elementWidth, {String? name, int numDimensionsUnpacked = 0}) { if (dimensions.isEmpty) { - //TODO: fix exception - //TODO: test - throw Exception('Array must have at least 1 dimension'); + throw LogicConstructionException( + 'Arrays must have at least 1 dimension.'); } if (numDimensionsUnpacked > dimensions.length) { - //TODO: fix exception - //TODO: test - throw Exception('Cannot unpack more than dim length'); + throw LogicConstructionException( + 'Cannot unpack more than all of the dimensions.'); } // calculate the next layer's dimensions final nextDimensions = dimensions.length == 1 ? null - : dimensions.getRange(1, dimensions.length).toList(growable: false); + : UnmodifiableListView( + dimensions.getRange(1, dimensions.length).toList(growable: false)); // if the total width will eventually be 0, then force element width to 0 if (elementWidth != 0 && dimensions.reduce((a, b) => a * b) == 0) { @@ -69,20 +61,29 @@ class LogicArray extends LogicStructure { (index) => (dimensions.length == 1 ? Logic(width: elementWidth) : LogicArray( - nextDimensions!, - elementWidth, + nextDimensions!, elementWidth, //TODO: test that this gets propagated down properly numDimensionsUnpacked: max(0, numDimensionsUnpacked - 1), + name: '${name}_$index', )) .._arrayIndex = index, growable: false), - name: name, - dimensions: dimensions, + dimensions: UnmodifiableListView(dimensions), elementWidth: elementWidth, numDimensionsUnpacked: numDimensionsUnpacked, + name: name, ); } + ///TODO + LogicArray._( + super.elements, { + required this.dimensions, + required this.elementWidth, + required this.numDimensionsUnpacked, + required super.name, + }); + factory LogicArray.port(String name, [List dimensions = const [1], int elementWidth = 1, diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index c83187328..41c171b54 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -60,9 +60,12 @@ class LogicStructure implements Logic { } } - int? _arrayIndex; + @override int? get arrayIndex => _arrayIndex; + @override + int? _arrayIndex; + @override bool get isArrayMember => parentStructure is LogicArray; @@ -258,6 +261,8 @@ class LogicStructure implements Logic { //TODO: to track naming @override LogicStructure? get parentStructure => _parentStructure; + + @override LogicStructure? _parentStructure; @override diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 9c6e51058..c0d67ca5c 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -11,6 +11,7 @@ import 'dart:math'; import 'package:collection/collection.dart'; import 'package:rohd/rohd.dart'; +import 'package:rohd/src/exceptions/exceptions.dart'; import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; @@ -444,7 +445,13 @@ void main() { }); test('no dim exception', () { - //TODO + expect( + () => LogicArray([], 3), throwsA(isA())); + }); + + test('overly unpacking exception', () { + expect(() => LogicArray([1, 2, 3], 4, numDimensionsUnpacked: 4), + throwsA(isA())); }); }); From c7f18de363c736f8144eaf5f2f524e2079c83338 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Thu, 1 Jun 2023 11:44:55 -0700 Subject: [PATCH 61/75] test that unpacked dims get passed correctly --- lib/src/signals/logic_array.dart | 4 ++-- lib/src/signals/logic_structure.dart | 2 -- test/logic_array_test.dart | 8 ++++++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index 1820a3975..1990c50da 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -61,8 +61,8 @@ class LogicArray extends LogicStructure { (index) => (dimensions.length == 1 ? Logic(width: elementWidth) : LogicArray( - nextDimensions!, elementWidth, - //TODO: test that this gets propagated down properly + nextDimensions!, + elementWidth, numDimensionsUnpacked: max(0, numDimensionsUnpacked - 1), name: '${name}_$index', )) diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 41c171b54..29a533597 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -269,11 +269,9 @@ class LogicStructure implements Logic { bool get isInput => parentModule?.isInput(this) ?? false; @override - // TODO: implement isOutput bool get isOutput => parentModule?.isOutput(this) ?? false; @override - // TODO: implement isPort bool get isPort => isInput || isOutput; @override diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index c0d67ca5c..dc531ca72 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -453,6 +453,14 @@ void main() { expect(() => LogicArray([1, 2, 3], 4, numDimensionsUnpacked: 4), throwsA(isA())); }); + + test('unpacked dims get passed down', () { + final arr = LogicArray([1, 2, 3], 4, numDimensionsUnpacked: 2); + expect(arr.numDimensionsUnpacked, 2); + expect((arr.elements[0] as LogicArray).numDimensionsUnpacked, 1); + expect( + (arr.elements[0].elements[0] as LogicArray).numDimensionsUnpacked, 0); + }); }); group('logicarray passthrough', () { From b29620f719d85cd072d551829f7c815d3fa101c5 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Thu, 1 Jun 2023 13:28:32 -0700 Subject: [PATCH 62/75] cleanup and improve checks on structure construction --- lib/src/signals/logic_structure.dart | 21 ++++++++++++++------- lib/src/synthesizers/systemverilog.dart | 4 ++-- test/logic_array_test.dart | 10 ++++++---- test/logic_structure_test.dart | 22 +++++++++++++++++++++- 4 files changed, 43 insertions(+), 14 deletions(-) diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 29a533597..6afe7b825 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -34,20 +34,26 @@ class LogicStructure implements Logic { static int _structIdx = 0; /// Creates a new [LogicStructure] with [elements] as elements. + /// + /// None of the [elements] can already be members of another [LogicStructure]. LogicStructure(Iterable elements, {String? name}) : name = (name == null || name.isEmpty) ? 'st${_structIdx++}' : Sanitizer.sanitizeSV(name) { - //TODO: make sure no components already have a parentComponent - // but do we care? _elements ..addAll(elements) ..forEach((element) { + if (element._parentStructure != null) { + throw LogicConstructionException( + '$element already is a member of a structure' + ' ${element._parentStructure}.'); + } + element._parentStructure = this; }); } - //TODO + @override String get structureName { if (parentStructure != null) { if (isArrayMember) { @@ -123,7 +129,6 @@ class LogicStructure implements Logic { var index = 0; for (final element in leafElements) { - //TODO: same as `gets`, iterate if element is array? conditionalAssigns .add(element < otherLogic.getRange(index, index + element.width)); index += element.width; @@ -132,12 +137,12 @@ class LogicStructure implements Logic { return ConditionalGroup(conditionalAssigns); } - //TODO - // mention they are in order! + /// A list of all leaf-level elements at the deepest hierarchy of this + /// structure provided in index order. late final List leafElements = UnmodifiableListView(_calculateLeafElements()); - //TODO: cache this + /// Compute the list of all leaf elements, to be cached in [leafElements]. List _calculateLeafElements() { final leaves = []; for (final element in elements) { @@ -386,9 +391,11 @@ class LogicStructure implements Logic { @override Logic lte(dynamic other) => packed.lte(other); + @Deprecated('Use value.isValid instead.') @override bool hasValidValue() => packed.hasValidValue(); + @Deprecated('Use value.isFloating instead.') @override bool isFloating() => packed.isFloating(); diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart index 3ba2ec169..228371937 100644 --- a/lib/src/synthesizers/systemverilog.dart +++ b/lib/src/synthesizers/systemverilog.dart @@ -315,7 +315,6 @@ class _SynthModuleDefinition { _synthInstantiationNameUniquifier.getUniqueName( initialName: initialName, nullStarter: 'm', reserved: reserved); - //TODO: why does this ever need to return null? _SynthLogic? _getSynthLogic(Logic? logic, bool allowPortName) { if (logic == null) { return null; @@ -326,7 +325,8 @@ class _SynthModuleDefinition { if (logic.isArrayMember) { // grab the parent array (potentially recursively) final parentArraySynthLogic = - _getSynthLogic(logic.parentStructure, allowPortName); + // ignore: unnecessary_null_checks + _getSynthLogic(logic.parentStructure!, allowPortName); newSynth = _SynthLogicArrayElement(logic, parentArraySynthLogic!); } else { diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index dc531ca72..1910e2216 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -539,7 +539,6 @@ void main() { final mod = SimpleLAPassthrough(LogicArray([1], 8, numDimensionsUnpacked: 1)); await testArrayPassthrough(mod, noSvSim: true, noIverilog: true); - //TODO: bug in iverilog? https://github.com/steveicarus/iverilog/issues/915 }); test('4d, half packed', () async { @@ -691,9 +690,9 @@ void main() { test('3d', () async { final mod = SimpleArraysAndHierarchy(LogicArray([2], 8)); await testArrayPassthrough(mod); - //TODO: BAD! where's the sub-module instatiation!! - expect(mod.generateSynth(), contains('SimpleLAPassthrough')); + expect(mod.generateSynth(), + contains('SimpleLAPassthrough unnamed_module')); }); test('3d unpacked', () async { @@ -711,7 +710,10 @@ void main() { test('3d', () async { final mod = FancyArraysAndHierarchy(LogicArray([4, 3, 2], 8)); await testArrayPassthrough(mod, checkNoSwizzle: false); - //TODO: BAD! where's the sub-module instatiation!! + + // make sure the 4th one is there (since we expect 4) + expect(mod.generateSynth(), + contains('SimpleLAPassthrough unnamed_module_2')); }); test('3d unpacked', () async { diff --git a/test/logic_structure_test.dart b/test/logic_structure_test.dart index 043650bce..701fe36ff 100644 --- a/test/logic_structure_test.dart +++ b/test/logic_structure_test.dart @@ -8,10 +8,10 @@ // Author: Max Korbel import 'package:rohd/rohd.dart'; +import 'package:rohd/src/exceptions/exceptions.dart'; import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; -//TODO: check coverage //TODO: test structures in conditional assignments //TODO: test structures in If/Case expressions @@ -85,6 +85,26 @@ void main() { expect(s.name, 'structure'); }); + + test('sub logic in two structures throws exception', () { + final s = LogicStructure([ + Logic(), + ], name: 'structure'); + + expect(() => LogicStructure([s.elements.first]), + throwsA(isA())); + }); + + test('sub structure in two structures throws exception', () { + final subS = LogicStructure([Logic()]); + + LogicStructure([ + subS, + ], name: 'structure'); + + expect(() => LogicStructure([subS]), + throwsA(isA())); + }); }); group('LogicStructures with modules', () { From 47290d6b5967fdce27dfb5380544afc0953da64e Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Thu, 1 Jun 2023 13:47:42 -0700 Subject: [PATCH 63/75] add test for fix on #254 with put vs const in sv gen --- test/logic_name_test.dart | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/logic_name_test.dart b/test/logic_name_test.dart index 670b7fc35..de9b17169 100644 --- a/test/logic_name_test.dart +++ b/test/logic_name_test.dart @@ -19,6 +19,19 @@ class LogicTestModule extends Module { } } +class LogicWithInternalSignalModule extends Module { + LogicWithInternalSignalModule(Logic i) { + i = addInput('i', i); + + final o = addOutput('o'); + + // this `put` should *not* impact the name of x + final x = Logic(name: 'shouldExist')..put(1); + + o <= i & x; + } +} + void main() { test( 'GIVEN logic name is valid ' @@ -59,4 +72,13 @@ void main() { }, throwsA((dynamic e) => e is InvalidPortNameException)); }); }); + + test( + 'non-synthesizable signal deposition should not impact generated verilog', + () async { + final mod = LogicWithInternalSignalModule(Logic()); + await mod.build(); + + expect(mod.generateSynth(), contains('shouldExist')); + }); } From 5990ea57d8cdfc14700e51a0a0cf1932516829a7 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Thu, 1 Jun 2023 14:00:09 -0700 Subject: [PATCH 64/75] more cleanup --- lib/src/signals/logic.dart | 16 +++++----------- lib/src/signals/logic_structure.dart | 9 ++++----- lib/src/signals/wire.dart | 2 -- lib/src/synthesizers/systemverilog.dart | 9 +++------ lib/src/utilities/simcompare.dart | 1 - 5 files changed, 12 insertions(+), 25 deletions(-) diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index 2497e11ea..709060c7d 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -10,9 +10,6 @@ part of signals; -//TODO: move _Wire and all of the "signals" to their own files, use "part" -// so that we can keep things safer instead of using @protected - /// Represents a logical signal of any width which can change values. class Logic { /// An internal counter for encouraging unique naming of unnamed signals. @@ -132,8 +129,6 @@ class Logic { /// If this is a part of a [LogicStructure], the structure which this is /// a part of. Otherwise, `null`. - /// - /// This is usually either a [LogicStructure] or [LogicArray]. LogicStructure? get parentStructure => _parentStructure; LogicStructure? _parentStructure; @@ -372,8 +367,7 @@ class Logic { /// A function that returns whatever [Logic] is provided. /// /// Used as a default no-op for short-hands. - @protected - static Logic nopS(Logic x) => x; + static Logic _nopS(Logic x) => x; /// Shorthand for a [Conditional] which increments this by [val]. /// @@ -395,7 +389,7 @@ class Logic { /// ]); /// /// ``` - Conditional incr({Logic Function(Logic) s = nopS, dynamic val = 1}) => + Conditional incr({Logic Function(Logic) s = _nopS, dynamic val = 1}) => s(this) < s(this) + val; /// Shorthand for a [Conditional] which decrements this by [val]. @@ -418,7 +412,7 @@ class Logic { /// ]); /// /// ``` - Conditional decr({Logic Function(Logic) s = nopS, dynamic val = 1}) => + Conditional decr({Logic Function(Logic) s = _nopS, dynamic val = 1}) => s(this) < s(this) - val; /// Shorthand for a [Conditional] which increments this by [val]. @@ -440,7 +434,7 @@ class Logic { /// ]); /// /// ``` - Conditional mulAssign({Logic Function(Logic) s = nopS, dynamic val}) => + Conditional mulAssign({Logic Function(Logic) s = _nopS, dynamic val}) => s(this) < s(this) * val; /// Shorthand for a [Conditional] which increments this by [val]. @@ -462,7 +456,7 @@ class Logic { /// ]); /// /// ``` - Conditional divAssign({Logic Function(Logic) s = nopS, dynamic val}) => + Conditional divAssign({Logic Function(Logic) s = _nopS, dynamic val}) => s(this) < s(this) / val; /// Conditional assignment operator. diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 6afe7b825..861b357a4 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -214,7 +214,7 @@ class LogicStructure implements Logic { /// Increments each element of [elements] using [Logic.incr]. @override Conditional incr( - {Logic Function(Logic p1) s = Logic.nopS, dynamic val = 1}) => + {Logic Function(Logic p1) s = Logic._nopS, dynamic val = 1}) => ConditionalGroup([ for (final element in elements) element.incr(s: s, val: val), ]); @@ -222,7 +222,7 @@ class LogicStructure implements Logic { /// Decrements each element of [elements] using [Logic.decr]. @override Conditional decr( - {Logic Function(Logic p1) s = Logic.nopS, dynamic val = 1}) => + {Logic Function(Logic p1) s = Logic._nopS, dynamic val = 1}) => ConditionalGroup([ for (final element in elements) element.decr(s: s, val: val), ]); @@ -230,7 +230,7 @@ class LogicStructure implements Logic { /// Divide-assigns each element of [elements] using [Logic.divAssign]. @override Conditional divAssign( - {Logic Function(Logic p1) s = Logic.nopS, dynamic val}) => + {Logic Function(Logic p1) s = Logic._nopS, dynamic val}) => ConditionalGroup([ for (final element in elements) element.divAssign(s: s, val: val), ]); @@ -238,7 +238,7 @@ class LogicStructure implements Logic { /// Multiply-assigns each element of [elements] using [Logic.mulAssign]. @override Conditional mulAssign( - {Logic Function(Logic p1) s = Logic.nopS, dynamic val}) => + {Logic Function(Logic p1) s = Logic._nopS, dynamic val}) => ConditionalGroup([ for (final element in elements) element.mulAssign(s: s, val: val), ]); @@ -263,7 +263,6 @@ class LogicStructure implements Logic { } } - //TODO: to track naming @override LogicStructure? get parentStructure => _parentStructure; diff --git a/lib/src/signals/wire.dart b/lib/src/signals/wire.dart index 6c309c82d..dd4b4e31d 100644 --- a/lib/src/signals/wire.dart +++ b/lib/src/signals/wire.dart @@ -167,8 +167,6 @@ class _Wire { /// Keeps track of whether there is an active put, to detect reentrance. bool _isPutting = false; - //TODO: update to reference `LogicValue.of`. - /// Puts a value [val] onto this signal, which may or may not be picked up /// for [changed] in this [Simulator] tick. /// diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart index 228371937..d8913b358 100644 --- a/lib/src/synthesizers/systemverilog.dart +++ b/lib/src/synthesizers/systemverilog.dart @@ -383,7 +383,6 @@ class _SynthModuleDefinition { final driver = receiver.srcConnection; - // TODO: is this fixing https://github.com/intel/rohd/issues/254? test? final receiverIsConstant = driver == null && receiver is Const; final receiverIsModuleInput = @@ -417,8 +416,6 @@ class _SynthModuleDefinition { logicsToTraverse.addAll(subModule.inputs.values); } else if (driver != null) { if (!module.isInput(receiver)) { - //TODO: This is suspicious, doesn't seem right - // stop at the input to this module logicsToTraverse.add(driver); assignments.add(_SynthAssignment(synthDriver, synthReceiver)); @@ -597,7 +594,7 @@ class _SynthLogic { _SynthLogic(this.logic, this._name, {bool renameable = true}) : _renameable = renameable && - //TODO: should we never rename arrays? + // don't rename arrays since its elements would need updating too logic is! LogicArray; @override @@ -641,8 +638,8 @@ class _SynthLogic { } } - //TODO: how to uniquify names of `LogicArray`? - + /// Computes the name of the signal at declaration time with appropriate + /// dimensions included. String definitionName() { String packedDims; String unpackedDims; diff --git a/lib/src/utilities/simcompare.dart b/lib/src/utilities/simcompare.dart index 16c6636dd..6e10afc8c 100644 --- a/lib/src/utilities/simcompare.dart +++ b/lib/src/utilities/simcompare.dart @@ -234,7 +234,6 @@ abstract class SimCompare { final signal = module.signals.firstWhere((e) => e.name == signalName); if (signal is LogicArray) { - //TODO: handle packed vs unpacked!! final unpackedDims = signal.dimensions.getRange(0, signal.numDimensionsUnpacked); final packedDims = signal.dimensions From 6d36f146d266a4edfc27591ef62f0e908ba31768 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Fri, 2 Jun 2023 11:07:55 -0700 Subject: [PATCH 65/75] got slice and getrange working nicely with arrays --- .../collections/iterable_removable_queue.dart | 2 +- .../exceptions/logic/logic_exceptions.dart | 1 + .../signal_width_mismatch_exception.dart | 21 +++++++ lib/src/signals/logic.dart | 24 ++++---- lib/src/signals/logic_structure.dart | 41 +++++++++----- lib/src/signals/signals.dart | 1 + lib/src/utilities/index_utilities.dart | 50 +++++++++++++++++ lib/src/values/logic_value.dart | 41 ++++---------- lib/src/values/values.dart | 1 + test/gate_test.dart | 16 +++--- test/logic_array_test.dart | 55 +++++++++++++++++-- test/logic_value_test.dart | 6 +- 12 files changed, 191 insertions(+), 68 deletions(-) create mode 100644 lib/src/exceptions/logic/signal_width_mismatch_exception.dart create mode 100644 lib/src/utilities/index_utilities.dart diff --git a/lib/src/collections/iterable_removable_queue.dart b/lib/src/collections/iterable_removable_queue.dart index bd0a561bc..d91d1178e 100644 --- a/lib/src/collections/iterable_removable_queue.dart +++ b/lib/src/collections/iterable_removable_queue.dart @@ -24,7 +24,7 @@ class IterableRemovableQueue { /// Adds a new item to the end of the queue. void add(T item) { final newElement = _IterableRemovableElement(item); - if (_first == null) { + if (isEmpty) { _first = newElement; _last = _first; } else { diff --git a/lib/src/exceptions/logic/logic_exceptions.dart b/lib/src/exceptions/logic/logic_exceptions.dart index ab7c343b0..e08fb1ad9 100644 --- a/lib/src/exceptions/logic/logic_exceptions.dart +++ b/lib/src/exceptions/logic/logic_exceptions.dart @@ -5,3 +5,4 @@ export 'invalid_multiplier_exception.dart'; export 'logic_construction_exception.dart'; export 'put_exception.dart'; export 'self_connecting_logic_exception.dart'; +export 'signal_width_mismatch_exception.dart'; diff --git a/lib/src/exceptions/logic/signal_width_mismatch_exception.dart b/lib/src/exceptions/logic/signal_width_mismatch_exception.dart new file mode 100644 index 000000000..806ec7f5c --- /dev/null +++ b/lib/src/exceptions/logic/signal_width_mismatch_exception.dart @@ -0,0 +1,21 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// port_width_mismatch_exception.dart +// Definition for exception when a signal has the wrong width. +// +// 2023 June 2 +// Author: Max Korbel + +import 'package:rohd/rohd.dart'; +import 'package:rohd/src/exceptions/rohd_exception.dart'; + +/// An [Exception] thrown when a signal has the wrong width. +class SignalWidthMismatchException extends RohdException { + /// Constructs a new [Exception] for when a signal has the wrong width. + SignalWidthMismatchException(Logic signal, int expectedWidth, + {String additionalMessage = ''}) + : super('Signal ${signal.name} has the wrong width.' + ' Expected $expectedWidth but found ${signal.width}.' + ' $additionalMessage'); +} diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index 709060c7d..fc78893dd 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -208,11 +208,16 @@ class Logic { 'This signal "$this" is already connected to "$srcConnection",' ' so it cannot be connected to "$other".'); } + if (_unassignable) { throw Exception('This signal "$this" has been marked as unassignable. ' 'It may be a constant expression or otherwise should' ' not be assigned.'); } + + if (other.width != width) { + throw SignalWidthMismatchException(other, width); + } } /// Injects a value onto this signal in the current [Simulator] tick. @@ -259,6 +264,7 @@ class Logic { if (_wire == other._wire) { throw SelfConnectingLogicException(this, other); } + _unassignable = true; _updateWire(other._wire); } @@ -541,9 +547,8 @@ class Logic { Logic slice(int endIndex, int startIndex) { // Given start and end index, if either of them are seen to be -ve index // value(s) then convert them to a +ve index value(s) - final modifiedStartIndex = - (startIndex < 0) ? width + startIndex : startIndex; - final modifiedEndIndex = (endIndex < 0) ? width + endIndex : endIndex; + final modifiedStartIndex = IndexUtilities.wrapIndex(startIndex, width); + final modifiedEndIndex = IndexUtilities.wrapIndex(endIndex, width); if (modifiedStartIndex == 0 && modifiedEndIndex == width - 1) { // ignore: avoid_returning_this @@ -589,13 +594,12 @@ class Logic { // Given start and end index, if either of them are seen to be -ve index // value(s) then conver them to a +ve index value(s) final modifiedStartIndex = - (startIndex < 0) ? width + startIndex : startIndex; - final modifiedEndIndex = (endIndex < 0) ? width + endIndex : endIndex; - if (modifiedEndIndex < modifiedStartIndex) { - throw Exception( - 'End $modifiedEndIndex(=$endIndex) cannot be less than start' - ' $modifiedStartIndex(=$startIndex).'); - } + IndexUtilities.wrapIndex(startIndex, width, allowWidth: true); + final modifiedEndIndex = + IndexUtilities.wrapIndex(endIndex, width, allowWidth: true); + + IndexUtilities.validateRange(modifiedStartIndex, modifiedEndIndex); + return slice(modifiedEndIndex - 1, modifiedStartIndex); } diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 861b357a4..56bb5c605 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -106,7 +106,7 @@ class LogicStructure implements Logic { @override void gets(Logic other) { if (other.width != width) { - throw PortWidthMismatchException(other, width); + throw SignalWidthMismatchException(other, width); } var index = 0; @@ -122,7 +122,7 @@ class LogicStructure implements Logic { final otherLogic = other is Logic ? other : Const(other, width: width); if (otherLogic.width != width) { - throw PortWidthMismatchException(otherLogic, width); + throw SignalWidthMismatchException(otherLogic, width); } final conditionalAssigns = []; @@ -165,16 +165,19 @@ class LogicStructure implements Logic { Logic getRange(int startIndex, [int? endIndex]) { endIndex ??= width; - //TODO: do math for modified indices! + final modifiedStartIndex = + IndexUtilities.wrapIndex(startIndex, width, allowWidth: true); + final modifiedEndIndex = + IndexUtilities.wrapIndex(endIndex, width, allowWidth: true); - //TODO: do range checks + IndexUtilities.validateRange(modifiedStartIndex, modifiedEndIndex); //TODO: test edge cases here // grab all elements that fall in this range, keeping track of the offset final matchingElements = []; - final requestedWidth = endIndex - startIndex; + final requestedWidth = modifiedEndIndex - modifiedStartIndex; var index = 0; for (final element in leafElements) { @@ -183,14 +186,15 @@ class LogicStructure implements Logic { final elementStart = index; final elementEnd = index + element.width; - final elementInRange = - ((elementStart >= startIndex) && (elementStart < endIndex)) || - ((elementEnd > startIndex) && (elementEnd <= endIndex)); + final elementInRange = ((elementStart >= modifiedStartIndex) && + (elementStart < modifiedEndIndex)) || + ((elementEnd > modifiedStartIndex) && + (elementEnd <= modifiedEndIndex)); if (elementInRange) { // figure out the subset of `element` that needs to be included - final elementStartGrab = max(elementStart, startIndex) - index; - final elementEndGrab = min(elementEnd, endIndex) - index; + final elementStartGrab = max(elementStart, modifiedStartIndex) - index; + final elementEndGrab = min(elementEnd, modifiedEndIndex) - index; matchingElements .add(element.getRange(elementStartGrab, elementEndGrab)); @@ -202,12 +206,21 @@ class LogicStructure implements Logic { assert(!(matchingElements.isEmpty && requestedWidth != 0), 'If the requested width is not 0, expect to get some matches.'); - return matchingElements.swizzle(); + return matchingElements.rswizzle(); } @override - Logic slice(int endIndex, int startIndex) => - packed.slice(endIndex, startIndex); //TODO: this should use getRange + Logic slice(int endIndex, int startIndex) { + final modifiedStartIndex = IndexUtilities.wrapIndex(startIndex, width); + final modifiedEndIndex = IndexUtilities.wrapIndex(endIndex, width); + + if (modifiedStartIndex <= modifiedEndIndex) { + return getRange(modifiedStartIndex, modifiedEndIndex + 1); + } else { + //TODO: special test for this reversed case, make sure SV looks right + return getRange(modifiedEndIndex, modifiedStartIndex + 1).reversed; + } + } //TODO: don't make these operate on per-element, just pack the whole thing and do it? @@ -298,6 +311,7 @@ class LogicStructure implements Logic { ? 0 : elements.map((e) => e.width).reduce((w1, w2) => w1 + w2); + // TODO: withset should return a similar structure?? special case for array also? @override Logic withSet(int startIndex, Logic update) => packed.withSet(startIndex, update); @@ -419,6 +433,7 @@ class LogicStructure implements Logic { @override Logic replicate(int multiplier) => packed.replicate(multiplier); + // TODO: reversed should do something more clever? reverse each individual one and swizzle? @override Logic get reversed => packed.reversed; diff --git a/lib/src/signals/signals.dart b/lib/src/signals/signals.dart index 9cf087d69..ac60e8da6 100644 --- a/lib/src/signals/signals.dart +++ b/lib/src/signals/signals.dart @@ -11,6 +11,7 @@ import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd/src/exceptions/exceptions.dart'; +import 'package:rohd/src/utilities/index_utilities.dart'; import 'package:rohd/src/utilities/sanitizer.dart'; import 'package:rohd/src/utilities/synchronous_propagator.dart'; diff --git a/lib/src/utilities/index_utilities.dart b/lib/src/utilities/index_utilities.dart new file mode 100644 index 000000000..ef25c45f1 --- /dev/null +++ b/lib/src/utilities/index_utilities.dart @@ -0,0 +1,50 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// index_utilities.dart +// Code for modifying and validating indices. +// +// 2023 June 1 +// Author: Max Korbel + +/// A utility class for modifying and validating indices. +abstract class IndexUtilities { + /// Computes a modified version of an index into an array that allows for + /// negative values to wrap around from the end. + /// + /// Guaranteed to either return an index in `[0, width)` or else throw + /// an exception. + /// + /// If [allowWidth], then the range is `[0, width]` instead. + static int wrapIndex(int originalIndex, int width, + {bool allowWidth = false}) { + final modifiedIndex = + (originalIndex < 0) ? width + originalIndex : originalIndex; + + // check that it meets indexing requirements + if (modifiedIndex < 0 || + modifiedIndex > width || + (!allowWidth && modifiedIndex == width)) { + // The suggestion in the deprecation for this constructor is not available + // before 2.19, so keep it in here for now. Eventually, switch to the + // new one. + // ignore: deprecated_member_use + throw IndexError( + originalIndex, + width, + 'IndexOutOfRange', + 'Index out of range: $modifiedIndex(=$originalIndex) for width $width.', + width); + } + + return modifiedIndex; + } + + /// Validates that the range is legal. + static void validateRange(int startIndex, int endIndex, + {bool allowEqual = true}) { + if (endIndex < startIndex || (!allowEqual && endIndex == startIndex)) { + throw RangeError('End $endIndex cannot be less than start $startIndex.'); + } + } +} diff --git a/lib/src/values/logic_value.dart b/lib/src/values/logic_value.dart index f53a61faf..c4188145a 100644 --- a/lib/src/values/logic_value.dart +++ b/lib/src/values/logic_value.dart @@ -595,15 +595,8 @@ abstract class LogicValue { /// ``` /// LogicValue operator [](int index) { - final modifiedIndex = (index < 0) ? width + index : index; - if (modifiedIndex >= width || modifiedIndex < 0) { - // The suggestion in the deprecation for this constructor is not available - // before 2.19, so keep it in here for now. Eventually, switch to the - // new one. - // ignore: deprecated_member_use - throw IndexError(index, this, 'LogicValueIndexOutOfRange', - 'Index out of range: $modifiedIndex(=$index).', width); - } + final modifiedIndex = IndexUtilities.wrapIndex(index, width); + return _getIndex(modifiedIndex); } @@ -643,24 +636,14 @@ abstract class LogicValue { /// LogicValue getRange(int startIndex, [int? endIndex]) { endIndex ??= width; + final modifiedStartIndex = - (startIndex < 0) ? width + startIndex : startIndex; - final modifiedEndIndex = (endIndex < 0) ? width + endIndex : endIndex; - if (modifiedEndIndex < modifiedStartIndex) { - throw Exception( - 'End $modifiedEndIndex(=$endIndex) cannot be less than start ' - '$modifiedStartIndex(=$startIndex).'); - } - if (modifiedEndIndex > width) { - throw Exception( - 'End $modifiedEndIndex(=$endIndex) must be less than width' - ' ($width).'); - } - if (modifiedStartIndex < 0) { - throw Exception( - 'Start $modifiedStartIndex(=$startIndex) must be greater than or ' - 'equal to 0.'); - } + IndexUtilities.wrapIndex(startIndex, width, allowWidth: true); + final modifiedEndIndex = + IndexUtilities.wrapIndex(endIndex, width, allowWidth: true); + + IndexUtilities.validateRange(modifiedStartIndex, modifiedEndIndex); + return _getRange(modifiedStartIndex, modifiedEndIndex); } @@ -684,9 +667,9 @@ abstract class LogicValue { /// LogicValue.ofString('xz01').slice(-2, -2); // LogicValue.ofString('z') /// ``` LogicValue slice(int endIndex, int startIndex) { - final modifiedStartIndex = - (startIndex < 0) ? width + startIndex : startIndex; - final modifiedEndIndex = (endIndex < 0) ? width + endIndex : endIndex; + final modifiedStartIndex = IndexUtilities.wrapIndex(startIndex, width); + final modifiedEndIndex = IndexUtilities.wrapIndex(endIndex, width); + if (modifiedStartIndex <= modifiedEndIndex) { return getRange(modifiedStartIndex, modifiedEndIndex + 1); } else { diff --git a/lib/src/values/values.dart b/lib/src/values/values.dart index 2a876b147..e9494af87 100644 --- a/lib/src/values/values.dart +++ b/lib/src/values/values.dart @@ -8,6 +8,7 @@ import 'dart:math' as math; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd/src/exceptions/exceptions.dart'; +import 'package:rohd/src/utilities/index_utilities.dart'; part 'logic_value.dart'; part 'small_logic_value.dart'; diff --git a/test/gate_test.dart b/test/gate_test.dart index 10eedc45d..3ea6163e1 100644 --- a/test/gate_test.dart +++ b/test/gate_test.dart @@ -440,10 +440,10 @@ void main() { expect(testLogicZero[-1].value.toInt(), 0); expect(testLogicInvalid[-1].value, LogicValue.x); - expect(() => testLogic[10], throwsException); - expect(() => testLogicOne[1], throwsException); - expect(() => testLogicZero[1], throwsException); - expect(() => testLogicInvalid[1], throwsException); + expect(() => testLogic[10], throwsA(isA())); + expect(() => testLogicOne[1], throwsA(isA())); + expect(() => testLogicZero[1], throwsA(isA())); + expect(() => testLogicInvalid[1], throwsA(isA())); }); test('index Logic(1bit) by Logic index out of bounds test', () { @@ -465,10 +465,10 @@ void main() { expect(testLogicZero.slice(0, 0), equals(testLogicZero)); expect(testLogicInvalid.slice(0, 0), equals(testLogicInvalid)); - expect(() => testLogic.slice(0, 10), throwsException); - expect(() => testLogicOne.slice(0, 1), throwsException); - expect(() => testLogicZero.slice(0, 1), throwsException); - expect(() => testLogicInvalid.slice(0, 1), throwsException); + expect(() => testLogic.slice(0, 10), throwsA(isA())); + expect(() => testLogicOne.slice(0, 1), throwsA(isA())); + expect(() => testLogicZero.slice(0, 1), throwsA(isA())); + expect(() => testLogicInvalid.slice(0, 1), throwsA(isA())); }); test('Index Logic by does not accept input other than int or Logic', () { diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 1910e2216..69f6d0135 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -42,6 +42,45 @@ class SimpleLAPassthrough extends Module { } } +class RangeAndSliceArrModule extends Module implements SimpleLAPassthrough { + @override + Logic get laOut => output('laOut'); + + RangeAndSliceArrModule(LogicArray laIn) { + laIn = addInputArray( + 'laIn', + laIn, + dimensions: [3, 3, 3], + elementWidth: 8, + ); + + addOutputArray( + 'laOut', + dimensions: laIn.dimensions, + elementWidth: laIn.elementWidth, + numDimensionsUnpacked: laIn.numDimensionsUnpacked, + ); + + laOut.elements[0] <= + [ + laIn.elements[0].getRange(16), + laIn.elements[0].getRange(0, 16), + ].swizzle(); + + laOut.elements[1] <= + [ + laIn.elements[1].slice(16, 3 * 3 * 8 - 1).reversed, + laIn.elements[1].slice(15, 0), + ].swizzle(); + + laOut.elements[2] <= + [ + laIn.elements[2].slice(-1, 0).getRange(3 * 3 * 8 ~/ 2), + laIn.elements[2].getRange(-3 * 3 * 8).getRange(0, 3 * 3 * 8 ~/ 2), + ].swizzle(); + } +} + enum LADir { laIn, laOut } class LAPassthroughIntf extends Interface { @@ -467,7 +506,8 @@ void main() { Future testArrayPassthrough(SimpleLAPassthrough mod, {bool checkNoSwizzle = true, bool noSvSim = false, - bool noIverilog = false}) async { + bool noIverilog = false, + bool dontDeleteTmpFiles = false}) async { await mod.build(); const randWidth = 23; @@ -483,12 +523,14 @@ void main() { ]; if (checkNoSwizzle) { - expect(mod.generateSynth().contains('swizzle'), false); + expect(mod.generateSynth().contains('swizzle'), false, + reason: 'Expected no swizzles but found one.'); } - await SimCompare.checkFunctionalVector(mod, vectors); + // await SimCompare.checkFunctionalVector(mod, vectors); if (!noIverilog) { - SimCompare.checkIverilogVector(mod, vectors, buildOnly: noSvSim); + SimCompare.checkIverilogVector(mod, vectors, + buildOnly: noSvSim, dontDeleteTmpFiles: dontDeleteTmpFiles); } } @@ -737,6 +779,11 @@ void main() { await testArrayPassthrough(mod); }); }); + + test('slice and dice', () async { + final mod = RangeAndSliceArrModule(LogicArray([3, 3, 3], 8)); + await testArrayPassthrough(mod, checkNoSwizzle: false); + }); }); group('array constant assignments', () { diff --git a/test/logic_value_test.dart b/test/logic_value_test.dart index 46502ae46..57c4eb897 100644 --- a/test/logic_value_test.dart +++ b/test/logic_value_test.dart @@ -368,7 +368,7 @@ void main() { // getRange - negative end index and start > end - error! start must // be less than end () => LogicValue.ofString('0101').getRange(-1, -2), - throwsA(isA())); + throwsA(isA())); expect( // getRange - same index results zero width value LogicValue.ofString('0101').getRange(-1, -1), @@ -376,11 +376,11 @@ void main() { expect( // getRange - bad inputs start > end () => LogicValue.ofString('0101').getRange(2, 1), - throwsA(isA())); + throwsA(isA())); expect( // getRange - bad inputs end > length-1 () => LogicValue.ofString('0101').getRange(0, 7), - throwsA(isA())); + throwsA(isA())); expect(LogicValue.ofString('xz01').slice(2, 1), equals(LogicValue.ofString('z0'))); expect(LogicValue.ofString('xz01').slice(-2, -3), From cc0cb44bd0ef52f3eb4b209af8872e47245d3698 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Fri, 2 Jun 2023 14:22:22 -0700 Subject: [PATCH 66/75] get withset working nicely --- lib/src/signals/logic.dart | 8 +++- lib/src/signals/logic_array.dart | 5 ++ lib/src/signals/logic_structure.dart | 72 ++++++++++++++++++++++++---- test/extend_test.dart | 4 +- test/logic_array_test.dart | 28 +++++++++++ 5 files changed, 104 insertions(+), 13 deletions(-) diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index fc78893dd..7ba5ed1ca 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -651,11 +651,15 @@ class Logic { /// if the position of the [update] would cause an overrun past the [width]. Logic withSet(int startIndex, Logic update) { if (startIndex + update.width > width) { - throw Exception( - 'Width of updatedValue $update at startIndex $startIndex would' + throw RangeError('Width of update $update at startIndex $startIndex would' ' overrun the width of the original ($width).'); } + if (startIndex < 0) { + throw RangeError( + 'Start index must be greater than zero but was $startIndex'); + } + return [ getRange(startIndex + update.width, width), update, diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index 1990c50da..4173aab44 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -75,6 +75,11 @@ class LogicArray extends LogicStructure { ); } + //TODO + @override + LogicArray clone({String? name}) => LogicArray(dimensions, elementWidth, + numDimensionsUnpacked: numDimensionsUnpacked, name: name ?? this.name); + ///TODO LogicArray._( super.elements, { diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 56bb5c605..f3e0fdf97 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -53,6 +53,13 @@ class LogicStructure implements Logic { }); } + //TODO: Test separately from array + LogicStructure clone({String? name}) => LogicStructure( + elements.map((e) => e is LogicStructure + ? e.clone() + : Logic(name: e.name, width: e.width)), + name: name ?? this.name); + @override String get structureName { if (parentStructure != null) { @@ -172,8 +179,6 @@ class LogicStructure implements Logic { IndexUtilities.validateRange(modifiedStartIndex, modifiedEndIndex); - //TODO: test edge cases here - // grab all elements that fall in this range, keeping track of the offset final matchingElements = []; @@ -217,7 +222,6 @@ class LogicStructure implements Logic { if (modifiedStartIndex <= modifiedEndIndex) { return getRange(modifiedStartIndex, modifiedEndIndex + 1); } else { - //TODO: special test for this reversed case, make sure SV looks right return getRange(modifiedEndIndex, modifiedStartIndex + 1).reversed; } } @@ -261,11 +265,10 @@ class LogicStructure implements Logic { for (final element in elements) ...element.dstConnections ]; - //TODO: is this safe to have a separate tracking here? - Module? _parentModule; - @override Module? get parentModule => _parentModule; + @override + Module? _parentModule; @protected @override @@ -313,8 +316,60 @@ class LogicStructure implements Logic { // TODO: withset should return a similar structure?? special case for array also? @override - Logic withSet(int startIndex, Logic update) => - packed.withSet(startIndex, update); + Logic withSet(int startIndex, Logic update) { + final endIndex = startIndex + update.width; + + if (endIndex > width) { + throw RangeError('Width of update $update at startIndex $startIndex would' + ' overrun the width of the original ($width).'); + } + + if (startIndex < 0) { + throw RangeError( + 'Start index must be greater than zero but was $startIndex'); + } + + //TODO: + // loop through elements, assign portions where needed? + + final newWithSet = clone(); + + var index = 0; + for (var i = 0; i < leafElements.length; i++) { + final newElement = newWithSet.leafElements[i]; + final element = leafElements[i]; + + final elementWidth = element.width; + + // if the *start* or *end* of `element` is within [startIndex, endIndex], + // then we have to include it in `matchingElements` + final elementStart = index; + final elementEnd = index + elementWidth; + + final elementInRange = + ((elementStart >= startIndex) && (elementStart < endIndex)) || + ((elementEnd > startIndex) && (elementEnd <= endIndex)); + + if (elementInRange) { + newElement <= + element.withSet( + startIndex - index, + update.getRange( + index - startIndex, index - startIndex + elementWidth)); + } else { + newElement <= element; + } + + index += width; + } + + return newWithSet; + // return [ + // getRange(startIndex + update.width, width), + // update, + // getRange(0, startIndex), + // ].swizzle(); + } ///////////////////////////////////////////////// ///////////////////////////////////////////////// @@ -433,7 +488,6 @@ class LogicStructure implements Logic { @override Logic replicate(int multiplier) => packed.replicate(multiplier); - // TODO: reversed should do something more clever? reverse each individual one and swizzle? @override Logic get reversed => packed.reversed; diff --git a/test/extend_test.dart b/test/extend_test.dart index 35431a173..b5b95e266 100644 --- a/test/extend_test.dart +++ b/test/extend_test.dart @@ -121,10 +121,10 @@ void main() { } test('setting with bigger number throws exception', () async { - expect(() => withSetVectors([], 0, 9), throwsException); + expect(() => withSetVectors([], 0, 9), throwsRangeError); }); test('setting with number in middle overrun throws exception', () async { - expect(() => withSetVectors([], 4, 5), throwsException); + expect(() => withSetVectors([], 4, 5), throwsRangeError); }); test('setting same width returns only new', () async { await withSetVectors([ diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 69f6d0135..d41bb080c 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -81,6 +81,29 @@ class RangeAndSliceArrModule extends Module implements SimpleLAPassthrough { } } +class WithSetArray extends Module implements SimpleLAPassthrough { + @override + Logic get laOut => output('laOut'); + + WithSetArray(LogicArray laIn) { + laIn = addInputArray( + 'laIn', + laIn, + dimensions: [2, 2], + elementWidth: 8, + ); + + addOutputArray( + 'laOut', + dimensions: laIn.dimensions, + elementWidth: laIn.elementWidth, + numDimensionsUnpacked: laIn.numDimensionsUnpacked, + ); + + laOut <= laIn.withSet(8, laIn.elements[0].elements[1]); + } +} + enum LADir { laIn, laOut } class LAPassthroughIntf extends Interface { @@ -784,6 +807,11 @@ void main() { final mod = RangeAndSliceArrModule(LogicArray([3, 3, 3], 8)); await testArrayPassthrough(mod, checkNoSwizzle: false); }); + + test('withset', () async { + final mod = WithSetArray(LogicArray([2, 2], 8)); + await testArrayPassthrough(mod); + }); }); group('array constant assignments', () { From 090fb65a635ab7d08495f3251e140cff0bc7bfe3 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Mon, 5 Jun 2023 09:58:50 -0700 Subject: [PATCH 67/75] fix up shorthands for structures --- .../signal_redriven_exception.dart | 4 +- lib/src/modules/conditional.dart | 73 +++++++-- lib/src/signals/logic.dart | 21 +-- lib/src/signals/logic_structure.dart | 32 ++-- test/conditionals_test.dart | 151 +++++++++++------- test/sequential_test.dart | 83 ++++++++++ 6 files changed, 261 insertions(+), 103 deletions(-) diff --git a/lib/src/exceptions/conditionals/signal_redriven_exception.dart b/lib/src/exceptions/conditionals/signal_redriven_exception.dart index 0d36d5f2c..8cd0b1b98 100644 --- a/lib/src/exceptions/conditionals/signal_redriven_exception.dart +++ b/lib/src/exceptions/conditionals/signal_redriven_exception.dart @@ -18,7 +18,7 @@ class SignalRedrivenException extends RohdException { /// with default error [message]. /// /// Creates a [SignalRedrivenException] with an optional error [message]. - SignalRedrivenException(String signals, + SignalRedrivenException(Iterable signals, [String message = 'Sequential drove the same signal(s) multiple times: ']) - : super(message + signals); + : super(message + signals.toString()); } diff --git a/lib/src/modules/conditional.dart b/lib/src/modules/conditional.dart index be396026d..2990ec954 100644 --- a/lib/src/modules/conditional.dart +++ b/lib/src/modules/conditional.dart @@ -42,9 +42,10 @@ abstract class _Always extends Module with CustomSystemVerilog { /// If [reset] is provided, then all signals driven by this block will be /// conditionally reset when the signal is high. /// The default reset value is to `0`, but if [resetValues] is provided then - /// the corresponding value - /// associated with the driven signal will be set to that value instead upon - /// reset. + /// the corresponding value associated with the driven signal will be set to + /// that value instead upon reset. If a signal is in [resetValues] but not + /// driven by any other [Conditional] in this block, it will be driven to the + /// specified reset value. _Always(this._conditionals, {Logic? reset, Map? resetValues, super.name = 'always'}) { // create a registration of all inputs and outputs of this module @@ -52,21 +53,55 @@ abstract class _Always extends Module with CustomSystemVerilog { // Get all Receivers final allReceivers = - conditionals.map((e) => e.receivers).expand((e) => e).toList(); + conditionals.map((e) => e.receivers).expand((e) => e).toSet(); // This will reset the conditionals on setting the `reset` flag if (reset != null) { + final allResetCondAssigns = []; + final signalsBeingReset = {}; + + if (resetValues != null) { + final toConsiderForElementsReset = [ + ...resetValues.keys, + ]; + + for (var i = 0; i < toConsiderForElementsReset.length; i++) { + final toConsider = toConsiderForElementsReset[i]; + + // if it's a structure, we need to consider its elements + if (toConsider is LogicStructure) { + toConsiderForElementsReset.addAll(toConsider.elements); + } + + // if we're already resetting this signal, flag an issue + if (signalsBeingReset.contains(toConsider)) { + throw SignalRedrivenException([toConsider], + 'Signal is already being reset by another reset value: '); + } + + if (resetValues.containsKey(toConsider)) { + // should only be true for top-level structures referenced + allResetCondAssigns.add(toConsider < resetValues[toConsider]); + } + + // always add the signal, even if this is a sub-element + signalsBeingReset.add(toConsider); + } + } + + // now add the reset to 0 for all the remaining ones + for (final receiver in allReceivers.toList()) { + if (!signalsBeingReset.contains(receiver)) { + allResetCondAssigns.add(receiver < 0); + } + } + _conditionals = [ // If resetValue for a receiver is defined, If( reset, // then use it for assigning receiver - then: [ - ...allReceivers.map((rec) { - final driver = resetValues?[rec] ?? 0; - return rec < driver; - }) - ], + then: allResetCondAssigns, // else assign zero as resetValue orElse: conditionals, ), @@ -344,6 +379,14 @@ class Sequential extends _Always { final List _clks = []; /// Constructs a [Sequential] single-triggered by [clk]. + /// + /// If `reset` is provided, then all signals driven by this block will be + /// conditionally reset when the signal is high. + /// The default reset value is to `0`, but if `resetValues` is provided then + /// the corresponding value associated with the driven signal will be set to + /// that value instead upon reset. If a signal is in `resetValues` but not + /// driven by any other [Conditional] in this block, it will be driven to the + /// specified reset value. Sequential(Logic clk, List conditionals, {Logic? reset, Map? resetValues, @@ -352,6 +395,14 @@ class Sequential extends _Always { name: name, reset: reset, resetValues: resetValues); /// Constructs a [Sequential] multi-triggered by any of [clks]. + /// + /// If `reset` is provided, then all signals driven by this block will be + /// conditionally reset when the signal is high. + /// The default reset value is to `0`, but if `resetValues` is provided then + /// the corresponding value associated with the driven signal will be set to + /// that value instead upon reset. If a signal is in `resetValues` but not + /// driven by any other [Conditional] in this block, it will be driven to the + /// specified reset value. Sequential.multi(List clks, super.conditionals, {super.reset, super.resetValues, super.name = 'sequential'}) { for (var i = 0; i < clks.length; i++) { @@ -497,7 +548,7 @@ class Sequential extends _Always { element.execute(allDrivenSignals, null); } if (allDrivenSignals.hasDuplicates) { - throw SignalRedrivenException(allDrivenSignals.duplicates.toString()); + throw SignalRedrivenException(allDrivenSignals.duplicates); } } diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index 7ba5ed1ca..f5e44f9a8 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -370,11 +370,6 @@ class Logic { /// Greater-than-or-equal-to. Logic operator >=(dynamic other) => GreaterThanOrEqual(this, other).out; - /// A function that returns whatever [Logic] is provided. - /// - /// Used as a default no-op for short-hands. - static Logic _nopS(Logic x) => x; - /// Shorthand for a [Conditional] which increments this by [val]. /// /// By default for a [Logic] variable, if no [val] is provided then the @@ -395,8 +390,8 @@ class Logic { /// ]); /// /// ``` - Conditional incr({Logic Function(Logic) s = _nopS, dynamic val = 1}) => - s(this) < s(this) + val; + Conditional incr({Logic Function(Logic)? s, dynamic val = 1}) => + s == null ? (this < this + val) : (s(this) < s(this) + val); /// Shorthand for a [Conditional] which decrements this by [val]. /// @@ -418,8 +413,8 @@ class Logic { /// ]); /// /// ``` - Conditional decr({Logic Function(Logic) s = _nopS, dynamic val = 1}) => - s(this) < s(this) - val; + Conditional decr({Logic Function(Logic)? s, dynamic val = 1}) => + s == null ? (this < this - val) : (s(this) < s(this) - val); /// Shorthand for a [Conditional] which increments this by [val]. /// @@ -440,8 +435,8 @@ class Logic { /// ]); /// /// ``` - Conditional mulAssign({Logic Function(Logic) s = _nopS, dynamic val}) => - s(this) < s(this) * val; + Conditional mulAssign(dynamic val, {Logic Function(Logic)? s}) => + s == null ? (this < this * val) : (s(this) < s(this) * val); /// Shorthand for a [Conditional] which increments this by [val]. /// @@ -462,8 +457,8 @@ class Logic { /// ]); /// /// ``` - Conditional divAssign({Logic Function(Logic) s = _nopS, dynamic val}) => - s(this) < s(this) / val; + Conditional divAssign(dynamic val, {Logic Function(Logic)? s}) => + s == null ? (this < this / val) : (s(this) < s(this) / val); /// Conditional assignment operator. /// diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index f3e0fdf97..326eb1d6d 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -154,7 +154,7 @@ class LogicStructure implements Logic { final leaves = []; for (final element in elements) { if (element is LogicStructure) { - leaves.addAll(element._calculateLeafElements()); + leaves.addAll(element.leafElements); } else { leaves.add(element); } @@ -226,39 +226,25 @@ class LogicStructure implements Logic { } } - //TODO: don't make these operate on per-element, just pack the whole thing and do it? - /// Increments each element of [elements] using [Logic.incr]. @override - Conditional incr( - {Logic Function(Logic p1) s = Logic._nopS, dynamic val = 1}) => - ConditionalGroup([ - for (final element in elements) element.incr(s: s, val: val), - ]); + Conditional incr({Logic Function(Logic p1)? s, dynamic val = 1}) => + s == null ? (this < this + val) : (s(this) < s(this) + val); /// Decrements each element of [elements] using [Logic.decr]. @override - Conditional decr( - {Logic Function(Logic p1) s = Logic._nopS, dynamic val = 1}) => - ConditionalGroup([ - for (final element in elements) element.decr(s: s, val: val), - ]); + Conditional decr({Logic Function(Logic p1)? s, dynamic val = 1}) => + s == null ? (this < this - val) : (s(this) < s(this) - val); /// Divide-assigns each element of [elements] using [Logic.divAssign]. @override - Conditional divAssign( - {Logic Function(Logic p1) s = Logic._nopS, dynamic val}) => - ConditionalGroup([ - for (final element in elements) element.divAssign(s: s, val: val), - ]); + Conditional divAssign(dynamic val, {Logic Function(Logic p1)? s}) => + s == null ? (this < this / val) : (s(this) < s(this) / val); /// Multiply-assigns each element of [elements] using [Logic.mulAssign]. @override - Conditional mulAssign( - {Logic Function(Logic p1) s = Logic._nopS, dynamic val}) => - ConditionalGroup([ - for (final element in elements) element.mulAssign(s: s, val: val), - ]); + Conditional mulAssign(dynamic val, {Logic Function(Logic p1)? s}) => + s == null ? (this < this * val) : (s(this) < s(this) * val); @override late final Iterable dstConnections = [ diff --git a/test/conditionals_test.dart b/test/conditionals_test.dart index 5b5a06fdb..352c1c2e8 100644 --- a/test/conditionals_test.dart +++ b/test/conditionals_test.dart @@ -16,8 +16,33 @@ import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; class ShorthandAssignModule extends Module { + final bool useArrays; + + @override + Logic addInput(String name, Logic x, {int width = 1}) { + assert(width.isEven, 'if arrays, split width in 2'); + if (useArrays) { + return super + .addInputArray(name, x, dimensions: [2], elementWidth: width ~/ 2); + } else { + return super.addInput(name, x, width: width); + } + } + + @override + Logic addOutput(String name, {int width = 1}) { + assert(width.isEven, 'if arrays, split width in 2'); + if (useArrays) { + return super + .addOutputArray(name, dimensions: [2], elementWidth: width ~/ 2); + } else { + return super.addOutput(name, width: width); + } + } + ShorthandAssignModule( - Logic preIncr, Logic preDecr, Logic mulAssign, Logic divAssign, Logic b) + Logic preIncr, Logic preDecr, Logic mulAssign, Logic divAssign, Logic b, + {this.useArrays = false}) : super(name: 'shorthandmodule') { preIncr = addInput('preIncr', preIncr, width: 8); preDecr = addInput('preDecr', preDecr, width: 8); @@ -44,8 +69,8 @@ class ShorthandAssignModule extends Module { pdOut.decr(s: s), piOutWithB.incr(s: s, val: b), pdOutWithB.decr(s: s, val: b), - maOut.mulAssign(s: s, val: b), - daOut.divAssign(s: s, val: b), + maOut.mulAssign(b, s: s), + daOut.divAssign(b, s: s), ]); } } @@ -634,56 +659,74 @@ void main() { } }); - test('shorthand operations', () async { - final mod = ShorthandAssignModule(Logic(width: 8), Logic(width: 8), - Logic(width: 8), Logic(width: 8), Logic(width: 8)); - await mod.build(); - final vectors = [ - Vector({ - 'preIncr': 5, - 'preDecr': 5, - 'mulAssign': 5, - 'divAssign': 5, - 'b': 5 - }, { - 'piOutWithB': 10, - 'pdOutWithB': 0, - 'piOut': 6, - 'pdOut': 4, - 'maOut': 25, - 'daOut': 1, - }), - Vector({ - 'preIncr': 5, - 'preDecr': 5, - 'mulAssign': 5, - 'divAssign': 5, - 'b': 0 - }, { - 'piOutWithB': 5, - 'pdOutWithB': 5, - 'piOut': 6, - 'pdOut': 4, - 'maOut': 0, - 'daOut': LogicValue.x, - }), - Vector({ - 'preIncr': 0, - 'preDecr': 0, - 'mulAssign': 0, - 'divAssign': 0, - 'b': 5 - }, { - 'piOutWithB': 5, - 'pdOutWithB': 0xfb, - 'piOut': 1, - 'pdOut': 0xff, - 'maOut': 0, - 'daOut': 0, - }) - ]; - await SimCompare.checkFunctionalVector(mod, vectors); - final simResult = SimCompare.iverilogVector(mod, vectors); - expect(simResult, equals(true)); + group('shorthand operations', () { + Future testShorthand( + {required bool useArrays, required bool useSequential}) async { + final mod = ShorthandAssignModule( + Logic(width: 8), + Logic(width: 8), + Logic(width: 8), + Logic(width: 8), + Logic(width: 8), + useArrays: useArrays, + ); + await mod.build(); + + var vectors = [ + Vector({ + 'preIncr': 5, + 'preDecr': 5, + 'mulAssign': 5, + 'divAssign': 5, + 'b': 5 + }, { + 'piOutWithB': 10, + 'pdOutWithB': 0, + 'piOut': 6, + 'pdOut': 4, + 'maOut': 25, + 'daOut': 1, + }), + Vector({ + 'preIncr': 5, + 'preDecr': 5, + 'mulAssign': 5, + 'divAssign': 5, + 'b': 0 + }, { + 'piOutWithB': 5, + 'pdOutWithB': 5, + 'piOut': 6, + 'pdOut': 4, + 'maOut': 0, + 'daOut': LogicValue.x, + }), + Vector({ + 'preIncr': 0, + 'preDecr': 0, + 'mulAssign': 0, + 'divAssign': 0, + 'b': 5 + }, { + 'piOutWithB': 5, + 'pdOutWithB': 0xfb, + 'piOut': 1, + 'pdOut': 0xff, + 'maOut': 0, + 'daOut': 0, + }) + ]; + + await SimCompare.checkFunctionalVector(mod, vectors); + SimCompare.checkIverilogVector(mod, vectors); + } + + test('normal logic', () async { + await testShorthand(useArrays: false, useSequential: false); + }); + + test('arrays', () async { + await testShorthand(useArrays: true, useSequential: false); + }); }); } diff --git a/test/sequential_test.dart b/test/sequential_test.dart index a0a0ae348..2e97920df 100644 --- a/test/sequential_test.dart +++ b/test/sequential_test.dart @@ -48,6 +48,54 @@ class DelaySignal extends Module { } } +class ShorthandSeqModule extends Module { + final bool useArrays; + + @override + Logic addOutput(String name, {int width = 1}) { + assert(width.isEven, 'if arrays, split width in 2'); + if (useArrays) { + return super + .addOutputArray(name, dimensions: [2], elementWidth: width ~/ 2); + } else { + return super.addOutput(name, width: width); + } + } + + ShorthandSeqModule(Logic reset, + {this.useArrays = false, + int initialVal = 16, + bool doubleResetError = false}) + : super(name: 'shorthandmodule') { + reset = addInput('reset', reset); + + final piOut = addOutput('piOut', width: 8); + final pdOut = addOutput('pdOut', width: 8); + final maOut = addOutput('maOut', width: 8); + final daOut = addOutput('daOut', width: 8); + + final clk = SimpleClockGenerator(10).clk; + + Sequential( + clk, + [ + piOut.incr(), + pdOut.decr(), + maOut.mulAssign(2), + daOut.divAssign(2), + ], + reset: reset, + resetValues: { + piOut: initialVal, + pdOut: initialVal, + maOut: initialVal, + daOut: initialVal, + if (useArrays && doubleResetError) daOut.elements[0]: initialVal, + }, + ); + } +} + void main() { tearDown(() async { await Simulator.reset(); @@ -78,4 +126,39 @@ void main() { final simResult = SimCompare.iverilogVector(dut, vectors); expect(simResult, equals(true)); }); + + group('shorthand with sequential', () { + Future testShorthand( + {required bool useArrays, bool doubleResetError = false}) async { + final mod = ShorthandSeqModule(Logic(), + useArrays: useArrays, doubleResetError: doubleResetError); + await mod.build(); + + final vectors = [ + Vector({'reset': 1}, {}), + Vector( + {'reset': 1}, {'piOut': 16, 'pdOut': 16, 'maOut': 16, 'daOut': 16}), + Vector( + {'reset': 0}, {'piOut': 16, 'pdOut': 16, 'maOut': 16, 'daOut': 16}), + Vector( + {'reset': 0}, {'piOut': 17, 'pdOut': 15, 'maOut': 32, 'daOut': 8}), + ]; + + // await SimCompare.checkFunctionalVector(mod, vectors); + SimCompare.checkIverilogVector(mod, vectors); + } + + test('normal logic', () async { + await testShorthand(useArrays: false); + }); + + test('arrays', () async { + await testShorthand(useArrays: true); + }); + + test('arrays with double reset error', () async { + expect(testShorthand(useArrays: true, doubleResetError: true), + throwsException); + }); + }); } From 84937bd7b4325066c76e5487bb5acf9faf87639e Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Mon, 5 Jun 2023 10:44:55 -0700 Subject: [PATCH 68/75] test cloning for structures --- lib/src/signals/logic_structure.dart | 10 ---------- test/logic_structure_test.dart | 19 ++++++++++++++++--- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 326eb1d6d..83a6ed910 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -53,7 +53,6 @@ class LogicStructure implements Logic { }); } - //TODO: Test separately from array LogicStructure clone({String? name}) => LogicStructure( elements.map((e) => e is LogicStructure ? e.clone() @@ -300,7 +299,6 @@ class LogicStructure implements Logic { ? 0 : elements.map((e) => e.width).reduce((w1, w2) => w1 + w2); - // TODO: withset should return a similar structure?? special case for array also? @override Logic withSet(int startIndex, Logic update) { final endIndex = startIndex + update.width; @@ -315,9 +313,6 @@ class LogicStructure implements Logic { 'Start index must be greater than zero but was $startIndex'); } - //TODO: - // loop through elements, assign portions where needed? - final newWithSet = clone(); var index = 0; @@ -350,11 +345,6 @@ class LogicStructure implements Logic { } return newWithSet; - // return [ - // getRange(startIndex + update.width, width), - // update, - // getRange(0, startIndex), - // ].swizzle(); } ///////////////////////////////////////////////// diff --git a/test/logic_structure_test.dart b/test/logic_structure_test.dart index 701fe36ff..72ed24b37 100644 --- a/test/logic_structure_test.dart +++ b/test/logic_structure_test.dart @@ -25,6 +25,9 @@ class MyStruct extends LogicStructure { ); MyStruct._(this.ready, this.valid) : super([ready, valid], name: 'myStruct'); + + @override + LogicStructure clone({String? name}) => MyStruct(); } class MyFancyStruct extends LogicStructure { @@ -42,8 +45,8 @@ class MyFancyStruct extends LogicStructure { : super([arr, bus, subStruct], name: 'myFancyStruct'); } -class ModStructPort extends Module { - ModStructPort(MyStruct struct) { +class StructPortModule extends Module { + StructPortModule(MyStruct struct) { final ready = addInput('ready', struct.ready); final valid = addOutput('valid'); struct.valid <= valid; @@ -105,12 +108,22 @@ void main() { expect(() => LogicStructure([subS]), throwsA(isA())); }); + + test('structure clone', () { + final orig = MyFancyStruct(); + final copy = orig.clone(); + + expect(copy.name, orig.name); + expect(copy.width, orig.width); + expect(copy.elements[0], isA()); + expect(copy.elements[2], isA()); + }); }); group('LogicStructures with modules', () { test('simple struct bi-directional', () async { final struct = MyStruct(); - final mod = ModStructPort(struct); + final mod = StructPortModule(struct); await mod.build(); final vectors = [ From 81afac56e705cf8d15d6ceae7bf88b5db515a800 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Mon, 5 Jun 2023 12:10:48 -0700 Subject: [PATCH 69/75] fix withset for structures --- lib/src/signals/logic.dart | 5 ++++ lib/src/signals/logic_structure.dart | 8 ++++-- test/logic_array_test.dart | 43 +++++++++++++++++++++++++--- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index f5e44f9a8..0fb00f64d 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -655,6 +655,11 @@ class Logic { 'Start index must be greater than zero but was $startIndex'); } + if (startIndex == 0 && update.width == width) { + // special case, setting whole thing, just return the same thing + return update; + } + return [ getRange(startIndex + update.width, width), update, diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 83a6ed910..18930d3d0 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -334,14 +334,16 @@ class LogicStructure implements Logic { if (elementInRange) { newElement <= element.withSet( - startIndex - index, + max(startIndex - index, 0), update.getRange( - index - startIndex, index - startIndex + elementWidth)); + max(index - startIndex, 0), + min(index - startIndex + elementWidth, elementWidth), + )); } else { newElement <= element; } - index += width; + index += element.width; } return newWithSet; diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index d41bb080c..79b74222d 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -81,11 +81,11 @@ class RangeAndSliceArrModule extends Module implements SimpleLAPassthrough { } } -class WithSetArray extends Module implements SimpleLAPassthrough { +class WithSetArrayModule extends Module implements SimpleLAPassthrough { @override Logic get laOut => output('laOut'); - WithSetArray(LogicArray laIn) { + WithSetArrayModule(LogicArray laIn) { laIn = addInputArray( 'laIn', laIn, @@ -104,6 +104,29 @@ class WithSetArray extends Module implements SimpleLAPassthrough { } } +class WithSetArrayOffsetModule extends Module implements SimpleLAPassthrough { + @override + Logic get laOut => output('laOut'); + + WithSetArrayOffsetModule(LogicArray laIn) { + laIn = addInputArray( + 'laIn', + laIn, + dimensions: [2, 2], + elementWidth: 8, + ); + + addOutputArray( + 'laOut', + dimensions: laIn.dimensions, + elementWidth: laIn.elementWidth, + numDimensionsUnpacked: laIn.numDimensionsUnpacked, + ); + + laOut <= laIn.withSet(3 + 16, laIn.elements[1].getRange(3, 3 + 9)); + } +} + enum LADir { laIn, laOut } class LAPassthroughIntf extends Interface { @@ -550,7 +573,7 @@ void main() { reason: 'Expected no swizzles but found one.'); } - // await SimCompare.checkFunctionalVector(mod, vectors); + await SimCompare.checkFunctionalVector(mod, vectors); if (!noIverilog) { SimCompare.checkIverilogVector(mod, vectors, buildOnly: noSvSim, dontDeleteTmpFiles: dontDeleteTmpFiles); @@ -809,9 +832,21 @@ void main() { }); test('withset', () async { - final mod = WithSetArray(LogicArray([2, 2], 8)); + final mod = WithSetArrayModule(LogicArray([2, 2], 8)); await testArrayPassthrough(mod); }); + + test('withset offset', () async { + final mod = WithSetArrayOffsetModule(LogicArray([2, 2], 8)); + await testArrayPassthrough(mod, checkNoSwizzle: false); + + // make sure we're reassigning both times it overlaps! + expect( + RegExp('assign laIn.*=.*swizzled') + .allMatches(mod.generateSynth()) + .length, + 2); + }); }); group('array constant assignments', () { From 8745fce8499cc36473047873ab41d276fcb8f3a3 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Mon, 5 Jun 2023 12:20:58 -0700 Subject: [PATCH 70/75] test inject and some sim actions with arrays --- lib/src/signals/logic_structure.dart | 8 +--- test/changed_test.dart | 65 +++++++++++++++++----------- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 18930d3d0..6596e26d9 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -100,13 +100,7 @@ class LogicStructure implements Logic { @override void inject(dynamic val, {bool fill = false}) { - final logicVal = LogicValue.of(val, fill: fill, width: width); - - var index = 0; - for (final element in leafElements) { - element.inject(logicVal.getRange(index, index + element.width)); - index += element.width; - } + Simulator.injectAction(() => put(val, fill: fill)); } @override diff --git a/test/changed_test.dart b/test/changed_test.dart index 28f434e62..4c1ffc58a 100644 --- a/test/changed_test.dart +++ b/test/changed_test.dart @@ -78,42 +78,55 @@ void main() { expect(numPosedges, equals(1)); }); - test('injection triggers flop', () async { - final baseClk = SimpleClockGenerator(10).clk; + group('injection triggers flop', () { + Future injectionTriggersFlop({required bool useArrays}) async { + final baseClk = SimpleClockGenerator(10).clk; - final clk = Logic(); - final d = Logic(); + Logic genSignal() => useArrays ? LogicArray([1], 1) : Logic(); - final q = FlipFlop(clk, d).q; + final clk = genSignal(); + final d = genSignal(); - var qHadPosedge = false; + final q = genSignal()..gets(FlipFlop(clk, d).q); - Simulator.setMaxSimTime(100); + var qHadPosedge = false; - unawaited(q.nextPosedge.then((value) { - qHadPosedge = true; - })); + Simulator.setMaxSimTime(100); - unawaited(Simulator.run()); + unawaited(q.nextPosedge.then((value) { + qHadPosedge = true; + })); - await baseClk.nextPosedge; - clk.inject(0); - d.inject(0); - await baseClk.nextPosedge; - clk.inject(1); - await baseClk.nextPosedge; - expect(q.value, equals(LogicValue.zero)); - clk.inject(0); - d.inject(1); - await baseClk.nextPosedge; - clk.inject(1); - await baseClk.nextPosedge; - expect(q.value, equals(LogicValue.one)); + unawaited(Simulator.run()); - await Simulator.simulationEnded; + await baseClk.nextPosedge; + clk.inject(0); + d.inject(0); + await baseClk.nextPosedge; + clk.inject(1); + await baseClk.nextPosedge; + expect(q.value, equals(LogicValue.zero)); + clk.inject(0); + d.inject(1); + await baseClk.nextPosedge; + clk.inject(1); + await baseClk.nextPosedge; + expect(q.value, equals(LogicValue.one)); - expect(qHadPosedge, equals(true)); + await Simulator.simulationEnded; + + expect(qHadPosedge, equals(true)); + } + + test('normal logic', () async { + await injectionTriggersFlop(useArrays: false); + }); + + test('arrays', () async { + await injectionTriggersFlop(useArrays: true); + }); }); + test('injection triggers flop with enable logicvalue 1', () async { final baseClk = SimpleClockGenerator(10).clk; From d91f23ff917377c4e6c92fcca8d5f9e3102ac6d6 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Mon, 5 Jun 2023 14:48:11 -0700 Subject: [PATCH 71/75] fix bug with negative 64bit constants, and test indexing of structs --- lib/src/values/logic_value.dart | 10 ++++--- test/logic_array_test.dart | 21 +++++++++++++++ test/logic_value_test.dart | 46 +++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 4 deletions(-) diff --git a/lib/src/values/logic_value.dart b/lib/src/values/logic_value.dart index c4188145a..6dc4f5de9 100644 --- a/lib/src/values/logic_value.dart +++ b/lib/src/values/logic_value.dart @@ -551,8 +551,9 @@ abstract class LogicValue { @override String toString({bool includeWidth = true}) { if (isValid && includeWidth) { - final hexValue = width > _INT_BITS - ? toBigInt().toRadixString(16) + // for ==_INT_BITS, still use BigInt so we don't get negatives + final hexValue = width >= _INT_BITS + ? toBigInt().toUnsigned(width).toRadixString(16) : toInt().toRadixString(16); return "$width'h$hexValue"; } else { @@ -1140,8 +1141,9 @@ abstract class LogicValue { } } - /// Returns new [LogicValue] replicated [multiplier] times. An exception will - /// be thrown in case the multiplier is <1 + /// Returns new [LogicValue] replicated [multiplier] times. + /// + /// An exception will be thrown in case the multiplier is <1. LogicValue replicate(int multiplier) { if (multiplier < 1) { throw InvalidMultiplierException(multiplier); diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 79b74222d..48593e6f6 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -468,6 +468,15 @@ class CondCompArray extends Module implements SimpleLAPassthrough { } } +class IndexBitOfArrayModule extends Module { + IndexBitOfArrayModule() { + final o = LogicArray([2, 2, 2], 8); + o <= Const(LogicValue.ofString('10').replicate(2 * 2 * 8)); + addOutput('o0') <= o[0]; + addOutput('o3') <= o[3]; + } +} + void main() { tearDown(() async { await Simulator.reset(); @@ -895,5 +904,17 @@ void main() { await testArrayConstantAssignments( numDimensionsUnpacked: 2, doSvSim: false); }); + + test('indexing single bit of array', () async { + final mod = IndexBitOfArrayModule(); + await mod.build(); + + final vectors = [ + Vector({}, {'o0': 0, 'o3': 1}) + ]; + + await SimCompare.checkFunctionalVector(mod, vectors); + SimCompare.checkIverilogVector(mod, vectors); + }); }); } diff --git a/test/logic_value_test.dart b/test/logic_value_test.dart index 57c4eb897..c88fe949c 100644 --- a/test/logic_value_test.dart +++ b/test/logic_value_test.dart @@ -275,6 +275,52 @@ void main() { expect(lv.toString(), equals("6'b01xzx0")); }); + group('LogicValue toString', () { + test('1 bit', () { + expect(LogicValue.one.toString(), "1'h1"); + }); + + test('1 bit invalid', () { + expect(LogicValue.x.toString(), "1'bx"); + }); + + test('<64-bit positive', () { + expect(LogicValue.ofInt(0x1234, 60).toString(), "60'h1234"); + }); + + test('<64-bit negative', () { + expect(LogicValue.ofInt(-1, 60).toString(), "60'hfffffffffffffff"); + }); + + test('64-bit positive', () { + expect(LogicValue.ofInt(0x1234, 64).toString(), "64'h1234"); + }); + + test('64-bit negative', () { + expect(LogicValue.ofInt(0xfaaaaaaa00000005, 64).toString(), + "64'hfaaaaaaa00000005"); + }); + + test('>64-bit positive', () { + expect( + LogicValue.ofBigInt(BigInt.parse('0x5faaaaaaa00000005'), 68) + .toString(), + "68'h5faaaaaaa00000005"); + }); + + test('>64-bit negative', () { + expect( + LogicValue.ofBigInt(BigInt.parse('0xffaaaaaaa00000005'), 68) + .toString(), + "68'hffaaaaaaa00000005"); + }); + + test('include width', () { + expect( + LogicValue.ofInt(0x55, 8).toString(includeWidth: false), '01010101'); + }); + }); + group('unary operations (including "to")', () { test('toMethods', () { expect( From ac85127d36de44b94698fde7270accb0d3ad5096 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Mon, 5 Jun 2023 14:55:11 -0700 Subject: [PATCH 72/75] test elements on basic logic --- lib/src/signals/logic.dart | 6 +++++- test/bus_test.dart | 5 ++--- test/conditionals_test.dart | 2 +- test/logic_structure_test.dart | 3 --- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index 0fb00f64d..3dc310b2d 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -517,7 +517,11 @@ class Logic { throw Exception('Expected `int` or `Logic`'); } - //TODO: test this + /// Provides a list of logical elements within this signal. + /// + /// For a normal [Logic], this will always be a list of 1-bit signals. + /// However, for derivatives of [Logic] like [LogicStructure] or [LogicArray], + /// each element may be any positive number of bits. late final List elements = UnmodifiableListView( List.generate(width, (index) => this[index], growable: false)); diff --git a/test/bus_test.dart b/test/bus_test.dart index 22cb5c607..6fd5bd6e1 100644 --- a/test/bus_test.dart +++ b/test/bus_test.dart @@ -140,7 +140,7 @@ class BusTestModule extends Module { aNegativeRange3 <= a.getRange(-1, 8); aNegativeRange4 <= a.getRange(-3); - aOperatorIndexing1 <= a[0]; + aOperatorIndexing1 <= a.elements[0]; aOperatorIndexing2 <= a[a.width - 1]; aOperatorIndexing3 <= a[4]; aOperatorNegIndexing1 <= a[-a.width]; @@ -375,8 +375,7 @@ void main() { ]; await SimCompare.checkFunctionalVector(gtm, vectors); - final simResult = SimCompare.iverilogVector(gtm, vectors); - expect(simResult, equals(true)); + SimCompare.checkIverilogVector(gtm, vectors); }); test('Bus shrink', () async { diff --git a/test/conditionals_test.dart b/test/conditionals_test.dart index 352c1c2e8..2deba13e0 100644 --- a/test/conditionals_test.dart +++ b/test/conditionals_test.dart @@ -672,7 +672,7 @@ void main() { ); await mod.build(); - var vectors = [ + final vectors = [ Vector({ 'preIncr': 5, 'preDecr': 5, diff --git a/test/logic_structure_test.dart b/test/logic_structure_test.dart index 72ed24b37..bddd4265f 100644 --- a/test/logic_structure_test.dart +++ b/test/logic_structure_test.dart @@ -12,9 +12,6 @@ import 'package:rohd/src/exceptions/exceptions.dart'; import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; -//TODO: test structures in conditional assignments -//TODO: test structures in If/Case expressions - class MyStruct extends LogicStructure { final Logic ready; final Logic valid; From 5c4b0566f321d4052c09b4842046d326162ce7cd Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Mon, 5 Jun 2023 15:12:09 -0700 Subject: [PATCH 73/75] cleanup and doc comments --- lib/src/signals/logic.dart | 6 +++--- lib/src/signals/logic_array.dart | 24 +++++++++++++++++++++--- lib/src/signals/logic_structure.dart | 9 +++------ lib/src/utilities/index_utilities.dart | 3 ++- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/lib/src/signals/logic.dart b/lib/src/signals/logic.dart index 3dc310b2d..8f2b79127 100644 --- a/lib/src/signals/logic.dart +++ b/lib/src/signals/logic.dart @@ -105,19 +105,19 @@ class Logic { /// Triggers at most once, the next time that this [Logic] changes /// value at the end of a [Simulator] tick. - Future get nextChanged => _wire.changed.first; + Future get nextChanged => _wire.nextChanged; /// Triggers at most once, the next time that this [Logic] changes /// value at the end of a [Simulator] tick from `0` to `1`. /// /// Throws an exception if [width] is not `1`. - Future get nextPosedge => _wire.posedge.first; + Future get nextPosedge => _wire.nextPosedge; /// Triggers at most once, the next time that this [Logic] changes /// value at the end of a [Simulator] tick from `1` to `0`. /// /// Throws an exception if [width] is not `1`. - Future get nextNegedge => _wire.negedge.first; + Future get nextNegedge => _wire.nextNegedge; /// The [Module] that this [Logic] exists within. /// diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index 4173aab44..35a24ac50 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -9,7 +9,13 @@ part of signals; +/// Represents a multi-dimensional array structure of independent [Logic]s. class LogicArray extends LogicStructure { + /// The number of elements at each level of the array, starting from the most + /// significant outermost level. + /// + /// For example `[3, 2]` would indicate a 2-dimensional array, where it is + /// an array with 3 arrays, each containing 2 arrays. final List dimensions; /// The width of leaf elements in this array. @@ -21,9 +27,15 @@ class LogicArray extends LogicStructure { @override String toString() => 'LogicArray($dimensions, $elementWidth): $name'; + /// The number of [dimensions] which should be treated as "unpacked", starting + /// from the outermost (first) elements of [dimensions]. + /// + /// This has no functional impact on simulation or behavior. It is only used + /// as a hint for [Synthesizer]s. final int numDimensionsUnpacked; - ///TODO + /// Creates an array with specified [dimensions] and [elementWidth] named + /// [name]. /// /// Setting the [numDimensionsUnpacked] gives a hint to [Synthesizer]s about /// the intent for declaration of signals. By default, all dimensions are @@ -75,12 +87,15 @@ class LogicArray extends LogicStructure { ); } - //TODO + /// Creates a new [LogicArray] which has the same [dimensions], + /// [elementWidth], [numDimensionsUnpacked] as `this`. + /// + /// If no new [name] is specified, then it will also have the same name. @override LogicArray clone({String? name}) => LogicArray(dimensions, elementWidth, numDimensionsUnpacked: numDimensionsUnpacked, name: name ?? this.name); - ///TODO + /// Private constructor for the factory [LogicArray] constructor. LogicArray._( super.elements, { required this.dimensions, @@ -89,6 +104,9 @@ class LogicArray extends LogicStructure { required super.name, }); + /// Constructs a new [LogicArray] with a more convenient constructor signature + /// for when many ports in an interface are declared together. Also performs + /// some basic checks on the legality of the array as a port of a [Module]. factory LogicArray.port(String name, [List dimensions = const [1], int elementWidth = 1, diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 6596e26d9..1f4029e6e 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -9,6 +9,8 @@ part of signals; +/// Collects a group of [Logic] signals into one entity which can be manipulated +/// in a similar way as an individual [Logic]. class LogicStructure implements Logic { /// All elements of this structure. @override @@ -53,6 +55,7 @@ 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() @@ -81,12 +84,6 @@ class LogicStructure implements Logic { @override bool get isArrayMember => parentStructure is LogicArray; - //TODO: delete this crap - /////////////////////////////////////////////// - /////////////////////////////////////////////// - /////////////////////////////////////////////// - /////////////////////////////////////////////// - @override void put(dynamic val, {bool fill = false}) { final logicVal = LogicValue.of(val, fill: fill, width: width); diff --git a/lib/src/utilities/index_utilities.dart b/lib/src/utilities/index_utilities.dart index ef25c45f1..f17d1f4db 100644 --- a/lib/src/utilities/index_utilities.dart +++ b/lib/src/utilities/index_utilities.dart @@ -33,7 +33,8 @@ abstract class IndexUtilities { originalIndex, width, 'IndexOutOfRange', - 'Index out of range: $modifiedIndex(=$originalIndex) for width $width.', + 'Index out of range:' + ' $modifiedIndex(=$originalIndex) for width $width.', width); } From ad368b556fe642cd21549a07a61ab1d088664d6f Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Mon, 5 Jun 2023 15:57:12 -0700 Subject: [PATCH 74/75] Added documentation for struct and array --- doc/user_guide/_docs/logic-arrays.md | 44 +++++++++ doc/user_guide/_docs/logic-structures.md | 68 +++++++++++++ lib/src/module.dart | 14 +-- lib/src/signals/logic_array.dart | 22 ++--- lib/src/synthesizers/systemverilog.dart | 2 +- lib/src/utilities/simcompare.dart | 4 +- test/logic_array_test.dart | 118 +++++++++++------------ test/logic_structure_test.dart | 19 ++++ 8 files changed, 211 insertions(+), 80 deletions(-) create mode 100644 doc/user_guide/_docs/logic-arrays.md create mode 100644 doc/user_guide/_docs/logic-structures.md diff --git a/doc/user_guide/_docs/logic-arrays.md b/doc/user_guide/_docs/logic-arrays.md new file mode 100644 index 000000000..882cde4d4 --- /dev/null +++ b/doc/user_guide/_docs/logic-arrays.md @@ -0,0 +1,44 @@ +--- +title: "Logic Arrays" +permalink: /docs/logic-arrays/ +last_modified_at: 2022-6-5 +toc: true +--- + +A `LogicArray` is a type of `LogicStructure` that mirrors multi-dimensional arrays in hardware languages like SystemVerilog. In ROHD, the `LogicArray` type inherits a lot of functionality from `LogicStructure`, so it can behave like a `Logic` where it makes sense or be individually referenced in other places. + +`LogicArray`s can be constructed easily using the constructor: + +```dart +// A 1D array with ten 8-bit elements. +LogicArray([10], 8); + +// A 4x3 2D array, with four arrays, each with three 2-bit elements. +LogicArray([4, 3], 2, name: 'array4x3'); + +// A 5x5x5 3D array, with 125 total elements, each 128 bits. +LogicArray([5, 5, 5], 128); +``` + +As long as the total width of a `LogicArray` and another type of `Logic` (including `Logic`, `LogicStructure`, and another `LogicArray`) are the same, assignments and bitwise operations will work in per-element order. This means you can assign two `LogicArray`s of different dimensions to each other as long as the total width matches. + +## Unpacked arrays + +In SystemVerilog, there is a concept of "packed" vs. "unpacked" arrays which have different use cases and capabilities. In ROHD, all arrays act the same and you get the best of both worlds. You can indicate when constructing a `LogicArray` that some number of the dimensions should be "unpacked" as a hint to `Synthesizer`s. Marking an array with a non-zero `numUnpackedDimensions`, for example, will make that many of the dimensions "unpacked" in generated SystemVerilog signal declarations. + +```dart +// A 4x3 2D array, with four arrays, each with three 2-bit elements. +// The first dimension (4) will be unpacked. +LogicArray( + [4, 3], + 2, + name: 'array4x3w1unpacked', + numUnpackedDimensions: 1, +); +``` + +## Array ports + +You can declare ports of `Module`s as being arrays (including with some dimensions "unpacked") using `addInputArray` and `addOutputArray`. Note that these do _not_ automatically do validation that the dimensions, element width, number of unpacked dimensions, etc. are equal between the port and the original signal. As long as the overall width matches, the assignment will be clean. + +Array ports in generated SystemVerilog will match dimensions (including unpacked) as specified when the port is created. \ No newline at end of file diff --git a/doc/user_guide/_docs/logic-structures.md b/doc/user_guide/_docs/logic-structures.md new file mode 100644 index 000000000..dd1ff2974 --- /dev/null +++ b/doc/user_guide/_docs/logic-structures.md @@ -0,0 +1,68 @@ +--- +title: "Logic Structures" +permalink: /docs/logic-structures/ +last_modified_at: 2022-6-5 +toc: true +--- + +A `LogicStructure` is a useful way to group or bundle related `Logic` signals together. They operate in a similar way to "`packed` `structs`" in SystemVerilog, or a `class` containing multiple `Logic`s in ROHD, but with some important differences. + +**`LogicStructure`s will _not_ convert to `struct`s in generated SystemVerilog.** They are purely a way to deal with signals during generation time in ROHD. + +**`LogicStructure`s can be used anywhere a `Logic` can be**. This means you can assign one structure to another structure, or inter-assign between normal signals and structures. As long as the overall width matches, the assignment will work. The order of assignment of bits is based on the order of the `elements` in the structure. + +**Elements within a `LogicStructure` can be individually assigned.** This is a notable difference from individual bits of a plain `Logic` where you'd have to use something like `withSet` to effectively modify bits within a signal. + +`LogicArray`s are a type of `LogicStructure` and thus inherit these behavioral traits. + +## Using `LogicStructure` to group signals + +The simplest way to use a `LogicStructure` is to just use its constructor, which requires a collection of `Logic`s. + +For example, if you wanted to bundle together a `ready` and a `valid` signal together into one structure, you could do this: + +```dart +final rvStruct = LogicStructure([Logic(name: 'ready'), Logic(name: 'valid')]); +``` + +You could now assign this like any other `Logic` all together: + +```dart +Logic ready, valid; +rvStruct <= [ready, valid].rswizzle(); +``` + +Or you can assign individual `elements`: + +```dart +rvStruct.elements[0] <= ready; +rvStruct.elements[1] <= valid; +``` + +## Making your own structure + +Referencing elements by index is often not ideal for named signals. We can do better by building our own structure that inherits from `LogicStructure`. + +```dart +class ReadyValidStruct extends LogicStructure { + final Logic ready; + final Logic valid; + + factory ReadyValidStruct() => MyStruct._( + Logic(name: 'ready'), + Logic(name: 'valid'), + ); + + ReadyValidStruct._(this.ready, this.valid) + : super([ready, valid], name: 'readyValid'); + + @override + LogicStructure clone({String? name}) => ReadyValidStruct(); +} +``` + +Here we've built a class that has `ready` and `valid` as fields, so we can reference those instead of by element index. We use some tricks with `factory`s to make this easier to work with. + +We override the `clone` function so that we can make a duplicate structure of the same type. + +There's a lot more that can be done with a custom class like this, but this is a good start. There are places where it may even make sense to prefer a custom `LogicStructure` to an `Interface`. diff --git a/lib/src/module.dart b/lib/src/module.dart index 6adc81a1d..f55a3baff 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -484,24 +484,24 @@ abstract class Module { } /// Registers and returns an input [LogicArray] port to this [Module] with - /// the specified [dimensions], [elementWidth], and [numDimensionsUnpacked] + /// the specified [dimensions], [elementWidth], and [numUnpackedDimensions] /// named [name]. /// /// This is very similar to [addInput], except for [LogicArray]s. /// /// Performs validation on overall width matching for [x], but not on - /// [dimensions], [elementWidth], or [numDimensionsUnpacked]. + /// [dimensions], [elementWidth], or [numUnpackedDimensions]. LogicArray addInputArray( String name, Logic x, { List dimensions = const [1], int elementWidth = 1, - int numDimensionsUnpacked = 0, + int numUnpackedDimensions = 0, }) { _checkForSafePortName(name); final inArr = LogicArray(dimensions, elementWidth, - name: name, numDimensionsUnpacked: numDimensionsUnpacked) + name: name, numUnpackedDimensions: numUnpackedDimensions) ..gets(x) // ignore: invalid_use_of_protected_member ..parentModule = this; @@ -529,7 +529,7 @@ abstract class Module { } /// Registers and returns an output [LogicArray] port to this [Module] with - /// the specified [dimensions], [elementWidth], and [numDimensionsUnpacked] + /// the specified [dimensions], [elementWidth], and [numUnpackedDimensions] /// named [name]. /// /// This is very similar to [addOutput], except for [LogicArray]s. @@ -537,12 +537,12 @@ abstract class Module { String name, { List dimensions = const [1], int elementWidth = 1, - int numDimensionsUnpacked = 0, + int numUnpackedDimensions = 0, }) { _checkForSafePortName(name); final outArr = LogicArray(dimensions, elementWidth, - name: name, numDimensionsUnpacked: numDimensionsUnpacked) + name: name, numUnpackedDimensions: numUnpackedDimensions) // ignore: invalid_use_of_protected_member ..parentModule = this; diff --git a/lib/src/signals/logic_array.dart b/lib/src/signals/logic_array.dart index 35a24ac50..1de2a4605 100644 --- a/lib/src/signals/logic_array.dart +++ b/lib/src/signals/logic_array.dart @@ -32,12 +32,12 @@ class LogicArray extends LogicStructure { /// /// This has no functional impact on simulation or behavior. It is only used /// as a hint for [Synthesizer]s. - final int numDimensionsUnpacked; + final int numUnpackedDimensions; /// Creates an array with specified [dimensions] and [elementWidth] named /// [name]. /// - /// Setting the [numDimensionsUnpacked] gives a hint to [Synthesizer]s about + /// Setting the [numUnpackedDimensions] gives a hint to [Synthesizer]s about /// the intent for declaration of signals. By default, all dimensions are /// packed, but if the value is set to more than `0`, then the outer-most /// dimensions (first in [dimensions]) will become unpacked. It must be less @@ -45,13 +45,13 @@ class LogicArray extends LogicStructure { /// impact on simulation functionality or behavior. In SystemVerilog, there /// are some differences in access patterns for packed vs. unpacked arrays. factory LogicArray(List dimensions, int elementWidth, - {String? name, int numDimensionsUnpacked = 0}) { + {String? name, int numUnpackedDimensions = 0}) { if (dimensions.isEmpty) { throw LogicConstructionException( 'Arrays must have at least 1 dimension.'); } - if (numDimensionsUnpacked > dimensions.length) { + if (numUnpackedDimensions > dimensions.length) { throw LogicConstructionException( 'Cannot unpack more than all of the dimensions.'); } @@ -75,32 +75,32 @@ class LogicArray extends LogicStructure { : LogicArray( nextDimensions!, elementWidth, - numDimensionsUnpacked: max(0, numDimensionsUnpacked - 1), + numUnpackedDimensions: max(0, numUnpackedDimensions - 1), name: '${name}_$index', )) .._arrayIndex = index, growable: false), dimensions: UnmodifiableListView(dimensions), elementWidth: elementWidth, - numDimensionsUnpacked: numDimensionsUnpacked, + numUnpackedDimensions: numUnpackedDimensions, name: name, ); } /// Creates a new [LogicArray] which has the same [dimensions], - /// [elementWidth], [numDimensionsUnpacked] as `this`. + /// [elementWidth], [numUnpackedDimensions] as `this`. /// /// If no new [name] is specified, then it will also have the same name. @override LogicArray clone({String? name}) => LogicArray(dimensions, elementWidth, - numDimensionsUnpacked: numDimensionsUnpacked, name: name ?? this.name); + numUnpackedDimensions: numUnpackedDimensions, name: name ?? this.name); /// Private constructor for the factory [LogicArray] constructor. LogicArray._( super.elements, { required this.dimensions, required this.elementWidth, - required this.numDimensionsUnpacked, + required this.numUnpackedDimensions, required super.name, }); @@ -110,12 +110,12 @@ class LogicArray extends LogicStructure { factory LogicArray.port(String name, [List dimensions = const [1], int elementWidth = 1, - int numDimensionsUnpacked = 0]) { + int numUnpackedDimensions = 0]) { if (!Sanitizer.isSanitary(name)) { throw InvalidPortNameException(name); } return LogicArray(dimensions, elementWidth, - numDimensionsUnpacked: numDimensionsUnpacked, name: name); + numUnpackedDimensions: numUnpackedDimensions, name: name); } } diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart index d8913b358..0544acb8e 100644 --- a/lib/src/synthesizers/systemverilog.dart +++ b/lib/src/synthesizers/systemverilog.dart @@ -654,7 +654,7 @@ class _SynthLogic { for (var i = 0; i < dims.length; i++) { final dim = dims[i]; final dimStr = _widthToRangeDef(dim, forceRange: true); - if (i < logicArr.numDimensionsUnpacked) { + if (i < logicArr.numUnpackedDimensions) { unpackedDimsBuf.write(dimStr); } else { packedDimsBuf.write(dimStr); diff --git a/lib/src/utilities/simcompare.dart b/lib/src/utilities/simcompare.dart index 6e10afc8c..b3e6a4554 100644 --- a/lib/src/utilities/simcompare.dart +++ b/lib/src/utilities/simcompare.dart @@ -235,9 +235,9 @@ abstract class SimCompare { if (signal is LogicArray) { final unpackedDims = - signal.dimensions.getRange(0, signal.numDimensionsUnpacked); + signal.dimensions.getRange(0, signal.numUnpackedDimensions); final packedDims = signal.dimensions - .getRange(signal.numDimensionsUnpacked, signal.dimensions.length); + .getRange(signal.numUnpackedDimensions, signal.dimensions.length); // ignore: parameter_assignments, prefer_interpolation_to_compose_strings return packedDims.map((d) => '[${d - 1}:0]').join() + ' [${signal.elementWidth - 1}:0] $signalName' + diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 48593e6f6..9e1ac23f9 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -28,15 +28,15 @@ class SimpleLAPassthrough extends Module { laIn, dimensions: dimOverride ?? laIn.dimensions, elementWidth: elemWidthOverride ?? laIn.elementWidth, - numDimensionsUnpacked: numUnpackedOverride ?? laIn.numDimensionsUnpacked, + numUnpackedDimensions: numUnpackedOverride ?? laIn.numUnpackedDimensions, ); addOutputArray( 'laOut', dimensions: dimOverride ?? laIn.dimensions, elementWidth: elemWidthOverride ?? laIn.elementWidth, - numDimensionsUnpacked: - numUnpackedOverride ?? laIn.numDimensionsUnpacked, + numUnpackedDimensions: + numUnpackedOverride ?? laIn.numUnpackedDimensions, ) <= laIn; } @@ -58,7 +58,7 @@ class RangeAndSliceArrModule extends Module implements SimpleLAPassthrough { 'laOut', dimensions: laIn.dimensions, elementWidth: laIn.elementWidth, - numDimensionsUnpacked: laIn.numDimensionsUnpacked, + numUnpackedDimensions: laIn.numUnpackedDimensions, ); laOut.elements[0] <= @@ -97,7 +97,7 @@ class WithSetArrayModule extends Module implements SimpleLAPassthrough { 'laOut', dimensions: laIn.dimensions, elementWidth: laIn.elementWidth, - numDimensionsUnpacked: laIn.numDimensionsUnpacked, + numUnpackedDimensions: laIn.numUnpackedDimensions, ); laOut <= laIn.withSet(8, laIn.elements[0].elements[1]); @@ -120,7 +120,7 @@ class WithSetArrayOffsetModule extends Module implements SimpleLAPassthrough { 'laOut', dimensions: laIn.dimensions, elementWidth: laIn.elementWidth, - numDimensionsUnpacked: laIn.numDimensionsUnpacked, + numUnpackedDimensions: laIn.numUnpackedDimensions, ); laOut <= laIn.withSet(3 + 16, laIn.elements[1].getRange(3, 3 + 9)); @@ -132,7 +132,7 @@ enum LADir { laIn, laOut } class LAPassthroughIntf extends Interface { final List dimensions; final int elementWidth; - final int numDimensionsUnpacked; + final int numUnpackedDimensions; Logic get laIn => port('laIn'); Logic get laOut => port('laOut'); @@ -140,16 +140,16 @@ class LAPassthroughIntf extends Interface { LAPassthroughIntf({ required this.dimensions, required this.elementWidth, - required this.numDimensionsUnpacked, + required this.numUnpackedDimensions, }) { setPorts([ - LogicArray.port('laIn', dimensions, elementWidth, numDimensionsUnpacked) + LogicArray.port('laIn', dimensions, elementWidth, numUnpackedDimensions) ], [ LADir.laIn ]); setPorts([ - LogicArray.port('laOut', dimensions, elementWidth, numDimensionsUnpacked) + LogicArray.port('laOut', dimensions, elementWidth, numUnpackedDimensions) ], [ LADir.laOut ]); @@ -159,7 +159,7 @@ class LAPassthroughIntf extends Interface { : this( dimensions: other.dimensions, elementWidth: other.elementWidth, - numDimensionsUnpacked: other.numDimensionsUnpacked, + numUnpackedDimensions: other.numUnpackedDimensions, ); } @@ -184,21 +184,21 @@ class SimpleLAPassthroughLogic extends Module implements SimpleLAPassthrough { Logic laIn, { required List dimensions, required int elementWidth, - required int numDimensionsUnpacked, + required int numUnpackedDimensions, }) { laIn = addInputArray( 'laIn', laIn, dimensions: dimensions, elementWidth: elementWidth, - numDimensionsUnpacked: numDimensionsUnpacked, + numUnpackedDimensions: numUnpackedDimensions, ); addOutputArray( 'laOut', dimensions: dimensions, elementWidth: elementWidth, - numDimensionsUnpacked: numDimensionsUnpacked, + numUnpackedDimensions: numUnpackedDimensions, ) <= laIn; } @@ -212,7 +212,7 @@ class PackAndUnpackPassthrough extends Module implements SimpleLAPassthrough { laIn = addInputArray('laIn', laIn, dimensions: laIn.dimensions, elementWidth: laIn.elementWidth, - numDimensionsUnpacked: laIn.numDimensionsUnpacked); + numUnpackedDimensions: laIn.numUnpackedDimensions); final intermediate = Logic(name: 'intermediate', width: laIn.width); @@ -221,7 +221,7 @@ class PackAndUnpackPassthrough extends Module implements SimpleLAPassthrough { addOutputArray('laOut', dimensions: laIn.dimensions, elementWidth: laIn.elementWidth, - numDimensionsUnpacked: laIn.numDimensionsUnpacked) <= + numUnpackedDimensions: laIn.numUnpackedDimensions) <= intermediate; } } @@ -236,7 +236,7 @@ class PackAndUnpackWithArraysPassthrough extends Module laIn = addInputArray('laIn', laIn, dimensions: laIn.dimensions, elementWidth: laIn.elementWidth, - numDimensionsUnpacked: laIn.numDimensionsUnpacked); + numUnpackedDimensions: laIn.numUnpackedDimensions); final intermediate1 = Logic(name: 'intermediate1', width: laIn.width); final intermediate3 = Logic(name: 'intermediate2', width: laIn.width); @@ -244,7 +244,7 @@ class PackAndUnpackWithArraysPassthrough extends Module // unpack with reversed dimensions final intermediate2 = LogicArray( laIn.dimensions.reversed.toList(), laIn.elementWidth, - name: 'intermediate2', numDimensionsUnpacked: intermediateUnpacked); + name: 'intermediate2', numUnpackedDimensions: intermediateUnpacked); intermediate1 <= laIn; intermediate2 <= intermediate1; @@ -253,7 +253,7 @@ class PackAndUnpackWithArraysPassthrough extends Module addOutputArray('laOut', dimensions: laIn.dimensions, elementWidth: laIn.elementWidth, - numDimensionsUnpacked: laIn.numDimensionsUnpacked) <= + numUnpackedDimensions: laIn.numUnpackedDimensions) <= intermediate3; } } @@ -266,19 +266,19 @@ class RearrangeArraysPassthrough extends Module implements SimpleLAPassthrough { laIn = addInputArray('laIn', laIn, dimensions: laIn.dimensions, elementWidth: laIn.elementWidth, - numDimensionsUnpacked: laIn.numDimensionsUnpacked); + numUnpackedDimensions: laIn.numUnpackedDimensions); // rearrange with reversed dimensions final intermediate = LogicArray( laIn.dimensions.reversed.toList(), laIn.elementWidth, - name: 'intermediate', numDimensionsUnpacked: intermediateUnpacked); + name: 'intermediate', numUnpackedDimensions: intermediateUnpacked); intermediate <= laIn; addOutputArray('laOut', dimensions: laIn.dimensions, elementWidth: laIn.elementWidth, - numDimensionsUnpacked: laIn.numDimensionsUnpacked) <= + numUnpackedDimensions: laIn.numUnpackedDimensions) <= intermediate; } } @@ -291,7 +291,7 @@ class ArrayNameConflicts extends Module implements SimpleLAPassthrough { laIn = addInputArray('laIn', laIn, dimensions: laIn.dimensions, elementWidth: laIn.elementWidth, - numDimensionsUnpacked: laIn.numDimensionsUnpacked); + numUnpackedDimensions: laIn.numUnpackedDimensions); final intermediate1 = Logic(name: 'intermediate', width: laIn.width); final intermediate3 = Logic(name: 'intermediate', width: laIn.width); @@ -300,11 +300,11 @@ class ArrayNameConflicts extends Module implements SimpleLAPassthrough { // unpack with reversed dimensions final intermediate2 = LogicArray( laIn.dimensions.reversed.toList(), laIn.elementWidth, - name: 'intermediate', numDimensionsUnpacked: intermediateUnpacked); + name: 'intermediate', numUnpackedDimensions: intermediateUnpacked); final intermediate4 = LogicArray( laIn.dimensions.reversed.toList(), laIn.elementWidth, - name: 'intermediate', numDimensionsUnpacked: intermediateUnpacked); + name: 'intermediate', numUnpackedDimensions: intermediateUnpacked); intermediate1 <= laIn; intermediate2 <= intermediate1; @@ -315,7 +315,7 @@ class ArrayNameConflicts extends Module implements SimpleLAPassthrough { addOutputArray('laOut', dimensions: laIn.dimensions, elementWidth: laIn.elementWidth, - numDimensionsUnpacked: laIn.numDimensionsUnpacked) <= + numUnpackedDimensions: laIn.numUnpackedDimensions) <= intermediate5; } } @@ -328,14 +328,14 @@ class SimpleArraysAndHierarchy extends Module implements SimpleLAPassthrough { laIn = addInputArray('laIn', laIn, dimensions: laIn.dimensions, elementWidth: laIn.elementWidth, - numDimensionsUnpacked: laIn.numDimensionsUnpacked); + numUnpackedDimensions: laIn.numUnpackedDimensions); final intermediate = SimpleLAPassthrough(laIn).laOut; addOutputArray('laOut', dimensions: laIn.dimensions, elementWidth: laIn.elementWidth, - numDimensionsUnpacked: laIn.numDimensionsUnpacked) <= + numUnpackedDimensions: laIn.numUnpackedDimensions) <= intermediate; } } @@ -348,10 +348,10 @@ class FancyArraysAndHierarchy extends Module implements SimpleLAPassthrough { laIn = addInputArray('laIn', laIn, dimensions: laIn.dimensions, elementWidth: laIn.elementWidth, - numDimensionsUnpacked: laIn.numDimensionsUnpacked); + numUnpackedDimensions: laIn.numUnpackedDimensions); final invertedLaIn = LogicArray(laIn.dimensions, laIn.elementWidth, - numDimensionsUnpacked: intermediateUnpacked) + numUnpackedDimensions: intermediateUnpacked) ..gets(~laIn); final x1 = SimpleLAPassthrough(laIn).laOut; @@ -370,7 +370,7 @@ class FancyArraysAndHierarchy extends Module implements SimpleLAPassthrough { addOutputArray('laOut', dimensions: laIn.dimensions, elementWidth: laIn.elementWidth, - numDimensionsUnpacked: laIn.numDimensionsUnpacked) <= + numUnpackedDimensions: laIn.numUnpackedDimensions) <= same; } } @@ -381,12 +381,12 @@ class ConstantAssignmentArrayModule extends Module { ConstantAssignmentArrayModule(LogicArray laIn) { laIn = addInputArray('laIn', laIn, dimensions: [3, 3, 3, 3], - numDimensionsUnpacked: laIn.numDimensionsUnpacked, + numUnpackedDimensions: laIn.numUnpackedDimensions, elementWidth: 8); addOutputArray('laOut', dimensions: laIn.dimensions, - numDimensionsUnpacked: laIn.numDimensionsUnpacked, + numUnpackedDimensions: laIn.numUnpackedDimensions, elementWidth: laIn.elementWidth); laOut.elements[1] <= @@ -414,14 +414,14 @@ class CondAssignArray extends Module implements SimpleLAPassthrough { laIn, dimensions: dimOverride ?? laIn.dimensions, elementWidth: elemWidthOverride ?? laIn.elementWidth, - numDimensionsUnpacked: numUnpackedOverride ?? laIn.numDimensionsUnpacked, + numUnpackedDimensions: numUnpackedOverride ?? laIn.numUnpackedDimensions, ); final laOut = addOutputArray( 'laOut', dimensions: dimOverride ?? laIn.dimensions, elementWidth: elemWidthOverride ?? laIn.elementWidth, - numDimensionsUnpacked: numUnpackedOverride ?? laIn.numDimensionsUnpacked, + numUnpackedDimensions: numUnpackedOverride ?? laIn.numUnpackedDimensions, ); Combinational([laOut < laIn]); @@ -443,14 +443,14 @@ class CondCompArray extends Module implements SimpleLAPassthrough { laIn, dimensions: dimOverride ?? laIn.dimensions, elementWidth: elemWidthOverride ?? laIn.elementWidth, - numDimensionsUnpacked: numUnpackedOverride ?? laIn.numDimensionsUnpacked, + numUnpackedDimensions: numUnpackedOverride ?? laIn.numUnpackedDimensions, ); final laOut = addOutputArray( 'laOut', dimensions: dimOverride ?? laIn.dimensions, elementWidth: elemWidthOverride ?? laIn.elementWidth, - numDimensionsUnpacked: numUnpackedOverride ?? laIn.numDimensionsUnpacked, + numUnpackedDimensions: numUnpackedOverride ?? laIn.numUnpackedDimensions, ); Combinational([ @@ -544,16 +544,16 @@ void main() { }); test('overly unpacking exception', () { - expect(() => LogicArray([1, 2, 3], 4, numDimensionsUnpacked: 4), + expect(() => LogicArray([1, 2, 3], 4, numUnpackedDimensions: 4), throwsA(isA())); }); test('unpacked dims get passed down', () { - final arr = LogicArray([1, 2, 3], 4, numDimensionsUnpacked: 2); - expect(arr.numDimensionsUnpacked, 2); - expect((arr.elements[0] as LogicArray).numDimensionsUnpacked, 1); + final arr = LogicArray([1, 2, 3], 4, numUnpackedDimensions: 2); + expect(arr.numUnpackedDimensions, 2); + expect((arr.elements[0] as LogicArray).numUnpackedDimensions, 1); expect( - (arr.elements[0].elements[0] as LogicArray).numDimensionsUnpacked, 0); + (arr.elements[0].elements[0] as LogicArray).numUnpackedDimensions, 0); }); }); @@ -622,7 +622,7 @@ void main() { test('1d, unpacked', () async { final mod = - SimpleLAPassthrough(LogicArray([3], 8, numDimensionsUnpacked: 1)); + SimpleLAPassthrough(LogicArray([3], 8, numUnpackedDimensions: 1)); // unpacked array assignment not fully supported in iverilog await testArrayPassthrough(mod, noSvSim: true); @@ -634,13 +634,13 @@ void main() { test('single element, unpacked', () async { final mod = - SimpleLAPassthrough(LogicArray([1], 8, numDimensionsUnpacked: 1)); + SimpleLAPassthrough(LogicArray([1], 8, numUnpackedDimensions: 1)); await testArrayPassthrough(mod, noSvSim: true, noIverilog: true); }); test('4d, half packed', () async { final mod = SimpleLAPassthrough( - LogicArray([5, 4, 3, 2], 8, numDimensionsUnpacked: 2)); + LogicArray([5, 4, 3, 2], 8, numUnpackedDimensions: 2)); // unpacked array assignment not fully supported in iverilog await testArrayPassthrough(mod, noSvSim: true); @@ -667,7 +667,7 @@ void main() { final mod = LAPassthroughWithIntf(LAPassthroughIntf( dimensions: [3, 2, 3], elementWidth: 8, - numDimensionsUnpacked: 0, + numUnpackedDimensions: 0, )); await testArrayPassthrough(mod); }); @@ -686,7 +686,7 @@ void main() { test('3d unpacked', () async { final mod = PackAndUnpackPassthrough( - LogicArray([5, 3, 2], 8, numDimensionsUnpacked: 2)); + LogicArray([5, 3, 2], 8, numUnpackedDimensions: 2)); // unpacked array assignment not fully supported in iverilog await testArrayPassthrough(mod, checkNoSwizzle: false, noSvSim: true); @@ -712,7 +712,7 @@ void main() { test('3d unpacked', () async { final mod = PackAndUnpackWithArraysPassthrough( - LogicArray([4, 3, 2], 8, numDimensionsUnpacked: 2), + LogicArray([4, 3, 2], 8, numUnpackedDimensions: 2), intermediateUnpacked: 1); // unpacked array assignment not fully supported in iverilog @@ -728,7 +728,7 @@ void main() { test('3d unpacked', () async { final mod = RearrangeArraysPassthrough( - LogicArray([4, 3, 2], 8, numDimensionsUnpacked: 2), + LogicArray([4, 3, 2], 8, numUnpackedDimensions: 2), intermediateUnpacked: 1); // unpacked array assignment not fully supported in iverilog @@ -741,8 +741,8 @@ void main() { group('different port and input widths', () { test('array param mismatch', () async { - final i = LogicArray([3, 2], 8, numDimensionsUnpacked: 1); - final o = LogicArray([3, 2], 8, numDimensionsUnpacked: 1); + final i = LogicArray([3, 2], 8, numUnpackedDimensions: 1); + final o = LogicArray([3, 2], 8, numUnpackedDimensions: 1); final mod = SimpleLAPassthrough( i, dimOverride: [1, 3], @@ -760,7 +760,7 @@ void main() { i, dimensions: [1, 3], elementWidth: 16, - numDimensionsUnpacked: 0, + numUnpackedDimensions: 0, ); o <= mod.laOut; await testArrayPassthrough(mod); @@ -775,7 +775,7 @@ void main() { test('3d unpacked', () async { final mod = ArrayNameConflicts( - LogicArray([4, 3, 2], 8, numDimensionsUnpacked: 2), + LogicArray([4, 3, 2], 8, numUnpackedDimensions: 2), intermediateUnpacked: 1); // unpacked array assignment not fully supported in iverilog @@ -794,7 +794,7 @@ void main() { test('3d unpacked', () async { final mod = SimpleArraysAndHierarchy( - LogicArray([4, 3, 2], 8, numDimensionsUnpacked: 2)); + LogicArray([4, 3, 2], 8, numUnpackedDimensions: 2)); // unpacked array assignment not fully supported in iverilog await testArrayPassthrough(mod, noSvSim: true); @@ -815,7 +815,7 @@ void main() { test('3d unpacked', () async { final mod = FancyArraysAndHierarchy( - LogicArray([4, 3, 2], 8, numDimensionsUnpacked: 2), + LogicArray([4, 3, 2], 8, numUnpackedDimensions: 2), intermediateUnpacked: 1); // unpacked array assignment not fully supported in iverilog @@ -860,9 +860,9 @@ void main() { group('array constant assignments', () { Future testArrayConstantAssignments( - {required int numDimensionsUnpacked, bool doSvSim = true}) async { + {required int numUnpackedDimensions, bool doSvSim = true}) async { final mod = ConstantAssignmentArrayModule(LogicArray([3, 3, 3, 3], 8, - numDimensionsUnpacked: numDimensionsUnpacked)); + numUnpackedDimensions: numUnpackedDimensions)); await mod.build(); final a = []; @@ -896,13 +896,13 @@ void main() { } test('with packed only', () async { - await testArrayConstantAssignments(numDimensionsUnpacked: 0); + await testArrayConstantAssignments(numUnpackedDimensions: 0); }); test('with unpacked also', () async { // unpacked array assignment not fully supported in iverilog await testArrayConstantAssignments( - numDimensionsUnpacked: 2, doSvSim: false); + numUnpackedDimensions: 2, doSvSim: false); }); test('indexing single bit of array', () async { diff --git a/test/logic_structure_test.dart b/test/logic_structure_test.dart index bddd4265f..fabfc2508 100644 --- a/test/logic_structure_test.dart +++ b/test/logic_structure_test.dart @@ -170,3 +170,22 @@ void main() { }); }); } + +void x() { + final rvStruct = LogicStructure([Logic(name: 'ready'), Logic(name: 'valid')]); + + // A 1D array with ten 8-bit elements. + LogicArray([10], 8); + + // A 4x3 2D array, with four arrays, each with three 2-bit elements. + // The first dimension (4) will be unpacked. + LogicArray( + [4, 3], + 2, + name: 'array4x3w1unpacked', + numUnpackedDimensions: 1, + ); + + // A 5x5x5 3D array, with 125 total elements, each 128 bits. + LogicArray([5, 5, 5], 128); +} From ba975252b78717527d5cd8b8b8a70422cb6893ed Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Mon, 5 Jun 2023 16:42:02 -0700 Subject: [PATCH 75/75] some cleanup --- doc/user_guide/_docs/logic-arrays.md | 2 +- lib/src/signals/logic_structure.dart | 9 --------- test/logic_structure_test.dart | 19 ------------------- 3 files changed, 1 insertion(+), 29 deletions(-) diff --git a/doc/user_guide/_docs/logic-arrays.md b/doc/user_guide/_docs/logic-arrays.md index 882cde4d4..f22e75456 100644 --- a/doc/user_guide/_docs/logic-arrays.md +++ b/doc/user_guide/_docs/logic-arrays.md @@ -41,4 +41,4 @@ LogicArray( You can declare ports of `Module`s as being arrays (including with some dimensions "unpacked") using `addInputArray` and `addOutputArray`. Note that these do _not_ automatically do validation that the dimensions, element width, number of unpacked dimensions, etc. are equal between the port and the original signal. As long as the overall width matches, the assignment will be clean. -Array ports in generated SystemVerilog will match dimensions (including unpacked) as specified when the port is created. \ No newline at end of file +Array ports in generated SystemVerilog will match dimensions (including unpacked) as specified when the port is created. diff --git a/lib/src/signals/logic_structure.dart b/lib/src/signals/logic_structure.dart index 1f4029e6e..f339c01b5 100644 --- a/lib/src/signals/logic_structure.dart +++ b/lib/src/signals/logic_structure.dart @@ -340,11 +340,6 @@ class LogicStructure implements Logic { return newWithSet; } - ///////////////////////////////////////////////// - ///////////////////////////////////////////////// - ///////////////////////////////////////////////// - ///////////////////////////////////////////////// - @override Logic operator ~() => ~packed; @@ -474,10 +469,6 @@ class LogicStructure implements Logic { // ignore: deprecated_member_use_from_same_package int get valueInt => packed.valueInt; - ///////////////////////////////////////////// - ///////////////////////////////////////////// - ///////////////////////////////////////////// - @override Logic? get _srcConnection => throw UnsupportedError('Delegated to elements'); diff --git a/test/logic_structure_test.dart b/test/logic_structure_test.dart index fabfc2508..bddd4265f 100644 --- a/test/logic_structure_test.dart +++ b/test/logic_structure_test.dart @@ -170,22 +170,3 @@ void main() { }); }); } - -void x() { - final rvStruct = LogicStructure([Logic(name: 'ready'), Logic(name: 'valid')]); - - // A 1D array with ten 8-bit elements. - LogicArray([10], 8); - - // A 4x3 2D array, with four arrays, each with three 2-bit elements. - // The first dimension (4) will be unpacked. - LogicArray( - [4, 3], - 2, - name: 'array4x3w1unpacked', - numUnpackedDimensions: 1, - ); - - // A 5x5x5 3D array, with 125 total elements, each 128 bits. - LogicArray([5, 5, 5], 128); -}