Skip to content

Commit

Permalink
Fix combinational sensitivity excessive pessimism, fix #233 (#240)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkorbel1 authored Jan 5, 2023
1 parent 11b411d commit 27ee267
Show file tree
Hide file tree
Showing 14 changed files with 349 additions and 52 deletions.
10 changes: 3 additions & 7 deletions lib/src/exceptions/conditionals/signal_redriven_exception.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,16 @@
/// Author: Yao Jing Quek <yao.jing.quek@intel.com>
import 'package:rohd/rohd.dart';
import 'package:rohd/src/exceptions/rohd_exception.dart';

/// An exception that thrown when a [Logic] signal is
/// operated multiple times.
class SignalRedrivenException implements Exception {
late final String _message;

class SignalRedrivenException extends RohdException {
/// 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;
: super(message + signals);
}
1 change: 1 addition & 0 deletions lib/src/exceptions/exceptions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
/// SPDX-License-Identifier: BSD-3-Clause
export './conditionals/conditional_exceptions.dart';
export './module/module_exceptions.dart';
export './name/name_exceptions.dart';
export './sim_compare/sim_compare_exceptions.dart';
4 changes: 4 additions & 0 deletions lib/src/exceptions/module/module_exceptions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/// Copyright (C) 2022 Intel Corporation
/// SPDX-License-Identifier: BSD-3-Clause
export 'module_not_built_exception.dart';
21 changes: 21 additions & 0 deletions lib/src/exceptions/module/module_not_built_exception.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/// Copyright (C) 2022 Intel Corporation
/// SPDX-License-Identifier: BSD-3-Clause
///
/// module_not_build_exception.dart
/// Definition for exception when module is not built
///
/// 2022 December 30
/// Author: Max Korbel <max.korbel@intel.com>
///
import 'package:rohd/rohd.dart';
import 'package:rohd/src/exceptions/rohd_exception.dart';

/// An [Exception] thrown when a [Module] was used in a way that required it
/// to be built first, but it was not yet built.
class ModuleNotBuiltException extends RohdException {
/// Constructs a new [Exception] for when a [Module] should have been built
/// before some action was taken.
ModuleNotBuiltException(
[super.message = 'Module has not yet built! Must call build() first.']);
}
14 changes: 5 additions & 9 deletions lib/src/exceptions/name/invalid_reserved_name_exception.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,15 @@
/// Author: Yao Jing Quek <yao.jing.quek@intel.com>
///
/// An exception that thrown when a reserved name is invalid.
class InvalidReservedNameException implements Exception {
late final String _message;
import 'package:rohd/src/exceptions/rohd_exception.dart';

/// An exception that thrown when a reserved name is invalid.
class InvalidReservedNameException extends RohdException {
/// 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 '
[super.message = 'Reserved Name need to follow proper naming '
'convention if reserved'
' name set to true'])
: _message = message;

@override
String toString() => _message;
' name set to true']);
}
14 changes: 5 additions & 9 deletions lib/src/exceptions/name/null_reserved_name_exception.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,14 @@
/// Author: Yao Jing Quek <yao.jing.quek@intel.com>
///
/// An exception that thrown when a reserved name is `null`.
class NullReservedNameException implements Exception {
late final String _message;
import 'package:rohd/src/exceptions/rohd_exception.dart';

/// An exception that thrown when a reserved name is `null`.
class NullReservedNameException extends RohdException {
/// 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;
[super.message = 'Reserved Name cannot be null '
'if reserved name set to true']);
}
21 changes: 21 additions & 0 deletions lib/src/exceptions/rohd_exception.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/// Copyright (C) 2022 Intel Corporation
/// SPDX-License-Identifier: BSD-3-Clause
///
/// rohd_exception.dart
/// Base class for all ROHD exceptions
///
/// 2022 December 30
/// Author: Max Korbel <max.korbel@intel.com>
///
/// A base type of exception that ROHD-specific exceptions inherit from.
abstract class RohdException implements Exception {
/// A description of what this exception means.
final String message;

/// Creates a new exception with description [message].
RohdException(this.message);

@override
String toString() => message;
}
10 changes: 3 additions & 7 deletions lib/src/exceptions/sim_compare/non_supported_type_exception.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,17 @@
/// Author: Yao Jing Quek <yao.jing.quek@intel.com>
///
import 'package:rohd/src/exceptions/rohd_exception.dart';
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;

