Repository: intel/rohd
Pull request #200 (2022-11-16 06:06:57)

[Issue-112] Automatically extract rohd version from pubspec to add into generated sv
<!-- Please make sure you check out the contribution guidelines before submitting a pull request! -->

## Description & Motivation

It would be nice if the generated outputs (e.g. SystemVerilog, waveform dumps) had a guaranteed accurate version number of ROHD that generated them. A version number (from pubspec.yaml) would be good, but even better would be to also include a git hash, so it can be determined exactly what generated the code.

## Related Issue(s)

issue #112 

## Testing
Add test to expect the rohd version and git commit are available in the generated system verilog code generation.

## Backwards-compatibility

> Is this a breaking change that will not be backwards-compatible?  If yes, how so?

no.

## Documentation

> Does the change require any updates to documentation?  If so, where?  Are they included?

Ya, added to each of the file created.



Patch:
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 8e0e70e0..f0fb86f2 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -34,7 +34,7 @@ linter:
     - avoid_function_literals_in_foreach_calls
     - avoid_implementing_value_types
     - avoid_init_to_null
-    - avoid_js_rounded_ints
+    # - avoid_js_rounded_ints
     - avoid_multiple_declarations_per_line
     - avoid_null_checks_in_equality_operators
     - avoid_positional_boolean_parameters
diff --git a/benchmark/benchmark.dart b/benchmark/benchmark.dart
index add1d3ed..5066fa56 100644
--- a/benchmark/benchmark.dart
+++ b/benchmark/benchmark.dart
@@ -8,8 +8,12 @@
 /// Author: Max Korbel <max.korbel@intel.com>
 ///
 
+import 'byte_enable_benchmark.dart';
+import 'logic_value_of_benchmark.dart';
 import 'pipeline_benchmark.dart';
 
 void main() async {
   await PipelineBenchmark().report();
+  LogicValueOfBenchmark().report();
+  ByteEnableBenchmark().report();
 }
diff --git a/benchmark/benchmarks.dart b/benchmark/benchmarks.dart
deleted file mode 100644
index 13de7e7a..00000000
--- a/benchmark/benchmarks.dart
+++ /dev/null
@@ -1,4 +0,0 @@
-/// Copyright (C) 2022 Intel Corporation
-/// SPDX-License-Identifier: BSD-3-Clause
-
-export 'pipeline_benchmark.dart';
diff --git a/benchmark/byte_enable_benchmark.dart b/benchmark/byte_enable_benchmark.dart
new file mode 100644
index 00000000..10a2c85b
--- /dev/null
+++ b/benchmark/byte_enable_benchmark.dart
@@ -0,0 +1,54 @@
+/// Copyright (C) 2022 Intel Corporation
+/// SPDX-License-Identifier: BSD-3-Clause
+///
+/// byte_enable_benchmark.dart
+/// Benchmarking for simple byte enable hardware
+///
+/// 2022 December 2
+/// Author: Max Korbel <max.korbel@intel.com>
+///
+
+import 'package:benchmark_harness/benchmark_harness.dart';
+import 'package:rohd/rohd.dart';
+
+class ByteEnableBenchmark extends BenchmarkBase {
+  late Logic result;
+  late Logic select;
+  late Logic original;
+
+  static const int numBytes = 128;
+
+  final List<LogicValue> vectors = List.generate(
+      numBytes,
+      (index) => LogicValue.ofString(
+          (index.toRadixString(2) * numBytes).substring(0, numBytes)));
+
+  ByteEnableBenchmark() : super('ByteEnable');
+
+  @override
+  void setup() {
+    select = Logic(name: 'select', width: numBytes);
+    original = Logic(name: 'original', width: numBytes * 8);
+    original.put(LogicValue.ofString('0x1z10xz' * numBytes));
+    select.put(0);
+    result = List.generate(
+        numBytes,
+        (index) => mux(
+              select[index],
+              original.getRange(index * 8, (index + 1) * 8),
+              Const(0, width: 8),
+            )).swizzle();
+  }
+
+  @override
+  void teardown() {}
+
+  @override
+  void run() {
+    vectors.forEach(select.put);
+  }
+}
+
+void main() {
+  ByteEnableBenchmark().report();
+}
diff --git a/benchmark/logic_value_of_benchmark.dart b/benchmark/logic_value_of_benchmark.dart
new file mode 100644
index 00000000..9d7e57e1
--- /dev/null
+++ b/benchmark/logic_value_of_benchmark.dart
@@ -0,0 +1,46 @@
+/// Copyright (C) 2022 Intel Corporation
+/// SPDX-License-Identifier: BSD-3-Clause
+///
+/// logic_value_of_benchmark.dart
+/// Benchmarking for concatenation of values together into one.
+///
+/// 2022 December 1
+/// Author: Max Korbel <max.korbel@intel.com>
+///
+
+import 'dart:math';
+
+import 'package:benchmark_harness/benchmark_harness.dart';
+import 'package:rohd/rohd.dart';
+
+class LogicValueOfBenchmark extends BenchmarkBase {
+  late List<LogicValue> toOf;
+
+  LogicValueOfBenchmark() : super('LogicValueOf');
+
+  @override
+  void setup() {
+    final rand = Random(1234);
+    toOf = List.generate(
+        1000,
+        (index) => LogicValue.ofString(([
+              '0' * rand.nextInt(10),
+              '1' * rand.nextInt(10),
+              'x' * rand.nextInt(10),
+              'z' * rand.nextInt(10),
+            ]..shuffle(rand))
+                .join()));
+  }
+
+  @override
+  void teardown() {}
+
+  @override
+  void run() {
+    LogicValue.of(toOf);
+  }
+}
+
+void main() async {
+  LogicValueOfBenchmark().report();
+}
diff --git a/lib/src/logic.dart b/lib/src/logic.dart
index 474510c7..f5b00436 100644
--- a/lib/src/logic.dart
+++ b/lib/src/logic.dart
@@ -26,7 +26,7 @@ class LogicValueChanged {
 
   /// Represents the event of a [Logic] changing value from [previousValue]
   /// to [newValue].
-  LogicValueChanged(this.newValue, this.previousValue);
+  const LogicValueChanged(this.newValue, this.previousValue);
 
   @override
   String toString() => '$previousValue  -->  $newValue';
@@ -49,12 +49,245 @@ class Const extends Logic {
   }
 }
 
