diff --git a/lib/src/collections/collections.dart b/lib/src/collections/collections.dart new file mode 100644 index 000000000..9a5d7d4d9 --- /dev/null +++ b/lib/src/collections/collections.dart @@ -0,0 +1,5 @@ +/// Copyright (C) 2022 Intel Corporation +/// SPDX-License-Identifier: BSD-3-Clause + +export 'duplicate_detection_set.dart'; +export 'traverseable_collection.dart'; diff --git a/lib/src/collections/duplicate_detection_set.dart b/lib/src/collections/duplicate_detection_set.dart new file mode 100644 index 000000000..8cc561a9f --- /dev/null +++ b/lib/src/collections/duplicate_detection_set.dart @@ -0,0 +1,74 @@ +/// Copyright (C) 2022 Intel Corporation +/// SPDX-License-Identifier: BSD-3-Clause +/// +/// redriven_monitor_set.dart +/// A set that monitor for duplication. +/// +/// 2022 November 2 +/// Author: Yao Jing Quek +/// +import 'dart:collection'; +import 'dart:core'; + +import 'package:collection/collection.dart'; + +/// A Set collection that monitor for duplication. +/// +/// The [DuplicateDetectionSet] is used to identify +/// duplicate elements in the Set. +class DuplicateDetectionSet extends SetBase { + /// The [Set] which contains unique values. + final Set _set = {}; + + /// The [Set] which contains duplicate values. + final Set _duplicates = {}; + + @override + bool add(T value) { + if (_set.contains(value)) { + _duplicates.add(value); + } + + return _set.add(value); + } + + @override + void addAll(Iterable elements) { + elements.forEach(add); + } + + /// The duplicate members in the collection + /// + /// Returns an [UnmodifiableSetView] from DuplicateDetectionSet collection + Set get duplicates => UnmodifiableSetView(_duplicates); + + /// Returns `true` if collection contains duplicates + bool get hasDuplicates => _duplicates.isNotEmpty; + + @override + bool contains(Object? element) => _set.contains(element); + + @override + Iterator get iterator => _set.iterator; + + @override + int get length => _set.length; + + @override + T? lookup(Object? element) => _set.lookup(element); + + /// Removes value from [DuplicateDetectionSet] collection. + /// + /// The [value] in [DuplicateDetectionSet] must not contain duplicates. + /// An [Exception] will be thrown if duplicates [value] found. + @override + bool remove(Object? value) { + if (_set.contains(value) && _duplicates.contains(value)) { + throw Exception('Duplication value detected inside Set!'); + } + return _set.remove(value); + } + + @override + Set toSet() => _set; +} diff --git a/lib/src/utilities/traverseable_collection.dart b/lib/src/collections/traverseable_collection.dart similarity index 100% rename from lib/src/utilities/traverseable_collection.dart rename to lib/src/collections/traverseable_collection.dart diff --git a/lib/src/exceptions/conditionals/conditional_exceptions.dart b/lib/src/exceptions/conditionals/conditional_exceptions.dart new file mode 100644 index 000000000..8b9983b36 --- /dev/null +++ b/lib/src/exceptions/conditionals/conditional_exceptions.dart @@ -0,0 +1,4 @@ +/// Copyright (C) 2022 Intel Corporation +/// SPDX-License-Identifier: BSD-3-Clause + +export 'signal_redriven_exception.dart'; diff --git a/lib/src/exceptions/conditionals/signal_redriven_exception.dart b/lib/src/exceptions/conditionals/signal_redriven_exception.dart new file mode 100644 index 000000000..6d27598c0 --- /dev/null +++ b/lib/src/exceptions/conditionals/signal_redriven_exception.dart @@ -0,0 +1,28 @@ +/// Copyright (C) 2022 Intel Corporation +/// SPDX-License-Identifier: BSD-3-Clause +/// +/// signal_redriven_exception.dart +/// An exception that thrown when a signal is +/// redriven multiple times. +/// +/// 2022 November 9 +/// Author: Yao Jing Quek + +import 'package:rohd/rohd.dart'; + +/// An exception that thrown when a [Logic] signal is +/// operated multiple times. +class SignalRedrivenException implements Exception { + late final String _message; + + /// Displays [signals] that are driven multiple times + /// with default error [message]. + /// + /// Creates a [SignalRedrivenException] with an optional error [message]. + SignalRedrivenException(String signals, + [String message = 'Sequential drove the same signal(s) multiple times: ']) + : _message = message + signals; + + @override + String toString() => _message; +} diff --git a/lib/src/exceptions/exceptions.dart b/lib/src/exceptions/exceptions.dart index bad954400..2be96bd65 100644 --- a/lib/src/exceptions/exceptions.dart +++ b/lib/src/exceptions/exceptions.dart @@ -1,4 +1,6 @@ /// Copyright (C) 2022 Intel Corporation /// SPDX-License-Identifier: BSD-3-Clause -export 'name_exceptions.dart'; +export './conditionals/conditional_exceptions.dart'; +export './name/name_exceptions.dart'; +export './sim_compare/sim_compare_exceptions.dart'; diff --git a/lib/src/exceptions/name/invalid_reserved_name_exception.dart b/lib/src/exceptions/name/invalid_reserved_name_exception.dart new file mode 100644 index 000000000..294343d57 --- /dev/null +++ b/lib/src/exceptions/name/invalid_reserved_name_exception.dart @@ -0,0 +1,26 @@ +/// Copyright (C) 2022 Intel Corporation +/// SPDX-License-Identifier: BSD-3-Clause +/// +/// invalid_reserved_name_exception.dart +/// An exception that thrown when a reserved name is invalid. +/// +/// 2022 October 25 +/// Author: Yao Jing Quek +/// + +/// An exception that thrown when a reserved name is invalid. +class InvalidReservedNameException implements Exception { + late final String _message; + + /// Display error [message] on invalid reserved name. + /// + /// Creates a [InvalidReservedNameException] with an optional error [message]. + InvalidReservedNameException( + [String message = 'Reserved Name need to follow proper naming ' + 'convention if reserved' + ' name set to true']) + : _message = message; + + @override + String toString() => _message; +} diff --git a/lib/src/exceptions/name/name_exceptions.dart b/lib/src/exceptions/name/name_exceptions.dart new file mode 100644 index 000000000..09ffeb24f --- /dev/null +++ b/lib/src/exceptions/name/name_exceptions.dart @@ -0,0 +1,5 @@ +/// Copyright (C) 2022 Intel Corporation +/// SPDX-License-Identifier: BSD-3-Clause + +export 'invalid_reserved_name_exception.dart'; +export 'null_reserved_name_exception.dart'; diff --git a/lib/src/exceptions/name/null_reserved_name_exception.dart b/lib/src/exceptions/name/null_reserved_name_exception.dart new file mode 100644 index 000000000..cfe61678e --- /dev/null +++ b/lib/src/exceptions/name/null_reserved_name_exception.dart @@ -0,0 +1,25 @@ +/// Copyright (C) 2022 Intel Corporation +/// SPDX-License-Identifier: BSD-3-Clause +/// +/// null_reserved_name_exception.dart +/// An exception that thrown when a reserved name is `null`. +/// +/// 2022 November 15 +/// Author: Yao Jing Quek +/// + +/// An exception that thrown when a reserved name is `null`. +class NullReservedNameException implements Exception { + late final String _message; + + /// Display error [message] on `null` reserved name. + /// + /// Creates a [NullReservedNameException] with an optional error [message]. + NullReservedNameException( + [String message = 'Reserved Name cannot be null ' + 'if reserved name set to true']) + : _message = message; + + @override + String toString() => _message; +} diff --git a/lib/src/exceptions/name_exceptions.dart b/lib/src/exceptions/name_exceptions.dart deleted file mode 100644 index 7dc5f7b86..000000000 --- a/lib/src/exceptions/name_exceptions.dart +++ /dev/null @@ -1,48 +0,0 @@ -/// Copyright (C) 2022 Intel Corporation -/// SPDX-License-Identifier: BSD-3-Clause -/// -/// name_exception.dart -/// Name Exception that have custom type to be thrown, -/// -/// 2022 October 25 -/// Author: Yao Jing Quek -/// -import 'package:rohd/rohd.dart'; - -/// This Exception show that reserved name eg. definitionName is NULL but -/// reserve flag eg. reserveDefinitionName is set to True. -/// -/// Please check on the class [Module] for the constructor argument. -class NullReservedNameException implements Exception { - late final String _message; - - /// constructor for NullReservedNameException, - /// pass custom message to the constructor - NullReservedNameException( - [String message = 'Reserved Name cannot be null ' - 'if reserved name set to true']) - : _message = message; - - @override - String toString() => _message; -} - -/// This Exception show that reserved name eg. definitionName naming convention -/// is invalid but reserve flag eg. reserveDefinitionName is set to True. -/// Please check on the syntax of the reservedName. -/// -/// Please check on the class [Module] for the constructor argument. -class InvalidReservedNameException implements Exception { - late final String _message; - - /// constructor for InvalidReservedNameException, - /// pass custom message to the constructor - InvalidReservedNameException( - [String message = 'Reserved Name need to follow proper naming ' - 'convention if reserved' - ' name set to true']) - : _message = message; - - @override - String toString() => _message; -} diff --git a/lib/src/exceptions/sim_compare/non_supported_type_exception.dart b/lib/src/exceptions/sim_compare/non_supported_type_exception.dart new file mode 100644 index 000000000..f4e6887f1 --- /dev/null +++ b/lib/src/exceptions/sim_compare/non_supported_type_exception.dart @@ -0,0 +1,29 @@ +/// Copyright (C) 2022 Intel Corporation +/// SPDX-License-Identifier: BSD-3-Clause +/// +/// non_supported_type_exception.dart +/// An exception that thrown when `runtimetype` of expected +/// vector output from SimCompare is invalid or unsupported. +/// +/// 2022 November 17 +/// Author: Yao Jing Quek +/// + +import 'package:rohd/src/utilities/simcompare.dart'; + +/// An exception that thrown when `runtimeType` of expected vector +/// output from [SimCompare] is invalid or unsupported. +class NonSupportedTypeException implements Exception { + late final String _message; + + /// Displays [vector] which have invalid or unsupported `runtimeType` + /// with default error [message]. + /// + /// Creates a [NonSupportedTypeException] with an optional error [message]. + NonSupportedTypeException(String vector, + [String message = 'The runtimetype of expected vector is unsupported: ']) + : _message = message + vector.runtimeType.toString(); + + @override + String toString() => _message; +} diff --git a/lib/src/exceptions/sim_compare/sim_compare_exceptions.dart b/lib/src/exceptions/sim_compare/sim_compare_exceptions.dart new file mode 100644 index 000000000..07389ce28 --- /dev/null +++ b/lib/src/exceptions/sim_compare/sim_compare_exceptions.dart @@ -0,0 +1,4 @@ +/// Copyright (C) 2022 Intel Corporation +/// SPDX-License-Identifier: BSD-3-Clause + +export 'non_supported_type_exception.dart'; diff --git a/lib/src/logic.dart b/lib/src/logic.dart index 3c881a450..0d1471b0e 100644 --- a/lib/src/logic.dart +++ b/lib/src/logic.dart @@ -12,6 +12,7 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; + import 'package:rohd/rohd.dart'; import 'package:rohd/src/utilities/sanitizer.dart'; import 'package:rohd/src/utilities/synchronous_propagator.dart'; diff --git a/lib/src/module.dart b/lib/src/module.dart index 293c193c4..7bddfc91c 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -14,7 +14,7 @@ import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/name_exceptions.dart'; +import 'package:rohd/src/exceptions/name/name_exceptions.dart'; import 'package:rohd/src/utilities/config.dart'; import 'package:rohd/src/utilities/sanitizer.dart'; import 'package:rohd/src/utilities/uniquifier.dart'; diff --git a/lib/src/modules/conditional.dart b/lib/src/modules/conditional.dart index c49e8aab8..ee05d721f 100644 --- a/lib/src/modules/conditional.dart +++ b/lib/src/modules/conditional.dart @@ -12,6 +12,8 @@ import 'dart:async'; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; +import 'package:rohd/src/collections/duplicate_detection_set.dart'; +import 'package:rohd/src/exceptions/conditionals/conditional_exceptions.dart'; import 'package:rohd/src/utilities/sanitizer.dart'; import 'package:rohd/src/utilities/uniquifier.dart'; @@ -218,7 +220,7 @@ class Combinational extends _Always { final drivenLogics = {}; for (final element in conditionals) { - drivenLogics.addAll(element.execute()); + element.execute(drivenLogics); } // combinational must always drive all outputs or else you get X! @@ -316,14 +318,26 @@ class Sequential extends _Always { // driving it, so hold onto it for later _driverInputsPendingPostUpdate.add(driverInput); if (!_pendingPostUpdate) { - unawaited(Simulator.postTick.first.then((value) { - // once the tick has completed, we can update the override maps - for (final driverInput in _driverInputsPendingPostUpdate) { - _inputToPreTickInputValuesMap[driverInput] = driverInput.value; - } - _driverInputsPendingPostUpdate.clear(); - _pendingPostUpdate = false; - })); + unawaited( + Simulator.postTick.first.then( + (value) { + // once the tick has completed, + // we can update the override maps + for (final driverInput in _driverInputsPendingPostUpdate) { + _inputToPreTickInputValuesMap[driverInput] = + driverInput.value; + } + _driverInputsPendingPostUpdate.clear(); + _pendingPostUpdate = false; + }, + ).catchError( + test: (error) => error is Exception, + // ignore: avoid_types_on_closure_parameters + (Object err, StackTrace stackTrace) { + Simulator.throwException(err as Exception, stackTrace); + }, + ), + ); } _pendingPostUpdate = true; } @@ -341,7 +355,13 @@ class Sequential extends _Always { // once the clocks are stable, execute the contents of the FF _execute(); _pendingExecute = false; - })); + }).catchError( + test: (error) => error is Exception, + // ignore: avoid_types_on_closure_parameters + (Object err, StackTrace stackTrace) { + Simulator.throwException(err as Exception, stackTrace); + }, + )); } _pendingExecute = true; }); @@ -370,21 +390,12 @@ class Sequential extends _Always { receiverOutput.put(LogicValue.x); } } else if (anyClkPosedge) { - final allDrivenSignals = []; + final allDrivenSignals = DuplicateDetectionSet(); for (final element in conditionals) { - allDrivenSignals.addAll(element.execute()); + element.execute(allDrivenSignals); } - if (allDrivenSignals.length != allDrivenSignals.toSet().length) { - final alreadySet = {}; - final redrivenSignals = {}; - for (final signal in allDrivenSignals) { - if (alreadySet.contains(signal)) { - redrivenSignals.add(signal); - } - alreadySet.add(signal); - } - throw Exception('Sequential drove the same signal(s) multiple times:' - ' $redrivenSignals.'); + if (allDrivenSignals.hasDuplicates) { + throw SignalRedrivenException(allDrivenSignals.duplicates.toString()); } } @@ -464,12 +475,14 @@ abstract class Conditional { Logic receiverOutput(Logic receiver) => _assignedReceiverToOutputMap[receiver]!; - /// Executes the functionality represented by this [Conditional]. + /// Executes the functionality of this [Conditional] and + /// populates [drivenSignals] with all [Logic]s that were driven + /// during execution. /// - /// Returns a [List] of all [Logic] signals which were driven during - /// execution. + /// The [drivenSignals] are used by the caller to determine if signals + /// were driven an appropriate number of times. @protected - Set execute(); + void execute(Set drivenSignals); /// Lists *all* receivers, recursively including all sub-[Conditional]s /// receivers. @@ -526,9 +539,12 @@ class ConditionalAssign extends Conditional { List getConditionals() => []; @override - Set execute() { + void execute(Set drivenSignals) { receiverOutput(receiver).put(driverValue(driver)); - return {receiver}; + + if (!drivenSignals.contains(receiver) || receiver.value.isValid) { + drivenSignals.add(receiver); + } } @override @@ -618,15 +634,16 @@ class Case extends Conditional { String get caseType => 'case'; @override - Set execute() { - final drivenLogics = {}; - + void execute(Set drivenSignals) { if (!expression.value.isValid) { // if expression has X or Z, then propogate X's! for (final receiver in getReceivers()) { receiverOutput(receiver).put(LogicValue.x); + if (!drivenSignals.contains(receiver) || receiver.value.isValid) { + drivenSignals.add(receiver); + } } - return {}; + return; } CaseItem? foundMatch; @@ -635,7 +652,7 @@ class Case extends Conditional { // match on the first matchinig item if (isMatch(item.value.value)) { for (final conditional in item.then) { - drivenLogics.addAll(conditional.execute()); + conditional.execute(drivenSignals); } if (foundMatch != null && conditionalType == ConditionalType.unique) { throw Exception('Unique case statement had multiple matching cases.' @@ -654,7 +671,7 @@ class Case extends Conditional { // no items matched if (foundMatch == null && defaultItem != null) { for (final conditional in defaultItem!) { - drivenLogics.addAll(conditional.execute()); + conditional.execute(drivenSignals); } } else if (foundMatch == null && (conditionalType == ConditionalType.unique || @@ -662,8 +679,6 @@ class Case extends Conditional { throw Exception('$conditionalType case statement had no matching case,' ' and type was $conditionalType.'); } - - return drivenLogics; } @override @@ -837,25 +852,25 @@ class IfBlock extends Conditional { IfBlock(this.iffs); @override - Set execute() { - final drivenLogics = {}; - + void execute(Set drivenSignals) { for (final iff in iffs) { if (driverValue(iff.condition)[0] == LogicValue.one) { for (final conditional in iff.then) { - drivenLogics.addAll(conditional.execute()); + conditional.execute(drivenSignals); } break; } else if (driverValue(iff.condition)[0] != LogicValue.zero) { // x and z propagation for (final receiver in getReceivers()) { receiverOutput(receiver).put(driverValue(iff.condition)[0]); + if (!drivenSignals.contains(receiver) || receiver.value.isValid) { + drivenSignals.add(receiver); + } } break; } // if it's 0, then continue searching down the path } - return drivenLogics; } @override @@ -970,23 +985,24 @@ class If extends Conditional { List getConditionals() => [...then, ...orElse]; @override - Set execute() { - final drivenLogics = {}; + void execute(Set drivenSignals) { if (driverValue(condition)[0] == LogicValue.one) { for (final conditional in then) { - drivenLogics.addAll(conditional.execute()); + conditional.execute(drivenSignals); } } else if (driverValue(condition)[0] == LogicValue.zero) { for (final conditional in orElse) { - drivenLogics.addAll(conditional.execute()); + conditional.execute(drivenSignals); } } else { // x and z propagation for (final receiver in getReceivers()) { receiverOutput(receiver).put(driverValue(condition)[0]); + if (!drivenSignals.contains(receiver) || receiver.value.isValid) { + drivenSignals.add(receiver); + } } } - return drivenLogics; } @override diff --git a/lib/src/simulator.dart b/lib/src/simulator.dart index 4950aa082..848241499 100644 --- a/lib/src/simulator.dart +++ b/lib/src/simulator.dart @@ -1,4 +1,4 @@ -/// Copyright (C) 2021 Intel Corporation +/// Copyright (C) 2021-2022 Intel Corporation /// SPDX-License-Identifier: BSD-3-Clause /// /// simulator.dart @@ -10,7 +10,9 @@ import 'dart:async'; import 'dart:collection'; + import 'package:logging/logging.dart'; + import 'package:rohd/rohd.dart'; /// An enum for the various phases of the [Simulator]. @@ -55,6 +57,9 @@ class Simulator { /// Tracks whether an end to the active simulation has been requested. static bool _simulationEndRequested = false; + /// Tracks for [_SimulatorException] that are thrown during the simulation. + static List<_SimulatorException> _simExceptions = []; + /// The maximum time the simulation can run. /// /// If set to -1 (the default), it means there is no maximum time limit. @@ -125,6 +130,9 @@ class Simulator { _currentTimestamp = 0; _simulationEndRequested = false; + + _simExceptions = []; + _maxSimTime = -1; if (!_preTickController.isClosed) { await _preTickController.close(); @@ -265,6 +273,14 @@ class Simulator { _simulationEndRequested = true; } + /// Collects an [exception] and associated [stackTrace] triggered + /// asynchronously during simulation to be thrown synchronously by [run]. + /// + /// Calling this function will end the simulation after this [tick] completes. + static void throwException(Exception exception, StackTrace stackTrace) { + _simExceptions.add(_SimulatorException(exception, stackTrace)); + } + /// Starts the simulation, executing all pending actions in time-order until /// it finishes or is stopped. static Future run() async { @@ -274,9 +290,15 @@ class Simulator { } while (hasStepsRemaining() && + _simExceptions.isEmpty && !_simulationEndRequested && (_maxSimTime < 0 || _currentTimestamp < _maxSimTime)) { - await tick(); // make this async so that await-ing events works + await tick(); + } + + for (final err in _simExceptions) { + logger.severe(err.exception.toString(), err.exception, err.stackTrace); + throw err.exception; } if (_currentTimestamp >= _maxSimTime && _maxSimTime > 0) { @@ -292,3 +314,15 @@ class Simulator { await simulationEnded; } } + +/// A simulator exception that produces object of exception and stack trace. +class _SimulatorException { + /// Tracks for [Exception] thrown during [Simulator] `run()`. + final Exception exception; + + /// Tracks for [StackTrace] thrown during [Simulator] `run()`. + final StackTrace stackTrace; + + /// Constructs a simulator exception, using [exception] and [stackTrace]. + _SimulatorException(this.exception, this.stackTrace); +} diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart index eeb226e59..263fadaaa 100644 --- a/lib/src/synthesizers/systemverilog.dart +++ b/lib/src/synthesizers/systemverilog.dart @@ -9,7 +9,7 @@ /// import 'package:rohd/rohd.dart'; -import 'package:rohd/src/utilities/traverseable_collection.dart'; +import 'package:rohd/src/collections/traverseable_collection.dart'; import 'package:rohd/src/utilities/uniquifier.dart'; /// A [Synthesizer] which generates equivalent SystemVerilog as the diff --git a/lib/src/utilities/simcompare.dart b/lib/src/utilities/simcompare.dart index c44d7d8fe..24ec5b6e0 100644 --- a/lib/src/utilities/simcompare.dart +++ b/lib/src/utilities/simcompare.dart @@ -14,6 +14,7 @@ import 'dart:async'; import 'dart:io'; import 'package:rohd/rohd.dart'; +import 'package:rohd/src/exceptions/exceptions.dart'; import 'package:test/test.dart'; /// Represents a single test case to check in a single clock cycle. @@ -107,15 +108,12 @@ abstract class SimCompare { for (final signalName in vector.expectedOutputValues.keys) { final value = vector.expectedOutputValues[signalName]; final o = module.output(signalName); + final errorReason = 'For vector #${vectors.indexOf(vector)} $vector,' ' expected $o to be $value, but it was ${o.value}.'; if (value is int) { - if (!o.value.isValid) { - // invalid value causes exception without helpful message, - // so throw it - throw Exception(errorReason); - } + expect(o.value.isValid, isTrue, reason: errorReason); expect(o.value.toInt(), equals(value), reason: errorReason); } else if (value is LogicValue) { if (o.width > 1 && @@ -127,11 +125,16 @@ abstract class SimCompare { expect(o.value, equals(value)); } } else { - throw Exception( - 'Value type ${value.runtimeType} is not supported (yet?)'); + throw NonSupportedTypeException(value.runtimeType.toString()); } } - }); + }).catchError( + test: (error) => error is Exception, + // ignore: avoid_types_on_closure_parameters + (Object err, StackTrace stackTrace) { + Simulator.throwException(err as Exception, stackTrace); + }, + ); } }); timestamp += Vector._period; diff --git a/test/conditionals_test.dart b/test/conditionals_test.dart index e53c113aa..468946514 100644 --- a/test/conditionals_test.dart +++ b/test/conditionals_test.dart @@ -8,6 +8,8 @@ /// Author: Max Korbel /// import 'package:rohd/rohd.dart'; +import 'package:rohd/src/exceptions/conditionals/conditional_exceptions.dart'; +import 'package:rohd/src/exceptions/sim_compare/sim_compare_exceptions.dart'; import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; @@ -217,6 +219,42 @@ class SingleIfOrElseModule extends Module { } } +class SignalRedrivenSequentialModule extends Module { + SignalRedrivenSequentialModule(Logic a, Logic b, Logic d) + : super(name: 'ffmodule') { + a = addInput('a', a); + b = addInput('b', b); + + final q = addOutput('q', width: d.width); + d = addInput('d', d, width: d.width); + + final k = addOutput('k', width: 8); + Sequential(SimpleClockGenerator(10).clk, [ + If(a, then: [ + k < k, + q < k, + q < d, + ]) + ]); + } +} + +class SignalRedrivenSequentialModuleWithX extends Module { + SignalRedrivenSequentialModuleWithX(Logic a, Logic c, Logic d) + : super(name: 'redrivenwithvalidinvalidsignal') { + a = addInput('a', a); + c = addInput('c', c); + d = addInput('d', d); + + final b = addOutput('b'); + + Sequential(SimpleClockGenerator(10).clk, [ + If(a, then: [b < c]), + If(d, then: [b < c]) + ]); + } +} + void main() { tearDown(Simulator.reset); @@ -335,36 +373,90 @@ void main() { signalToWidthMap: {'d': 8, 'q': 8}); expect(simResult, equals(true)); }); + }); - test( - 'should return true on simcompare when ' - 'execute if.s() for single if...else conditional without orElse.', - () async { - final mod = SingleIfModule(Logic()); - await mod.build(); - final vectors = [ - Vector({'a': 1}, {'q': 1}), - ]; + test( + 'should return true on simcompare when ' + 'execute if.s() for single if...else conditional without orElse.', + () async { + final mod = SingleIfModule(Logic()); + await mod.build(); + final vectors = [ + Vector({'a': 1}, {'q': 1}), + ]; + await SimCompare.checkFunctionalVector(mod, vectors); + final simResult = SimCompare.iverilogVector( + mod.generateSynth(), mod.runtimeType.toString(), vectors); + expect(simResult, equals(true)); + }); + + test( + 'should return true on simcompare when ' + 'execute if.s() for single if...else conditional with orElse.', () async { + final mod = SingleIfOrElseModule(Logic(), Logic()); + await mod.build(); + final vectors = [ + Vector({'a': 1}, {'q': 1}), + Vector({'a': 0}, {'x': 1}), + ]; + await SimCompare.checkFunctionalVector(mod, vectors); + final simResult = SimCompare.iverilogVector( + mod.generateSynth(), mod.runtimeType.toString(), vectors); + expect(simResult, equals(true)); + }); + + test( + 'should return SignalRedrivenException when there are multiple drivers ' + 'for a flop.', () async { + final mod = + SignalRedrivenSequentialModule(Logic(), Logic(), Logic(width: 8)); + await mod.build(); + final vectors = [ + Vector({'a': 1, 'd': 1}, {}), + Vector({'a': 0, 'b': 0, 'd': 2}, {'q': 1}), + ]; + + try { await SimCompare.checkFunctionalVector(mod, vectors); - final simResult = SimCompare.iverilogVector( - mod.generateSynth(), mod.runtimeType.toString(), vectors); - expect(simResult, equals(true)); - }); + fail('Exception not thrown!'); + } on Exception catch (e) { + expect(e.runtimeType, equals(SignalRedrivenException)); + } + }); - test( - 'should return true on simcompare when ' - 'execute if.s() for single if...else conditional with orElse.', - () async { - final mod = SingleIfOrElseModule(Logic(), Logic()); - await mod.build(); - final vectors = [ - Vector({'a': 1}, {'q': 1}), - Vector({'a': 0}, {'x': 1}), - ]; + test( + 'should return NonSupportedTypeException when ' + 'simcompare expected output values has invalid runtime type. ', () async { + final mod = SequentialModule(Logic(), Logic(), Logic(width: 8)); + await mod.build(); + final vectors = [ + Vector({'a': 1, 'd': 1}, {}), + Vector({'a': 0, 'b': 0, 'd': 2}, {'q': 'invalid runtime type'}), + ]; + + try { await SimCompare.checkFunctionalVector(mod, vectors); - final simResult = SimCompare.iverilogVector( - mod.generateSynth(), mod.runtimeType.toString(), vectors); - expect(simResult, equals(true)); - }); + fail('Exception not thrown!'); + } on Exception catch (e) { + expect(e.runtimeType, equals(NonSupportedTypeException)); + } + }); + + test( + 'should return SignalRedrivenException when driven with ' + 'x signals and valid signals.', () async { + final mod = SignalRedrivenSequentialModuleWithX(Logic(), Logic(), Logic()); + await mod.build(); + final vectors = [ + Vector({'a': LogicValue.x, 'd': 1, 'c': 1}, {'b': LogicValue.z}), + Vector({'a': 1, 'd': 1, 'c': 1}, {'b': 1}), + ]; + + try { + await SimCompare.checkFunctionalVector(mod, vectors); + fail('Exception not thrown!'); + } on Exception catch (e) { + expect(e.runtimeType, equals(SignalRedrivenException)); + } }); } diff --git a/test/definition_name_test.dart b/test/definition_name_test.dart index 080a0a222..ddd269f0b 100644 --- a/test/definition_name_test.dart +++ b/test/definition_name_test.dart @@ -8,7 +8,7 @@ /// Author: Yao Jing Quek /// import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/name_exceptions.dart'; +import 'package:rohd/src/exceptions/name/name_exceptions.dart'; import 'package:test/test.dart'; class DefinitionName { diff --git a/test/duplicate_detection_set_test.dart b/test/duplicate_detection_set_test.dart new file mode 100644 index 000000000..804bac652 --- /dev/null +++ b/test/duplicate_detection_set_test.dart @@ -0,0 +1,50 @@ +/// Copyright (C) 2022 Intel Corporation +/// SPDX-License-Identifier: BSD-3-Clause +/// +/// duplicate_detection_set_test.dart +/// Unit tests for DuplicateDetectionSet +/// +/// 2022 November 11 +/// Author: Yao Jing Quek +/// + +import 'package:rohd/src/collections/duplicate_detection_set.dart'; +import 'package:test/test.dart'; + +void main() { + group('Add Function: ', () { + test( + 'should return duplicates if duplicate value exists ' + 'through addAll method', () async { + final testDuplicateSet = DuplicateDetectionSet()..addAll([1, 2, 1]); + + expect(testDuplicateSet.hasDuplicates, equals(true)); + expect(testDuplicateSet.duplicates, equals({1})); + }); + test( + 'should return duplicates if duplicate value exists through add method', + () async { + final testDuplicateSet = DuplicateDetectionSet() + ..addAll([1, 2, 3]) + ..add(1); + + expect(testDuplicateSet.hasDuplicates, equals(true)); + expect(testDuplicateSet.duplicates, equals({1})); + }); + }); + + group('remove function: ', () { + test('should return value if removed value are not duplicate', () async { + final testDuplicateSet = DuplicateDetectionSet()..addAll([3, 1, 2]); + expect(testDuplicateSet.remove(1), equals(true)); + }); + test('should return exception if removed value that are duplicate', + () async { + final testDuplicateSet = DuplicateDetectionSet() + ..addAll([3, 1, 2, 1]); + expect(() { + testDuplicateSet.remove(1); + }, throwsException); + }); + }); +}