Repository: intel/rohd (dart)

Issue:
Add support for `++` and `--` types of operations in conditional logic
## Motivation
There are cases in procedural execution in `_Always` blocks where `++` and `--` are useful.  Dart does not allow overloading these operators, however.

## Desired solution
Some sort of function on `Logic` which acts like `++` and `--`, both postfix and prefix (i.e. `++x` vs. `x++`).  Some thought has to go into the API and function names to make this intuitive.

To effectively use these requires some careful thought.  A prefix is simple, i.e. `++x` is the same thing as `x + 1`.  A postfix requires that the original signal is returned, but then a modified conditional operation is performed afterwards.  This means that it needs to return some special type of object that other `Conditional`s can handle, but that also might need to play nicely with other `Logic` manipulation within conditional procedural logic.  There's a number of combinations to consider, and this could involve some modifications to the way things like `ConditionalAssignment`s work in general.

## Alternatives considered  
Just omit the feature.

## Additional details
There are real designs where users had wished for a feature like this.

Pull request description:
<!-- Please make sure you check out the contribution guidelines before submitting a pull request! -->

## Description & Motivation
Adding functionality to Logic class
- `incr(Logic? a=Const(1))` - Pre Increment. That is shorthand for addition (default by 1) and assigning  back to same Logic
- `decr(Logic? a=Const(1))` - Pre Devrement. That is shorthand for subtraction (default by 1) and assigning  back to same Logic
- `mulAssing(Logic a)` - Shorthand for multiply and assign back to same Logic
- `divAssing(Logic a)` - Shorthand for division and assign back to same Logic

All function above returns ConditionalAssignment obj

## Related Issue(s)
For more details refer to, Issue #141 

## Testing
- [x] part of Shorthand Assignment Module


## Documentation
- [x] Written Docs for incr, decr, mulAssign, divAssign

Patch:
diff --git a/lib/src/logic.dart b/lib/src/logic.dart
index 0d1471b0e..f1c0b0090 100644
--- a/lib/src/logic.dart
+++ b/lib/src/logic.dart
@@ -576,6 +576,72 @@ class Logic {
   /// Greater-than-or-equal-to.
   Logic operator >=(dynamic other) => GreaterThanOrEqual(this, other).out;
 
+  /// Shorthand for a [Conditional] which increments this by [incrVal]
+  ///
+  /// By default for a [Logic] variable, if no [incrVal] is provided
+  /// result is ++variable else result is variable+=[incrVal]
+  ///
+  /// ```dart
+  ///
+  /// // Given a and b Logic input and piOut as output
+  /// Combinational([
+  ///   piOut < a,
+  ///   piOut.incr(b),
+  /// ]);
+  ///
+  /// ```
+  ///
+  ConditionalAssign incr([dynamic incrVal]) => this < this + (incrVal ?? 1);
+
+  /// Shorthand for a [Conditional] which decrements this by [decrVal]
+  ///
+  /// By default for a [Logic] variable, if no [decrVal] is provided
+  /// result is --variable else result is var-=[decrVal]
+  ///
+  /// ```dart
+  ///
+  /// // Given a and b Logic input and pdOut as output
+  /// Combinational([
+  ///   pdOut < a,
+  ///   pdOut.decr(b),
+  /// ]);
+  ///
+  /// ```
+  ///
+  ConditionalAssign decr([dynamic decrVal]) => this < this - (decrVal ?? 1);
+
+  /// Shorthand for a [Conditional] which increments this by [mulVal]
+  ///
+  /// For a [Logic] variable, this is variable *= [mulVal]
+  ///
+  /// ```dart
+  ///
+  /// // Given a and b Logic input and maOut as output
+  /// Combinational([
+  ///   maOut < a,
+  ///   maOut.mulAssign(b),
+  /// ]);
+  ///
+  /// ```
+  ///
+  ConditionalAssign mulAssign(dynamic mulVal) => this < this * mulVal;
+
+  /// Shorthand for a [Conditional] which increments this by [divVal]
+  ///
+  /// For a [Logic] variable, this is variable /= [divVal]
+  ///
+  /// ```dart
+  ///
+  /// // Given a and b Logic input and daOut as output
+  /// Combinational([
+  ///   daOut < a,
+  ///   daOut.divAssign(b),
+  /// ]);
+  ///
+  /// ```
+  ///
+  ConditionalAssign divAssign(dynamic divVal) => this < this / divVal;
+
   /// Conditional assignment operator.
   ///
   /// Represents conditionally asigning the value of another signal to this.


Test patch:
diff --git a/test/conditionals_test.dart b/test/conditionals_test.dart
index 468946514..5ceb833f5 100644
--- a/test/conditionals_test.dart
+++ b/test/conditionals_test.dart
@@ -13,6 +13,41 @@ import 'package:rohd/src/exceptions/sim_compare/sim_compare_exceptions.dart';
 import 'package:rohd/src/utilities/simcompare.dart';
 import 'package:test/test.dart';
 
+class ShorthandAssignModule extends Module {
+  ShorthandAssignModule(
+      Logic preIncr, Logic preDecr, Logic mulAssign, Logic divAssign, Logic b)
+      : super(name: 'shorthandmodule') {
+    preIncr = addInput('preIncr', preIncr, width: 8);
+    preDecr = addInput('preDecr', preDecr, width: 8);
+    mulAssign = addInput('mulAssign', mulAssign, width: 8);
+    divAssign = addInput('divAssign', divAssign, width: 8);
+    b = addInput('b', b, width: 8);
+
+    final piOut = addOutput('piOut', width: 8);
+    final pdOut = addOutput('pdOut', width: 8);
+    final maOut = addOutput('maOut', width: 8);
+    final daOut = addOutput('daOut', width: 8);
+    final piOutWithB = addOutput('piOutWithB', width: 8);
+    final pdOutWithB = addOutput('pdOutWithB', width: 8);
+
+    Combinational([
+      piOutWithB < preIncr,
+      pdOutWithB < preDecr,
+      piOut < preIncr,
+      pdOut < preDecr,
+      maOut < mulAssign,
+      daOut < divAssign,
+      // Add these tests
+      piOut.incr(),
+      pdOut.decr(),
+      piOutWithB.incr(b),
+      pdOutWithB.decr(b),
+      maOut.mulAssign(b),
+      daOut.divAssign(b),
+    ]);
+  }
+}
+
 class LoopyCombModule extends Module {
   Logic get a => input('a');
   Logic get x => output('x');
@@ -459,4 +494,70 @@ void main() {
       expect(e.runtimeType, equals(SignalRedrivenException));
     }
   });