+/// 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<LogicValueChanged> get glitch => _glitchController.emitter;
+  final SynchronousPropagator<LogicValueChanged> _glitchController =
+      SynchronousPropagator<LogicValueChanged>();
+
+  /// Controller for stable events that can be safely consumed at the
+  /// end of a [Simulator] tick.
+  final StreamController<LogicValueChanged> _changedController =
+      StreamController<LogicValueChanged>.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<LogicValueChanged> 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<void> _preTickSubscription;
+
+  /// The subscription to the [Simulator]'s `postTick`.
+  ///
+  /// Only non-null if [_changedBeingWatched] is true.
+  late final StreamSubscription<void> _postTickSubscription;
+
+  /// Cancels all [Simulator] subscriptions and uses [newChanged] as the
+  /// source to replace all [changed] events for this [_Wire].
+  void _migrateChangedTriggers(Stream<LogicValueChanged> 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<LogicValueChanged>? _negedge;
+
+  /// Store the [posedge] stream to avoid creating multiple copies
+  /// of streams.
+  Stream<LogicValueChanged>? _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<LogicValueChanged> 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<LogicValueChanged> 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<LogicValueChanged> 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<LogicValueChanged> 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<LogicValueChanged> 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, {bool fill = false}) {
+    Simulator.injectAction(() => put(val, fill: fill));
+  }
+
+  /// Keeps track of whether there is an active put, to detect reentrance.
+  bool _isPutting = false;
+
+  /// 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].
+  ///
+  /// 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, {bool fill = false}) {
+    LogicValue newValue;
+    if (val is int) {
+      if (fill) {
+        newValue = LogicValue.filled(
+            width,
+            val == 0
+                ? LogicValue.zero
+                : val == 1
+                    ? LogicValue.one
+                    : throw Exception('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 Exception('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 Exception(
+            'Failed to fill value with $val.  To fill, it should be 1 bit.');
+      } else {
+        newValue = val;
+      }
+    } else {
+      throw Exception('Unrecognized value "$val" to deposit on this signal. '
+          'Unknown type ${val.runtimeType} cannot be deposited.');
+    }
+
+    if (newValue.width != width) {
+      throw Exception(
+          '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.
   static int _signalIdx = 0;
 
-  // special quiet flag to prevent <= and < where inappropriate
+  // A special quiet flag to prevent `<=` and `<` where inappropriate
   bool _unassignable = false;
 
   /// Makes it so that this signal cannot be assigned by any full (`<=`) or
@@ -64,19 +297,16 @@ class Logic {
   /// The name of this signal.
   final String name;
 
-  /// The current active value of this signal.
-  LogicValue _currentValue;
-
-  /// The last value of this signal before the [Simulator] tick.
+  /// The [_Wire] which holds the current value and listeners for this [Logic].
   ///
-  /// This is useful for detecting when to trigger an edge.
-  LogicValue? _preTickValue;
+  /// May be a shared object between multiple [Logic]s.
+  _Wire _wire;
 
   /// The number of bits in this signal.
-  final int width;
+  int get width => _wire.width;
 
   /// The current active value of this signal.
-  LogicValue get value => _currentValue;
+  LogicValue get value => _wire._currentValue;
 
   /// The current active value of this signal if it has width 1, as
   /// a [LogicValue].
@@ -84,7 +314,7 @@ class Logic {
   /// Throws an Exception if width is not 1.
   @Deprecated('Use `value` instead.'
       '  Check `width` separately to confirm single-bit.')
-  LogicValue get bit => _currentValue.bit;
+  LogicValue get bit => value.bit;
 
   /// The current valid active value of this signal as an [int].
   ///
@@ -100,7 +330,7 @@ class Logic {
   BigInt get valueBigInt => value.toBigInt();
 
   /// Returns `true` iff the value of this signal is valid (no `x` or `z`).
-  bool hasValidValue() => _currentValue.isValid;
+  bool hasValidValue() => value.isValid;
 
   /// Returns `true` iff *all* bits of the current value are floating (`z`).
   bool isFloating() => value.isFloating;
@@ -122,65 +352,41 @@ class Logic {
   /// 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<LogicValueChanged> get glitch => _glitchController.emitter;
-  final SynchronousPropagator<LogicValueChanged> _glitchController =
-      SynchronousPropagator<LogicValueChanged>();
-
-  /// Controller for stable events that can be safely consumed at the
-  /// end of a [Simulator] tick.
-  final StreamController<LogicValueChanged> _changedController =
-      StreamController<LogicValueChanged>.broadcast(sync: true);
-
-  /// Tracks whether is being subscribed to by anything/anyone.
-  bool _changedBeingWatched = false;
+  SynchronousEmitter<LogicValueChanged> get glitch => _wire.glitch;
 
   /// A [Stream] of [LogicValueChanged] events which triggers at most once
   /// per [Simulator] tick, iff the value of the [Logic] has changed.
-  Stream<LogicValueChanged> get changed {
-    if (!_changedBeingWatched) {
-      // only do these simulator subscriptions if someone has asked for
-      // them! saves performance!
-      _changedBeingWatched = true;
-
-      Simulator.preTick.listen((event) {
-        _preTickValue = value;
-      });
-      Simulator.postTick.listen((event) {
-        if (value != _preTickValue && _preTickValue != null) {
-          _changedController.add(LogicValueChanged(value, _preTickValue!));
-        }
-      });
-    }
-    return _changedController.stream;
-  }
+  Stream<LogicValueChanged> get changed => _wire.changed;
 
   /// 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`.
-  Stream<LogicValueChanged> get negedge => changed.where((args) =>
-      width == 1 &&
-      LogicValue.isNegedge(args.previousValue[0], args.newValue[0],
-          ignoreInvalid: true));
+  ///
+  /// Throws an exception if [width] is not `1`.
+  Stream<LogicValueChanged> get negedge => _wire.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`.
-  Stream<LogicValueChanged> get posedge => changed.where((args) =>
-      width == 1 &&
-      LogicValue.isPosedge(args.previousValue[0], args.newValue[0],
-          ignoreInvalid: true));
+  ///
+  /// Throws an exception if [width] is not `1`.
+  Stream<LogicValueChanged> get posedge => _wire.posedge;
 
   /// Triggers at most once, the next time that this [Logic] changes
   /// value at the end of a [Simulator] tick.
-  Future<LogicValueChanged> get nextChanged => changed.first;
+  Future<LogicValueChanged> get nextChanged => _wire.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`.
-  Future<LogicValueChanged> get nextPosedge => posedge.first;
+  ///
+  /// Throws an exception if [width] is not `1`.
+  Future<LogicValueChanged> get nextPosedge => _wire.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`.
-  Future<LogicValueChanged> get nextNegedge => negedge.first;
+  ///
+  /// Throws an exception if [width] is not `1`.
+  Future<LogicValueChanged> get nextNegedge => _wire.negedge.first;
 
   /// The [Module] that this [Logic] exists within.
   ///
@@ -218,9 +424,9 @@ class Logic {
   ///
   /// The default value for [width] is 1.  The [name] should be synthesizable
   /// to the desired output (e.g. SystemVerilog).
-  Logic({String? name, this.width = 1})
+  Logic({String? name, int width = 1})
       : name = name == null ? 's${_signalIdx++}' : Sanitizer.sanitizeSV(name),
-        _currentValue = LogicValue.filled(width, LogicValue.z) {
+        _wire = _Wire(width: width) {
     if (width < 0) {
       throw Exception('Logic width must be greater than or equal to 0.');
     }
@@ -243,6 +449,24 @@ class Logic {
     }
   }
 
+  /// Injects a value onto this signal in the current [Simulator] tick.
+  ///
+  /// This function calls [put()] in [Simulator.injectAction()].
+  void inject(dynamic val, {bool fill = false}) =>
+      _wire.inject(val, fill: fill);
+
+  /// 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].
+  ///
+  /// 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, {bool fill = false}) => _wire.put(val, fill: fill);
+
   /// Connects this [Logic] directly to [other].
   ///
   /// Every time [other] transitions (`glitch`es), this signal will transition
@@ -258,19 +482,19 @@ class Logic {
 
   /// Handles the actual connection of this [Logic] to [other].
   void _connect(Logic other) {
-    if (other.width != width) {
-      throw Exception('Bus widths must match.'
-          ' Cannot connect $this to $other which have different widths.');
-    }
-
     _unassignable = true;
+    _updateWire(other._wire);
+  }
 
-    if (value != other.value) {
-      put(other.value);
+  /// Updates the current active [_Wire] for this [Logic] and also
+  /// notifies all downstream [Logic]s of the new source [_Wire].
+  void _updateWire(_Wire newWire) {
+    newWire._adopt(_wire);
+    _wire = newWire;
+
+    for (final dstConnection in dstConnections) {
+      dstConnection._updateWire(newWire);
     }
-    other.glitch.listen((args) {
-      put(other.value);
-    });
   }
 
   /// Connects this [Logic] directly to another [Logic].
@@ -311,16 +535,16 @@ class Logic {
   Logic operator /(dynamic other) => Divide(this, other).out;
 
   /// Modulo operation.
-  Logic operator %(Logic other) => Modulo(this, other).out;
+  Logic operator %(dynamic other) => Modulo(this, other).out;
 
   /// Arithmetic right-shift.
-  Logic operator >>(Logic other) => ARShift(this, other).out;
+  Logic operator >>(dynamic other) => ARShift(this, other).out;
 
   /// Logical left-shift.
-  Logic operator <<(Logic other) => LShift(this, other).out;
+  Logic operator <<(dynamic other) => LShift(this, other).out;
 
   /// Logical right-shift.
-  Logic operator >>>(Logic other) => RShift(this, other).out;
+  Logic operator >>>(dynamic other) => RShift(this, other).out;
 
   /// Unary AND.
   Logic and() => AndUnary(this).out;
@@ -365,90 +589,6 @@ class Logic {
     }
   }
 
-  /// Injects a value onto this signal in the current [Simulator] tick.
-  ///
-  /// This function calls [put()] in [Simulator.injectAction()].
-  void inject(dynamic val, {bool fill = false}) {
-    Simulator.injectAction(() => put(val, fill: fill));
-  }
-
-  /// Keeps track of whether there is an active put, to detect reentrance.
-  bool _isPutting = false;
-
-  /// 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].
-  ///
-  /// 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, {bool fill = false}) {
-    LogicValue newValue;
-    if (val is int) {
-      if (fill) {
-        newValue = LogicValue.filled(
-            width,
-            val == 0
-                ? LogicValue.zero
-                : val == 1
-                    ? LogicValue.one
-                    : throw Exception('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 Exception('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 Exception(
-            'Failed to fill value with $val.  To fill, it should be 1 bit.');
-      } else {
-        newValue = val;
-      }
-    } else {
-      throw Exception('Unrecognized value "$val" to deposit on this signal. '
-          'Unknown type ${val.runtimeType} cannot be deposited.');
-    }
-
-    if (newValue.width != width) {
-      throw Exception(
-          '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;
-    }
-  }
-
   /// Accesses the [index]th bit of this signal.
   ///
   /// Accepts both [int] and [Logic] as [index].
@@ -574,18 +714,19 @@ class Logic {
   /// The [newWidth] must be greater than or equal to the current width or
   /// an exception will be thrown.
   Logic signExtend(int newWidth) {
-    if (newWidth < width) {
-      throw Exception(
-          'New width $newWidth must be greater than or equal to width $width.');
+    if (width == 1) {
+      return ReplicationOp(this, newWidth).replicated;
+    } else if (newWidth > width) {
+      return [
+        ReplicationOp(this[width - 1], newWidth - width).replicated,
+        this,
+      ].swizzle();
+    } else if (newWidth == width) {
+      return this;
     }
-    return [
-      mux(
-        this[width - 1],
-        Const(1, width: newWidth - width, fill: true),
-        Const(0, width: newWidth - width),
-      ),
-      this,
-    ].swizzle();
+
+    throw Exception(
+        'New width $newWidth must be greater than or equal to width $width.');
   }
 
   /// Returns a copy of this [Logic] with the bits starting from [startIndex]
diff --git a/lib/src/module.dart b/lib/src/module.dart
index a6208998..293c193c 100644
--- a/lib/src/module.dart
+++ b/lib/src/module.dart
@@ -12,8 +12,10 @@ import 'dart:collection';
 
 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/utilities/config.dart';
 import 'package:rohd/src/utilities/sanitizer.dart';
 import 'package:rohd/src/utilities/uniquifier.dart';
 
@@ -407,6 +409,9 @@ abstract class Module {
     } else {
       if (!dontAddSignal && !isOutput(signal) && subModule == null) {
         _addInternalSignal(signal);
+        for (final dstConnection in signal.dstConnections) {
+          await _traceInputForModuleContents(dstConnection);
+        }
       }
       if (signal.srcConnection != null) {
         await _traceOutputForModuleContents(signal.srcConnection!);
@@ -498,6 +503,7 @@ abstract class Module {
 /**
  * Generated by ROHD - www.github.com/intel/rohd
  * Generation time: ${DateTime.now()}
+ * ROHD Version: ${Config.version}
  */
 
 ''';
diff --git a/lib/src/modules/bus.dart b/lib/src/modules/bus.dart
index fd79b964..8daee20d 100644
--- a/lib/src/modules/bus.dart
+++ b/lib/src/modules/bus.dart
@@ -147,8 +147,7 @@ class Swizzle extends Module with InlineSystemVerilog {
 
   /// Executes the functional behavior of this gate.
   void _execute() {
-    final updatedVal =
-        _swizzleInputs.reversed.map((e) => e.value).toList().swizzle();
+    final updatedVal = LogicValue.of(_swizzleInputs.map((e) => e.value));
     out.put(updatedVal);
   }
 
diff --git a/lib/src/modules/conditional.dart b/lib/src/modules/conditional.dart
index 589a3d52..c49e8aab 100644
--- a/lib/src/modules/conditional.dart
+++ b/lib/src/modules/conditional.dart
@@ -351,6 +351,7 @@ class Sequential extends _Always {
   void _execute() {
     var anyClkInvalid = false;
     var anyClkPosedge = false;
+
     for (var i = 0; i < _clks.length; i++) {
       // if the pre-tick value is null, then it should have the same value as
       // it currently does
@@ -796,6 +797,11 @@ class ElseIf {
 
   /// If [condition] is 1, then [then] will be executed.
   ElseIf(this.condition, this.then);
+
+  /// If [condition] is 1, then [then] will be executed.
+  ///
+  /// Use this constructor when you only have a single [then] condition.
+  ElseIf.s(Logic condition, Conditional then) : this(condition, [then]);
 }
 
 /// A conditional block to execute only if `condition` is satisified.
@@ -928,6 +934,14 @@ class If extends Conditional {
   /// If [condition] is 1, then [then] executes, otherwise [orElse] is executed.
   If(this.condition, {this.then = const [], this.orElse = const []});
 
+  /// If [condition] is 1, then [then] is excutes,
+  /// otherwise [orElse] is executed.
+  ///
+  /// Use this constructor when you only have a single [then] condition.
+  /// An optional [orElse] condition can be passed.
+  If.s(Logic condition, Conditional then, [Conditional? orElse])
+      : this(condition, then: [then], orElse: orElse == null ? [] : [orElse]);
+
   @override
   List<Logic> getReceivers() {
     final allReceivers = <Logic>[];
diff --git a/lib/src/modules/gates.dart b/lib/src/modules/gates.dart
index 07b537eb..e01154b4 100644
--- a/lib/src/modules/gates.dart
+++ b/lib/src/modules/gates.dart
@@ -530,7 +530,7 @@ class XorUnary extends _OneInputUnaryGate {
 /// A logical right-shift module.
 class RShift extends _ShiftGate {
   /// Calculates the value of [in_] shifted right (logically) by [shiftAmount].
-  RShift(Logic in_, Logic shiftAmount, {String name = 'rshift'})
+  RShift(Logic in_, dynamic shiftAmount, {String name = 'rshift'})
       : // Note: >>> vs >> is backwards for SystemVerilog and Dart
         super((a, shamt) => a >>> shamt, '>>', in_, shiftAmount, name: name);
 }
@@ -539,7 +539,7 @@ class RShift extends _ShiftGate {
 class ARShift extends _ShiftGate {
   /// Calculates the value of [in_] shifted right (arithmetically) by
   /// [shiftAmount].
-  ARShift(Logic in_, Logic shiftAmount, {String name = 'arshift'})
+  ARShift(Logic in_, dynamic shiftAmount, {String name = 'arshift'})
       : // Note: >>> vs >> is backwards for SystemVerilog and Dart
         super((a, shamt) => a >> shamt, '>>>', in_, shiftAmount,
             name: name, signed: true);
@@ -548,7 +548,7 @@ class ARShift extends _ShiftGate {
 /// A logical left-shift module.
 class LShift extends _ShiftGate {
   /// Calculates the value of [in_] shifted left by [shiftAmount].
-  LShift(Logic in_, Logic shiftAmount, {String name = 'lshift'})
+  LShift(Logic in_, dynamic shiftAmount, {String name = 'lshift'})
       : super((a, shamt) => a << shamt, '<<', in_, shiftAmount, name: name);
 }
 
@@ -724,3 +724,58 @@ class IndexGate extends Module with InlineSystemVerilog {
     return '$target[$idx]';
   }
 }
+
+/// A Replication Operator [Module].
+///
+/// It takes two inputs (bit and width) and outputs a [Logic] representing
+/// the input bit repeated over the input width
+class ReplicationOp extends Module with InlineSystemVerilog {
+  // input component name
+  final String _inputName;
+  // output component name
+  final String _outputName;
+  // Width of the output signal
+  final int _width;
+
+  /// The primary input to this gate.
+  Logic get _input => input(_inputName);
+
+  /// The output of this gate.
+  Logic get replicated => output(_outputName);
+
+  /// Constructs a ReplicationOp
+  ///
+  /// The bit [bit] will be repeated over the [_width] as an output.
+  /// [Module] is in-lined as SystemVerilog, it will use {width{bit}}
+  ReplicationOp(Logic bit, this._width)
+      : _inputName = 'input_${bit.name}',
+        _outputName = Module.unpreferredName('output_${bit.name}') {
+    addInput(_inputName, bit);
+    addOutput(_outputName, width: _width);
+    _setup();
+  }
+
+  /// Performs setup steps for custom functional behavior.
+  void _setup() {
+    _execute(); // for initial values
+    _input.glitch.listen((args) {
+      _execute();
+    });
+  }
+
+  /// Executes the functional behavior of this gate.
+  void _execute() {
+    replicated.put(_input.value, fill: true);
+  }
+
+  @override
+  String inlineVerilog(Map<String, String> inputs) {
+    if (inputs.length != 1) {
+      throw Exception('Gate has exactly one input.');
+    }
+
+    final target = inputs[_inputName]!;
+    final width = _width;
+    return '{$width{$target}}';
+  }
+}
diff --git a/lib/src/modules/pipeline.dart b/lib/src/modules/pipeline.dart
index f22ea39a..6ca72055 100644
--- a/lib/src/modules/pipeline.dart
+++ b/lib/src/modules/pipeline.dart
@@ -122,11 +122,11 @@ class Pipeline {
 
     for (var stageIndex = 0; stageIndex < _numStages; stageIndex++) {
       Combinational([
-        ..._registeredLogics
-            .map((logic) => get(logic, stageIndex) < _i(logic, stageIndex)),
+        for (Logic l in _registeredLogics)
+          get(l, stageIndex) < _i(l, stageIndex),
         ...combMiddles[stageIndex],
-        ..._registeredLogics
-            .map((logic) => _o(logic, stageIndex) < get(logic, stageIndex)),
+        for (Logic l in _registeredLogics)
+          _o(l, stageIndex) < get(l, stageIndex),
       ], name: 'comb_stage$stageIndex');
     }
   }
diff --git a/lib/src/synthesizers/systemverilog.dart b/lib/src/synthesizers/systemverilog.dart
index f776b304..eeb226e5 100644
--- a/lib/src/synthesizers/systemverilog.dart
+++ b/lib/src/synthesizers/systemverilog.dart
@@ -298,19 +298,21 @@ class _SynthModuleDefinition {
   @override
   String toString() => "module name: '${module.name}'";
 
-  late final Uniquifier _synthLogicNameUniquifier;
+  /// Used to uniquify any identifiers, including signal names
+  /// and module instances.
+  late final Uniquifier _synthInstantiationNameUniquifier;
+
   String _getUniqueSynthLogicName(String? initialName, bool portName) {
     if (portName && initialName == null) {
       throw Exception('Port name cannot be null.');
     }
-    return _synthLogicNameUniquifier.getUniqueName(
+    return _synthInstantiationNameUniquifier.getUniqueName(
         initialName: initialName, reserved: portName);
   }
 
-  final Uniquifier _synthSubModuleInstantiationNameUniquifier = Uniquifier();
   String _getUniqueSynthSubModuleInstantiationName(
           String? initialName, bool reserved) =>
-      _synthSubModuleInstantiationNameUniquifier.getUniqueName(
+      _synthInstantiationNameUniquifier.getUniqueName(
           initialName: initialName, nullStarter: 'm', reserved: reserved);
 
   _SynthLogic? _getSynthLogic(Logic? logic, bool allowPortName) {
@@ -328,7 +330,7 @@ class _SynthModuleDefinition {
   }
 
   _SynthModuleDefinition(this.module) {
-    _synthLogicNameUniquifier = Uniquifier(
+    _synthInstantiationNameUniquifier = Uniquifier(
         reservedNames: {...module.inputs.keys, ...module.outputs.keys});
 
     // start by traversing output signals
diff --git a/lib/src/utilities/config.dart b/lib/src/utilities/config.dart
new file mode 100644
index 00000000..6fd4d492
--- /dev/null
+++ b/lib/src/utilities/config.dart
@@ -0,0 +1,15 @@
+/// Copyright (C) 2022 Intel Corporation
+/// SPDX-License-Identifier: BSD-3-Clause
+///
+/// config.dart
+/// A configuration file of ROHD.
+///
+/// 2022 December 1
+/// Author: Yao Jing Quek <yao.jing.quek@intel.com>
+///
+
+/// A utility for ROHD configuration file.
+class Config {
+  /// The version of the ROHD framework.
+  static const String version = '0.4.0';
+}
diff --git a/lib/src/utilities/simcompare.dart b/lib/src/utilities/simcompare.dart
index 48e9b608..c44d7d8f 100644
--- a/lib/src/utilities/simcompare.dart
+++ b/lib/src/utilities/simcompare.dart
@@ -162,10 +162,10 @@ abstract class SimCompare {
       }
     }
 
-    final allSignals = vectors
-        .map((e) => [...e.inputValues.keys, ...e.expectedOutputValues.keys])
-        .reduce((a, b) => [...a, ...b])
-        .toSet();
+    final allSignals = <String>{
+      for (final e in vectors) ...e.inputValues.keys,
+      for (final e in vectors) ...e.expectedOutputValues.keys,
+    };
     final localDeclarations =
         allSignals.map((e) => 'logic ${signalDeclaration(e)};').join('\n');
     final moduleConnections = allSignals.map((e) => '.$e($e)').join(', ');
diff --git a/lib/src/utilities/synchronous_propagator.dart b/lib/src/utilities/synchronous_propagator.dart
index b5b2a251..84425854 100644
--- a/lib/src/utilities/synchronous_propagator.dart
+++ b/lib/src/utilities/synchronous_propagator.dart
@@ -8,6 +8,8 @@
 /// Author: Max Korbel <max.korbel@intel.com>
 ///
 
+/***/
+
 /// A controller for a [SynchronousEmitter] that allows for
 /// adding of events of type [T] to be emitted.
 class SynchronousPropagator<T> {
@@ -51,4 +53,14 @@ class SynchronousEmitter<T> {
     }
     _isEmitting = false;
   }
+
+  /// Tells this emitter to adopt all behavior of [other].
+  ///
+  /// Tells this emitter to perform all the actions of [other] each
+  /// time this would propagate.  Also clears all actions from [other]
+  /// so that it will not execute anything in the future.
+  void adopt(SynchronousEmitter<T> other) {
+    _actions.addAll(other._actions);
+    other._actions.clear();
+  }
 }
diff --git a/lib/src/values/big_logic_value.dart b/lib/src/values/big_logic_value.dart
index 1984e0ca..78ce405b 100644
--- a/lib/src/values/big_logic_value.dart
+++ b/lib/src/values/big_logic_value.dart
@@ -10,6 +10,30 @@
 
 part of values;
 
+/// Extends [BigInt] with utility functions that are useful for dealing with
+/// large bit vectors and conversion between types.
+extension BigLogicValueBigIntUtilities on BigInt {
+  /// Returns this [BigInt] as an [int].
+  ///
+  /// Always interprets the number as unsigned, and thus never clamps to fit.
+  int toIntUnsigned(int width) {
+    if (width > LogicValue._INT_BITS) {
+      throw Exception('Cannot convert to BigInt when width $width'
+          ' is greater than ${LogicValue._INT_BITS}');
+    } else if (width == LogicValue._INT_BITS) {
+      // When width is 64, `BigInt.toInt()` will clamp values assuming that
+      // it's a signed number.  To avoid that, if the width is 64, then do the
+      // conversion in two 32-bit chunks and bitwise-or them together.
+      const maskWidth = 32;
+      final mask = _BigLogicValue._maskOfWidth(maskWidth);
+      return (this & mask).toInt() |
+          (((this >> maskWidth) & mask).toInt() << maskWidth);
+    } else {
+      return toInt();
+    }
+  }
+}
+
 /// A [LogicValue] whose number of bits is greater than the size of an [int].
 ///
 /// The implementation is similar to [_SmallLogicValue], except it uses
@@ -64,8 +88,9 @@ class _BigLogicValue extends LogicValue {
           (_invalid >> start) & _maskOfWidth(newWidth), newWidth);
     } else {
       return _SmallLogicValue(
-          ((_value >> start) & _maskOfWidth(newWidth)).toInt(),
-          ((_invalid >> start) & _maskOfWidth(newWidth)).toInt(),
+          ((_value >> start) & _maskOfWidth(newWidth)).toIntUnsigned(newWidth),
+          ((_invalid >> start) & _maskOfWidth(newWidth))
+              .toIntUnsigned(newWidth),
           newWidth);
     }
   }
@@ -178,4 +203,16 @@ class _BigLogicValue extends LogicValue {
               shamt,
           _invalid >> shamt,
           width);
+
+  @override
+  BigInt get _bigIntInvalid => _invalid;
+
+  @override
+  BigInt get _bigIntValue => _value;
+
+  @override
+  int get _intInvalid => _invalid.toIntUnsigned(width);
+
+  @override
+  int get _intValue => _value.toIntUnsigned(width);
 }
diff --git a/lib/src/values/filled_logic_value.dart b/lib/src/values/filled_logic_value.dart
index 63b129ec..894c94b1 100644
--- a/lib/src/values/filled_logic_value.dart
+++ b/lib/src/values/filled_logic_value.dart
@@ -244,4 +244,28 @@ class _FilledLogicValue extends LogicValue {
   LogicValue _shiftArithmeticRight(int shamt) => LogicValue.ofString(
       ((_value == _LogicValueEnum.one ? '1' : '0') * shamt) +
           (this[0]._bitString() * (width - shamt)));
+
+  @override
+  BigInt get _bigIntInvalid =>
+      (_value == _LogicValueEnum.z || _value == _LogicValueEnum.x)
+          ? _BigLogicValue._maskOfWidth(width)
+          : BigInt.zero;
+
+  @override
+  BigInt get _bigIntValue =>
+      (_value == _LogicValueEnum.one || _value == _LogicValueEnum.z)
+          ? _BigLogicValue._maskOfWidth(width)
+          : BigInt.zero;
+
+  @override
+  int get _intInvalid =>
+      (_value == _LogicValueEnum.z || _value == _LogicValueEnum.x)
+          ? _SmallLogicValue._maskOfWidth(width)
+          : 0;
+
+  @override
+  int get _intValue =>
+      (_value == _LogicValueEnum.one || _value == _LogicValueEnum.z)
+          ? _SmallLogicValue._maskOfWidth(width)
+          : 0;
 }
diff --git a/lib/src/values/logic_value.dart b/lib/src/values/logic_value.dart
index b589800e..e988aa01 100644
--- a/lib/src/values/logic_value.dart
+++ b/lib/src/values/logic_value.dart
@@ -25,18 +25,21 @@ abstract class LogicValue {
   // ignore: constant_identifier_names
   static const int _INT_BITS = 64;
 
-  /// Logical value of `0`
+  /// Logical value of `0`.
   static const LogicValue zero = _FilledLogicValue(_LogicValueEnum.zero, 1);
 
-  /// Logical value of `1`
+  /// Logical value of `1`.
   static const LogicValue one = _FilledLogicValue(_LogicValueEnum.one, 1);
 
-  /// Logical value of `x`
+  /// Logical value of `x`.
   static const LogicValue x = _FilledLogicValue(_LogicValueEnum.x, 1);
 
-  /// Logical value of `z`
+  /// Logical value of `z`.
   static const LogicValue z = _FilledLogicValue(_LogicValueEnum.z, 1);
 
+  /// A zero-width value.
+  static const LogicValue empty = _FilledLogicValue(_LogicValueEnum.zero, 0);
+
   /// The number of bits in this `LogicValue`.
   final int width;
 
@@ -79,7 +82,7 @@ abstract class LogicValue {
   /// [width] must be greater than or equal to 0.
   static LogicValue ofBigInt(BigInt value, int width) => width > _INT_BITS
       ? _BigLogicValue(value, BigInt.zero, width)
-      : _SmallLogicValue(value.toInt(), 0, width);
+      : _SmallLogicValue(value.toIntUnsigned(width), 0, width);
 
   /// Converts `BigInt` [value] to a valid [LogicValue] with [width]
   /// number of bits.
@@ -115,16 +118,86 @@ abstract class LogicValue {
   /// Constructs a [LogicValue] from [it].
   ///
   /// The order of the created [LogicValue] will be such that the `i`th entry in
-  /// [it] corresponds to the `i`th bit.  That is, the 0th element of [it] will
-  /// be the 0th bit of the returned [LogicValue].
+  /// [it] corresponds to the `i`th group of bits.  That is, the 0th element of
+  /// [it] will be the least significant chunk of bits of the returned
+  /// [LogicValue].  Bits within each element of [it] are kept in the same
+  /// order as they were originally.
   ///
+  /// For example:
   /// ```dart
-  /// var it = [LogicValue.zero, LogicValue.x, LogicValue.one];
+  /// var it = [LogicValue.zero, LogicValue.x, LogicValue.ofString('01xz')];
   /// var lv = LogicValue.of(it);
-  /// print(lv); // This prints `3b'1x0`
+  /// print(lv); // This prints `6'b01xzx0`
   /// ```
-  static LogicValue of(Iterable<LogicValue> it) => LogicValue.ofString(
-      it.map((e) => e.toString(includeWidth: false)).toList().reversed.join());
+  static LogicValue of(Iterable<LogicValue> it) {
+    var smallBuffer = LogicValue.empty;
+    var fullResult = LogicValue.empty;
+
+    // shift small chunks in together before shifting BigInt's, since
+    // shifting BigInt's is expensive
+    for (final lv in it) {
+      final lvPlusSmall = lv.width + smallBuffer.width;
+      if (lvPlusSmall <= _INT_BITS) {
+        smallBuffer = lv._concatenate(smallBuffer);
+      } else {
+        // only put 64-bit chunks onto `fullResult`, rest onto `smallBuffer`
+        final upperBound =
+            _INT_BITS * (lvPlusSmall ~/ _INT_BITS) - smallBuffer.width;
+        fullResult = lv
+            .getRange(0, upperBound)
+            ._concatenate(smallBuffer)
+            ._concatenate(fullResult);
+        smallBuffer = lv.getRange(upperBound, lv.width);
+      }
+
+      assert(smallBuffer.width <= _INT_BITS,
+          'Keep smallBuffer small to meet invariants and efficiency');
+    }
+
+    // grab what's left
+    return smallBuffer._concatenate(fullResult);
+  }
+
+  /// Appends [other] to the least significant side of `this`.
+  ///
+  /// The new value will have `this`'s current value shifted left by
+  /// the width of [other].
+  LogicValue _concatenate(LogicValue other) {
+    if (other.width == 0) {
+      return this;
+    } else if (width == 0) {
+      return other;
+    }
+
+    final newWidth = width + other.width;
+
+    if (this is _FilledLogicValue &&
+        other is _FilledLogicValue &&
+        other[0] == this[0]) {
+      // can keep it filled
+      return _FilledLogicValue(other._value, newWidth);
+    } else if (newWidth > LogicValue._INT_BITS) {
+      // BigInt's only
+      return _BigLogicValue(_bigIntValue << other.width | other._bigIntValue,
+          _bigIntInvalid << other.width | other._bigIntInvalid, newWidth);
+    } else {
+      // int's ok
+      return _SmallLogicValue(_intValue << other.width | other._intValue,
+          _intInvalid << other.width | other._intInvalid, newWidth);
+    }
+  }
+
+  /// Returns `_value` in the form of a [BigInt].
+  BigInt get _bigIntValue;
+
+  /// Returns `_invalid` in the form of a [BigInt].
+  BigInt get _bigIntInvalid;
+
+  /// Returns `_value` in the form of an [int].
+  int get _intValue;
+
+  /// Returns `_invalid` in the form of an [int].
+  int get _intInvalid;
 
   /// Constructs a [LogicValue] from [it].
   ///
@@ -803,10 +876,7 @@ int _unsignedBinaryParse(String source) {
   if (val != null) {
     return val;
   } else {
-    final hex = BigInt.parse(source, radix: 2).toRadixString(16);
-
-    // With `0x` in front of a hex literal, it will be interpreted as unsigned.
-    return int.parse('0x$hex');
+    return BigInt.parse(source, radix: 2).toIntUnsigned(source.length);
   }
 }
 
diff --git a/lib/src/values/small_logic_value.dart b/lib/src/values/small_logic_value.dart
index ad5ec151..f4bd0aa0 100644
--- a/lib/src/values/small_logic_value.dart
+++ b/lib/src/values/small_logic_value.dart
@@ -180,4 +180,18 @@ class _SmallLogicValue extends LogicValue {
               _mask,
           _invalid >> shamt,
           width);
+
+  @override
+  BigInt get _bigIntInvalid =>
+      BigInt.from(_invalid) & _BigLogicValue._maskOfWidth(width);
+
+  @override
+  BigInt get _bigIntValue =>
+      BigInt.from(_value) & _BigLogicValue._maskOfWidth(width);
+
+  @override
+  int get _intInvalid => _invalid;
+
+  @override
+  int get _intValue => _value;
 }
diff --git a/lib/src/values/values.dart b/lib/src/values/values.dart
index 6866bce0..6c982b1a 100644
--- a/lib/src/values/values.dart
+++ b/lib/src/values/values.dart
@@ -4,7 +4,6 @@
 library values;
 
 import 'package:meta/meta.dart';
-// ignore: unused_import
 import 'package:rohd/rohd.dart';
 
 part 'logic_value.dart';
diff --git a/lib/src/wave_dumper.dart b/lib/src/wave_dumper.dart
index afc987b5..216e40b6 100644
--- a/lib/src/wave_dumper.dart
+++ b/lib/src/wave_dumper.dart
@@ -10,6 +10,7 @@
 
 import 'dart:io';
 import 'package:rohd/rohd.dart';
+import 'package:rohd/src/utilities/config.dart';
 import 'package:rohd/src/utilities/sanitizer.dart';
 import 'package:rohd/src/utilities/uniquifier.dart';
 
@@ -127,7 +128,7 @@ class WaveDumper {
   $dateString
 \$end
 \$version
-  ROHD
+  ROHD v${Config.version}
 \$end
 \$comment
   Generated by ROHD - www.github.com/intel/rohd
diff --git a/pubspec.yaml b/pubspec.yaml
index 8f3c8eba..c43af41b 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -17,3 +17,4 @@ dependencies:
 
 dev_dependencies:
   benchmark_harness: ^2.2.0
+  yaml: ^3.1.1


Test patch:
diff --git a/test/benchmark_test.dart b/test/benchmark_test.dart
index b79acdd0..d463769e 100644
--- a/test/benchmark_test.dart
+++ b/test/benchmark_test.dart
@@ -9,10 +9,21 @@
 ///
 
 import 'package:test/test.dart';
-import '../benchmark/benchmarks.dart';
+
+import '../benchmark/byte_enable_benchmark.dart';
+import '../benchmark/logic_value_of_benchmark.dart';
+import '../benchmark/pipeline_benchmark.dart';
 
 void main() {
   test('pipeline benchmark', () async {
     await PipelineBenchmark().measure();
   });
+
+  test('logic value of benchmark', () {
+    LogicValueOfBenchmark().measure();
+  });
+
+  test('byte enable benchmark', () {
+    ByteEnableBenchmark().measure();
+  });
 }
diff --git a/test/changed_test.dart b/test/changed_test.dart
index 6ada837c..cd24965b 100644
--- a/test/changed_test.dart
+++ b/test/changed_test.dart
@@ -112,4 +112,61 @@ void main() {
 
     expect(qHadPosedge, equals(true));
   });
+
+  test('reconnected signal still hits changed events', () async {
+    final a = Logic(name: 'a');
+    final b = Logic(name: 'b');
+
+    var detectedAChanged = false;
+    a.changed.listen((event) {
+      detectedAChanged = true;
+    });
+
+    a <= b;
+
+    Simulator.registerAction(100, () {
+      b.put(1);
+    });
+
+    await Simulator.run();
+
+    expect(detectedAChanged, isTrue);
+  });
+
+  test('chain of reconnected signals still changes', () async {
+    final a = Logic(name: 'a');
+    final b = Logic(name: 'b');
+    final c = Logic(name: 'c');
+
+    var detectedAChanged = false;
+    a.changed.listen((event) {
+      detectedAChanged = true;
+    });
+
+    a <= b;
+    b <= c;
+
+    Simulator.registerAction(100, () {
+      c.put(1);
+    });
+
+    await Simulator.run();
+
+    expect(detectedAChanged, isTrue);
+  });
+
+  test('chain of reconnected signals still glitches', () async {
+    final a = Logic(name: 'a');
+    final b = Logic(name: 'b');
+    final c = Logic(name: 'c');
+
+    a.put(0);
+
+    a <= b;
+    b <= c;
+
+    c.put(1);
+
+    expect(a.value, equals(LogicValue.one));
+  });
 }
diff --git a/test/conditionals_test.dart b/test/conditionals_test.dart
index d8e11953..e53c113a 100644
--- a/test/conditionals_test.dart
+++ b/test/conditionals_test.dart
@@ -80,6 +80,19 @@ class IfBlockModule extends Module {
   }
 }
 
+class SingleIfBlockModule extends Module {
+  SingleIfBlockModule(Logic a) : super(name: 'singleifblockmodule') {
+    a = addInput('a', a);
+    final c = addOutput('c');
+
+    Combinational([
+      IfBlock([
+        Iff.s(a, c < 1),
+      ])
+    ]);
+  }
+}
+
 class ElseIfBlockModule extends Module {
   ElseIfBlockModule(Logic a, Logic b) : super(name: 'ifblockmodule') {
     a = addInput('a', a);
@@ -97,6 +110,21 @@ class ElseIfBlockModule extends Module {
   }
 }
 
+class SingleElseIfBlockModule extends Module {
+  SingleElseIfBlockModule(Logic a) : super(name: 'singleifblockmodule') {
+    a = addInput('a', a);
+    final c = addOutput('c');
+    final d = addOutput('d');
+
+    Combinational([
+      IfBlock([
+        ElseIf.s(a, c < 1),
+        Else([c < 0, d < 1])
+      ])
+    ]);
+  }
+}
+
 class CombModule extends Module {
   CombModule(Logic a, Logic b, Logic d) : super(name: 'combmodule') {
     a = addInput('a', a);
@@ -159,6 +187,36 @@ class SequentialModule extends Module {
   }
 }
 
+class SingleIfModule extends Module {
+  SingleIfModule(Logic a) : super(name: 'combmodule') {
+    a = addInput('a', a);
+
+    final q = addOutput('q');
+
+    Combinational(
+      [
+        If.s(a, q < 1),
+      ],
+    );
+  }
+}
+
+class SingleIfOrElseModule extends Module {
+  SingleIfOrElseModule(Logic a, Logic b) : super(name: 'combmodule') {
+    a = addInput('a', a);
+    b = addInput('b', b);
+
+    final q = addOutput('q');
+    final x = addOutput('x');
+
+    Combinational(
+      [
+        If.s(a, q < 1, x < 1),
+      ],
+    );
+  }
+}
+
 void main() {
   tearDown(Simulator.reset);
 
@@ -205,6 +263,18 @@ void main() {
       expect(simResult, equals(true));
     });
 
+    test('single iffblock comb', () async {
+      final mod = SingleIfBlockModule(Logic());
+      await mod.build();
+      final vectors = [
+        Vector({'a': 1}, {'c': 1}),
+      ];
+      await SimCompare.checkFunctionalVector(mod, vectors);
+      final simResult = SimCompare.iverilogVector(
+          mod.generateSynth(), mod.runtimeType.toString(), vectors);
+      expect(simResult, equals(true));
+    });
+
     test('elseifblock comb', () async {
       final mod = ElseIfBlockModule(Logic(), Logic());
       await mod.build();
@@ -220,6 +290,19 @@ void main() {
       expect(simResult, equals(true));
     });
 
+    test('single elseifblock comb', () async {
+      final mod = SingleElseIfBlockModule(Logic());
+      await mod.build();
+      final vectors = [
+        Vector({'a': 1}, {'c': 1}),
+        Vector({'a': 0}, {'c': 0, 'd': 1}),
+      ];
+      await SimCompare.checkFunctionalVector(mod, vectors);
+      final simResult = SimCompare.iverilogVector(
+          mod.generateSynth(), mod.runtimeType.toString(), vectors);
+      expect(simResult, equals(true));
+    });
+
     test('case comb', () async {
       final mod = CaseModule(Logic(), Logic());
       await mod.build();
@@ -252,5 +335,36 @@ 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}),
+      ];
+      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));
+    });
   });
 }
