Repository: intel/rohd
Pull request #445 (2023-12-13 00:49:50)

Support compiling ROHD to JavaScript
<!-- Please make sure you check out the contribution guidelines before submitting a pull request! -->

## Description & Motivation

ROHD is written in Dart, and Dart can compile to JavaScript, so it should be possible to compile ROHD to JavaScript.  In practice, there are a handful of differences when compiling Dart to JavaScript that make ROHD not quite work properly (mostly related to how numbers are handled).

See here for some details: https://dart.dev/guides/language/numbers#bitwise-operations

This PR makes changes so that ROHD can compile and run correctly in JavaScript.  Most of the test suite has been adjusted to be runnable (and meaningful) when compiled to JavaScript, which validates that most functionality works properly now.

Additionally, the changes have revealed bugs in the native Dart ROHD functionality, so this PR fixes those bugs in both platforms.

Some of the changes:

- Added `isZero` to `LogicValue`.
- Fixed bug where `clog2` was off-by-one on `width == INT_BITS` with only the upper-most bit set.
- Fixed crash bug when comparing two positive `LogicValue`s of `width == INT_BITS`
- Fixed bug where conversion of `BigInt`s to unsigned `int`s of width `< INT_BITS` could carry upper bits, causing incorrect functionality in places like arithmetic right shift under some scenarios.

*NOTE:* The translations test is failing in JavaScript, still not sure why.

## Related Issue(s)

N/A

## Testing

Added new tests, updated existing tests

## Backwards-compatibility

> Is this a breaking change that will not be backwards-compatible? If yes, how so?

Some minor things:
- Division (and modulo) by 0 in `LogicValue` now returns `x` instead of throwing an exception

## Documentation

> Does the change require any updates to documentation? If so, where? Are they included?

No



Patch:
diff --git a/.github/workflows/general.yml b/.github/workflows/general.yml
index c87c32aa..c0ff061a 100644
--- a/.github/workflows/general.yml
+++ b/.github/workflows/general.yml
@@ -31,6 +31,11 @@ jobs:
       - name: Setup Dart
         uses: dart-lang/setup-dart@v1
 
+      - name: Setup Node
+        uses: actions/setup-node@v4
+        with:
+          node-version: 20
+
       - name: Install project dependencies
         run: tool/gh_actions/install_dependencies.sh
 