+  test('shorthand operations', () async {
+    final mod = ShorthandAssignModule(Logic(width: 8), Logic(width: 8),
+        Logic(width: 8), Logic(width: 8), Logic(width: 8));
+    await mod.build();
+    final vectors = [
+      Vector({
+        'preIncr': 5,
+        'preDecr': 5,
+        'mulAssign': 5,
+        'divAssign': 5,
+        'b': 5
+      }, {
+        'piOutWithB': 10,
+        'pdOutWithB': 0,
+        'piOut': 6,
+        'pdOut': 4,
+        'maOut': 25,
+        'daOut': 1,
+      }),
+      Vector({
+        'preIncr': 5,
+        'preDecr': 5,
+        'mulAssign': 5,
+        'divAssign': 5,
+        'b': 0
+      }, {
+        'piOutWithB': 5,
+        'pdOutWithB': 5,
+        'piOut': 6,
+        'pdOut': 4,
+        'maOut': 0,
+        'daOut': LogicValue.x,
+      }),
+      Vector({
+        'preIncr': 0,
+        'preDecr': 0,
+        'mulAssign': 0,
+        'divAssign': 0,
+        'b': 5
+      }, {
+        'piOutWithB': 5,
+        'pdOutWithB': 0xfb,
+        'piOut': 1,
+        'pdOut': 0xff,
+        'maOut': 0,
+        'daOut': 0,
+      })
+    ];
+    await SimCompare.checkFunctionalVector(mod, vectors);
+    final simResult = SimCompare.iverilogVector(
+        mod.generateSynth(), mod.runtimeType.toString(), vectors,
+        signalToWidthMap: {
+          'preIncr': 8,
+          'preDecr': 8,
+          'mulAssign': 8,
+          'divAssign': 8,
+          'b': 8,
+          'piOutWithB': 8,
+          'pdOutWithB': 8,
+          'piOut': 8,
+          'pdOut': 8,
+          'maOut': 8,
+          'daOut': 8
+        });
+    expect(simResult, equals(true));
+  });
 }