diff --git a/test/config_test.dart b/test/config_test.dart
new file mode 100644
index 00000000..7b103f32
--- /dev/null
+++ b/test/config_test.dart
@@ -0,0 +1,68 @@
+/// Copyright (C) 2022 Intel Corporation
+/// SPDX-License-Identifier: BSD-3-Clause
+///
+/// version_hash_dumper_test.dart
+/// Tests to verify if ROHD configuration being output to
+/// the generation of system verilog.
+///
+/// 2022 December 1
+/// Author: Yao Jing Quek <yao.jing.quek@intel.com>
+///
+
+import 'dart:io';
+import 'package:rohd/rohd.dart';
+import 'package:rohd/src/utilities/config.dart';
+import 'package:test/test.dart';
+import 'package:yaml/yaml.dart';
+import 'wave_dumper_test.dart';
+
+class SimpleModule extends Module {
+  SimpleModule(Logic a, Logic b) {
+    a = addInput('a', a);
+    b = addInput('b', b);
+    final c = addOutput('c');
+
+    Combinational([
+      If(a, then: [c < a], orElse: [c < b])
+    ]);
+  }
+}
+
+void main() async {
+  test(
+      'should return true if rohd version is similar'
+      ' in both pubspec.yaml and config class.', () async {
+    final yamlText = File('./pubspec.yaml').readAsStringSync();
+    final yaml = loadYaml(yamlText) as Map;
+
+    expect(Config.version, equals(yaml['version']));
+  });
+
+  test('should contains ROHD version number when sv is generated.', () async {
+    const version = Config.version;
+
+    final mod = SimpleModule(Logic(), Logic());
+    await mod.build();
+
+    final sv = mod.generateSynth();
+
+    expect(sv, contains(version));
+  });
+
+  test('should contains ROHD version number when wavedumper is generated.',
+      () async {
+    const version = Config.version;
+
+    final mod = SimpleModule(Logic(), Logic());
+    await mod.build();
+
+    const dumpName = 'simplemodule';
+
+    createTemporaryDump(mod, dumpName);
+
+    final vcdContents = await File(temporaryDumpPath(dumpName)).readAsString();
+    expect(vcdContents, contains(version));
+
+    deleteTemporaryDump(dumpName);
+  });
+}
diff --git a/test/counter_test.dart b/test/counter_test.dart
index e9bb9f5d..b48074fa 100644
--- a/test/counter_test.dart
+++ b/test/counter_test.dart
@@ -7,10 +7,8 @@
 /// 2021 May 10
 /// Author: Max Korbel <max.korbel@intel.com>
 ///