diff --git a/lib/src/modules/gates.dart b/lib/src/modules/gates.dart
index a787406a..fad9bf2f 100644
--- a/lib/src/modules/gates.dart
+++ b/lib/src/modules/gates.dart
@@ -215,14 +215,7 @@ abstract class _TwoInputBitwiseGate extends Module with InlineSystemVerilog {
 
   /// Executes the functional behavior of this gate.
   void _execute() {
-    dynamic toPut;
-    try {
-      toPut = _op(_in0.value, _in1.value);
-    } on Exception {
-      // in case of things like divide by 0
-      toPut = LogicValue.x;
-    }
-    out.put(toPut);
+    out.put(_op(_in0.value, _in1.value));
   }
 
   @override
diff --git a/lib/src/utilities/simcompare.dart b/lib/src/utilities/simcompare.dart
index 95ac4b34..443dbd0d 100644
--- a/lib/src/utilities/simcompare.dart
+++ b/lib/src/utilities/simcompare.dart
@@ -14,6 +14,7 @@ import 'dart:io';
 
 import 'package:collection/collection.dart';
 import 'package:rohd/rohd.dart';
+import 'package:rohd/src/utilities/web.dart';
 import 'package:test/test.dart';
 
 /// Represents a single test case to check in a single clock cycle.
@@ -242,6 +243,11 @@ abstract class SimCompare {
     bool maskKnownWarnings = true,
     bool buildOnly = false,
   }) {
+    if (kIsWeb) {
+      // if running in web mode, then we can't run icarus verilog
+      return true;
+    }
+
     String signalDeclaration(String signalName) {
       final signal = module.signals.firstWhere((e) => e.name == signalName);
 
diff --git a/lib/src/utilities/web.dart b/lib/src/utilities/web.dart
new file mode 100644
index 00000000..132b5f22
--- /dev/null
+++ b/lib/src/utilities/web.dart
@@ -0,0 +1,39 @@
+// Copyright (C) 2021-2023 Intel Corporation
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// web.dart
+// Utilities for running ROHD safely on the web or in JavaScript.
+//
+// 2023 December 8
+// Author: Max Korbel <max.korbel@intel.com>
+
+import 'dart:math';
+
+import 'package:rohd/rohd.dart';
+
+/// Borrowed from Flutter's implementation to determine whether Dart is
+/// compiled to run on the web.  This is relevant for ROHD because when the
+/// code is compiled to JavaScript, it affects the ability for [LogicValue]
+/// to store different sizes of data in different implementations.
+///
+/// See more details here:
+/// https://api.flutter.dev/flutter/foundation/kIsWeb-constant.html
+// ignore: do_not_use_environment
+const bool kIsWeb = bool.fromEnvironment('dart.library.js_util');
+
+/// The number of bits in an int.
+// ignore: constant_identifier_names
+const int INT_BITS = kIsWeb ? 32 : 64;
+
+/// Calculates the `int` result of `1 << shamt` in a safe way considering
+/// whether it is run in JavaScript or native Dart.
+///
+/// In JavaScript, the shift amount is `&`ed with `0x1f`, so `1 << 32 == 0`.
+int oneSllBy(int shamt) {
+  if (kIsWeb) {
+    assert(shamt <= 52, 'Loss of precision in JavaScript beyond 53 bits.');
+    return pow(2, shamt) as int;
+  } else {
+    return 1 << shamt;
+  }
+}
diff --git a/lib/src/values/big_logic_value.dart b/lib/src/values/big_logic_value.dart
index 223c5693..8dbfc278 100644
--- a/lib/src/values/big_logic_value.dart
+++ b/lib/src/values/big_logic_value.dart
@@ -16,10 +16,12 @@ extension BigLogicValueBigIntUtilities on BigInt {
   ///
   /// Always interprets the number as unsigned, and thus never clamps to fit.
   int toIntUnsigned(int width) {
-    if (width > LogicValue._INT_BITS) {
+    if (width > INT_BITS) {
       throw Exception('Cannot convert Int when width $width'
-          ' is greater than ${LogicValue._INT_BITS}');
-    } else if (width == LogicValue._INT_BITS) {
+          ' is greater than $INT_BITS');
+    } else if (width == INT_BITS && INT_BITS > 32) {
+      assert(!kIsWeb, 'This code is not set up to work with JS.');
+
       // 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.
@@ -28,7 +30,9 @@ extension BigLogicValueBigIntUtilities on BigInt {
       return (this & mask).toInt() |
           (((this >> maskWidth) & mask).toInt() << maskWidth);
     } else {
-      return toInt();
+      return (this & _BigLogicValue._maskOfWidth(width))
+          .toInt()
+          .toSigned(INT_BITS);
     }
   }
 }
@@ -52,13 +56,13 @@ class _BigLogicValue extends LogicValue {
   }
 
   /// Constructs a new [_BigLogicValue], intended to hold values
-  /// with more than [_INT_BITS] bits.
+  /// with more than [INT_BITS] bits.
   ///
   /// Set [allowInefficientRepresentation] to `true` to bypass
   /// inefficient representation assertions.
   _BigLogicValue(BigInt value, BigInt invalid, int width,
       {bool allowInefficientRepresentation = false})
-      : assert(width > LogicValue._INT_BITS,
+      : assert(width > INT_BITS,
             '_BigLogicValue should only be used for large values'),
         super._(width) {
     _value = (_mask & value).toUnsigned(width);
@@ -95,7 +99,7 @@ class _BigLogicValue extends LogicValue {
   @override
   LogicValue _getRange(int start, int end) {
     final newWidth = end - start;
-    if (newWidth > LogicValue._INT_BITS) {
+    if (newWidth > INT_BITS) {
       return LogicValue._bigLogicValueOrFilled(
           (_value >> start) & _maskOfWidth(newWidth),
           (_invalid >> start) & _maskOfWidth(newWidth),
@@ -130,7 +134,7 @@ class _BigLogicValue extends LogicValue {
   int toInt() {
     final bigInt = toBigInt();
     if (bigInt.isValidInt) {
-      return bigInt.toIntUnsigned(LogicValue._INT_BITS);
+      return bigInt.toIntUnsigned(INT_BITS).toSigned(INT_BITS);
     } else {
       throw InvalidTruncationException(
           'LogicValue $this is too long to convert to int.'
@@ -241,4 +245,7 @@ class _BigLogicValue extends LogicValue {
 
   @override
   int get _intValue => _value.toIntUnsigned(width);
+
+  @override
+  late final bool isZero = _value == BigInt.zero && _invalid == BigInt.zero;
 }
diff --git a/lib/src/values/filled_logic_value.dart b/lib/src/values/filled_logic_value.dart
index 51436a60..2d84d932 100644
--- a/lib/src/values/filled_logic_value.dart
+++ b/lib/src/values/filled_logic_value.dart
@@ -94,7 +94,7 @@ class _FilledLogicValue extends LogicValue {
       return 0;
     } else if (!isValid) {
       throw InvalidValueOperationException(this, 'toInt');
-    } else if (width > LogicValue._INT_BITS) {
+    } else if (width > INT_BITS) {
       throw InvalidTruncationException(
           'LogicValue $this is too long to convert to int.'
           ' Use toBigInt() instead.');
@@ -340,4 +340,7 @@ class _FilledLogicValue extends LogicValue {
       (_value == _LogicValueEnum.one || _value == _LogicValueEnum.z)
           ? _SmallLogicValue._maskOfWidth(width)
           : 0;
+
+  @override
+  bool get isZero => _value == _LogicValueEnum.zero;
 }
diff --git a/lib/src/values/logic_value.dart b/lib/src/values/logic_value.dart
index 2f4e23d1..990b6f87 100644
--- a/lib/src/values/logic_value.dart
+++ b/lib/src/values/logic_value.dart
@@ -22,10 +22,6 @@ typedef LogicValues = LogicValue;
 /// [LogicValue] is unsigned.
 @immutable
 abstract class LogicValue implements Comparable<LogicValue> {
-  /// The number of bits in an int.
-  // ignore: constant_identifier_names
-  static const int _INT_BITS = 64;
-
   /// Logical value of `0`.
   static const LogicValue zero = _FilledLogicValue(_LogicValueEnum.zero, 1);
 
@@ -66,9 +62,9 @@ abstract class LogicValue implements Comparable<LogicValue> {
   /// of bits.
   ///
   /// [width] must be greater than or equal to 0.
-  static LogicValue ofInt(int value, int width) => width > _INT_BITS
+  static LogicValue ofInt(int value, int width) => width > INT_BITS
       ? _bigLogicValueOrFilled(
-          BigInt.from(value).toUnsigned(_INT_BITS), BigInt.zero, width)
+          BigInt.from(value).toUnsigned(INT_BITS), BigInt.zero, width)
       : _smallLogicValueOrFilled(value, 0, width);
 
   /// Converts `int` [value] to a valid [LogicValue] with [width]
@@ -82,7 +78,7 @@ abstract class LogicValue implements Comparable<LogicValue> {
   /// number of bits.
   ///
   /// [width] must be greater than or equal to 0.
-  static LogicValue ofBigInt(BigInt value, int width) => width > _INT_BITS
+  static LogicValue ofBigInt(BigInt value, int width) => width > INT_BITS
       ? _bigLogicValueOrFilled(value, BigInt.zero, width)
       : _smallLogicValueOrFilled(value.toIntUnsigned(width), 0, width);
 
@@ -144,7 +140,7 @@ abstract class LogicValue implements Comparable<LogicValue> {
     } else if (val is LogicValue) {
       width = val.width;
     } else {
-      throw UnsupportedTypeException(val, [int, BigInt, LogicValue]);
+      throw UnsupportedTypeException(val, const [int, BigInt, LogicValue]);
     }
 
     return LogicValue.of(val, width: width);
@@ -293,8 +289,8 @@ abstract class LogicValue implements Comparable<LogicValue> {
     } else if (val == null) {
       throw LogicValueConstructionException('Cannot construct from `null`.');
     } else {
-      throw UnsupportedTypeException(
-          val, [LogicValue, int, BigInt, bool, String, Iterable<LogicValue>]);
+      throw UnsupportedTypeException(val,
+          const [LogicValue, int, BigInt, bool, String, Iterable<LogicValue>]);
     }
   }
 
@@ -320,12 +316,12 @@ abstract class LogicValue implements Comparable<LogicValue> {
     // shifting BigInt's is expensive
     for (final lv in it) {
       final lvPlusSmall = lv.width + smallBuffer.width;
-      if (lvPlusSmall <= _INT_BITS) {
+      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;
+            INT_BITS * (lvPlusSmall ~/ INT_BITS) - smallBuffer.width;
         fullResult = lv
             .getRange(0, upperBound)
             ._concatenate(smallBuffer)
@@ -333,7 +329,7 @@ abstract class LogicValue implements Comparable<LogicValue> {
         smallBuffer = lv.getRange(upperBound, lv.width);
       }
 
-      assert(smallBuffer.width <= _INT_BITS,
+      assert(smallBuffer.width <= INT_BITS,
           'Keep smallBuffer small to meet invariants and efficiency');
     }
 
@@ -360,7 +356,7 @@ abstract class LogicValue implements Comparable<LogicValue> {
         other[0] == this[0]) {
       // can keep it filled
       return _FilledLogicValue(other._value, newWidth);
-    } else if (newWidth > LogicValue._INT_BITS) {
+    } else if (newWidth > INT_BITS) {
       // BigInt's only
       return _BigLogicValue(_bigIntValue << other.width | other._bigIntValue,
           _bigIntInvalid << other.width | other._bigIntInvalid, newWidth);
@@ -412,8 +408,9 @@ abstract class LogicValue implements Comparable<LogicValue> {
 
   /// Returns true if bits in [x] are all 1
   static bool _intIs1s(int x, int width) =>
-      x & _SmallLogicValue._maskOfWidth(width) ==
-      _SmallLogicValue._maskOfWidth(width);
+      (x ^ _SmallLogicValue._maskOfWidth(width)) &
+          _SmallLogicValue._maskOfWidth(width) ==
+      0;
 
   /// Returns a [String] representing the `_value` to be used by implementations
   /// relying on `_value` and `_invalid`.
@@ -455,7 +452,7 @@ abstract class LogicValue implements Comparable<LogicValue> {
     final invalidString = _invalidString(stringRepresentation);
     final width = stringRepresentation.length;
 
-    if (width <= _INT_BITS) {
+    if (width <= INT_BITS) {
       final value = _unsignedBinaryParse(valueString);
       final invalid = _unsignedBinaryParse(invalidString);
       return _smallLogicValueOrFilled(value, invalid, width);
@@ -469,10 +466,10 @@ abstract class LogicValue implements Comparable<LogicValue> {
   /// Returns either a [_BigLogicValue] or a [_FilledLogicValue] based on
   /// [value], [invalid], and [width].
   ///
-  /// Only use if [width] > [_INT_BITS].
+  /// Only use if [width] > [INT_BITS].
   static LogicValue _bigLogicValueOrFilled(
       BigInt value, BigInt invalid, int width) {
-    assert(width > _INT_BITS, 'Should only be used for big values');
+    assert(width > INT_BITS, 'Should only be used for big values');
 
     return _filledIfPossible(
           _bigIntIs1s(value, width),
@@ -487,10 +484,10 @@ abstract class LogicValue implements Comparable<LogicValue> {
   /// Returns either a [_BigLogicValue] or a [_FilledLogicValue] based on
   /// [value], [invalid], and [width].
   ///
-  /// Only use if [width] <= [_INT_BITS].
+  /// Only use if [width] <= [INT_BITS].
   static LogicValue _smallLogicValueOrFilled(
       int value, int invalid, int width) {
-    assert(width <= _INT_BITS, 'Should only be used for small values');
+    assert(width <= INT_BITS, 'Should only be used for small values');
 
     return _filledIfPossible(
           _intIs1s(value, width),
@@ -547,9 +544,11 @@ abstract class LogicValue implements Comparable<LogicValue> {
     if (other is! LogicValue) {
       return false;
     }
+
     if (other.width != width) {
       return false;
     }
+
     return _equals(other);
   }
 
@@ -591,8 +590,8 @@ abstract class LogicValue implements Comparable<LogicValue> {
   @override
   String toString({bool includeWidth = true}) {
     if (isValid && includeWidth) {
-      // for ==_INT_BITS, still use BigInt so we don't get negatives
-      final hexValue = width >= _INT_BITS
+      // for ==INT_BITS, still use BigInt so we don't get negatives
+      final hexValue = width >= INT_BITS
           ? toBigInt().toUnsigned(width).toRadixString(16)
           : toInt().toRadixString(16);
       return "$width'h$hexValue";
@@ -630,7 +629,7 @@ abstract class LogicValue implements Comparable<LogicValue> {
       throw ValueWidthMismatchException(this, other);
     }
 
-    if (width > _INT_BITS || other.width > _INT_BITS) {
+    if (width > INT_BITS || other.width > INT_BITS) {
       final a = toBigInt();
       final b = other.toBigInt();
       final valueZero = BigInt.zero;
@@ -926,7 +925,7 @@ abstract class LogicValue implements Comparable<LogicValue> {
     if (!isValid) {
       return LogicValue.filled(width, LogicValue.x);
     }
-    if (width > _INT_BITS) {
+    if (width > INT_BITS) {
       final a = toBigInt();
       return LogicValue.ofBigInt(op(a, width) as BigInt, width);
     } else {
@@ -941,12 +940,19 @@ abstract class LogicValue implements Comparable<LogicValue> {
   /// Returns ceil of log base 2 of [a]  having same type as of input [a].
   static dynamic _clog2(dynamic a, int width) {
     if (a is int) {
-      return a < 0 ? width : (a - 1).bitLength;
+      return a < 0
+          ? (a << 1 == 0) // handle b100000... of INT_BITS width
+              ? width - 1
+              : width
+          : (a - 1).bitLength;
     }
 
     if (a is BigInt) {
+      assert(a >= BigInt.zero, 'Expected only positive BigInt here');
       return a < BigInt.zero
-          ? BigInt.from(width)
+          ? (a << 1 == BigInt.zero)
+              ? BigInt.from(width - 1)
+              : BigInt.from(width)
           : BigInt.from((a - BigInt.one).bitLength);
     }
   }
@@ -956,13 +962,13 @@ abstract class LogicValue implements Comparable<LogicValue> {
   /// Handles width and bounds checks as well as proper conversion between
   /// different types of representation.
   ///
-  /// If the math [isDivision], then 64-bit ([_INT_BITS]) operations have some
+  /// If the math [isDivision], then 64-bit ([INT_BITS]) operations have some
   /// special consideration for two's complement math, so it will use an
   /// unsigned [BigInt] for math.
   LogicValue _doMath(dynamic other, dynamic Function(dynamic a, dynamic b) op,
       {bool isDivision = false}) {
     if (!(other is int || other is LogicValue || other is BigInt)) {
-      throw UnsupportedTypeException(other, [int, LogicValue, BigInt]);
+      throw UnsupportedTypeException(other, const [int, LogicValue, BigInt]);
     }
 
     if (other is LogicValue && other.width != width) {
@@ -973,11 +979,18 @@ abstract class LogicValue implements Comparable<LogicValue> {
       return LogicValue.filled(width, LogicValue.x);
     }
 
+    if (isDivision &&
+        (other == 0 ||
+            other == BigInt.zero ||
+            (other is LogicValue && other.isZero))) {
+      return LogicValue.filled(width, LogicValue.x);
+    }
+
     if (other is LogicValue && !other.isValid) {
       return LogicValue.filled(other.width, LogicValue.x);
     }
 
-    final widthComparison = isDivision ? _INT_BITS - 1 : _INT_BITS;
+    final widthComparison = isDivision ? INT_BITS - 1 : INT_BITS;
 
     if (width > widthComparison ||
         (other is LogicValue && other.width > widthComparison)) {
@@ -985,7 +998,7 @@ abstract class LogicValue implements Comparable<LogicValue> {
       final b = other is BigInt
           ? other
           : other is int
-              ? BigInt.from(other).toUnsigned(_INT_BITS)
+              ? BigInt.from(other).toUnsigned(INT_BITS)
               : (other as LogicValue).toBigInt();
       return LogicValue.ofBigInt(op(a, b) as BigInt, width);
     } else {
@@ -995,6 +1008,9 @@ abstract class LogicValue implements Comparable<LogicValue> {
     }
   }
 
+  /// Returns true if this value is `0`.
+  bool get isZero;
+
   /// Equal-to operation.
   ///
   /// This is different from [==] because it returns a [LogicValue] instead
@@ -1055,7 +1071,7 @@ abstract class LogicValue implements Comparable<LogicValue> {
       } else if (!exponent.isValidInt) {
         throw InvalidTruncationException(
             "BigInt (${exponent.bitLength} bits) won't fit in "
-            'int ($_INT_BITS bits)');
+            'int ($INT_BITS bits)');
       } else {
         return base.pow(exponent.toInt());
       }
@@ -1068,7 +1084,7 @@ abstract class LogicValue implements Comparable<LogicValue> {
   /// different types of representation.
   LogicValue _doCompare(dynamic other, bool Function(dynamic a, dynamic b) op) {
     if (!(other is int || other is LogicValue || other is BigInt)) {
-      throw UnsupportedTypeException(other, [int, LogicValue, BigInt]);
+      throw UnsupportedTypeException(other, const [int, LogicValue, BigInt]);
     }
 
     if (other is LogicValue && other.width != width) {
@@ -1084,39 +1100,50 @@ abstract class LogicValue implements Comparable<LogicValue> {
 
     dynamic a;
     dynamic b;
-    if (width > _INT_BITS || (other is LogicValue && other.width > _INT_BITS)) {
+    if (width > INT_BITS || (other is LogicValue && other.width > INT_BITS)) {
       a = toBigInt();
       b = other is BigInt
           ? other
           : other is int
-              ? BigInt.from(other).toUnsigned(_INT_BITS)
+              ? BigInt.from(other).toUnsigned(INT_BITS)
               : (other as LogicValue).toBigInt();
     } else {
-      if (width < _INT_BITS) {
+      if (width < INT_BITS) {
         a = toInt();
         b = other is int ? other : (other as LogicValue).toInt();
       } else {
-        // Here we now know: width == _INT_BITS
+        // Here we now know: width == INT_BITS
         final ai = toInt();
         final bi = other is int ? other : (other as LogicValue).toInt();
+
         if ((ai < 0) || (bi < 0)) {
-          final abig = LogicValue.ofBigInt(BigInt.from(ai), _INT_BITS + 1);
-          final bbig = LogicValue.ofBigInt(BigInt.from(bi), _INT_BITS + 1);
+          final abig = LogicValue.ofBigInt(BigInt.from(ai), INT_BITS + 1);
+          final bbig = LogicValue.ofBigInt(BigInt.from(bi), INT_BITS + 1);
           return abig._doCompare(bbig, op);
         }
+
+        a = ai;
+        b = bi;
       }
     }
+
     return op(a, b) ? LogicValue.one : LogicValue.zero;
   }
 
   /// Arithmetic right-shift operation.
+  ///
+  /// Shifted in bits will match the sign (upper-most bit).
   LogicValue operator >>(dynamic shamt) =>
       _shift(shamt, _ShiftType.arithmeticRight);
 
   /// Logical left-shift operation.
+  ///
+  /// Shifted in bits are all 0.
   LogicValue operator <<(dynamic shamt) => _shift(shamt, _ShiftType.left);
 
   /// Logical right-shift operation.
+  ///
+  /// Shifted in bits are all 0.
   LogicValue operator >>>(dynamic shamt) => _shift(shamt, _ShiftType.right);
 
   /// Performs a shift by a huge amount (more than [width]).
@@ -1143,7 +1170,7 @@ abstract class LogicValue implements Comparable<LogicValue> {
         return LogicValue.filled(width, LogicValue.x);
       }
 
-      if (shamt.width > _INT_BITS) {
+      if (shamt.width > INT_BITS) {
         shamtNum = shamt.toBigInt();
       } else {
         shamtNum = shamt.toInt();
@@ -1160,7 +1187,7 @@ abstract class LogicValue implements Comparable<LogicValue> {
       }
 
       assert(
-          shamtNum <= BigInt.from(-1).toUnsigned(_INT_BITS),
+          shamtNum <= BigInt.from(-1).toUnsigned(INT_BITS),
           'It should not be possible for the shift amount to be less '
           'than the width, but more than fits in an int.');
 
@@ -1169,11 +1196,11 @@ abstract class LogicValue implements Comparable<LogicValue> {
 
       shamtInt = shamtNum.toInt();
     } else {
-      throw UnsupportedTypeException(shamt, [int, BigInt, LogicValue]);
+      throw UnsupportedTypeException(shamt, const [int, BigInt, LogicValue]);
     }
 
     if (shamtInt < 0) {
-      // since we're limited in width to 2^(_INT_BITS-1) (must be positive),
+      // since we're limited in width to 2^(INT_BITS-1) (must be positive),
       // we know that any negative shift amount must quality as "huge"
       return _shiftHuge(direction);
     }
@@ -1364,9 +1391,11 @@ int bin(String s) => _unsignedBinaryParse(s.replaceAll('_', ''));
 int _unsignedBinaryParse(String source) {
   final val = int.tryParse(source, radix: 2);
   if (val != null) {
-    return val;
+    return val.toSigned(INT_BITS);
   } else {
-    return BigInt.parse(source, radix: 2).toIntUnsigned(source.length);
+    return BigInt.parse(source, radix: 2)
+        .toIntUnsigned(source.length)
+        .toSigned(INT_BITS);
   }
 }
 
diff --git a/lib/src/values/random_logic_value.dart b/lib/src/values/random_logic_value.dart
index 72912c14..173c4fa5 100644
--- a/lib/src/values/random_logic_value.dart
+++ b/lib/src/values/random_logic_value.dart
@@ -16,7 +16,8 @@ extension RandLogicValue on math.Random {
   BigInt _nextBigInt({required int numBits}) {
     var result = BigInt.zero;
     for (var i = 0; i < numBits; i += 32) {
-      result = (result << 32) | BigInt.from(nextInt(1 << 32));
+      // BigInt is safe with it, though
+      result = (result << 32) | BigInt.from(nextInt(oneSllBy(32)));
     }
     return result & ((BigInt.one << numBits) - BigInt.one);
   }
@@ -67,13 +68,12 @@ extension RandLogicValue on math.Random {
 
       return LogicValue.ofString(bitString.toString());
     } else {
-      if (width <= LogicValue._INT_BITS) {
+      if (width <= INT_BITS) {
         final ranNum = width <= 32
-            ? LogicValue.ofInt(nextInt(1 << width), width)
+            ? LogicValue.ofInt(nextInt(oneSllBy(width)), width)
             : LogicValue.ofInt(_nextBigInt(numBits: width).toInt(), width);
 
-        if (max == null ||
-            (max is BigInt && max.bitLength > LogicValue._INT_BITS)) {
+        if (max == null || (max is BigInt && max.bitLength > INT_BITS)) {
           return ranNum;
         } else {
           return LogicValue.ofInt(
diff --git a/lib/src/values/small_logic_value.dart b/lib/src/values/small_logic_value.dart
index 62962c4c..12c8c198 100644
--- a/lib/src/values/small_logic_value.dart
+++ b/lib/src/values/small_logic_value.dart
@@ -35,30 +35,35 @@ class _SmallLogicValue extends LogicValue {
   static final Map<int, int> _masksOfWidth = HashMap();
   static int _maskOfWidth(int width) {
     if (!_masksOfWidth.containsKey(width)) {
-      _masksOfWidth[width] = (1 << width) - 1;
+      _masksOfWidth[width] =
+          (oneSllBy(width) - 1).toUnsigned(width).toSigned(INT_BITS);
     }
     return _masksOfWidth[width]!;
   }
 
   /// Constructs a new [_SmallLogicValue], intended to hold values
-  /// between 1 and [_INT_BITS] bits, inclusive.
+  /// between 1 and [INT_BITS] bits, inclusive.
   ///
   /// Set [allowInefficientRepresentation] to `true` to bypass
   /// inefficient representation assertions.
   const _SmallLogicValue(int value, int invalid, super.width,
       {bool allowInefficientRepresentation = false})
-      : assert(width <= LogicValue._INT_BITS,
-            '_SmallLogicValue should have low number of bits'),
-        assert(width != 0, '_SmallLogicValue should have at least one bit'),
+      : assert(width <= INT_BITS,
+            '_SmallLogicValue should have low number of bits ($width found)'),
+        assert(width != 0,
+            '_SmallLogicValue should have at least one bit ($width found)'),
         assert(
             allowInefficientRepresentation ||
-                !(((value & (1 << width) - 1) == (1 << width) - 1 ||
-                        (value & (1 << width) - 1) == 0) &&
-                    ((invalid & (1 << width) - 1) == (1 << width) - 1 ||
-                        (invalid & (1 << width) - 1) == 0)),
-            'Should not be expressable as filled'),
-        _value = ((1 << width) - 1) & value,
-        _invalid = ((1 << width) - 1) & invalid,
+                !(((value & ((1 << width - 1) * 2) - 1) ==
+                            ((1 << width - 1) * 2) - 1 ||
+                        (value & ((1 << width - 1) * 2) - 1) == 0) &&
+                    ((invalid & ((1 << width - 1) * 2) - 1) ==
+                            ((1 << width - 1) * 2) - 1 ||
+                        (invalid & ((1 << width - 1) * 2) - 1) == 0)),
+            'Should not be expressable as filled: '
+            '(value: $value, invalid: $invalid)'),
+        _value = (((1 << width - 1) * 2) - 1) & value,
+        _invalid = (((1 << width - 1) * 2) - 1) & invalid,
         super._();
 
   @override
@@ -66,9 +71,11 @@ class _SmallLogicValue extends LogicValue {
     if (other is _FilledLogicValue) {
       return other == this;
     }
+
     if (other is! _SmallLogicValue) {
       return false;
     }
+
     return _value == other._value && _invalid == other._invalid;
   }
 
@@ -109,7 +116,7 @@ class _SmallLogicValue extends LogicValue {
     if (_invalid != 0) {
       throw Exception('Cannot convert invalid LogicValue to int: $this');
     }
-    return _value;
+    return _value.toSigned(INT_BITS);
   }
 
   @override
@@ -186,11 +193,15 @@ class _SmallLogicValue extends LogicValue {
   LogicValue _shiftArithmeticRight(int shamt) {
     final upperMostBit = this[-1];
 
-    var value =
-        ((_value | (upperMostBit == LogicValue.one ? ~_mask : 0)) >> shamt) &
-            _mask;
+    // bits affected by the sign
+    final upperMask = ~_maskOfWidth(width - shamt);
+
+    var value = _value >>> shamt;
+    if (upperMostBit == LogicValue.one) {
+      value |= upperMask;
+    }
 
-    var invalid = _invalid >> shamt;
+    var invalid = _invalid >>> shamt;
 
     // if uppermost bit is invalid, then turn the shifted bits into X's
     if (!upperMostBit.isValid) {
@@ -198,7 +209,7 @@ class _SmallLogicValue extends LogicValue {
       value &= _mask >>> shamt;
 
       // for affected bits of invalid: make sure they are high
-      invalid |= ~_mask >> shamt;
+      invalid |= upperMask;
     }
 
     return LogicValue._smallLogicValueOrFilled(value, invalid, width);
@@ -217,4 +228,7 @@ class _SmallLogicValue extends LogicValue {
 
   @override
   int get _intValue => _value;
+
+  @override
+  bool get isZero => _value == 0 && _invalid == 0;
 }
diff --git a/lib/src/values/values.dart b/lib/src/values/values.dart
index 685ab4b6..b58ce040 100644
--- a/lib/src/values/values.dart
+++ b/lib/src/values/values.dart
@@ -10,6 +10,8 @@ import 'package:meta/meta.dart';
 import 'package:rohd/rohd.dart';
 import 'package:rohd/src/exceptions/logic_value/invalid_random_logic_value_exception.dart';
 import 'package:rohd/src/utilities/index_utilities.dart';
+import 'package:rohd/src/utilities/web.dart';
+
 part 'logic_value.dart';
 part 'small_logic_value.dart';
 part 'big_logic_value.dart';
diff --git a/tool/gh_actions/install_node.sh b/tool/gh_actions/install_node.sh
new file mode 100755
index 00000000..c98ce3e2
--- /dev/null
+++ b/tool/gh_actions/install_node.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Copyright (C) 2023 Intel Corporation
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# install_node.sh
+# Install Node
+#
+# 2023 December 12
+# Author: Max Korbel <max.korbel@intel.com>
+
+
+set -euo pipefail
+
+# based on https://deb.nodesource.com/ to get a recent version of node
+
+sudo apt-get update && sudo apt-get install -y ca-certificates curl gnupg
+curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
+NODE_MAJOR=20
+echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
+sudo apt-get update && sudo apt-get install nodejs -y
\ No newline at end of file
diff --git a/tool/gh_codespaces/run_setup.sh b/tool/gh_codespaces/run_setup.sh
index f32d372c..6523e414 100755
--- a/tool/gh_codespaces/run_setup.sh
+++ b/tool/gh_codespaces/run_setup.sh
@@ -19,3 +19,6 @@ tool/gh_actions/install_dependencies.sh
 
 # Install Icarus Verilog.
 tool/gh_actions/install_iverilog.sh
+
+# Install Node
+tool/gh_actions/install_node.sh
\ No newline at end of file


Test patch:
diff --git a/test/benchmark_test.dart b/test/benchmark_test.dart
index 663d7d4a..83dd702d 100644
--- a/test/benchmark_test.dart
+++ b/test/benchmark_test.dart
@@ -7,6 +7,7 @@
 // 2022 September 28
 // Author: Max Korbel <max.korbel@intel.com>
 
+import 'package:rohd/src/utilities/web.dart';
 import 'package:test/test.dart';
 
 import '../benchmark/byte_enable_benchmark.dart';
@@ -31,9 +32,11 @@ void main() {
       ByteEnableBenchmark().measure();
     });
 
-    test('waveform', () async {
-      await WaveDumpBenchmark().measure();
-    });
+    if (!kIsWeb) {
+      test('waveform', () async {
+        await WaveDumpBenchmark().measure();
+      });
+    }
 
     group('many seq and comb', () {
       for (final connectionType in ManySeqAndCombCombConnectionType.values) {
diff --git a/test/config_test.dart b/test/config_test.dart
index daff6568..28cd2e7d 100644
--- a/test/config_test.dart
+++ b/test/config_test.dart
@@ -11,6 +11,7 @@
 import 'dart:io';
 import 'package:rohd/rohd.dart';
 import 'package:rohd/src/utilities/config.dart';
+import 'package:rohd/src/utilities/web.dart';
 import 'package:test/test.dart';
 import 'package:yaml/yaml.dart';
 import 'wave_dumper_test.dart';
@@ -28,14 +29,16 @@ class SimpleModule extends Module {
 }
 
 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']));
-  });
+  if (!kIsWeb) {
+    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;
@@ -48,22 +51,25 @@ void main() async {
     expect(sv, contains(version));
   });
 
-  test('should contains ROHD version number when wavedumper is generated.',
-      () async {
-    const version = Config.version;
+  if (!kIsWeb) {
+    test('should contains ROHD version number when wavedumper is generated.',
+        () async {
+      const version = Config.version;
 
-    final mod = SimpleModule(Logic(), Logic());
-    await mod.build();
+      final mod = SimpleModule(Logic(), Logic());
+      await mod.build();
 
-    const dumpName = 'simplemodule';
+      const dumpName = 'simplemodule';
 
-    createTemporaryDump(mod, dumpName);
+      createTemporaryDump(mod, dumpName);
 
-    await Simulator.run();
+      await Simulator.run();
 
-    final vcdContents = await File(temporaryDumpPath(dumpName)).readAsString();
-    expect(vcdContents, contains(version));
+      final vcdContents =
+          await File(temporaryDumpPath(dumpName)).readAsString();
+      expect(vcdContents, contains(version));
 
-    deleteTemporaryDump(dumpName);
-  });
+      deleteTemporaryDump(dumpName);
+    });
+  }
 }
diff --git a/test/fsm_test.dart b/test/fsm_test.dart
index 50ca502e..7c408865 100644
--- a/test/fsm_test.dart
+++ b/test/fsm_test.dart
@@ -11,6 +11,7 @@ import 'dart:io';
 
 import 'package:rohd/rohd.dart';
 import 'package:rohd/src/utilities/simcompare.dart';
+import 'package:rohd/src/utilities/web.dart';
 import 'package:test/test.dart';
 
 enum MyStates { state1, state2, state3, state4 }
@@ -44,8 +45,12 @@ class TestModule extends Module {
       ]),
     ];
 
-    FiniteStateMachine<MyStates>(clk, reset, MyStates.state1, states)
-        .generateDiagram(outputPath: _simpleFSMPath);
+    final fsm =
+        FiniteStateMachine<MyStates>(clk, reset, MyStates.state1, states);
+
+    if (!kIsWeb) {
+      fsm.generateDiagram(outputPath: _simpleFSMPath);
+    }
   }
 }
 
@@ -126,12 +131,16 @@ class TrafficTestModule extends Module {
       ),
     ];
 
-    FiniteStateMachine<LightStates>(
+    final fsm = FiniteStateMachine<LightStates>(
       clk,
       reset,
       LightStates.northFlowing,
       states,
-    ).generateDiagram(outputPath: _trafficFSMPath);
+    );
+
+    if (!kIsWeb) {
+      fsm.generateDiagram(outputPath: _trafficFSMPath);
+    }
   }
 }
 
@@ -140,7 +149,11 @@ void main() {
     await Simulator.reset();
   });
 
-  setUpAll(() => Directory(_tmpDir).createSync(recursive: true));
+  setUpAll(() {
+    if (!kIsWeb) {
+      Directory(_tmpDir).createSync(recursive: true);
+    }
+  });
 
   test('zero-out receivers in default case', () async {
     final pipem = TestModule(Logic(), Logic(), Logic());
@@ -245,6 +258,10 @@ void main() {
 }
 
 void verifyMermaidStateDiagram(String filePath) {
+  if (kIsWeb) {
+    return;
+  }
+
   // check if the diagram exist
   final file = File(filePath);
   final existDiagram = file.existsSync();
diff --git a/test/gate_test.dart b/test/gate_test.dart
index f53bba3f..36f03f4a 100644
--- a/test/gate_test.dart
+++ b/test/gate_test.dart
@@ -605,22 +605,36 @@ void main() {
           }, {
             'a_lshift_const': 0,
             'a_rshift_const': 0,
-            'a_arshift_const': LogicValue.filled(40, LogicValue.one),
+            'a_arshift_const': LogicValue.filled(40, LogicValue.zero),
             'a_lshift_b': 0,
             'a_rshift_b': 0,
-            'a_arshift_b': LogicValue.filled(40, LogicValue.one),
+            'a_arshift_b': LogicValue.filled(40, LogicValue.zero),
           }),
         ];
         await SimCompare.checkFunctionalVector(gtm, vectors);
         SimCompare.checkIverilogVector(gtm, vectors);
       });
 
-      // test plan:
-      // - large logic shifted by int
-      // - large logic shifted by (big) BigInt
-      // - large logic shifted by large logic
-      // - small logic shifted by large logic
-      // excessively large number (>64bits)
+      test('very small logic shifted by huge value on large bus', () async {
+        final gtm = ShiftTestModule(Logic(width: 20), Logic(width: 200),
+            constant: BigInt.one << 100);
+        await gtm.build();
+        final vectors = [
+          Vector({
+            'a': BigInt.one << 200 | BigInt.one << 100,
+            'b': BigInt.one << 100
+          }, {
+            'a_lshift_const': 0,
+            'a_rshift_const': 0,
+            'a_arshift_const': LogicValue.filled(20, LogicValue.zero),
+            'a_lshift_b': 0,
+            'a_rshift_b': 0,
+            'a_arshift_b': LogicValue.filled(20, LogicValue.zero),
+          }),
+        ];
+        await SimCompare.checkFunctionalVector(gtm, vectors);
+        SimCompare.checkIverilogVector(gtm, vectors);
+      });
     });
 
     test('And2Gate single bit', () async {
diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart
index 2b1670da..60a90bc4 100644
--- a/test/logic_array_test.dart
+++ b/test/logic_array_test.dart
@@ -12,6 +12,7 @@ import 'dart:math';
 import 'package:collection/collection.dart';
 import 'package:rohd/rohd.dart';
 import 'package:rohd/src/utilities/simcompare.dart';
+import 'package:rohd/src/utilities/web.dart';
 import 'package:test/test.dart';
 
 class SimpleLAPassthrough extends Module {
@@ -568,9 +569,10 @@ void main() {
       final rand = Random(1234);
       final values = List.generate(
           10,
-          (index) => LogicValue.ofInt(rand.nextInt(1 << randWidth), randWidth)
-              .replicate(mod.laOut.width ~/ randWidth + 1)
-              .getRange(0, mod.laOut.width));
+          (index) =>
+              LogicValue.ofInt(rand.nextInt(oneSllBy(randWidth)), randWidth)
+                  .replicate(mod.laOut.width ~/ randWidth + 1)
+                  .getRange(0, mod.laOut.width));
 
       final vectors = [
         for (final value in values) Vector({'laIn': value}, {'laOut': value})
diff --git a/test/logic_value_test.dart b/test/logic_value_test.dart
index 2429c1d7..a04c4740 100644
--- a/test/logic_value_test.dart
+++ b/test/logic_value_test.dart
@@ -11,6 +11,7 @@ import 'dart:math';
 
 import 'package:rohd/rohd.dart';
 import 'package:rohd/src/exceptions/logic_value/invalid_random_logic_value_exception.dart';
+import 'package:rohd/src/utilities/web.dart';
 import 'package:test/test.dart';
 
 // All logicvalues to support trying all possiblities
@@ -20,6 +21,15 @@ const allLv = [LogicValue.zero, LogicValue.one, LogicValue.x, LogicValue.z];
 const lv = LogicValue.ofString;
 LogicValue large(LogicValue lv) => LogicValue.filled(100, lv);
 
+int repeatedInt(int value, int width, int times) {
+  var result = 0;
+  for (var i = 0; i < times; i++) {
+    result <<= width;
+    result |= value;
+  }
+  return result.toSigned(INT_BITS);
+}
+
 void main() {
   test('bin with underscores', () {
     var x = bin('11_1');
@@ -95,12 +105,13 @@ void main() {
     });
 
     test('big unsigned int string', () {
-      expect(LogicValue.ofString('1' * 64), equals(LogicValue.ofInt(-1, 64)));
+      expect(LogicValue.ofString('1' * INT_BITS),
+          equals(LogicValue.ofInt(-1, INT_BITS)));
     });
 
     test('-1 int vs. 64 1s for big width', () {
       expect(LogicValue.ofInt(-1, 100),
-          LogicValue.ofBigInt((BigInt.one << 64) - BigInt.one, 100));
+          LogicValue.ofBigInt((BigInt.one << INT_BITS) - BigInt.one, 100));
     });
 
     test('invalid string ofString', () {
@@ -414,6 +425,8 @@ void main() {
   });
 
   group('LogicValue toString', () {
+    const intBitsMinus4 = INT_BITS - 4;
+
     test('1 bit', () {
       expect(LogicValue.one.toString(), "1'h1");
     });
@@ -423,20 +436,22 @@ void main() {
     });
 
     test('<64-bit positive', () {
-      expect(LogicValue.ofInt(0x1234, 60).toString(), "60'h1234");
+      expect(LogicValue.ofInt(0x1234, intBitsMinus4).toString(),
+          "$intBitsMinus4'h1234");
     });
 
     test('<64-bit negative', () {
-      expect(LogicValue.ofInt(-1, 60).toString(), "60'hfffffffffffffff");
+      expect(LogicValue.ofInt(-1, intBitsMinus4).toString(),
+          "$intBitsMinus4'h${'f' * (intBitsMinus4 ~/ 4)}");
     });
 
     test('64-bit positive', () {
-      expect(LogicValue.ofInt(0x1234, 64).toString(), "64'h1234");
+      expect(LogicValue.ofInt(0x1234, INT_BITS).toString(), "$INT_BITS'h1234");
     });
 
     test('64-bit negative', () {
-      expect(LogicValue.ofInt(0xfaaaaaaa00000005, 64).toString(),
-          "64'hfaaaaaaa00000005");
+      expect(LogicValue.ofInt(-11, INT_BITS).toString(),
+          "$INT_BITS'h${'f' * (INT_BITS ~/ 4 - 1)}5");
     });
 
     test('>64-bit positive', () {
@@ -633,61 +648,83 @@ void main() {
     });
 
     test('small int boundary shift right logical', () {
+      const lower = 14;
+      const upper = INT_BITS - lower;
+      const shamt = 20;
+      const remainder = INT_BITS - shamt;
+
       // at boundary
-      expect((LogicValue.ofInt(-5, 64) >>> 28).toInt(), 0xfffffffff);
-      expect(LogicValue.ofString('x' * 50 + '0' * 14) >>> 20,
-          LogicValue.ofString('0' * 20 + 'x' * 44));
-      expect(LogicValue.ofString('z' * 50 + '0' * 14) >>> 20,
-          LogicValue.ofString('0' * 20 + 'z' * 44));
+      expect((LogicValue.ofInt(-5, INT_BITS) >>> 24).toInt(),
+          repeatedInt(0xf, 4, (INT_BITS - 24) ~/ 4));
+      expect(LogicValue.ofString('x' * upper + '0' * lower) >>> shamt,
+          LogicValue.ofString('0' * shamt + 'x' * remainder));
+      expect(LogicValue.ofString('z' * upper + '0' * lower) >>> shamt,
+          LogicValue.ofString('0' * shamt + 'z' * remainder));
 
       // below boundary
-      expect((LogicValue.ofInt(-5, 60) >>> 28).toInt(), 0xffffffff);
-      expect(LogicValue.ofString('x' * 50 + '0' * 13) >>> 20,
-          LogicValue.ofString('0' * 20 + 'x' * 43));
-      expect(LogicValue.ofString('z' * 50 + '0' * 13) >>> 20,
-          LogicValue.ofString('0' * 20 + 'z' * 43));
+      expect((LogicValue.ofInt(-5, INT_BITS - 4) >>> 24).toInt(),
+          repeatedInt(0xf, 4, (INT_BITS - 4 - 24) ~/ 4));
+      expect(LogicValue.ofString('x' * upper + '0' * (lower - 1)) >>> shamt,
+          LogicValue.ofString('0' * shamt + 'x' * (remainder - 1)));
+      expect(LogicValue.ofString('z' * upper + '0' * (lower - 1)) >>> shamt,
+          LogicValue.ofString('0' * shamt + 'z' * (remainder - 1)));
     });
 
     test('small int boundary shift left logical', () {
+      const upper = 14;
+      const lower = INT_BITS - upper;
+      const shamt = 20;
+      const remainder = INT_BITS - shamt;
+
       // at boundary
-      expect((LogicValue.ofInt(-1, 64) << 20).toInt(), -1 << 20);
-      expect(LogicValue.ofString('0' * 14 + 'x' * 50) << 20,
-          LogicValue.ofString('x' * 44 + '0' * 20));
-      expect(LogicValue.ofString('0' * 14 + 'z' * 50) << 20,
-          LogicValue.ofString('z' * 44 + '0' * 20));
+      expect(
+          (LogicValue.ofInt(-1, INT_BITS) << shamt).toInt(),
+          (repeatedInt(0xf, 4, (INT_BITS - shamt) ~/ 4) << shamt)
+              .toSigned(INT_BITS));
+      expect(LogicValue.ofString('0' * upper + 'x' * lower) << shamt,
+          LogicValue.ofString('x' * remainder + '0' * shamt));
+      expect(LogicValue.ofString('0' * upper + 'z' * lower) << shamt,
+          LogicValue.ofString('z' * remainder + '0' * shamt));
 
       // below boundary
-      expect((LogicValue.ofInt(-1, 32) << 24).toInt(), 0xff000000);
-      expect(LogicValue.ofString('0' * 14 + 'x' * 49) << 20,
-          LogicValue.ofString('x' * 43 + '0' * 20));
-      expect(LogicValue.ofString('0' * 14 + 'z' * 49) << 20,
-          LogicValue.ofString('z' * 43 + '0' * 20));
+      expect((LogicValue.ofInt(-1, 28) << 24).toInt(), 0xf000000);
+      expect(LogicValue.ofString('0' * upper + 'x' * (lower - 1)) << shamt,
+          LogicValue.ofString('x' * (remainder - 1) + '0' * shamt));
+      expect(LogicValue.ofString('0' * upper + 'z' * (lower - 1)) << shamt,
+          LogicValue.ofString('z' * (remainder - 1) + '0' * shamt));
     });
 
     test('small int boundary shift right arithmetic', () {
+      const lower = 14;
+      const upper = INT_BITS - lower;
+      const shamt1 = 20;
+      const shamt2 = 10;
+      const remainder = INT_BITS - shamt1;
+
       // at boundary
-      expect((LogicValue.ofInt(0xffff, 64) >> 8).toInt(), 0xff);
-      expect((LogicValue.ofInt(-5, 64) >> 20).toInt(), -1);
-      expect(LogicValue.ofString('x' * 50 + '0' * 14) >> 20,
-          LogicValue.filled(64, LogicValue.x));
-      expect(LogicValue.ofString('x' * 50 + '0' * 14) >> 10,
-          LogicValue.ofString('x' * 60 + '0' * 4));
-      expect(LogicValue.ofString('z' * 50 + '0' * 14) >> 20,
-          LogicValue.ofString('x' * 20 + 'z' * 44));
+      expect((LogicValue.ofInt(0xffff, INT_BITS) >> 8).toInt(), 0xff);
+      expect((LogicValue.ofInt(-5, INT_BITS) >> shamt1).toInt(), -1);
+      expect(LogicValue.ofString('x' * upper + '0' * lower) >> shamt1,
+          LogicValue.filled(INT_BITS, LogicValue.x));
+      expect(LogicValue.ofString('x' * upper + '0' * lower) >> shamt2,
+          LogicValue.ofString('x' * (upper + shamt2) + '0' * 4));
+      expect(LogicValue.ofString('z' * upper + '0' * lower) >> shamt1,
+          LogicValue.ofString('x' * shamt1 + 'z' * remainder));
 
       // below boundary
-      expect((LogicValue.ofInt(0xffff, 63) >> 8).toInt(), 0xff);
-      expect((LogicValue.ofInt(-5, 63) >> 20).toInt(), -1 >>> 1);
-      expect(LogicValue.ofString('x' * 50 + '0' * 13) >> 20,
-          LogicValue.filled(63, LogicValue.x));
-      expect(LogicValue.ofString('x' * 50 + '0' * 13) >> 10,
-          LogicValue.ofString('x' * 60 + '0' * 3));
-      expect(LogicValue.ofString('z' * 50 + '0' * 13) >> 20,
-          LogicValue.ofString('x' * 20 + 'z' * 43));
+      expect((LogicValue.ofInt(0xffff, INT_BITS - 1) >> 8).toInt(), 0xff);
+      expect((LogicValue.ofInt(-5, INT_BITS - 1) >> shamt1).toInt(), -1 >>> 1);
+      expect(LogicValue.ofString('x' * upper + '0' * (lower - 1)) >> shamt1,
+          LogicValue.filled(INT_BITS - 1, LogicValue.x));
+      expect(LogicValue.ofString('x' * upper + '0' * (lower - 1)) >> shamt2,
+          LogicValue.ofString('x' * (upper + shamt2) + '0' * 3));
+      expect(LogicValue.ofString('z' * upper + '0' * (lower - 1)) >> shamt1,
+          LogicValue.ofString('x' * shamt1 + 'z' * (remainder - 1)));
     });
 
     test('big shift right logical', () {
-      expect((LogicValue.ofInt(-5, 65) >>> 28).toInt(), 0xfffffffff);
+      expect((LogicValue.ofInt(-5, INT_BITS + 1) >>> 28).toInt(),
+          repeatedInt(0xf, 4, (INT_BITS - 28) ~/ 4));
       expect(LogicValue.ofBigInt(-BigInt.two, 128) >>> 96,
           LogicValue.ofInt(0xffffffff, 128));
       expect(LogicValue.ofString('x' * 51 + '0' * 14) >>> 20,
@@ -697,8 +734,8 @@ void main() {
     });
 
     test('big shift left logical', () {
-      expect((LogicValue.ofInt(-1, 65) << 20).toBigInt(),
-          (-BigInt.one).toUnsigned(65 - 20) << 20);
+      expect((LogicValue.ofInt(-1, INT_BITS + 1) << 20).toBigInt(),
+          (-BigInt.one).toUnsigned(INT_BITS + 1 - 20) << 20);
       expect((LogicValue.ofBigInt(-BigInt.two, 128) << 20).toBigInt(),
           (-BigInt.two).toUnsigned(128 - 20) << 20);
       expect(LogicValue.ofString('0' * 15 + 'x' * 50) << 20,
@@ -760,6 +797,7 @@ void main() {
     });
 
     test('more than width', () {
+      expect(LogicValue.ofInt(10, 25) >> 25, LogicValue.ofInt(0, 25));
       expect(LogicValue.ofInt(10, 50) >> 50, LogicValue.ofInt(0, 50));
       expect(LogicValue.ofInt(10, 64) >> 65, LogicValue.ofInt(0, 64));
       expect(LogicValue.ofInt(10, 80) >> 100, LogicValue.ofInt(0, 80));
@@ -770,12 +808,14 @@ void main() {
     });
 
     test('huge left', () {
+      expect((LogicValue.ofInt(45, 20) << -1).toInt(), 0);
       expect((LogicValue.ofInt(45, 32) << -1).toInt(), 0);
       expect((LogicValue.ofInt(45, 64) << -4).toInt(), 0);
       expect((LogicValue.ofInt(45, 80) << -4).toInt(), 0);
       expect((LogicValue.ofInt(-39, 80) << -4).toInt(), 0);
       expect((LogicValue.ofInt(-39, 60) << -4).toInt(), 0);
 
+      expect((LogicValue.ofInt(45, 20) << BigInt.from(-1)).toInt(), 0);
       expect((LogicValue.ofInt(45, 32) << BigInt.from(-1)).toInt(), 0);
       expect((LogicValue.ofInt(45, 64) << BigInt.from(-4)).toInt(), 0);
       expect((LogicValue.ofInt(45, 80) << BigInt.from(-4)).toInt(), 0);
@@ -785,12 +825,14 @@ void main() {
     });
 
     test('huge right', () {
+      expect((LogicValue.ofInt(45, 20) >>> -4).toInt(), 0);
       expect((LogicValue.ofInt(45, 32) >>> -4).toInt(), 0);
       expect((LogicValue.ofInt(45, 64) >>> -4).toInt(), 0);
       expect((LogicValue.ofInt(45, 80) >>> -4).toInt(), 0);
       expect((LogicValue.ofInt(-39, 80) >>> -4).toInt(), 0);
       expect((LogicValue.ofInt(-39, 60) >>> -4).toInt(), 0);
 
+      expect((LogicValue.ofInt(45, 20) >>> BigInt.from(-4)).toInt(), 0);
       expect((LogicValue.ofInt(45, 32) >>> BigInt.from(-4)).toInt(), 0);
       expect((LogicValue.ofInt(45, 64) >>> BigInt.from(-4)).toInt(), 0);
       expect((LogicValue.ofInt(45, 80) >>> BigInt.from(-4)).toInt(), 0);
@@ -800,16 +842,22 @@ void main() {
     });
 
     test('huge right arithmetic', () {
+      expect((LogicValue.ofInt(45, 20) >> -1).toInt(), 0);
       expect((LogicValue.ofInt(45, 32) >> -1).toInt(), 0);
-      expect((LogicValue.ofInt(-45, 64) >> -12).toInt(), -1);
+      expect((LogicValue.ofInt(-45, INT_BITS) >> -12).toInt(), -1);
       expect((LogicValue.ofInt(-45, 8) >> -18).toInt(), 0xff);
       expect((LogicValue.ofInt(45, 80) >> -1).toInt(), 0);
       expect((LogicValue.ofInt(-45, 128) >> -18).and().toBool(), false);
       expect((LogicValue.ofBigInt(BigInt.from(-45), 128) >> -18).and().toBool(),
           true);
 
+      expect((LogicValue.ofInt(45, 20) >> BigInt.from(-4)).toInt(), 0);
       expect((LogicValue.ofInt(45, 32) >> BigInt.from(-4)).toInt(), 0);
-      expect((LogicValue.ofInt(-45, 64) >> BigInt.from(-4)).toInt(), -1);
+      expect(
+          (LogicValue.ofInt(-45, INT_BITS) >> BigInt.from(-4))
+              .toInt()
+              .toSigned(INT_BITS),
+          -1);
       expect((LogicValue.ofInt(-45, 8) >> BigInt.from(-4)).toInt(), 0xff);
       expect((LogicValue.ofInt(45, 80) >> BigInt.from(-4)).toInt(), 0);
       expect((LogicValue.ofInt(-45, 128) >> BigInt.from(-4)).and().toBool(),
@@ -823,6 +871,15 @@ void main() {
       expect((LogicValue.ofInt(-45, 8) >> (BigInt.one << 80)).toInt(), 0xff);
     });
 
+    test('huge right arithmetic zero', () {
+      for (var i = 0; i < 100; i++) {
+        expect(
+            LogicValue.ofBigInt(BigInt.one << 200 | BigInt.one << 100, i) >>
+                (BigInt.one << 100),
+            LogicValue.filled(i, LogicValue.zero));
+      }
+    });
+
     group('invalid values', () {
       test('shift left', () {
         expect(LogicValue.filled(10, LogicValue.x) << 3,
@@ -867,8 +924,8 @@ void main() {
     });
 
     test('example large shifts', () {
-      expect((LogicValue.filled(64, LogicValue.one) >> 2).toInt(),
-          equals(-1 >> 2));
+      expect((LogicValue.filled(INT_BITS, LogicValue.one) >> 2).toInt(),
+          (-1 >> 2).toSigned(INT_BITS));
       expect(
           LogicValue.filled(65, LogicValue.one) >>> 10,
           equals([
@@ -1151,8 +1208,8 @@ void main() {
           );
       expect(
           // mod-by-0
-          () => LogicValue.ofString('0100') % LogicValue.ofString('0000'),
-          throwsA(isA<Exception>()));
+          LogicValue.ofString('0100') % LogicValue.ofString('0000'),
+          equals(LogicValue.ofString('xxxx')));
       expect(
           // % num by num
           LogicValue.ofString('0100') % LogicValue.ofString('0100'),
@@ -1177,8 +1234,8 @@ void main() {
           );
       expect(
           // div-by-0
-          () => LogicValue.ofString('0100') / LogicValue.ofString('0000'),
-          throwsA(isA<Exception>()));
+          LogicValue.ofString('0100') / LogicValue.ofString('0000'),
+          equals(LogicValue.ofString('xxxx')));
       expect(
           // * overflow
           LogicValue.ofString('0100') * LogicValue.ofString('0100'),
@@ -1465,18 +1522,22 @@ void main() {
         BigInt.parse('f' * 16 + 'f0' * 8, radix: 16),
         128,
       );
-      final smaller = extraWide.getRange(0, 64);
-      expect(smaller.toInt(), equals(0xf0f0f0f0f0f0f0f0));
+      final smaller = extraWide.getRange(0, INT_BITS);
+
+      expect(smaller.toInt(), equals(repeatedInt(0xf0, 8, INT_BITS ~/ 8)));
     });
+
     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));
+          LogicValue.ofBigInt(BigInt.parse('fa' * 8, radix: 16), INT_BITS);
+
+      expect(bigInt64Lv.toInt(), equals(repeatedInt(0xfa, 8, INT_BITS ~/ 8)));
     });
+
     test('64-bit binary negatives are converted properly with bin', () {
-      expect(bin('1110' * 16), equals(0xeeeeeeeeeeeeeeee));
+      expect(bin('1110' * (INT_BITS ~/ 4)), repeatedInt(0xe, 4, INT_BITS ~/ 4));
     });
   });
 
@@ -1495,6 +1556,7 @@ void main() {
         expect(lv.hashCode, equals(lvEnum.hashCode));
       }
     });
+
     test('zero-width', () {
       expect(LogicValue.filled(0, LogicValue.one),
           equals(LogicValue.filled(0, LogicValue.zero)));
@@ -1504,6 +1566,10 @@ void main() {
   });
   group('Utility operations', () {
     test('clog2 operation', () {
+      expect(LogicValue.ofInt(15, INT_BITS).clog2().toInt(), 4);
+      expect(LogicValue.ofInt(16, INT_BITS).clog2().toInt(), 4);
+      expect(LogicValue.ofInt(17, INT_BITS).clog2().toInt(), 5);
+
       expect(
           // int
           LogicValue.ofInt(0, 8).clog2(),
@@ -1530,8 +1596,8 @@ void main() {
           equals(LogicValue.ofInt(5, 64)));
       expect(
           // int
-          LogicValue.ofInt(-1 >>> 1, 64).clog2(),
-          equals(LogicValue.ofInt(63, 64)));
+          LogicValue.ofInt(-1 >>> 1, INT_BITS).clog2(),
+          equals(LogicValue.ofInt(INT_BITS - 1, INT_BITS)));
       expect(
           //  BigInt
           LogicValue.ofBigInt(BigInt.zero, 128).clog2(),
@@ -1563,9 +1629,9 @@ void main() {
       expect(LogicValue.ofInt(-128, 8).clog2().toInt(), 7);
       expect(LogicValue.ofInt(-127, 8).clog2().toInt(), 8);
 
-      expect(LogicValue.ofInt(-128, 64).clog2().toInt(), 64);
-      expect(LogicValue.ofInt(-127, 64).clog2().toInt(), 64);
-      expect(LogicValue.ofInt(-1, 64).clog2().toInt(), 64);
+      expect(LogicValue.ofInt(-128, INT_BITS).clog2().toInt(), INT_BITS);
+      expect(LogicValue.ofInt(-127, INT_BITS).clog2().toInt(), INT_BITS);
+      expect(LogicValue.ofInt(-1, INT_BITS).clog2().toInt(), INT_BITS);
 
       expect(LogicValue.ofInt(-32768, 16).clog2().toInt(), 15);
       expect(LogicValue.ofInt(-32767, 16).clog2().toInt(), 16);
@@ -1591,6 +1657,22 @@ void main() {
       expect(LogicValue.ofBigInt(BigInt.from(-1), 128).clog2().toBigInt(),
           BigInt.from(128));
     });
+
+    test('clog2 with 1 then 0s', () {
+      expect(LogicValue.ofInt(1, INT_BITS).clog2().toInt(), 0);
+      expect(
+          LogicValue.ofInt(bin('1${'0' * (INT_BITS - 1)}'), INT_BITS)
+              .clog2()
+              .toInt(),
+          INT_BITS - 1);
+
+      for (var i = 0; i < INT_BITS; i++) {
+        expect(LogicValue.ofInt(oneSllBy(i), i + 1).clog2().toInt(), i);
+      }
+      for (var i = INT_BITS; i < 3 * INT_BITS; i++) {
+        expect(LogicValue.ofBigInt(BigInt.one << i, i + 1).clog2().toInt(), i);
+      }
+    });
   });
 
   group('random value generation ', () {
@@ -1648,7 +1730,7 @@ void main() {
         ' having different width and max constraint.', () {
       const maxValInt = 8888;
 
-      for (var i = 1; i <= 64; i++) {
+      for (var i = 1; i <= INT_BITS; i++) {
         final lvRand = Random(5).nextLogicValue(width: i);
         final lvRandMaxInt = Random(5).nextLogicValue(width: i, max: maxValInt);
         final lvMaxBigInt = Random(5)
@@ -1671,7 +1753,7 @@ void main() {
           LogicValue.ofBigInt(maxValBigInt, maxValBigInt.bitLength);
       const maxInt = 30;
 
-      for (var i = 65; i <= 500; i++) {
+      for (var i = INT_BITS + 1; i <= 500; i++) {
         final lvRand = Random(5).nextLogicValue(width: i);
         final lvRandMax = Random(5).nextLogicValue(width: i, max: maxValBigInt);
 
@@ -1683,6 +1765,7 @@ void main() {
       }
     });
   });
+
   group('Comparable LogicValue', () {
     test('positive - int', () {
       final a = LogicValue.ofInt(3, 8);
@@ -1708,15 +1791,16 @@ void main() {
         expect(values[i].toInt(), expected[i]);
       }
     });
+
     test('unsigned values - int 64 bits', () {
-      final a = LogicValue.ofInt(3, 64);
-      final b = LogicValue.ofInt(0, 64);
-      final c = LogicValue.ofInt(1, 64);
-      final d = LogicValue.ofInt(2, 64);
-      final e = LogicValue.ofInt(23, 64);
-      final f = LogicValue.ofInt(-127, 64);
-      final g = LogicValue.ofInt(-128, 64);
-      final h = LogicValue.ofInt(-1, 64);
+      final a = LogicValue.ofInt(3, INT_BITS);
+      final b = LogicValue.ofInt(0, INT_BITS);
+      final c = LogicValue.ofInt(1, INT_BITS);
+      final d = LogicValue.ofInt(2, INT_BITS);
+      final e = LogicValue.ofInt(23, INT_BITS);
+      final f = LogicValue.ofInt(-127, INT_BITS);
+      final g = LogicValue.ofInt(-128, INT_BITS);
+      final h = LogicValue.ofInt(-1, INT_BITS);
 
       final values = <LogicValue>[a, b, c, d, e, f, g, h];
 
@@ -1735,11 +1819,6 @@ void main() {
       }
     });
 
-    test('Unsigned BigInt Width Exception', () {
-      expect(
-          () => BigInt.from(1).toIntUnsigned(100), throwsA(isA<Exception>()));
-    });
-
     test('unsigned values - int 8 bits', () {
       final a = LogicValue.ofInt(3, 8);
       final b = LogicValue.ofInt(0, 8);
@@ -1767,15 +1846,19 @@ void main() {
             expected[i]);
       }
     });
-    test('unsigned  BigInt & int ', () {
-      final a = LogicValue.ofBigInt(BigInt.parse('3'), 64);
-      final b = LogicValue.ofBigInt(BigInt.zero, 64);
-      final c = LogicValue.ofBigInt(BigInt.from(-1), 64);
-      final d = LogicValue.ofBigInt(BigInt.one, 64);
-      final e = LogicValue.ofInt(-4611686018427387903, 64);
-      final f = LogicValue.ofInt(-9223372036854775808, 64);
-      final g = LogicValue.ofInt(-9223372036854775807, 64);
-      final h = LogicValue.ofInt(-1, 64);
+
+    test('unsigned BigInt & int', () {
+      final a = LogicValue.ofBigInt(BigInt.parse('3'), INT_BITS);
+      final b = LogicValue.ofBigInt(BigInt.zero, INT_BITS);
+      final c = LogicValue.ofBigInt(BigInt.from(-1), INT_BITS);
+      final d = LogicValue.ofBigInt(BigInt.one, INT_BITS);
+      final e = LogicValue.ofBigInt(
+          BigInt.parse('-0x3${'f' * (INT_BITS ~/ 4 - 1)}'), INT_BITS);
+      final f = LogicValue.ofBigInt(
+          BigInt.parse('-0x8${'0' * (INT_BITS ~/ 4 - 1)}'), INT_BITS);
+      final g = LogicValue.ofBigInt(
+          BigInt.parse('-0x7${'f' * (INT_BITS ~/ 4 - 1)}'), INT_BITS);
+      final h = LogicValue.ofInt(-1, INT_BITS);
 
       final values = <LogicValue>[a, b, c, d, e, f, g, h];
 
@@ -1787,11 +1870,20 @@ void main() {
 
       values.sort();
       expected.sort();
+
       for (var i = 0; i < values.length; i++) {
         expect(BigInt.from(values[i].toInt()).toUnsigned(values[i].width),
             expected[i]);
       }
     });
+
+    test('compare interpretable neg int-width numbers', () {
+      final allfs = LogicValue.ofInt(0xffffffff, 32);
+      final an8and0s = LogicValue.ofInt(0x80000000, 32);
+
+      expect(allfs > an8and0s, LogicValue.one);
+    });
+
     test('unsigned BigInt', () {
       final a = LogicValue.ofBigInt(BigInt.parse('3'), 128);
       final b = LogicValue.ofBigInt(BigInt.zero, 128);
@@ -1819,6 +1911,7 @@ void main() {
         expect(values[i].toBigInt().toUnsigned(values[i].width), expected[i]);
       }
     });
+
     test('Exceptions', () {
       final a64 = LogicValue.ofBigInt(BigInt.parse('3'), 64);
       final a128 = LogicValue.ofBigInt(BigInt.parse('3'), 128);
@@ -1827,8 +1920,8 @@ void main() {
       final d64 = LogicValue.ofBigInt(BigInt.one, 64);
       final e64 = LogicValue.ofInt(23, 64);
       final e8 = LogicValue.ofInt(23, 8);
-      final f64 = LogicValue.ofInt(-9223372036854775808, 64);
-      final g64 = LogicValue.ofInt(-9223372036854775807, 64);
+      final f64 = LogicValue.ofInt(-3, 64);
+      final g64 = LogicValue.ofInt(-2, 64);
       final h64 = LogicValue.ofInt(-1, 64);
       final invalidLogicX = LogicValue.filled(8, LogicValue.x);
       final invalidLogicZ = LogicValue.filled(8, LogicValue.z);
@@ -1847,4 +1940,46 @@ void main() {
           throwsA(isA<InvalidValueOperationException>()));
     });
   });
+
+  test('BigInt toIntUnsigned extension', () {
+    expect(BigInt.parse('0xf${'0' * 30}').toIntUnsigned(INT_BITS), 0);
+    expect(BigInt.parse('0xf${'0' * 30}').toIntUnsigned(INT_BITS - 1), 0);
+
+    expect(BigInt.parse('0x1f${'0' * 30}').toIntUnsigned(INT_BITS), 0);
+    expect(BigInt.parse('0x1f${'0' * 30}').toIntUnsigned(INT_BITS - 1), 0);
+
+    expect(BigInt.parse('0x${'5' * 30}').toIntUnsigned(INT_BITS),
+        repeatedInt(0x5, 4, INT_BITS ~/ 4));
+    expect(BigInt.parse('0x${'5' * 30}').toIntUnsigned(INT_BITS - 1),
+        repeatedInt(0x5, 4, INT_BITS ~/ 4));
+
+    expect(BigInt.parse('0x${'a' * 30}').toIntUnsigned(INT_BITS),
+        repeatedInt(0xa, 4, INT_BITS ~/ 4));
+    expect(BigInt.parse('0x${'a' * 30}').toIntUnsigned(INT_BITS - 4),
+        repeatedInt(0xa, 4, INT_BITS ~/ 4 - 1));
+  });
+
+  test('BigInt toIntUnsigned Width Exception', () {
+    expect(() => BigInt.from(1).toIntUnsigned(100), throwsA(isA<Exception>()));
+  });
+
+  test('one shift left by', () {
+    expect(oneSllBy(0), 1);
+    expect(oneSllBy(1), 2);
+    expect(oneSllBy(2), 4);
+    expect(oneSllBy(31), 0x80000000);
+    expect(oneSllBy(32), 0x100000000);
+    expect(oneSllBy(52), 0x10000000000000);
+  });
+
+  test('precision adjustment handled for all widths', () {
+    for (var i = 1; i < 100; i++) {
+      expect(
+          LogicValue.of('${'1' * i}0'),
+          [
+            LogicValue.ofBigInt(BigInt.parse('1' * i, radix: 2), i),
+            LogicValue.zero
+          ].swizzle());
+    }
+  });
 }
diff --git a/test/logic_value_width_test.dart b/test/logic_value_width_test.dart
index 19626231..423793b4 100644
--- a/test/logic_value_width_test.dart
+++ b/test/logic_value_width_test.dart
@@ -8,6 +8,7 @@
 // Author: Desmond Kirkpatrick <desmond.a.kirkpatrick@intel.com>
 
 import 'package:rohd/rohd.dart';
+import 'package:rohd/src/utilities/web.dart';
 import 'package:test/test.dart';
 
 void main() {
@@ -49,8 +50,13 @@ void main() {
         (LogicValue.ofInt(-5, 80) % 3).toInt());
   });
 
+  test('compare two positive int-width numbers', () {
+    expect(LogicValue.ofInt(6, INT_BITS) < LogicValue.ofInt(7, INT_BITS),
+        LogicValue.one);
+  });
+
   group('values test', () {
-    for (final len in [63, 64, 65, 66, 67]) {
+    for (var len = INT_BITS - 2; len <= INT_BITS + 4; len++) {
       final sslv = LogicValue.ofInt(4, len); // small Int hold Big
       final bslv = LogicValue.ofInt(-0xFFFF, len); // 18446744073709486081
       final fslv = LogicValue.ofInt(-2, len); // 18446744073709551614
@@ -94,7 +100,7 @@ void main() {
       });
 
       test('cross compare len=$len', () {
-        if (len <= 64) {
+        if (len <= INT_BITS) {
           expect(bslv.eq(bblv), LogicValue.one);
         } else {
           expect(bslv < bblv, LogicValue.one);
@@ -140,15 +146,15 @@ void main() {
         for (final l in [len - 5, len - 4, len - 3, len - 2]) {
           final bignum = LogicValue.ofBigInt(BigInt.from(1) << l, len);
           expect(bignum.clog2(), LogicValue.ofInt(l, len));
-          if (len < 64) {
-            final smallnum = LogicValue.ofInt(1 << l, len);
+          if (len < INT_BITS) {
+            final smallnum = LogicValue.ofInt(oneSllBy(l), len);
             expect(smallnum.clog2(), LogicValue.ofInt(l, len));
           }
         }
         for (final l in [len - 5, len - 4, len - 3]) {
           final bignum = LogicValue.ofBigInt(BigInt.from(2) << l, len);
           expect(bignum.clog2().toBigInt(), BigInt.from(l + 1));
-          if (len < 64) {
+          if (len < INT_BITS) {
             final smallnum = LogicValue.ofInt(2 << l, len);
             expect(smallnum.clog2(), LogicValue.ofInt(l + 1, len));
           }
@@ -156,7 +162,7 @@ void main() {
         for (final l in [len - 5, len - 4, len - 3]) {
           final bignum = LogicValue.ofBigInt(BigInt.from(3) << l, len);
           expect(bignum.clog2(), LogicValue.ofInt(l + 2, len));
-          if (len < 64) {
+          if (len < INT_BITS) {
             final smallnum = LogicValue.ofInt(3 << l, len);
             expect(smallnum.clog2(), LogicValue.ofInt(l + 2, len));
           }
diff --git a/test/multimodule4_test.dart b/test/multimodule4_test.dart
index c033114d..52470dc8 100644
--- a/test/multimodule4_test.dart
+++ b/test/multimodule4_test.dart
@@ -59,8 +59,5 @@ void main() {
     // "z = 1" means it correctly traversed down from inputs
     assert(synth.contains('z = 1'),
         'Should correctly traverse from inputs to z=1');
-
-    // print(ftm.hierarchy());
-    // File('tmp4.sv').writeAsStringSync(synth);
   });
 }
diff --git a/test/translations_test.dart b/test/translations_test.dart
index 12fa481c..d656fde9 100644
--- a/test/translations_test.dart
+++ b/test/translations_test.dart
@@ -9,6 +9,12 @@
 
 // ignore_for_file: avoid_multiple_declarations_per_line
 
+// TODO(mkorbel1): reenable this test on JavaScript pending dart sdk issue,
+//  https://github.com/dart-lang/sdk/issues/54329.
+
+@TestOn('vm')
+library;
+
 import 'package:rohd/rohd.dart';
 import 'package:rohd/src/utilities/simcompare.dart';
 import 'package:test/test.dart';
@@ -114,8 +120,7 @@ void main() {
         List<Logic>.generate(numWrPorts, (index) => Logic(width: 16)),
       );
       await ftm.build();
-      // File('tmp.sv').writeAsStringSync(ftm.generateSynth())
-      // WaveDumper(ftm);
+
       final vectors = [
         Vector({'lrst': 0}, {}),
         Vector({'lrst': 1}, {}),
@@ -126,8 +131,7 @@ void main() {
         Vector({'wrEn1': 0, 'rdEn0': 0}, {'rdData0': 0xf}),
       ];
       await SimCompare.checkFunctionalVector(ftm, vectors);
-      final simResult = SimCompare.iverilogVector(ftm, vectors);
-      expect(simResult, equals(true));
+      SimCompare.checkIverilogVector(ftm, vectors);
     });
   });
 }
diff --git a/test/vcd_parser_test.dart b/test/vcd_parser_test.dart
index 6208ccf5..d47ba332 100644
--- a/test/vcd_parser_test.dart
+++ b/test/vcd_parser_test.dart
@@ -7,6 +7,9 @@
 // 2023 February 7
 // Author: Max Korbel <max.korbel@intel.com>
 
+@TestOn('vm')
+library;
+
 import 'dart:io';
 
 import 'package:rohd/rohd.dart';
diff --git a/test/wave_dumper_test.dart b/test/wave_dumper_test.dart
index 3024e8d4..eae660ce 100644
--- a/test/wave_dumper_test.dart
+++ b/test/wave_dumper_test.dart
@@ -7,6 +7,9 @@
 // 2021 November 4
 // Author: Max Korbel <max.korbel@intel.com>
 
+@TestOn('vm')
+library;
+
 import 'dart:async';
 import 'dart:io';
 
diff --git a/tool/gh_actions/run_tests.sh b/tool/gh_actions/run_tests.sh
index e3435489..4f82a45a 100755
--- a/tool/gh_actions/run_tests.sh
+++ b/tool/gh_actions/run_tests.sh
@@ -12,3 +12,6 @@
 set -euo pipefail
 
 dart test
+
+# run tests in JS
+dart test --platform node
\ No newline at end of file