Repository: intel/rohd (dart)

Issue:
Allow negative indices on `Logic` and `LogicValue` to relative to the end
## Motivation
Python allows you to access the end of a list using negative numbers, which is a nifty feature.

## Desired solution
Functions like `operator []`, `getRange`, and `slice` should handle negative numbers, treating it as from the end of the value/signal.  -1 is the last element, etc.

## Alternatives considered  
SystemVerilog does not support this.

Pull request:
Issue #99 

- Now one can use negative values in indices for indexing or subset slicing.
- This effect is applied on `BusSubset` that is used by `Logic slice()`
- Indirectly this effect is also passed to function that uses `slice()` like `operator[]` (indexing operator) and `getRange()`

Patch:
diff --git a/lib/src/logic.dart b/lib/src/logic.dart
index 50c9d50c3..453344b1e 100644
--- a/lib/src/logic.dart
+++ b/lib/src/logic.dart
@@ -449,15 +449,53 @@ class Logic {
   }
 
   /// Accesses the [index]th bit of this signal.
+  ///
+  /// Negative/Positive index values are allowed. (The negative indexing starts from the end=[width]-1)
+  /// -([width]) <= [index] < [width]
+  ///
+  /// ```dart
+  /// Logic nextVal = addOutput('nextVal', width: width);
+  /// // Example: val = 0xce, val.width = 8, bin(0xce) = "0b11001110"
+  /// // Positive Indexing
+  /// nextVal <= val[3]; // output: 1
+  ///
+  /// // Negative Indexing
+  /// nextVal <= val[-5]; // output: 1, also val[3] == val[-5]
+  ///
+  /// // Error cases
+  /// nextVal <= val[-9]; // Error!: allowed values [-8, 7]
+  /// nextVal <= val[8]; // Error!: allowed values [-8, 7]
+  /// ```
+  ///
   Logic operator [](int index) => slice(index, index);
 
   /// Accesses a subset of this signal from [startIndex] to [endIndex],
   /// both inclusive.
   ///
-  /// If [endIndex] is less than [startIndex], the returned value will be
-  /// reversed relative to the original signal.
-  Logic slice(int endIndex, int startIndex) =>
-      BusSubset(this, startIndex, endIndex).subset;
+  /// If [endIndex] comes before the [startIndex] on position, the returned
+  /// value will be reversed relative to the original signal.
+  /// Negative/Positive index values are allowed. (The negative indexing starts from where the array ends)
+  ///
+  /// ```dart
+  /// Logic nextVal = addOutput('nextVal', width: width);
+  /// // Example: val = 0xce, val.width = 8, bin(0xce) = "0b11001110"
+  /// // Negative Slicing
+  /// nextVal <= val.slice(val.width - 1, -3); // = val.slice(7,5) & output: 0b110, where the output.width=3
+  ///
+  /// // Positive Slicing
+  /// nextVal <= val.slice(5, 0); // = val.slice(-3, -8) & output: 0b001110, where the output.width=6
+  /// ```
+  ///
+  Logic slice(int endIndex, int startIndex) {
+    // Given start and end index, if either of them are seen to be -ve index
+    // value(s) then convert them to a +ve index value(s)
+    final modifiedStartIndex =
+        (startIndex < 0) ? width + startIndex : startIndex;
+    final modifiedEndIndex = (endIndex < 0) ? width + endIndex : endIndex;
+
+    // Create a new bus subset
+    return BusSubset(this, modifiedStartIndex, modifiedEndIndex).subset;
+  }
 
   /// Returns a version of this [Logic] with the bit order reversed.
   Logic get reversed => slice(0, width - 1);