-import 'dart:async';
-
-// import 'dart:io';
 
+import 'dart:async';
 import 'package:rohd/rohd.dart';
 import 'package:rohd/src/utilities/simcompare.dart';
 import 'package:test/test.dart';
@@ -50,13 +48,9 @@ void main() {
       final counter = Counter(Logic(), reset);
       await counter.build();
       // WaveDumper(counter);
-      // File('tmp_counter.sv').writeAsStringSync(counter.generateSynth());
 
-      // check that 1 timestep after reset, the value has reset properly
       unawaited(reset.nextPosedge
-          .then((value) => Simulator.registerAction(Simulator.time + 1, () {
-                expect(counter.val.value.toInt(), equals(0));
-              })));
+          .then((value) => expect(counter.val.value.toInt(), equals(0))));
 
       final vectors = [
         Vector({'en': 0, 'reset': 0}, {}),
diff --git a/test/extend_test.dart b/test/extend_test.dart
index bc5f18e2..9d62fd2e 100644
--- a/test/extend_test.dart
+++ b/test/extend_test.dart
@@ -18,11 +18,12 @@ class ExtendModule extends Module {
   ExtendModule(Logic a, int newWidth, ExtendType extendType) {
     a = addInput('a', a, width: a.width);
     final b = addOutput('b', width: newWidth);
-
-    b <=
-        (extendType == ExtendType.zero
-            ? a.zeroExtend(newWidth)
-            : a.signExtend(newWidth));
+    // Make it debug-able
+    if (extendType == ExtendType.zero) {
+      b <= a.zeroExtend(newWidth);
+    } else {
+      b <= a.signExtend(newWidth);
+    }
   }
 }
 
@@ -41,13 +42,15 @@ void main() {
     tearDown(Simulator.reset);
     group('extend', () {
       Future<void> extendVectors(
-          List<Vector> vectors, int newWidth, ExtendType extendType) async {
-        final mod = ExtendModule(Logic(width: 8), newWidth, extendType);
+          List<Vector> vectors, int newWidth, ExtendType extendType,
+          {int originalWidth = 8}) async {
+        final mod =
+            ExtendModule(Logic(width: originalWidth), newWidth, extendType);
         await mod.build();
         await SimCompare.checkFunctionalVector(mod, vectors);
         final simResult = SimCompare.iverilogVector(
             mod.generateSynth(), mod.runtimeType.toString(), vectors,
-            signalToWidthMap: {'a': 8, 'b': newWidth});
+            signalToWidthMap: {'a': originalWidth, 'b': newWidth});
         expect(simResult, equals(true));
       }
 
@@ -87,6 +90,22 @@ void main() {
           Vector({'a': 0xff}, {'b': 0xfff}),
         ], 12, ExtendType.sign);
       });