class NonSupportedTypeException extends RohdException {
/// 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;
: super(message + vector.runtimeType.toString());
}
127 changes: 124 additions & 3 deletions lib/src/module.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import 'package:collection/collection.dart';
import 'package:meta/meta.dart';

import 'package:rohd/rohd.dart';
import 'package:rohd/src/collections/traverseable_collection.dart';
import 'package:rohd/src/exceptions/module/module_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';
Expand Down Expand Up @@ -121,7 +123,8 @@ abstract class Module {
/// unique name within its scope.
String get uniqueInstanceName => hasBuilt || reserveName
? _uniqueInstanceName
: throw Exception('Module must be built to access uniquified name.'
: throw ModuleNotBuiltException(
'Module must be built to access uniquified name.'
' Call build() before accessing this.');
String _uniqueInstanceName;

Expand Down Expand Up @@ -188,7 +191,8 @@ abstract class Module {
/// Only returns valid information after [build].
Iterable<Module> hierarchy() {
if (!hasBuilt) {
throw Exception('Module must be built before accessing hierarchy.'
throw ModuleNotBuiltException(
'Module must be built before accessing hierarchy.'
' Call build() before executing this.');
}
Module? pModule = this;
Expand Down Expand Up @@ -224,6 +228,9 @@ abstract class Module {
/// starting up interactions with independent processes (e.g. cosimulation).
///
/// This function should only be called one time per [Module].
///
/// The hierarchy is built "bottom-up", so leaf-level [Module]s are built
/// before the [Module]s which contain them.
@mustCallSuper
Future<void> build() async {
if (hasBuilt) {
Expand Down Expand Up @@ -252,6 +259,120 @@ abstract class Module {
_hasBuilt = true;
}

/// A mapping of purely combinational paths from each input port to all
/// downstream output ports.
///
/// Each key of the returned [Map] is an [input] of this [Module]. Each
/// value of the [Map] is a [List] of [output]s of this [Module] which may
/// change combinationally (no sequential logic in-between) as a result
/// of the corresponding key [input] changing.
///
/// This is the stored result from calling [getCombinationalPaths] at [build]
/// time. The module should be built before calling this (or call it itself)
/// or else it may cache an incomplete picture.
Map<Logic, List<Logic>> get combinationalPaths =>
_combinationalPaths ??= _getCombinationalPaths();

/// Internal cache storage of [combinationalPaths].
Map<Logic, List<Logic>>? _combinationalPaths;

/// Returns a mapping of purely combinational paths from each input port
/// to all downstream output ports.
///
/// Each key of the returned [Map] is an [input] of this [Module]. Each
/// value of the [Map] is a [List] of [output]s of this [Module] which may
/// change combinationally (no sequential logic in-between) as a result
/// of the corresponding key [input] changing.
///
/// The default behavior of this function is to search through from all
/// inputs to all potential outputs. If a [Module] implements custom behavior
/// internally (e.g. a custom gate or a cosimulated module), then it makes
/// sense to override this function to give an accurate picture. If the
/// default behavior doesn't work (because no visible connectivity exists
/// inside the [Module]), then the return value will end up with all empty
/// [List]s in the values of the [Map].
///
/// The result of this function is intended to be stored at [build] time, and
/// it should be called at [build] time. The result is primarily used for
/// calculating valid and complete sensitivity lists for [Combinational]
/// execution.
@protected
Map<Logic, List<Logic>> getCombinationalPaths() {
final comboPaths = <Logic, List<Logic>>{};
for (final inputPort in inputs.values) {
final comboOutputs = <Logic>[];
final searchList = TraverseableCollection<Logic>()..add(inputPort);
for (var i = 0; i < searchList.length; i++) {
for (final dstConnection in inputPort.dstConnections) {
if (dstConnection.isInput && dstConnection.parentModule != this) {
// this is an input port of a sub-module, jump over it
searchList.addAll(
dstConnection.parentModule!.combinationalPaths[dstConnection]!);
} else if (isOutput(dstConnection)) {
// this is an output port of this module, store it!
comboOutputs.add(dstConnection);
} else {
// this is a wire within this module, keep tracing
searchList.addAll(dstConnection.dstConnections);
}
}
}
comboPaths[inputPort] = comboOutputs;
}
return comboPaths;
}

/// Returns the value of [getCombinationalPaths] wrapped safely with
/// unmodifiable views for caching.
Map<Logic, List<Logic>> _getCombinationalPaths() {
final initialComboPaths = getCombinationalPaths();
return UnmodifiableMapView(
Map.fromEntries(inputs.values.map((inputPort) => MapEntry(
inputPort,
initialComboPaths.containsKey(inputPort)
? UnmodifiableListView(initialComboPaths[inputPort]!)
: const <Logic>[],
))));
}

/// The opposite of [combinationalPaths], where every key of the [Map] is an
/// output and the values are lists of inputs which could combinationally
/// affect that output.
///
/// This module must be built before calling this.
Map<Logic, List<Logic>> get reverseCombinationalPaths =>
_reverseCombinationalPaths ??= _getReverseCombinationalPaths();

/// Internal storage of [reverseCombinationalPaths], cached.
Map<Logic, List<Logic>>? _reverseCombinationalPaths;

/// Calculates the opposite of [combinationalPaths].
Map<Logic, List<Logic>> _getReverseCombinationalPaths() {
if (!_hasBuilt) {
throw ModuleNotBuiltException();
}

assert(_reverseCombinationalPaths == null,
'Should not recreate if already cached result.');

final reverseComboPaths = <Logic, List<Logic>>{};
for (final inputPort in combinationalPaths.keys) {
for (final outputPort in combinationalPaths[inputPort]!) {
reverseComboPaths
.putIfAbsent(outputPort, () => <Logic>[])
.add(inputPort);
}
}

return UnmodifiableMapView(
Map.fromEntries(outputs.values.map((outputPort) => MapEntry(
outputPort,
reverseComboPaths.containsKey(outputPort)
? UnmodifiableListView(reverseComboPaths[outputPort]!)
: const <Logic>[],
))));
}

/// Adds a [Module] to this as a subModule.
Future<void> _addAndBuildModule(Module module) async {
if (module.parent != null) {
Expand Down Expand Up @@ -496,7 +617,7 @@ abstract class Module {
/// may have other output formats, languages, files, etc.
String generateSynth() {
if (!_hasBuilt) {
throw Exception('Module has not yet built! Must call build() first.');
throw ModuleNotBuiltException();
}

final synthHeader = '''
Expand Down
4 changes: 2 additions & 2 deletions lib/src/modules/bus.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import 'package:rohd/rohd.dart';
///
/// The returned signal is inclusive of both the [startIndex] and [endIndex].
/// The output [subset] will have width equal to `|endIndex - startIndex| + 1`.
class BusSubset extends Module with InlineSystemVerilog {
class BusSubset extends Module with InlineSystemVerilog, FullyCombinational {
/// Name for the input port of this module.
late final String _original;

Expand Down Expand Up @@ -127,7 +127,7 @@ class BusSubset extends Module with InlineSystemVerilog {
///
/// You can use convenience functions [swizzle()] or [rswizzle()] to more easily
/// use this [Module].
class Swizzle extends Module with InlineSystemVerilog {
class Swizzle extends Module with InlineSystemVerilog, FullyCombinational {
final String _out = Module.unpreferredName('swizzled');

/// The output port containing concatenated signals.
Expand Down
Loading

0 comments on commit 27ee267

Please sign in to comment.