@@ -465,24 +503,36 @@ class Logic {
   /// Returns a subset [Logic].  It is inclusive of [startIndex], exclusive of
   /// [endIndex].
   ///
-  /// [startIndex] must be less than [endIndex]. If [startIndex] and [endIndex]
-  /// are equal, then a zero-width signal is returned.
+  /// The [startIndex] must come before the [endIndex]. If [startIndex] and
+  /// [endIndex] are equal, then a zero-width signal is returned.
+  /// Negative/Positive index values are allowed. (The negative indexing starts from where the array ends)
+  ///
+  /// ```dart
+  /// Logic nextVal = addOutput('nextVal', width: width);
+  /// // Example: val = 0xce, val.width = 8, bin(0xce) = "0b11001110"
+  /// // Negative getRange
+  /// nextVal <= val.getRange(-3, val.width); // = val.getRange(5,8) & output: 0b110, where the output.width=3
+  ///
+  /// // Positive getRange
+  /// nextVal <= val.getRange(0, 6); // = val.slice(0, -2) & output: 0b001110, where the output.width=6
+  /// ```
+  ///
   Logic getRange(int startIndex, int endIndex) {
-    if (endIndex < startIndex) {
-      throw Exception(
-          'End ($endIndex) cannot be less than start ($startIndex).');
-    }
-    if (endIndex > width) {
-      throw Exception('End ($endIndex) must be less than width ($width).');
-    }
-    if (startIndex < 0) {
-      throw Exception(
-          'Start ($startIndex) must be greater than or equal to 0.');
-    }
     if (endIndex == startIndex) {
       return Const(0, width: 0);
     }
-    return slice(endIndex - 1, startIndex);
+
+    // Given start and end index, if either of them are seen to be -ve index
+    // value(s) then conver them to a +ve index value(s)
+    final modifiedStartIndex =
+        (startIndex < 0) ? width + startIndex : startIndex;
+    final modifiedEndIndex = (endIndex < 0) ? width + endIndex : endIndex;
+    if (modifiedEndIndex < modifiedStartIndex) {
+      throw Exception(
+          'End $modifiedEndIndex(=$endIndex) cannot be less than start'
+          ' $modifiedStartIndex(=$startIndex).');
+    }
+    return slice(modifiedEndIndex - 1, modifiedStartIndex);
   }
 
   /// Returns a new [Logic] with width [newWidth] where new bits added are zeros
diff --git a/lib/src/modules/bus.dart b/lib/src/modules/bus.dart
index b24393f5e..c8be6c200 100644
--- a/lib/src/modules/bus.dart
+++ b/lib/src/modules/bus.dart
@@ -38,13 +38,18 @@ class BusSubset extends Module with InlineSystemVerilog {
   BusSubset(Logic bus, this.startIndex, this.endIndex,
       {String name = 'bussubset'})
       : super(name: name) {
+    // If a converted index value is still -ve then it's an Index out of bounds
+    // on a Logic Bus
     if (startIndex < 0 || endIndex < 0) {
-      throw Exception('Cannot access negative indices!'
-          '  Indices $startIndex and/or $endIndex are invalid.');
+      throw Exception(
+          'Start ($startIndex) and End ($endIndex) must be greater than or '
+          'equal to 0.');
     }
+    // If the +ve indices are more than Logic bus width, Index out of bounds
     if (endIndex > bus.width - 1 || startIndex > bus.width - 1) {
-      throw Exception('Index out of bounds, indices $startIndex and'
-          ' $endIndex must be less than width-1');
+      throw Exception(
+          'Index out of bounds, indices $startIndex and $endIndex must be less'
+          ' than ${bus.width}');
     }
 
     // original name can't be unpreferred because you cannot do a bit slice
diff --git a/lib/src/utilities/simcompare.dart b/lib/src/utilities/simcompare.dart
index 4afee1946..48e9b6085 100644
--- a/lib/src/utilities/simcompare.dart
+++ b/lib/src/utilities/simcompare.dart
@@ -204,7 +204,7 @@ abstract class SimCompare {
     Directory(dir).createSync(recursive: true);
     File(tmpTestFile).writeAsStringSync(testbench);
     final compileResult = Process.runSync('iverilog',
-        ['-g2012', tmpTestFile, '-o', tmpOutput] + iverilogExtraArgs);
+        ['-g2012', '-o', tmpOutput, ...iverilogExtraArgs, tmpTestFile]);
     bool printIfContentsAndCheckError(dynamic output) {
       if (output.toString().isNotEmpty) {
         print(output);
diff --git a/lib/src/values/logic_value.dart b/lib/src/values/logic_value.dart
index 4acd9058d..8d8c22629 100644
--- a/lib/src/values/logic_value.dart
+++ b/lib/src/values/logic_value.dart
@@ -298,12 +298,28 @@ abstract class LogicValue {
   }
 
   /// Returns the `i`th bit of this [LogicValue]
+  ///
+  /// The [index] provided can be positive or negative. For positive [index],
+  /// the indexing is performed from front of the LogicValue.
+  /// For negative [index], the indexing started from last index and
+  /// goes to front.
+  /// Note: the [index] value must follow, -[width] <= [index] < [width]
+  ///
+  /// ```dart
+  /// LogicValue.ofString('1111')[2];  // == LogicValue.one
+  /// LogicValue.ofString('0111')[-1]; // == LogicValue.zero
+  /// LogicValue.ofString('0100')[-2]; // == LogicValue.one
+  /// LogicValue.ofString('0101')[-5]; // Error - out of range
+  /// LogicValue.ofString('0101')[10]; // Error - out of range
+  /// ```
+  ///
   LogicValue operator [](int index) {
-    if (index >= width || index < 0) {
+    final modifiedIndex = (index < 0) ? width + index : index;
+    if (modifiedIndex >= width || modifiedIndex < 0) {
       throw IndexError(index, this, 'LogicValueIndexOutOfRange',
-          'Index out of range: $index.', width);
+          'Index out of range: $modifiedIndex(=$index).', width);
     }
-    return _getIndex(index);
+    return _getIndex(modifiedIndex);
   }
 
   /// Returns the `i`th bit of this [LogicValue].  Performs no boundary checks.
@@ -316,21 +332,39 @@ abstract class LogicValue {
   /// Returns a subset [LogicValue].  It is inclusive of [startIndex], exclusive
   /// of [endIndex].
   ///
-  /// [startIndex] must be less than [endIndex]. If [startIndex] and [endIndex]
-  /// are equal, then a zero-width value is returned.
+  /// [startIndex] must come before the [endIndex] on position. If [startIndex]
+  /// and [endIndex] are equal, then a zero-width value is returned.
+  /// Negative/Positive index values are allowed. (The negative indexing starts from the end=[width]-1)
+  ///
+  /// ```dart [TODO]
+  /// LogicValue.ofString('0101').getRange(0, 2);   // == LogicValue.ofString('01')
+  /// LogicValue.ofString('0101').getRange(1, -2);  // == LogicValue.zero
+  /// LogicValue.ofString('0101').getRange(-3, 4);  // == LogicValue.ofString('010')
+  /// LogicValue.ofString('0101').getRange(-1, -2); // Error - negative end index and start > end - error! start must be less than end
+  /// LogicValue.ofString('0101').getRange(2, 1);   // Error - bad inputs start > end
+  /// LogicValue.ofString('0101').getRange(0, 7);   // Error - bad inputs end > length-1
+  /// ```
+  ///
   LogicValue getRange(int startIndex, int endIndex) {
-    if (endIndex < startIndex) {
+    final modifiedStartIndex =
+        (startIndex < 0) ? width + startIndex : startIndex;
+    final modifiedEndIndex = (endIndex < 0) ? width + endIndex : endIndex;
+    if (modifiedEndIndex < modifiedStartIndex) {
       throw Exception(
-          'End ($endIndex) cannot be less than start ($startIndex).');
+          'End $modifiedEndIndex(=$endIndex) cannot be less than start '
+          '$modifiedStartIndex(=$startIndex).');
     }
-    if (endIndex > width) {
-      throw Exception('End ($endIndex) must be less than width ($width).');
+    if (modifiedEndIndex > width) {
+      throw Exception(
+          'End $modifiedEndIndex(=$endIndex) must be less than width'
+          ' ($width).');
     }
-    if (startIndex < 0) {
+    if (modifiedStartIndex < 0) {
       throw Exception(
-          'Start ($startIndex) must be greater than or equal to 0.');
+          'Start $modifiedStartIndex(=$startIndex) must be greater than or '
+          'equal to 0.');
     }
-    return _getRange(startIndex, endIndex);
+    return _getRange(modifiedStartIndex, modifiedEndIndex);
   }
 
   /// Returns a subset [LogicValue].  It is inclusive of [start], exclusive of
@@ -344,11 +378,22 @@ abstract class LogicValue {
   ///
   /// If [endIndex] is less than [startIndex], the returned value will be
   /// reversed relative to the original value.
+  ///
+  /// ```dart [TODO]
+  /// LogicValue.ofString('xz01').slice(2, 1);    // == LogicValue.ofString('z0')
+  /// LogicValue.ofString('xz01').slice(-2, -3);  // == LogicValue.ofString('z0')
+  /// LogicValue.ofString('xz01').slice(1, 3);    // == LogicValue.ofString('0zx')
+  /// LogicValue.ofString('xz01').slice(-3, -1);  // == LogicValue.ofString('0zx')
+  /// LogicValue.ofString('xz01').slice(-2, -2);  // == LogicValue.ofString('z')
+  /// ```
   LogicValue slice(int endIndex, int startIndex) {
-    if (startIndex <= endIndex) {
-      return getRange(startIndex, endIndex + 1);
+    final modifiedStartIndex =
+        (startIndex < 0) ? width + startIndex : startIndex;
+    final modifiedEndIndex = (endIndex < 0) ? width + endIndex : endIndex;
+    if (modifiedStartIndex <= modifiedEndIndex) {
+      return getRange(modifiedStartIndex, modifiedEndIndex + 1);
     } else {
-      return getRange(endIndex, startIndex + 1).reversed;
+      return getRange(modifiedEndIndex, modifiedStartIndex + 1).reversed;
     }
   }
 


Test patch:
diff --git a/test/bus_test.dart b/test/bus_test.dart
index bb5d1b596..4709ec041 100644
--- a/test/bus_test.dart
+++ b/test/bus_test.dart
@@ -12,17 +12,46 @@ import 'package:rohd/src/utilities/simcompare.dart';
 import 'package:test/test.dart';
 
 class BusTestModule extends Module {
+  // --- Getters ---
   Logic get aBar => output('a_bar');
   Logic get aAndB => output('a_and_b');
-  Logic get aShrunk => output('a_shrunk');
-  Logic get aRSliced => output('a_rsliced');
-  Logic get aReversed => output('a_reversed');
-  Logic get aRange => output('a_range');
   Logic get aBJoined => output('a_b_joined');
   Logic get a1 => output('a1');
   Logic get aPlusB => output('a_plus_b');
 
+  // Getters for Logic subset test variables
+  Logic get aReversed => output('a_reversed');
+  // --- Getters for Slicing
+  Logic get aShrunk1 => output('a_shrunk1');
+  Logic get aShrunk2 => output('a_shrunk2');
+  Logic get aShrunk3 => output('a_shrunk3');
+  Logic get aNegShrunk1 => output('a_neg_shrunk1');
+  Logic get aNegShrunk2 => output('a_neg_shrunk2');
+  Logic get aNegShrunk3 => output('a_neg_shrunk3');
+  // --- Getters for Reverse Slicing
+  Logic get aRSliced1 => output('a_rsliced1');
+  Logic get aRSliced2 => output('a_rsliced2');
+  Logic get aRSliced3 => output('a_rsliced3');
+  Logic get aRNegativeSliced1 => output('a_r_neg_sliced1');
+  Logic get aRNegativeSliced2 => output('a_r_neg_sliced2');
+  Logic get aRNegativeSliced3 => output('a_r_neg_sliced3');
+  // --- Getters for getRange
+  Logic get aRange1 => output('a_range1');
+  Logic get aRange2 => output('a_range2');
+  Logic get aRange3 => output('a_range3');
+  Logic get aNegativeRange1 => output('a_neg_range1');
+  Logic get aNegativeRange2 => output('a_neg_range2');
+  Logic get aNegativeRange3 => output('a_neg_range3');
+  // --- Getters for operator[]
+  Logic get aOperatorIndexing1 => output('a_operator_indexing1');
+  Logic get aOperatorIndexing2 => output('a_operator_indexing2');
+  Logic get aOperatorIndexing3 => output('a_operator_indexing3');
+  Logic get aOperatorNegIndexing1 => output('a_operator_neg_indexing1');
+  Logic get aOperatorNegIndexing2 => output('a_operator_neg_indexing2');
+  Logic get aOperatorNegIndexing3 => output('a_operator_neg_indexing3');
+
   BusTestModule(Logic a, Logic b) : super(name: 'bustestmodule') {
+    // --- Declaration ---
     if (a.width != b.width) {
       throw Exception('a and b must be same width, but found "$a" and "$b".');
     }
@@ -34,26 +63,86 @@ class BusTestModule extends Module {
 
     final aBar = addOutput('a_bar', width: a.width);
     final aAndB = addOutput('a_and_b', width: a.width);
-    final aShrunk = addOutput('a_shrunk', width: 3);
-    final aRSliced = addOutput('a_rsliced', width: 5);
-    final aReversed = addOutput('a_reversed', width: a.width);
-    final aRange = addOutput('a_range', width: 3);
+
     final aBJoined = addOutput('a_b_joined', width: a.width + b.width);
     final aPlusB = addOutput('a_plus_b', width: a.width);
     final a1 = addOutput('a1');
     final expressionBitSelect = addOutput('expression_bit_select', width: 4);
 
+    // Logic Reverse value
+    final aReversed = addOutput('a_reversed', width: a.width);
+    // Slicing with Positive Indices
+    final aShrunk1 = addOutput('a_shrunk1', width: 3);
+    final aShrunk2 = addOutput('a_shrunk2', width: 2);
+    final aShrunk3 = addOutput('a_shrunk3');
+    // Slicing with negative indices
+    final aNegativeShrunk1 = addOutput('a_neg_shrunk1', width: 3);
+    final aNegativeShrunk2 = addOutput('a_neg_shrunk2', width: 2);
+    final aNegativeShrunk3 = addOutput('a_neg_shrunk3');
+    // Slicing and reversing the value
+    final aRSliced1 = addOutput('a_rsliced1', width: 5);
+    final aRSliced2 = addOutput('a_rsliced2', width: 2);
+    final aRSliced3 = addOutput('a_rsliced3');
+    // Slicing and reversing the value via negative indices
+    final aRNegativeSliced1 = addOutput('a_r_neg_sliced1', width: 5);
+    final aRNegativeSliced2 = addOutput('a_r_neg_sliced2', width: 2);
+    final aRNegativeSliced3 = addOutput('a_r_neg_sliced3');
+    // Getting the range of consecutive values over the Logic (subset)
+    final aRange1 = addOutput('a_range1', width: 3);
+    final aRange2 = addOutput('a_range2', width: 2);
+    final aRange3 = addOutput('a_range3');
+    final aNegativeRange1 = addOutput('a_neg_range1', width: 3);
+    final aNegativeRange2 = addOutput('a_neg_range2', width: 2);
+    final aNegativeRange3 = addOutput('a_neg_range3');
+    // Operator Indexing with positive index value
+    final aOperatorIndexing1 = addOutput('a_operator_indexing1');
+    final aOperatorIndexing2 = addOutput('a_operator_indexing2');
+    final aOperatorIndexing3 = addOutput('a_operator_indexing3');
+    // Operator Indexing with negative index value
+    final aOperatorNegIndexing1 = addOutput('a_operator_neg_indexing1');
+    final aOperatorNegIndexing2 = addOutput('a_operator_neg_indexing2');
+    final aOperatorNegIndexing3 = addOutput('a_operator_neg_indexing3');
+
+    // --- Assignments ---
     aBar <= ~a;
     aAndB <= a & b;
-    aShrunk <= a.slice(2, 0);
-    aRSliced <= a.slice(3, 7);
-    aReversed <= a.reversed;
-    aRange <= a.getRange(5, 8);
     aBJoined <= [b, a].swizzle();
     a1 <= a[1];
     aPlusB <= a + b;
+
+    // Logic Subset functionality
+    aShrunk1 <= a.slice(2, 0);
+    aShrunk2 <= a.slice(1, 0);
+    aShrunk3 <= a.slice(0, 0);
+    aNegativeShrunk1 <= a.slice(-6, 0);
+    aNegativeShrunk2 <= a.slice(-7, 0);
+    aNegativeShrunk3 <= a.slice(-8, 0);
+
+    aRSliced1 <= a.slice(3, 7);
+    aRSliced2 <= a.slice(6, 7);
+    aRSliced3 <= a.slice(7, 7);
+    aRNegativeSliced1 <= a.slice(-5, -1);
+    aRNegativeSliced2 <= a.slice(-2, -1);
+    aRNegativeSliced3 <= a.slice(-1, -1);
+
+    aRange1 <= a.getRange(5, 8);
+    aRange2 <= a.getRange(6, 8);
+    aRange3 <= a.getRange(7, 8);
+    aNegativeRange1 <= a.getRange(-3, 8); // NOTE: endIndex value is exclusive
+    aNegativeRange2 <= a.getRange(-2, 8);
+    aNegativeRange3 <= a.getRange(-1, 8);
+
+    aOperatorIndexing1 <= a[0];
+    aOperatorIndexing2 <= a[a.width - 1];
+    aOperatorIndexing3 <= a[4];
+    aOperatorNegIndexing1 <= a[-a.width];
+    aOperatorNegIndexing2 <= a[-1];
+    aOperatorNegIndexing3 <= a[-2];
+
+    aReversed <= a.reversed;
+
     expressionBitSelect <=
-        [aBJoined, aShrunk, aRange, aRSliced, aPlusB].swizzle().slice(3, 0);
+        [aBJoined, aShrunk1, aRange1, aRSliced1, aPlusB].swizzle().slice(3, 0);
   }
 }
 
@@ -103,7 +192,7 @@ void main() {
       final a = Logic(width: 8);
       final b = Logic(width: 8);
       final gtm = BusTestModule(a, b);
-      final out = gtm.aShrunk;
+      final out = gtm.aShrunk1;
       await gtm.build();
       a.put(0);
       expect(out.value.toInt(), equals(0));
@@ -113,6 +202,31 @@ void main() {
       expect(out.value.toInt(), equals(5));
     });
 
+    test('Operator Indexing', () async {
+      final a = Logic(width: 8);
+      final b = Logic(width: 8);
+      final gtm = BusTestModule(a, b);
+      final out1 = gtm.aOperatorIndexing1;
+      final out2 = gtm.aOperatorIndexing2;
+      final out3 = gtm.aOperatorIndexing3;
+      final out4 = gtm.aOperatorNegIndexing1;
+      final out5 = gtm.aOperatorNegIndexing2;
+      final out6 = gtm.aOperatorNegIndexing3;
+      await gtm.build();
+      a.put(bin('11111110'));
+      expect(out1.value.toInt(), equals(0));
+      a.put(bin('10000000'));
+      expect(out2.value.toInt(), equals(bin('1')));
+      a.put(bin('11101111'));
+      expect(out3.value.toInt(), equals(bin('0')));
+      a.put(bin('11111110'));
+      expect(out4.value.toInt(), equals(0));
+      a.put(bin('10000000'));
+      expect(out5.value.toInt(), equals(bin('1')));
+      a.put(bin('10111111'));
+      expect(out6.value.toInt(), equals(bin('0')));
+    });
+
     test('Bus swizzle', () async {
       final a = Logic(width: 8);
       final b = Logic(width: 8);
@@ -143,12 +257,42 @@ void main() {
       'b': 8,
       'a_bar': 8,
       'a_and_b': 8,
-      'a_shrunk': 3,
-      'a_rsliced': 5,
-      'a_reversed': 8,
-      'a_range': 3,
       'a_b_joined': 16,
       'a_plus_b': 8,
+
+      // Slicing
+      'a_shrunk1': 3,
+      'a_shrunk2': 2,
+      'a_shrunk3': 1,
+      'a_neg_shrunk1': 3,
+      'a_neg_shrunk2': 2,
+      'a_neg_shrunk3': 1,
+      // Reverse Slicing
+      'a_rsliced1': 5,
+      'a_rsliced2': 2,
+      'a_rsliced3': 1,
+      'a_r_neg_sliced1': 5,
+      'a_r_neg_sliced2': 2,
+      'a_r_neg_sliced3': 1,
+
+      // getRange
+      'a_range1': 3,
+      'a_range2': 2,
+      'a_range3': 1,
+      'a_neg_range1': 3,
+      'a_neg_range2': 2,
+      'a_neg_range3': 1,
+
+      // operator[]
+      'a_operator_indexing1': 1,
+      'a_operator_indexing2': 1,
+      'a_operator_indexing3': 1,
+      'a_operator_neg_indexing1': 1,
+      'a_operator_neg_indexing2': 1,
+      'a_operator_neg_indexing3': 1,
+
+      // Logic bus value Reversed
+      'a_reversed': 8,
       'expression_bit_select': 4,
     };
     test('NotGate bus', () async {
@@ -177,6 +321,26 @@ void main() {
         Vector({'a': 1, 'b': 1}, {'a_and_b': 1}),
         Vector({'a': 0xff, 'b': 0xaa}, {'a_and_b': 0xaa}),
       ];
+
+      await SimCompare.checkFunctionalVector(gtm, vectors);
+      final simResult = SimCompare.iverilogVector(
+          gtm.generateSynth(), gtm.runtimeType.toString(), vectors,
+          signalToWidthMap: signalToWidthMap);
+      expect(simResult, equals(true));
+    });
+
+    test('Operator indexing', () async {
+      final gtm = BusTestModule(Logic(width: 8), Logic(width: 8));
+      await gtm.build();
+      final vectors = [
+        Vector({'a': bin('11111110')}, {'a_operator_indexing1': 0}),
+        Vector({'a': bin('10000000')}, {'a_operator_indexing2': 1}),
+        Vector({'a': bin('11101111')}, {'a_operator_indexing3': 0}),
+        Vector({'a': bin('11111110')}, {'a_operator_neg_indexing1': 0}),
+        Vector({'a': bin('10000000')}, {'a_operator_neg_indexing2': 1}),
+        Vector({'a': bin('10111111')}, {'a_operator_neg_indexing3': 0}),
+      ];
+
       await SimCompare.checkFunctionalVector(gtm, vectors);
       final simResult = SimCompare.iverilogVector(
           gtm.generateSynth(), gtm.runtimeType.toString(), vectors,
@@ -188,9 +352,33 @@ void main() {
       final gtm = BusTestModule(Logic(width: 8), Logic(width: 8));
       await gtm.build();
       final vectors = [
-        Vector({'a': 0}, {'a_shrunk': 0}),
-        Vector({'a': 0xff}, {'a_shrunk': bin('111')}),
-        Vector({'a': 0xf5}, {'a_shrunk': 5}),
+        // Positive Indexing
+        // Test set 1
+        Vector({'a': 0}, {'a_shrunk1': 0}),
+        Vector({'a': 0xfa}, {'a_shrunk1': bin('010')}),
+        Vector({'a': 0xab}, {'a_shrunk1': 3}),
+        // Test set 2
+        Vector({'a': 0}, {'a_shrunk2': 0}),
+        Vector({'a': 0xec}, {'a_shrunk2': bin('00')}),
+        Vector({'a': 0xfa}, {'a_shrunk2': 2}),
+        // Test set 3
+        Vector({'a': 0}, {'a_shrunk3': 0}),
+        Vector({'a': 0xff}, {'a_shrunk3': bin('1')}),
+        Vector({'a': 0xba}, {'a_shrunk3': 0}),
+
+        // Negative Indexing
+        // Test set 1
+        Vector({'a': 0}, {'a_neg_shrunk1': 0}),
+        Vector({'a': 0xfa}, {'a_neg_shrunk1': bin('010')}),
+        Vector({'a': 0xab}, {'a_neg_shrunk1': 3}),
+        // Test set 2
+        Vector({'a': 0}, {'a_neg_shrunk2': 0}),
+        Vector({'a': 0xec}, {'a_neg_shrunk2': bin('00')}),
+        Vector({'a': 0xfa}, {'a_neg_shrunk2': 2}),
+        // Test set 3
+        Vector({'a': 0}, {'a_neg_shrunk3': 0}),
+        Vector({'a': 0xff}, {'a_neg_shrunk3': bin('1')}),
+        Vector({'a': 0xba}, {'a_neg_shrunk3': 0})
       ];
       await SimCompare.checkFunctionalVector(gtm, vectors);
       final simResult = SimCompare.iverilogVector(
@@ -203,9 +391,33 @@ void main() {
       final gtm = BusTestModule(Logic(width: 8), Logic(width: 8));
       await gtm.build();
       final vectors = [
-        Vector({'a': 0}, {'a_rsliced': 0}),
-        Vector({'a': 0xff}, {'a_rsliced': bin('11111')}),
-        Vector({'a': 0xf5}, {'a_rsliced': 0xf}),
+        // Positive Indexing
+        // Test set 1
+        Vector({'a': 0}, {'a_rsliced1': 0}),
+        Vector({'a': 0xac}, {'a_rsliced1': bin('10101')}),
+        Vector({'a': 0xf5}, {'a_rsliced1': 0xf}),
+        // Test set 2
+        Vector({'a': 0}, {'a_rsliced2': 0}),
+        Vector({'a': 0xab}, {'a_rsliced2': bin('01')}),
+        Vector({'a': 0xac}, {'a_rsliced2': 1}),
+        // Test set 3
+        Vector({'a': 0}, {'a_rsliced3': 0}),
+        Vector({'a': 0xaf}, {'a_rsliced3': bin('1')}),
+        Vector({'a': 0xaf}, {'a_rsliced3': 1}),
+
+        // Negative Indexing
+        // Test set 1
+        Vector({'a': 0}, {'a_r_neg_sliced1': 0}),
+        Vector({'a': 0xac}, {'a_r_neg_sliced1': bin('10101')}),
+        Vector({'a': 0xf5}, {'a_r_neg_sliced1': 0xf}),
+        // Test set 2
+        Vector({'a': 0}, {'a_r_neg_sliced2': 0}),
+        Vector({'a': 0xab}, {'a_r_neg_sliced2': bin('01')}),
+        Vector({'a': 0xac}, {'a_r_neg_sliced2': 1}),
+        // Test set 3
+        Vector({'a': 0}, {'a_r_neg_sliced3': 0}),
+        Vector({'a': 0xaf}, {'a_r_neg_sliced3': bin('1')}),
+        Vector({'a': 0xaf}, {'a_r_neg_sliced3': 1})
       ];
       await SimCompare.checkFunctionalVector(gtm, vectors);
       final simResult = SimCompare.iverilogVector(
@@ -233,9 +445,33 @@ void main() {
       final gtm = BusTestModule(Logic(width: 8), Logic(width: 8));
       await gtm.build();
       final vectors = [
-        Vector({'a': 0}, {'a_range': 0}),
-        Vector({'a': 0xff}, {'a_range': 7}),
-        Vector({'a': bin('10100101')}, {'a_range': bin('101')}),
+        // Positive Indexing
+        // Test set 1
+        Vector({'a': 0}, {'a_range1': 0}),
+        Vector({'a': 0xaf}, {'a_range1': 5}),
+        Vector({'a': bin('11000101')}, {'a_range1': bin('110')}),
+        // Test set 2
+        Vector({'a': 0}, {'a_range2': 0}),
+        Vector({'a': 0xaf}, {'a_range2': 2}),
+        Vector({'a': bin('10111111')}, {'a_range2': bin('10')}),
+        // Test set 3
+        Vector({'a': 0}, {'a_range3': 0}),
+        Vector({'a': 0x80}, {'a_range3': 1}),
+        Vector({'a': bin('10000000')}, {'a_range3': bin('1')}),
+
+        // Negative Indexing
+        // Test set 1
+        Vector({'a': 0}, {'a_neg_range1': 0}),
+        Vector({'a': 0xaf}, {'a_neg_range1': 5}),
+        Vector({'a': bin('11000101')}, {'a_neg_range1': bin('110')}),
+        // Test set 2
+        Vector({'a': 0}, {'a_neg_range2': 0}),
+        Vector({'a': 0xaf}, {'a_neg_range2': 2}),
+        Vector({'a': bin('10111111')}, {'a_neg_range2': bin('10')}),
+        // Test set 3
+        Vector({'a': 0}, {'a_neg_range3': 0}),
+        Vector({'a': 0x80}, {'a_neg_range3': 1}),
+        Vector({'a': bin('10000000')}, {'a_neg_range3': bin('1')}),
       ];
       await SimCompare.checkFunctionalVector(gtm, vectors);
       final simResult = SimCompare.iverilogVector(
diff --git a/test/logic_value_test.dart b/test/logic_value_test.dart
index 0e8ae6bcb..36d2a44c7 100644
--- a/test/logic_value_test.dart
+++ b/test/logic_value_test.dart
@@ -318,9 +318,17 @@ void main() {
           () => LogicValue.ofString('0101')[10],
           throwsA(isA<IndexError>()));
       expect(
-          // index - negative
-          () => LogicValue.ofString('0101')[-1],
+          // index - out of range
+          () => LogicValue.ofString('0101')[-5],
           throwsA(isA<IndexError>()));
+      expect(
+          // index - negative
+          LogicValue.ofString('0111')[-1],
+          equals(LogicValue.zero));
+      expect(
+          // index - negative
+          LogicValue.ofString('0100')[-2],
+          equals(LogicValue.one));
       expect(
           // reversed
           LogicValue.ofString('0101').reversed,
@@ -330,9 +338,22 @@ void main() {
           LogicValue.ofString('0101').getRange(0, 2),
           equals(LogicValue.ofString('01')));
       expect(
-          // getRange - bad inputs start < 0
-          () => LogicValue.ofString('0101').getRange(-2, 1),
+          // getRange - negative end index and start < end
+          LogicValue.ofString('0101').getRange(1, -2),
+          LogicValue.zero);
+      expect(
+          // getRange - negative end index and start < end
+          LogicValue.ofString('0101').getRange(-3, 4),
+          equals(LogicValue.ofString('010')));
+      expect(
+          // getRange - negative end index and start > end - error! start must
+          // be less than end
+          () => LogicValue.ofString('0101').getRange(-1, -2),
           throwsA(isA<Exception>()));
+      expect(
+          // getRange - same index results zero width value
+          LogicValue.ofString('0101').getRange(-1, -1),
+          LogicValue.ofString(''));
       expect(
           // getRange - bad inputs start > end
           () => LogicValue.ofString('0101').getRange(2, 1),
@@ -343,8 +364,14 @@ void main() {
           throwsA(isA<Exception>()));
       expect(LogicValue.ofString('xz01').slice(2, 1),
           equals(LogicValue.ofString('z0')));
+      expect(LogicValue.ofString('xz01').slice(-2, -3),
+          equals(LogicValue.ofString('z0')));
       expect(LogicValue.ofString('xz01').slice(1, 3),
           equals(LogicValue.ofString('0zx')));
+      expect(LogicValue.ofString('xz01').slice(-3, -1),
+          equals(LogicValue.ofString('0zx')));
+      expect(LogicValue.ofString('xz01').slice(-2, -2),
+          equals(LogicValue.ofString('z')));
       expect(
           // isValid - valid
           LogicValue.ofString('0101').isValid,