+      test('sign extend for invalid Logic pads LogicValue.x', () async {
+        await extendVectors([
+          Vector({'a': LogicValue.ofString('x0100100')},
+              {'b': LogicValue.ofString('xxxxx0100100')}),
+        ], 12, ExtendType.sign);
+      });
+      test('sign extend single bit(0) pads 0s', () async {
+        await extendVectors([
+          Vector({'a': LogicValue.zero}, {'b': 0x000}),
+        ], 12, ExtendType.sign, originalWidth: 1);
+      });
+      test('sign extend single bit(1) pads 0s', () async {
+        await extendVectors([
+          Vector({'a': LogicValue.one}, {'b': 0xfff}),
+        ], 12, ExtendType.sign, originalWidth: 1);
+      });
     });
 
     group('withSet', () {
diff --git a/test/logic_value_test.dart b/test/logic_value_test.dart
index 23dec967..23a01580 100644
--- a/test/logic_value_test.dart
+++ b/test/logic_value_test.dart
@@ -267,6 +267,13 @@ void main() {
           equals(LogicValue.of([LogicValue.zero, LogicValue.zero])));
     });
   });
+
+  test('LogicValue.of example', () {
+    final it = [LogicValue.zero, LogicValue.x, LogicValue.ofString('01xz')];
+    final lv = LogicValue.of(it);
+    expect(lv.toString(), equals("6'b01xzx0"));
+  });
+
   group('unary operations (including "to")', () {
     test('toMethods', () {
       expect(
@@ -838,4 +845,27 @@ void main() {
           equals(LogicValue.x));
     });
   });
+
+  group('64-bit conversions', () {
+    test(
+        '64-bit LogicValues larger than maximum positive value on integer'
+        ' are properly converted when converted from BigInt', () {
+      final extraWide = LogicValue.ofBigInt(
+        BigInt.parse('f' * 16 + 'f0' * 8, radix: 16),
+        128,
+      );
+      final smaller = extraWide.getRange(0, 64);
+      expect(smaller.toInt(), equals(0xf0f0f0f0f0f0f0f0));
+    });
+    test(
+        '64-bit BigInts larger than max pos int value constructing'
+        ' a LogicValue is correct', () {
+      final bigInt64Lv =
+          LogicValue.ofBigInt(BigInt.parse('fa' * 8, radix: 16), 64);
+      expect(bigInt64Lv.toInt(), equals(0xfafafafafafafafa));
+    });
+    test('64-bit binary negatives are converted properly with bin', () {
+      expect(bin('1110' * 16), equals(0xeeeeeeeeeeeeeeee));
+    });
+  });
 }
diff --git a/test/long_chain_test.dart b/test/long_chain_test.dart
new file mode 100644
index 00000000..b1ee90f4
--- /dev/null
+++ b/test/long_chain_test.dart
@@ -0,0 +1,39 @@
+/// Copyright (C) 2022 Intel Corporation
+/// SPDX-License-Identifier: BSD-3-Clause
+///
+/// long_chain_test.dart
+/// Tests with long chains of combinational logic.
+///
+/// 2022 November 8
+/// Author: Max Korbel <max.korbel@intel.com>
+///
+
+import 'package:rohd/rohd.dart';
+import 'package:rohd/src/modules/passthrough.dart';
+import 'package:test/test.dart';
+
+class LongChain extends Module {
+  final int length;
+
+  Logic get chainOut => output('chainOut');
+
+  LongChain(Logic chainIn, {this.length = 1000}) : super(name: 'longChain') {
+    chainIn = addInput('chainIn', chainIn);
+    var intermediate = chainIn;
+    for (var i = 0; i < length; i++) {
+      intermediate = ~Passthrough(intermediate).out;
+    }
+    addOutput('chainOut') <= intermediate;
+  }
+}
+
+void main() {
+  test('long chain of combinational logic and modules', () async {
+    final chainIn = Logic(name: 'chainIn');
+    final chain = LongChain(chainIn);
+    await chain.build();
+
+    chainIn.put(0);
+    expect(chain.chainOut.value.toInt(), equals(chain.length % 2));
+  });
+}
diff --git a/test/math_test.dart b/test/math_test.dart
index b078b7b7..213e782c 100644
--- a/test/math_test.dart
+++ b/test/math_test.dart
@@ -12,21 +12,8 @@ import 'package:rohd/src/utilities/simcompare.dart';
 import 'package:test/test.dart';
 
 class MathTestModule extends Module {
-  Logic get a => input('a');
-  Logic get b => input('b');
-
-  Logic get aPlusB => output('a_plus_b');
-  Logic get aMinusB => output('a_minus_b');
-  Logic get aTimesB => output('a_times_b');
-  Logic get aDividedByB => output('a_dividedby_b');
-  Logic get aModuloB => output('a_modulo_b');
-
-  Logic get aPlusConst => output('a_plus_const');
-  Logic get aMinusConst => output('a_minus_const');
-  Logic get aTimesConst => output('a_times_const');
-  Logic get aDividedByConst => output('a_dividedby_const');
-
   final int c;
+
   MathTestModule(Logic a, Logic b, {this.c = 5})
       : super(name: 'gatetestmodule') {
     if (a.width != b.width) {
@@ -40,22 +27,40 @@ class MathTestModule extends Module {
     final aTimesB = addOutput('a_times_b', width: a.width);
     final aDividedByB = addOutput('a_dividedby_b', width: a.width);
     final aModuloB = addOutput('a_modulo_b', width: a.width);
+    final aModuloConst = addOutput('a_modulo_const', width: a.width);
 
     final aPlusConst = addOutput('a_plus_const', width: a.width);
     final aMinusConst = addOutput('a_minus_const', width: a.width);
     final aTimesConst = addOutput('a_times_const', width: a.width);
     final aDividedByConst = addOutput('a_dividedby_const', width: a.width);
 
+    final aSlB = addOutput('a_sl_b', width: a.width);
+    final aSrlB = addOutput('a_srl_b', width: a.width);
+    final aSraB = addOutput('a_sra_b', width: a.width);
+
+    final aSlConst = addOutput('a_sl_const', width: a.width);
+    final aSrlConst = addOutput('a_srl_const', width: a.width);
+    final aSraConst = addOutput('a_sra_const', width: a.width);
+
     aPlusB <= a + b;
     aMinusB <= a - b;
     aTimesB <= a * b;
     aDividedByB <= a / b;
     aModuloB <= a % b;
+    aModuloConst <= a % c;
 
     aPlusConst <= a + c;
     aMinusConst <= a - c;
     aTimesConst <= a * c;
     aDividedByConst <= a / c;
+
+    aSlB <= a << b;
+    aSrlB <= a >>> b;
+    aSraB <= a >> b;
+
+    aSlConst <= a << c;
+    aSrlConst <= a >>> c;
+    aSraConst <= a >> c;
   }
 }
 
@@ -75,12 +80,27 @@ void main() {
       'a_minus_const': 8,
       'a_times_const': 8,
       'a_dividedby_const': 8,
+      'a_modulo_const': 8,
+      'a_sl_b': 8,
+      'a_srl_b': 8,
+      'a_sra_b': 8,
+      'a_sl_const': 8,
+      'a_srl_const': 8,
+      'a_sra_const': 8,
     };
 
-    test('addition', () async {
+    Future<void> runMathVectors(List<Vector> vectors) async {
       final gtm = MathTestModule(Logic(width: 8), Logic(width: 8));
       await gtm.build();
-      final vectors = [
+      await SimCompare.checkFunctionalVector(gtm, vectors);
+      final simResult = SimCompare.iverilogVector(
+          gtm.generateSynth(), gtm.runtimeType.toString(), vectors,
+          signalToWidthMap: signalToWidthMap);
+      expect(simResult, equals(true));
+    }
+
+    test('addition', () async {
+      await runMathVectors([
         Vector({'a': 0, 'b': 0}, {'a_plus_b': 0}),
         Vector({'a': 0, 'b': 1}, {'a_plus_b': 1}),
         Vector({'a': 1, 'b': 0}, {'a_plus_b': 1}),
@@ -89,80 +109,72 @@ void main() {
         Vector({'a': 6}, {'a_plus_const': 11}),
         // Vector({'a': -6, 'b': 7}, {'a_plus_b': 1}),
         // Vector({'a': -6, 'b': 2}, {'a_plus_b': -4}),
-      ];
-      await SimCompare.checkFunctionalVector(gtm, vectors);
-      final simResult = SimCompare.iverilogVector(
-          gtm.generateSynth(), gtm.runtimeType.toString(), vectors,
-          signalToWidthMap: signalToWidthMap);
-      expect(simResult, equals(true));
+      ]);
     });
 
     test('subtraction', () async {
-      final gtm = MathTestModule(Logic(width: 8), Logic(width: 8));
-      await gtm.build();
-      final vectors = [
+      await runMathVectors([
         Vector({'a': 0, 'b': 0}, {'a_minus_b': 0}),
         Vector({'a': 1, 'b': 0}, {'a_minus_b': 1}),
         Vector({'a': 0xff, 'b': 0xff}, {'a_minus_b': 0}),
         Vector({'a': 12, 'b': 5}, {'a_minus_b': 7}),
         Vector({'a': 6}, {'a_minus_const': 1}),
-      ];
-      await SimCompare.checkFunctionalVector(gtm, vectors);
-      final simResult = SimCompare.iverilogVector(
-          gtm.generateSynth(), gtm.runtimeType.toString(), vectors,
-          signalToWidthMap: signalToWidthMap);
-      expect(simResult, equals(true));
+      ]);
     });
 
     test('multiplication', () async {
-      final gtm = MathTestModule(Logic(width: 8), Logic(width: 8));
-      await gtm.build();
-      final vectors = [
+      await runMathVectors([
         Vector({'a': 0, 'b': 0}, {'a_times_b': 0}),
         Vector({'a': 1, 'b': 1}, {'a_times_b': 1}),
         Vector({'a': 3, 'b': 4}, {'a_times_b': 12}),
         Vector({'a': 6}, {'a_times_const': 30}),
-      ];
-      await SimCompare.checkFunctionalVector(gtm, vectors);
-      final simResult = SimCompare.iverilogVector(
-          gtm.generateSynth(), gtm.runtimeType.toString(), vectors,
-          signalToWidthMap: signalToWidthMap);
-      expect(simResult, equals(true));
+      ]);
     });
 
     test('division', () async {
-      final gtm = MathTestModule(Logic(width: 8), Logic(width: 8));
-      await gtm.build();
-      final vectors = [
+      await runMathVectors([
         Vector({'a': 0, 'b': 0}, {'a_dividedby_b': LogicValue.x}),
         Vector({'a': 1, 'b': 0}, {'a_dividedby_b': LogicValue.x}),
         Vector({'a': 4, 'b': 2}, {'a_dividedby_b': 2}),
         Vector({'a': 5, 'b': 2}, {'a_dividedby_b': 2}),
         Vector({'a': 9, 'b': 3}, {'a_dividedby_b': 3}),
         Vector({'a': 6}, {'a_dividedby_const': 1}),
-      ];
-      await SimCompare.checkFunctionalVector(gtm, vectors);
-      final simResult = SimCompare.iverilogVector(
-          gtm.generateSynth(), gtm.runtimeType.toString(), vectors,
-          signalToWidthMap: signalToWidthMap);
-      expect(simResult, equals(true));
+      ]);
     });
 
     test('modulo', () async {
-      final gtm = MathTestModule(Logic(width: 8), Logic(width: 8));
-      await gtm.build();
-      final vectors = [
+      await runMathVectors([
         Vector({'a': 0, 'b': 0}, {'a_modulo_b': LogicValue.x}),
         Vector({'a': 1, 'b': 0}, {'a_modulo_b': LogicValue.x}),
         Vector({'a': 4, 'b': 2}, {'a_modulo_b': 0}),
         Vector({'a': 5, 'b': 2}, {'a_modulo_b': 1}),
         Vector({'a': 9, 'b': 3}, {'a_modulo_b': 0}),
-      ];
-      await SimCompare.checkFunctionalVector(gtm, vectors);
-      final simResult = SimCompare.iverilogVector(
-          gtm.generateSynth(), gtm.runtimeType.toString(), vectors,
-          signalToWidthMap: signalToWidthMap);
-      expect(simResult, equals(true));
+        Vector({'a': 11}, {'a_modulo_const': 1})
+      ]);
+    });
+
+    test('shift left', () async {
+      await runMathVectors([
+        Vector({'a': 0xf, 'b': 0}, {'a_sl_b': 0xf}),
+        Vector({'a': 0xf, 'b': 2}, {'a_sl_b': 0xf << 2}),
+        Vector({'a': 0x2}, {'a_sl_const': 0x2 << 5}),
+      ]);
+    });
+
+    test('shift right logical', () async {
+      await runMathVectors([
+        Vector({'a': 0xf, 'b': 0}, {'a_srl_b': 0xf}),
+        Vector({'a': 0xff, 'b': 2}, {'a_srl_b': 0x3f}),
+        Vector({'a': bin('11000000')}, {'a_srl_const': bin('00000110')}),
+      ]);
+    });
+
+    test('shift right arithmetic', () async {
+      await runMathVectors([
+        Vector({'a': 0xf, 'b': 0}, {'a_sra_b': 0xf}),
+        Vector({'a': 0xfe, 'b': 2}, {'a_sra_b': 0xff}),
+        Vector({'a': bin('11000000')}, {'a_sra_const': bin('11111110')}),
+      ]);
     });
   });
 }
diff --git a/test/multimodule5_test.dart b/test/multimodule5_test.dart
new file mode 100644
index 00000000..3a818860
--- /dev/null
+++ b/test/multimodule5_test.dart
@@ -0,0 +1,39 @@
+/// Copyright (C) 2022 Intel Corporation
+/// SPDX-License-Identifier: BSD-3-Clause
+///
+/// multimodule5_test.dart
+/// Unit tests for a hierarchy of multiple modules and multiple instantiation
+/// (another type)
+///
+/// 2022 November 22
+/// Author: Max Korbel <max.korbel@intel.com>
+///
+
+import 'package:rohd/rohd.dart';
+import 'package:rohd/src/modules/passthrough.dart';
+import 'package:test/test.dart';
+
+class TopModule extends Module {
+  TopModule(Logic inPort) {
+    inPort = addInput('inPort', inPort);
+
+    final internalNet = Logic(name: 'internalNet');
+    final outPort = addOutput('outPort');
+
+    Combinational([internalNet < inPort]);
+    Combinational([outPort < internalNet]);
+
+    Passthrough(internalNet);
+  }
+}
+
+void main() {
+  test('multimodules5', () async {
+    final mod = TopModule(Logic());
+    await mod.build();
+
+    final sv = mod.generateSynth();
+
+    expect(sv, contains('Passthrough'));
+  });
+}
diff --git a/test/name_test.dart b/test/name_test.dart
index 0a29608f..9314bcb7 100644
--- a/test/name_test.dart
+++ b/test/name_test.dart
@@ -6,9 +6,11 @@
 ///
 /// 2022 March 7
 /// Author: Max Korbel <max.korbel@intel.com>
+///
 
 // ignore_for_file: avoid_positional_boolean_parameters
 import 'package:rohd/rohd.dart';
+import 'package:rohd/src/utilities/simcompare.dart';
 import 'package:test/test.dart';
 
 class TopModule extends Module {
@@ -23,18 +25,138 @@ class TopModule extends Module {
 }
 
 class SpeciallyNamedModule extends Module {
-  SpeciallyNamedModule(Logic a, bool reserveDefName, bool reserveInstanceName)
-      : super(
-          name: 'specialNameInstance',
+  SpeciallyNamedModule(
+    Logic a,
+    bool reserveDefName,
+    bool reserveInstanceName, {
+    super.name = 'specialInstanceName',
+    super.definitionName = 'specialName',
+  }) : super(
           reserveName: reserveInstanceName,
-          definitionName: 'specialName',
           reserveDefinitionName: reserveDefName,
         ) {
     addInput('a', a, width: a.width);
   }
 }
 
+class RenameableModule extends Module {
+  final String inputPortName;
+  final String outputPortName;
+  RenameableModule(
+    Logic inputPort, {
+    this.outputPortName = 'outputPort',
+    String internalSignalName = 'internalSignal',
+    String internalModuleInstanceName = 'internalModuleInstanceName',
+    String internalModuleDefinitionName = 'internalModuleDefinitionName',
+    super.definitionName = 'moduleDefinitionName',
+    super.name = 'moduleInstanceName',
+    super.reserveDefinitionName = true,
+    super.reserveName = true,
+  }) : inputPortName = inputPort.name {
+    inputPort = addInput(inputPort.name, inputPort);
+    final outputPort = addOutput(outputPortName);
+
+    final internalSignal = Logic(name: internalSignalName);
+
+    Combinational([internalSignal < ~inputPort]);
+    Combinational([outputPort < internalSignal]);
+
+    SpeciallyNamedModule(
+      ~internalSignal,
+      true,
+      false,
+      name: internalModuleInstanceName,
+      definitionName: internalModuleDefinitionName,
+    );
+  }
+}
+
+enum NameType {
+  inputPort,
+  outputPort,
+  internalSignal,
+  internalModuleInstance,
+  internalModuleDefinition,
+  topDefinitionName,
+  topName
+}
+
 void main() {
+  tearDown(Simulator.reset);
+
+  group('signal and module naming conflicts', () {
+    Future<void> runTest(RenameableModule mod) async {
+      await mod.build();
+
+      final vectors = [
+        Vector({mod.inputPortName: 0}, {mod.outputPortName: 1}),
+        Vector({mod.inputPortName: 1}, {mod.outputPortName: 0}),
+      ];
+
+      await SimCompare.checkFunctionalVector(mod, vectors);
+      final simResult = SimCompare.iverilogVector(
+        mod.generateSynth(),
+        mod.definitionName,
+        vectors,
+      );
+      expect(simResult, equals(true));
+    }
+
+    Future<void> runTestGen(Map<NameType, String> names) async =>
+        runTest(RenameableModule(
+          Logic(name: names[NameType.inputPort]),
+          outputPortName: names[NameType.outputPort]!,
+          internalSignalName: names[NameType.internalSignal]!,
+          internalModuleInstanceName: names[NameType.internalModuleInstance]!,
+          internalModuleDefinitionName:
+              names[NameType.internalModuleDefinition]!,
+          definitionName: names[NameType.topDefinitionName],
+          name: names[NameType.topName]!,
+        ));
+
+    for (var i = 0; i < NameType.values.length; i++) {
+      for (var j = i + 1; j < NameType.values.length; j++) {
+        final nameType1 = NameType.values[i];
+        final nameType2 = NameType.values[j];
+        final nameTypes = [nameType1, nameType2];
+
+        // skip ones that actually *should* cause a failure
+        final skips = [
+          [NameType.internalModuleDefinition, NameType.topDefinitionName],
+          [NameType.inputPort, NameType.outputPort]
+        ];
+
+        var doSkip = false;
+        for (final skip in skips) {
+          if (nameTypes.contains(skip[0]) && nameTypes.contains(skip[1])) {
+            doSkip = true;
+            break;
+          }
+        }
+        if (doSkip) {
+          continue;
+        }
+
+        test('${nameType1.name} == ${nameType2.name}', () async {
+          final testMap = Map.fromEntries(List.generate(NameType.values.length,
+              (k) => MapEntry(NameType.values[k], 'uniqueName$k')));
+          testMap[nameType1] = 'conflictingName';
+          testMap[nameType2] = testMap[nameType1]!;
+          await runTestGen(testMap);
+        });
+      }
+    }
+
+    test('input port name != internal signal name', () async {
+      await runTest(
+          RenameableModule(Logic(name: 'apple'), internalSignalName: 'apple'));
+    });
+    test('output port name != internal signal name', () async {
+      await runTest(RenameableModule(Logic(),
+          internalSignalName: 'apple', outputPortName: 'apple'));
+    });
+  });
+
   group('definition name', () {
     test('respected with no conflicts', () async {
       final mod = SpeciallyNamedModule(Logic(), false, false);
@@ -60,8 +182,9 @@ void main() {
       final mod = TopModule(Logic(), false, false);
       await mod.build();
       final sv = mod.generateSynth();
-      expect(sv, contains('specialNameInstance('));
-      expect(sv, contains('specialNameInstance_0('));
+
+      expect(sv, contains('specialInstanceName('));
+      expect(sv, contains('specialInstanceName_0('));
     });
     test('reserved throws exception with conflicts', () async {
       final mod = TopModule(Logic(), false, true);
diff --git a/test/swizzle_test.dart b/test/swizzle_test.dart
index 2f6ddf06..88beba08 100644
--- a/test/swizzle_test.dart
+++ b/test/swizzle_test.dart
@@ -37,7 +37,7 @@ void main() {
           equals(LogicValue.ofString('zx01')));
     });
   });
-  group('LogicValue', () {
+  group('LogicValue of multi-bit', () {
     test('simple swizzle', () {
       expect([LogicValue.ofString('10'), LogicValue.ofString('xz')].swizzle(),
           equals(LogicValue.ofString('10xz')));
@@ -47,6 +47,95 @@ void main() {
       expect([LogicValue.ofString('10'), LogicValue.ofString('xz')].rswizzle(),
           equals(LogicValue.ofString('xz10')));
     });
+
+    test('64-bit swizzle', () {
+      expect(
+          List.generate(
+              32,
+              (index) => index.isEven
+                  ? LogicValue.ofString('10')
+                  : LogicValue.ofString('xz')).swizzle(),
+          equals(LogicValue.ofString('10xz' * 16)));
+    });
+    test('>64-bit swizzle single concat', () {
+      final str1 = '10xz' * 16;
+      final str2 = 'xz10' * 10;
+      expect([LogicValue.ofString(str2), LogicValue.ofString(str1)].swizzle(),
+          equals(LogicValue.ofString(str2 + str1)));
+    });
+    test('>64-bit swizzle', () {
+      expect(
+          List.generate(
+              40,
+              (index) => index.isEven
+                  ? LogicValue.ofString('10')
+                  : LogicValue.ofString('xz')).swizzle(),
+          equals(LogicValue.ofString('10xz' * 20)));
+    });
+
+    group('variety of sizes', () {
+      test('smaller', () {
+        final bits = ['0', '1', 'x', 'z'];
+        final swizzleStrings = List.generate(
+            100,
+            (index) =>
+                bits[index % bits.length] * (index % 17) +
+                bits[(index + 1) % bits.length] * (index % 2));
+        expect(LogicValue.of(swizzleStrings.map(LogicValue.ofString)),
+            equals(LogicValue.ofString(swizzleStrings.reversed.join())));
+      });
+
+      test('larger', () {
+        final bits = ['0', '1', 'x', 'z'];
+        final swizzleStrings = List.generate(
+            1000,
+            (index) =>
+                bits[index % bits.length] * (index % 71) +
+                bits[(index + 1) % bits.length] * (index % 2));
+        expect(LogicValue.of(swizzleStrings.map(LogicValue.ofString)),
+            equals(LogicValue.ofString(swizzleStrings.reversed.join())));
+      });
+    });
+
+    group('filled', () {
+      test('simple swizzle', () {
+        expect(
+            [LogicValue.ofString('1' * 4), LogicValue.ofString('1' * 4)]
+                .swizzle(),
+            equals(LogicValue.ofString('1' * 8)));
+      });
+      test('big swizzle', () {
+        expect(List.generate(100, (index) => LogicValue.one).swizzle(),
+            equals(LogicValue.ofString('1' * 100)));
+      });
+      test('0-width both swizzle', () {
+        expect([LogicValue.ofString(''), LogicValue.ofString('')].swizzle(),
+            equals(LogicValue.ofString('')));
+      });
+      test('0-width lhs swizzle', () {
+        expect([LogicValue.ofString(''), LogicValue.ofString('111')].swizzle(),
+            equals(LogicValue.ofString('111')));
+      });
+      test('0-width rhs swizzle', () {
+        expect([LogicValue.ofString('111'), LogicValue.ofString('')].swizzle(),
+            equals(LogicValue.ofString('111')));
+      });
+    });
+
+    group('non-filled', () {
+      test('0-width both swizzle', () {
+        expect([LogicValue.ofString(''), LogicValue.ofString('')].swizzle(),
+            equals(LogicValue.ofString('')));
+      });
+      test('0-width lhs swizzle', () {
+        expect([LogicValue.ofString(''), LogicValue.ofString('1zx0')].swizzle(),
+            equals(LogicValue.ofString('1zx0')));
+      });
+      test('0-width rhs swizzle', () {
+        expect([LogicValue.ofString('1zx0'), LogicValue.ofString('')].swizzle(),
+            equals(LogicValue.ofString('1zx0')));
+      });
+    });
   });
 
   group('Logic', () {