diff --git a/doc/components/adder.md b/doc/components/adder.md index 002b0fe74..204694fb2 100644 --- a/doc/components/adder.md +++ b/doc/components/adder.md @@ -121,9 +121,11 @@ The compound adder forms a select chain around a set of adders specified by: - `adderGen`: an adder generator functor option to build the block adders with the default being a closure returning a functor returning `ParallelPrefixAdder`. This functor has the signature: This functor has the signature: ```dart -(Logic a, Logic b, {Logic? carryIn, Logic? subtractIn, String name = ''})=> Adder +(Logic a, Logic b, {Logic? carryIn, dynamic subtract, String name = ''})=> Adder ``` +Note that the subtract input can either be a `bool` or a `Logic` signal to enable static subtraction or dynamic control of subtraction. + - `splitSelectAdderAlgorithmSingleBlock: - The `CarrySelectCompoundAdder.splitSelectAdderAlgorithmNBit` algorithm splits the adder into blocks of n-bit adders with the first one width adjusted down. - The [CarrySelectCompoundAdder.splitSelectAdderAlgorithmSingleBlock](https://intel.github.io/rohd-hcl/rohd_hcl/CarrySelectCompoundAdder/splitSelectAdderAlgorithmSingleBlock.html) algorithm generates only one sub-block with the full bit-width of the adder. @@ -151,7 +153,7 @@ ROHD-HCL has an implementation of a `CompoundAdder` that uses a `OnesComplement` By providing `outputCarryOut` and/or `outputCarryOutP1` settings, the outputs `carryOut` and `carryOutP1` are provided which also ensures the adder does not convert to 2s complement but instead does the efficient 1s complement subtract (or add) and provides the end-around carry as an output. Otherwise, the adder will add back the end-around carry to the result to convert back to 2s complement. A `sign` and `signP1` is also output for the result. -Both Logic control `subtractIn` and boolean control `subtract` are provided for enabling subtraction either by control signal or by a hard configuration (no signal control). +Both `Logic` control and boolean control are provided via the `subtract` option for enabling subtraction. ```dart final adder = CarrySelectOnesComplementCompoundAdder(a, b, diff --git a/doc/components/multiplier.md b/doc/components/multiplier.md index 1ba1cf32f..8d96dd355 100644 --- a/doc/components/multiplier.md +++ b/doc/components/multiplier.md @@ -100,7 +100,10 @@ The parameters of the - Signed or unsigned operands: - `signedMultiplicand` parameter: whether the multiplicand (first arg) should be treated as signed (twos' complement) or unsigned. - `signedMultiplier` parameter: whether the multiplier (second arg) should be treated as signed (twos' complement) or unsigned. -- As booleans, these parameters satically configure the multiplier to support signed opernads. Alternatively the multiplier supports runtime control of signage by passing a `Logic` signal instead and control logic will be added to support signed or unsigned operands. + - Note that these parameters are optional and can be specified as: + - `null`: default behavior is unsigned. + - `bool`: statically configure the component to treat operand as unsigned. + - `Logic`: specify a `Logic` control signal to dynamically treat operand as signed or unsigned. - An optional `clk`, as well as `enable` and `reset` that are used to add a pipestage in the `ColumnCompressor` to allow for pipelined operation, making the multiplier operate in 2 cycles. Here is an example of use of the `CompressionTreeMultiplier` with one signed input: @@ -135,8 +138,6 @@ The additional parameters of the - The accumulate input term `c` which must have width as sum of the two operand widths + 1. - Addend signage: - `signedAddend` parameter: whether the addend (third argument) should be treated as signed (twos' complement) or unsigned -OR - - An optional `selectSignedAddend` control signal allows for runtime control of signed or unsigned operation with the same hardware. `signedAddend` must be false if using this control signal. - An optional `clk`, as well as `enable` and `reset` that are used to add a pipestage in the `ColumnCompressor` to allow for pipelined operation. The output width of the `CompressionTreeMultiplier` is the sum of the product term widths plus one to accommodate the additional accumulate term. diff --git a/doc/components/static_or_dynamic_configuration.md b/doc/components/static_or_dynamic_configuration.md new file mode 100644 index 000000000..dec34ba47 --- /dev/null +++ b/doc/components/static_or_dynamic_configuration.md @@ -0,0 +1,45 @@ +# Static or Dynamic Component Configuration + +Features of a hardware component may be selectable either at generation time or controllable by a hardware signal. In the former case, we can statically configure the generator to generate the logic needed to support that feature set by the parameter. In the latter case we would generate the logic for the feature as well as control logic to enable or disable the feature. + +An example use of this is in specifying signed operands in ROHD-HCL multipliers where we pass in a parameter during construction: + +```dart +dynamic signedMultiplicand, +... +``` + +Internally, we can convert this dynamic to either a boolean or a Logic depending on what was passed in, and perform the appropriate logic generation for either statically configurating signed-multiplicand operation, or adding the Logic provided as an input for controlling signed-multiplicand operation as a hardware feature. This will enforce that the parameter is either not provided (null), a boolean, or a `Logic` control signal for the feature. + +Inside a component, we can interpret and check the type of the parameter passed in: + +```dart +final signedMultiplicandParameter = StaticOrLogicParameter.ofDynamic(signedMultiplicand); +``` + +We can check if it is a hardware control signal: + +```dart +if (signedMultiplicandParameter.dynamicConfig != null) +``` + +If not dynamically configurable, we can then grab the static boolean: + +```dart +final doSignedMultiplicand = signedMultiplicandParameter.staticConfig; +``` + +Since this is an input parameter with potentially a control `Logic` signal, the signal would have to be added as an input to a module. The API for `StaticOrDynamicParameter` adds the input and creates a new copy by cloning with the current module as an argument (taking care of ensuring the internal signal of the Module is used) for passing to submodules: + +```dart +class MyModule extends Module { + MyModule({dynamic hardwareFeatureOne}) { + // Make sure to pass the signal of the current module's added input and + // not the signal from outside to the submodule. + final submodule = MySubModule(hardwareFeature.clone(this)); + } +} + +final hardwareFeatureOn = Const(1); +final myModule = MyModule(hardwareFeatureOne: hardwareFeatureOn); +``` diff --git a/lib/rohd_hcl.dart b/lib/rohd_hcl.dart index ffd1c7f91..149f2e46e 100644 --- a/lib/rohd_hcl.dart +++ b/lib/rohd_hcl.dart @@ -27,7 +27,7 @@ export 'src/serialization/serialization.dart'; export 'src/shift_register.dart'; export 'src/signed_shifter.dart'; export 'src/sort.dart'; -export 'src/static_or_runtime_parameter.dart'; +export 'src/static_or_dynamic_parameter.dart'; export 'src/summation/summation.dart'; export 'src/toggle_gate.dart'; export 'src/utils.dart'; diff --git a/lib/src/arithmetic/compound_adder.dart b/lib/src/arithmetic/compound_adder.dart index aa16dc374..7296bbca0 100644 --- a/lib/src/arithmetic/compound_adder.dart +++ b/lib/src/arithmetic/compound_adder.dart @@ -101,7 +101,7 @@ class CarrySelectCompoundAdder extends CompoundAdder { /// Default adder functor to use. static Adder _defaultBlockAdder(Logic a, Logic b, - {Logic? carryIn, Logic? subtractIn, String name = ''}) => + {Logic? carryIn, dynamic subtract, String name = ''}) => ParallelPrefixAdder(a, b, carryIn: carryIn, name: name); /// Constructs a [CarrySelectCompoundAdder]. @@ -120,7 +120,7 @@ class CarrySelectCompoundAdder extends CompoundAdder { Logic? subtractIn, super.carryIn, Adder Function(Logic a, Logic b, - {Logic? carryIn, Logic? subtractIn, String name}) + {Logic? carryIn, dynamic subtract, String name}) adderGen = _defaultBlockAdder, List Function(int) widthGen = splitSelectAdderAlgorithmSingleBlock, super.name = 'cs_compound_adder', @@ -159,10 +159,10 @@ class CarrySelectCompoundAdder extends CompoundAdder { // Build sub adders for carryIn=0 and carryIn=1 final fullAdder0 = adderGen(a.getRange(blockStartIdx, blockEnd), b.getRange(blockStartIdx, blockEnd), - subtractIn: subtractIn, carryIn: Const(0), name: 'block0_$i'); + subtract: subtractIn, carryIn: Const(0), name: 'block0_$i'); final fullAdder1 = adderGen(a.getRange(blockStartIdx, blockEnd), b.getRange(blockStartIdx, blockEnd), - subtractIn: subtractIn, carryIn: Const(1), name: 'block1_$i'); + subtract: subtractIn, carryIn: Const(1), name: 'block1_$i'); sum0Ary <= ((i == 0) ? fullAdder0.sum @@ -220,32 +220,31 @@ class CarrySelectOnesComplementCompoundAdder extends CompoundAdder { /// Subtraction controlled by an optional logic [subtractIn] @protected - late final Logic? subtractIn; + late final StaticOrDynamicParameter subtractIn; /// Constructs a [CarrySelectCompoundAdder] using a set of /// [OnesComplementAdder] in a carry-select configuration. Adds (or subtracts) /// [a] and [b] to produce [sum] and [sumP1] (sum plus 1). - /// - [adderGen] is the adder generator [Function] inside the - /// [OnesComplementAdder]. - /// - [subtractIn] is an optional [Logic] control for subtraction. - /// - [subtract] is a boolean control for subtraction. It must be - /// `false`(default) if a [subtractIn] [Logic] is provided. + /// - [adderGen] is the adder used inside the [OnesComplementAdder]. + /// - The optional [subtract] parameter configures the adder to subtract [b] + /// from [a] statically using a `bool` to indicate a ssubtraction (default + /// is `false`, or addition) or dynamically with a 1-bit [Logic] input. + /// Passing something other null, `bool`, or [Logic] will result in a throw. /// - [generateCarryOut] set to `true` will create output [carryOut] and /// employ the ones-complement optimization of not adding '1' to convert /// back to 2s complement during subtraction on the [sum]. /// - [generateCarryOutP1] set to `true` will create output [carryOutP1] and /// employ the ones-complement optimization of not adding '1' to convert /// back to 2s complement during subtraction on the [sumP1]. - /// - [widthGen] is a [Function] which produces a list for splitting the adder + /// - [widthGen] is a function which produces a list for splitting the adder /// for the carry-select chain. The default is /// [CarrySelectCompoundAdder.splitSelectAdderAlgorithmSingleBlock], CarrySelectOnesComplementCompoundAdder(super.a, super.b, {Adder Function(Logic, Logic, {Logic? carryIn}) adderGen = NativeAdder.new, - Logic? subtractIn, bool generateCarryOut = false, bool generateCarryOutP1 = false, - bool subtract = false, + dynamic subtract, List Function(int) widthGen = CarrySelectCompoundAdder.splitSelectAdderAlgorithmSingleBlock, super.name, @@ -255,9 +254,7 @@ class CarrySelectOnesComplementCompoundAdder extends CompoundAdder { : super( definitionName: definitionName ?? 'CarrySelectOnesComplementCompoundAdder_W${a.width}') { - subtractIn = (subtractIn != null) - ? addInput('subtractIn', subtractIn, width: subtractIn.width) - : null; + subtractIn = StaticOrDynamicParameter.ofDynamic(subtract).clone(this); if (generateCarryOut) { addOutput('carryOut'); @@ -266,19 +263,18 @@ class CarrySelectOnesComplementCompoundAdder extends CompoundAdder { addOutput('carryOutP1'); } - final doSubtract = subtractIn ?? (subtract ? Const(subtract) : Const(0)); + final doSubtract = subtractIn.getLogic(this); final csadder = CarrySelectCompoundAdder(a, b, widthGen: widthGen, - subtractIn: subtractIn, - adderGen: (a, b, {carryIn, subtractIn, name = 'ones_complement'}) => + subtractIn: doSubtract, + adderGen: (a, b, {carryIn, subtract, name = 'ones_complement'}) => OnesComplementAdder(a, b, adderGen: adderGen, carryIn: carryIn, generateEndAroundCarry: true, - subtract: subtract, chainable: true, - subtractIn: subtractIn)); + subtract: subtract)); addOutput('sign') <= mux(doSubtract, ~csadder.sum[-1], Const(0)); addOutput('signP1') <= mux(doSubtract, ~csadder.sumP1[-1], Const(0)); diff --git a/lib/src/arithmetic/floating_point/floating_point_adder_dualpath.dart b/lib/src/arithmetic/floating_point/floating_point_adder_dualpath.dart index 5948c5c96..fc49b3b99 100644 --- a/lib/src/arithmetic/floating_point/floating_point_adder_dualpath.dart +++ b/lib/src/arithmetic/floating_point/floating_point_adder_dualpath.dart @@ -154,7 +154,7 @@ class FloatingPointAdderDualPath - signedMultiplicandParameter.tryRuntimeInput(this); - - /// If not null, use this signal to select between signed and unsigned - /// multiplier [b] - @protected - Logic? get selectSignedMultiplier => - signedMultiplierParameter.tryRuntimeInput(this); + late final StaticOrDynamicParameter signedMultiplierParameter; /// [Logic] that tells us [product] is signed. @protected @@ -74,18 +54,15 @@ abstract class Multiplier extends Module { /// multiplication result. /// /// The optional [signedMultiplicand] parameter configures the multiplicand - /// [a] statically using a [bool] as a signed multiplicand (default is - /// `false`, or unsigned) or dynamically with a 1-bit [Logic] - /// [selectSignedMultiplicand] input. You can pass either a [bool] (for static - /// configuration) or a [Logic] (dynamically configuring the type handled) - /// with a signal to this parameter, otherwise this constructor will throw. + /// [a] statically using a `bool` to indicate a signed multiplicand (default + /// is `false`, or unsigned) or dynamically with a 1-bit [Logic] input. + /// Passing something other null, `bool`, or [Logic] will result in a throw. + /// /// /// The optional [signedMultiplier] parameter configures the multiplier [b] - /// statically using a [bool] as a signed multiplier (default is `false`, or - /// unsigned) or dynamically with a 1-bit [Logic] [selectSignedMultiplier] - /// input. You can pass either a [bool] (for static configuration) or a - /// [Logic] (dynamically configuring the type handled with a signal) to this - /// parameter, otherwise this constructor will throw. + /// statically using a `bool` to indicate a signed multiplier (default is + /// `false`, or unsigned) or dynamically with a 1-bit [Logic] input. Passing + /// something other null, `bool` or [Logic] will result in a throw. /// /// If [clk] is not null then a set of flops are used to make the multiply a /// 2-cycle latency operation. [reset] and [enable] are optional inputs to @@ -110,12 +87,12 @@ abstract class Multiplier extends Module { a = addInput('a', a, width: a.width); b = addInput('b', b, width: b.width); + // We clone parameters in case they contain [Logic] signals that must be + // added as inputs to the current module. signedMultiplicandParameter = - StaticOrRuntimeParameter.ofDynamic(signedMultiplicand); - this.signedMultiplicand = signedMultiplicandParameter.staticConfig; + StaticOrDynamicParameter.ofDynamic(signedMultiplicand).clone(this); signedMultiplierParameter = - StaticOrRuntimeParameter.ofDynamic(signedMultiplier); - this.signedMultiplier = signedMultiplierParameter.staticConfig; + StaticOrDynamicParameter.ofDynamic(signedMultiplier).clone(this); addOutput('product', width: a.width + b.width); addOutput('isProductSigned') <= @@ -124,35 +101,36 @@ abstract class Multiplier extends Module { } /// This is a helper function that prints out the kind of multiplicand - /// (selected by a [Logic] or set statically via [bool]). + /// (selected by a [Logic] or set statically via `bool`). /// - UD: unsigned multiplicand. /// - SD: signed multiplicand. /// - SSD: dynamic selection of signed multiplicand. static String signedMD(dynamic mdConfig) => - ((mdConfig is! StaticOrRuntimeParameter) | (mdConfig == null)) + ((mdConfig is! StaticOrDynamicParameter) | (mdConfig == null)) ? 'UD' - : ((mdConfig as StaticOrRuntimeParameter).runtimeConfig != null) + : ((mdConfig as StaticOrDynamicParameter).dynamicConfig != null) ? 'SSD' : mdConfig.staticConfig ? 'SD' : 'UD'; /// This is a helper function that prints out the kind of multiplier (selected - /// by a [Logic] or set statically via [bool]).) + /// by a [Logic] or set statically via `bool`).) /// - UM: unsigned multiplier. /// - SM: signed multiplier. /// - SSM: dynamic selection of signed multiplier. static String signedML(dynamic mlConfig) => - ((mlConfig is! StaticOrRuntimeParameter) | (mlConfig == null)) + ((mlConfig is! StaticOrDynamicParameter) | (mlConfig == null)) ? 'UM' - : (mlConfig as StaticOrRuntimeParameter).runtimeConfig != null + : (mlConfig as StaticOrDynamicParameter).dynamicConfig != null ? 'SSM' : mlConfig.staticConfig ? 'SM' : 'UM'; } -/// A class which wraps the native '*' operator so that it can be passed +/// A class which wraps the native '*' operator so that it support our +/// [Multiplier] interface. This is useful for passing the native multiplier /// into other modules as a parameter for using the native operation. class NativeMultiplier extends Multiplier { /// The width of input [a] and [b] must be the same. @@ -178,28 +156,30 @@ class NativeMultiplier extends Multiplier { final Logic extendedMultiplicand; final Logic extendedMultiplier; - if (selectSignedMultiplicand == null) { - extendedMultiplicand = - signedMultiplicand ? a.signExtend(pW) : a.zeroExtend(pW); + if (signedMultiplicandParameter.dynamicConfig == null) { + extendedMultiplicand = signedMultiplicandParameter.staticConfig + ? a.signExtend(pW) + : a.zeroExtend(pW); } else { final len = a.width; final sign = a[len - 1]; final extension = [ for (var i = len; i < pW; i++) - mux(selectSignedMultiplicand!, sign, Const(0)) + mux(signedMultiplicandParameter.dynamicConfig!, sign, Const(0)) ]; extendedMultiplicand = (a.elements + extension).rswizzle(); } - if (selectSignedMultiplier == null) { - extendedMultiplier = - (signedMultiplier ? b.signExtend(pW) : b.zeroExtend(pW)) - .named('extended_multiplier', naming: Naming.mergeable); + if (signedMultiplierParameter.dynamicConfig == null) { + extendedMultiplier = (signedMultiplierParameter.staticConfig + ? b.signExtend(pW) + : b.zeroExtend(pW)) + .named('extended_multiplier', naming: Naming.mergeable); } else { final len = b.width; final sign = b[len - 1]; final extension = [ for (var i = len; i < pW; i++) - mux(selectSignedMultiplier!, sign, Const(0)) + mux(signedMultiplierParameter.dynamicConfig!, sign, Const(0)) ]; extendedMultiplier = (b.elements + extension) .rswizzle() @@ -249,10 +229,8 @@ class CompressionTreeMultiplier extends Multiplier { '${Multiplier.signedML(signedMultiplier)}_' 'with${adderGen(a, a).definitionName}') { final pp = PartialProduct(a, b, RadixEncoder(radix), - selectSignedMultiplicand: selectSignedMultiplicand, - signedMultiplicand: signedMultiplicand, - selectSignedMultiplier: selectSignedMultiplier, - signedMultiplier: signedMultiplier, + signedMultiplicand: signedMultiplicandParameter, + signedMultiplier: signedMultiplierParameter, name: 'comp_partial_product'); signExtensionGen(pp.array).signExtend(); diff --git a/lib/src/arithmetic/multiplier_components/column_compressor.dart b/lib/src/arithmetic/multiplier_components/column_compressor.dart index 1d1ef8b36..b0cea7f6b 100644 --- a/lib/src/arithmetic/multiplier_components/column_compressor.dart +++ b/lib/src/arithmetic/multiplier_components/column_compressor.dart @@ -231,7 +231,6 @@ class ColumnCompressor extends Module { for (var row = 0; row < inRows.length; row++) addInput('row_$row', inRows[row], width: inRows[row].width) ]; - // pp = PartialProductMatrixStore(inputRows, rowShift); columns = List.generate(maxWidth(), (i) => ColumnQueue()); for (var row = 0; row < _rows.length; row++) { diff --git a/lib/src/arithmetic/multiplier_components/evaluate_partial_product.dart b/lib/src/arithmetic/multiplier_components/evaluate_partial_product.dart index 9c7d7c7cc..6068b491f 100644 --- a/lib/src/arithmetic/multiplier_components/evaluate_partial_product.dart +++ b/lib/src/arithmetic/multiplier_components/evaluate_partial_product.dart @@ -12,15 +12,13 @@ import 'package:rohd_hcl/rohd_hcl.dart'; /// The following routines are useful only during testing. extension TestPartialProductSignage on PartialProductGeneratorBase { - /// Return `true` if multiplicand is truly signed (fixed or runtime). - bool isSignedMultiplicand() => (selectSignedMultiplicand == null) - ? signedMultiplicand - : !selectSignedMultiplicand!.value.isZero; + /// Return `true` if multiplicand is truly signed (fixed or runtime) + bool isSignedMultiplicand() => + StaticOrDynamicParameter.ofDynamic(signedMultiplicand).value; - /// Return `true` if multiplier is truly signed (fixed or runtime). - bool isSignedMultiplier() => (selectSignedMultiplier == null) - ? signedMultiplier - : !selectSignedMultiplier!.value.isZero; + /// Return `true` if multiplier is truly signed (fixed or runtime) + bool isSignedMultiplier() => + StaticOrDynamicParameter.ofDynamic(signedMultiplier).value; /// Return `true` if accumulate result is truly signed (fixed or runtime). bool isSignedResult() => isSignedMultiplicand() | isSignedMultiplier(); diff --git a/lib/src/arithmetic/multiplier_components/multiplicand_selector.dart b/lib/src/arithmetic/multiplier_components/multiplicand_selector.dart index 9053c6828..210878b1e 100644 --- a/lib/src/arithmetic/multiplier_components/multiplicand_selector.dart +++ b/lib/src/arithmetic/multiplier_components/multiplicand_selector.dart @@ -31,30 +31,30 @@ class MultiplicandSelector { /// Multiples sliced into columns for select to access. late final multiplesSlice = []; - /// Build a [MultiplicandSelector] generationg required [multiples] of - /// [multiplicand] to [select] using a [RadixEncoder] argument. + /// Build a [MultiplicandSelector] generating required [multiples] of + /// [multiplicand] to [select] using a [RadixEncoder] output. /// - /// [multiplicand] is base multiplicand multiplied by Booth encodings of - /// the [RadixEncoder] during [select]. - /// - /// [signedMultiplicand] generates a fixed signed selector versus using - /// [selectSignedMultiplicand] which is a runtime sign selection [Logic] - /// in which case [signedMultiplicand] must be `false`. - MultiplicandSelector(this.radix, this.multiplicand, - {Logic? selectSignedMultiplicand, bool signedMultiplicand = false}) - : shift = log2Ceil(radix) { - if (signedMultiplicand && (selectSignedMultiplicand != null)) { - throw RohdHclException('sign reconfiguration requires signed=false'); - } + /// The optional [signedMultiplicand] parameter configures the [multiplicand] + /// statically using a `bool` to indicate a signed multiplicand (default is + /// `false`, or unsigned) or dynamically with a 1-bit [Logic] input. Passing + /// something other null, `bool`, or [Logic] will result in a throw. + MultiplicandSelector( + this.radix, + this.multiplicand, { + dynamic signedMultiplicand, + }) : shift = log2Ceil(radix) { + final signedMultiplicandParameter = + StaticOrDynamicParameter.ofDynamic(signedMultiplicand); if (radix > 16) { throw RohdHclException('Radices beyond 16 are not yet supported'); } final width = multiplicand.width + shift; final numMultiples = radix ~/ 2; multiples = LogicArray([numMultiples], width, name: 'multiples'); + final Logic extendedMultiplicand; - if (selectSignedMultiplicand == null) { - extendedMultiplicand = signedMultiplicand + if (signedMultiplicandParameter.dynamicConfig == null) { + extendedMultiplicand = signedMultiplicandParameter.staticConfig ? multiplicand.signExtend(width) : multiplicand.zeroExtend(width); } else { @@ -62,7 +62,7 @@ class MultiplicandSelector { final sign = multiplicand[len - 1]; final extension = [ for (var i = len; i < width; i++) - mux(selectSignedMultiplicand, sign, Const(0)) + mux(signedMultiplicandParameter.dynamicConfig!, sign, Const(0)) ]; extendedMultiplicand = (multiplicand.elements + extension).rswizzle(); } diff --git a/lib/src/arithmetic/multiplier_components/multiplier_encoder.dart b/lib/src/arithmetic/multiplier_components/multiplier_encoder.dart index f25107792..7df64ba4e 100644 --- a/lib/src/arithmetic/multiplier_components/multiplier_encoder.dart +++ b/lib/src/arithmetic/multiplier_components/multiplier_encoder.dart @@ -113,21 +113,25 @@ class MultiplierEncoder { late final _encodings = []; - /// Generate the Booth encoding of an input [multiplier] using - /// [radixEncoder]. + /// Generate the Booth encoding of an input [multiplier] using [radixEncoder]. /// - /// [signedMultiplier] generates a fixed signed encoder versus using - /// [selectSignedMultiplier] which is a runtime sign selection [Logic] - /// in which case [signedMultiplier] must be `false`. - MultiplierEncoder(this.multiplier, RadixEncoder radixEncoder, - {Logic? selectSignedMultiplier, bool signedMultiplier = false}) - : _encoder = radixEncoder { - if (signedMultiplier && (selectSignedMultiplier != null)) { - throw RohdHclException('sign reconfiguration requires signed=false'); - } + /// When using signed multipliers, the [signedMultiplier] option configures + /// sign extension of the multiplier and additional rows needed for encoding. + /// This optional parameter configures the [multiplier] statically using a + /// `bool` to indicate a signed multiplier (default is `false`, or unsigned) + /// or dynamically with a 1-bit [Logic] input. Passing something other null, + /// `bool`, or [Logic] will result in a throw. + MultiplierEncoder( + this.multiplier, + RadixEncoder radixEncoder, { + dynamic signedMultiplier, + }) : _encoder = radixEncoder { + final signedMultiplierParameter = + StaticOrDynamicParameter.ofDynamic(signedMultiplier); // Unsigned encoding wants to overlap past the multipler - if (signedMultiplier) { - rows = ((multiplier.width + (signedMultiplier ? 0 : 1)) / + if (signedMultiplierParameter.dynamicConfig == null) { + rows = ((multiplier.width + + (signedMultiplierParameter.staticConfig ? 0 : 1)) / log2Ceil(radixEncoder.radix)) .ceil(); } else { @@ -137,8 +141,8 @@ class MultiplierEncoder { ((multiplier.width + 1) ~/ log2Ceil(radixEncoder.radix)); } // slices overlap by 1 and start at -1a - if (selectSignedMultiplier == null) { - _extendedMultiplier = (signedMultiplier + if (signedMultiplierParameter.dynamicConfig == null) { + _extendedMultiplier = (signedMultiplierParameter.staticConfig ? multiplier.signExtend(rows * (log2Ceil(radixEncoder.radix))) : multiplier.zeroExtend(rows * (log2Ceil(radixEncoder.radix)))) .named('extended_multiplier', naming: Naming.mergeable); @@ -147,7 +151,7 @@ class MultiplierEncoder { final sign = multiplier[len - 1]; final extension = [ for (var i = len; i < (rows * (log2Ceil(radixEncoder.radix))); i++) - mux(selectSignedMultiplier, sign, Const(0)) + mux(signedMultiplierParameter.dynamicConfig!, sign, Const(0)) ]; _extendedMultiplier = (multiplier.elements + extension) .rswizzle() diff --git a/lib/src/arithmetic/multiplier_components/partial_product_generator.dart b/lib/src/arithmetic/multiplier_components/partial_product_generator.dart index 732ef3cbd..0a44b5a00 100644 --- a/lib/src/arithmetic/multiplier_components/partial_product_generator.dart +++ b/lib/src/arithmetic/multiplier_components/partial_product_generator.dart @@ -41,6 +41,7 @@ abstract class PartialProductArray { /// The actual shift in each row. This value will be modified by the /// sign extension routine used when folding in a sign bit from another /// row. + /// row. final rowShift = []; /// Partial Products output. Generated by selector and extended by sign @@ -185,9 +186,9 @@ abstract class PartialProductArray { partialProducts[row].insertAll(col - rowShift[row], list); } -/// A [PartialProductGeneratorBase] class that generates a set of partial -/// products. Essentially a set of shifted rows of [Logic] addends generated by -/// Booth recoding and manipulated by sign extension, before being compressed. +/// A [PartialProductGeneratorBase] class generates a set of partial products. +/// Essentially a set of shifted rows of [Logic] addends generated by Booth +/// recoding and manipulated by sign extension, before being compressed. abstract class PartialProductGeneratorBase extends PartialProductArray { /// Get the shift increment between neighboring product rows. int get shift => selector.shift; @@ -205,60 +206,48 @@ abstract class PartialProductGeneratorBase extends PartialProductArray { /// multiples of the multiplicand and generate partial products. late final MultiplicandSelector selector; - /// [multiplicand] operand is always signed. - final bool signedMultiplicand; - - /// [multiplier] operand is always signed. - final bool signedMultiplier; - /// Used to avoid sign extending more than once. bool isSignExtended = false; - /// If not null, use this signal to select between signed and unsigned - /// [multiplicand]. - final Logic? selectSignedMultiplicand; + /// The parameter controlling whether the multiplicand is signed. + final dynamic signedMultiplicand; - /// If not null, use this signal to select between signed and unsigned - /// [multiplier]. - final Logic? selectSignedMultiplier; + /// The parameter controlling whether the multiplier is signed. + final dynamic signedMultiplier; - /// Construct a [PartialProductGeneratorBase] -- the partial product matrix. + /// Construct a [PartialProductGeneratorBase] -- the partial product rows + /// represented by the base [PartialProductArray] -- by using [radixEncoder] + /// to Booth-encode the [multiplier] and then select the appropriate multiple + /// of the [multiplicand] for each row. /// - /// [signedMultiplicand] generates a fixed signed encoder versus using - /// [selectSignedMultiplicand] which is a runtime sign selection [Logic] - /// in which case [signedMultiplicand] must be `false`. + /// The optional [signedMultiplicand] parameter configures the [multiplicand] + /// statically using a `bool` to indicate a signed multiplicand (default is + /// `false`, or unsigned) or dynamically with a 1-bit [Logic] input. Passing + /// something other null, `bool`, or [Logic] will result in a throw. /// - /// [signedMultiplier] generates a fixed signed encoder versus using - /// [selectSignedMultiplier] which is a runtime sign selection [Logic] - /// in which case [signedMultiplier] must be `false`. + /// The optional [signedMultiplier] parameter configures the [multiplier] + /// statically using a `bool` to indicate a signed multiplier (default is + /// `false`, or unsigned) or dynamically with a 1-bit [Logic] input. Passing + /// something other null, `bool`, or [Logic] will result in a throw. PartialProductGeneratorBase( Logic multiplicand, Logic multiplier, RadixEncoder radixEncoder, - {this.signedMultiplicand = false, - this.signedMultiplier = false, - this.selectSignedMultiplicand, - this.selectSignedMultiplier, - super.name = 'ppg'}) { - if (signedMultiplicand && (selectSignedMultiplicand != null)) { - throw RohdHclException('multiplicand sign reconfiguration requires ' - 'signedMultiplicand=false'); - } - if (signedMultiplier && (selectSignedMultiplier != null)) { - throw RohdHclException('sign reconfiguration requires signed=false'); - } + {this.signedMultiplicand, this.signedMultiplier, super.name = 'ppg'}) { + final multiplierParameter = + StaticOrDynamicParameter.ofDynamic(signedMultiplier); selector = MultiplicandSelector(radixEncoder.radix, multiplicand, - signedMultiplicand: signedMultiplicand, - selectSignedMultiplicand: selectSignedMultiplicand); + signedMultiplicand: signedMultiplicand); encoder = MultiplierEncoder(multiplier, radixEncoder, - signedMultiplier: signedMultiplier, - selectSignedMultiplier: selectSignedMultiplier); + signedMultiplier: signedMultiplier); if (multiplicand.width < selector.shift) { throw RohdHclException('multiplicand width must be greater than ' 'or equal to ${selector.shift}'); } - if (multiplier.width < (selector.shift + (signedMultiplier ? 1 : 0))) { + if (multiplier.width < + (selector.shift + (multiplierParameter.staticConfig ? 1 : 0))) { throw RohdHclException('multiplier width must be greater than ' - 'or equal to ${selector.shift + (signedMultiplier ? 1 : 0)}'); + 'or equal to ' + '${selector.shift + (multiplierParameter.staticConfig ? 1 : 0)}'); } _build(); } @@ -330,20 +319,29 @@ abstract class PartialProductMatrix extends Module { /// and generation of outputs in an actual module as opposed to an inline /// generator class. class PartialProduct extends PartialProductMatrix { - /// Create a [PartialProduct] [Module] which manages a - /// [PartialProductArray]. + /// Construct a [PartialProduct] module which constructs a + /// [PartialProductArray] by using [radixEncoder] to Booth-encode the + /// [multiplier] and then select the appropriate multiple of the + /// [multiplicand] for each row. It does this by calling [genPPG], a + /// [PartialProductGeneratorBase] which allows for customization of the row + /// encodings as well as Booth encoding outside the scope of a module. + /// + /// The optional [signedMultiplicand] parameter configures the [multiplicand] + /// statically using a `bool` to indicate a signed multiplicand (default is + /// `false`, or unsigned) or dynamically with a 1-bit [Logic] input. Passing + /// something other null, `bool`, or [Logic] will result in a throw. + /// + /// The optional [signedMultiplier] parameter configures the [multiplier] + /// statically using a `bool` to indicate a signed multiplier (default is + /// `false`, or unsigned) or dynamically with a 1-bit [Logic] input. Passing + /// something other null, `bool`, or [Logic] will result in a throw. PartialProduct( Logic multiplicand, Logic multiplier, RadixEncoder radixEncoder, - {bool signedMultiplicand = false, - bool signedMultiplier = false, - Logic? selectSignedMultiplicand, - Logic? selectSignedMultiplier, + {dynamic signedMultiplicand, + dynamic signedMultiplier, PartialProductGeneratorBase Function( Logic multiplicand, Logic multiplier, RadixEncoder radixEncoder, - {bool signedMultiplicand, - bool signedMultiplier, - Logic? selectSignedMultiplicand, - Logic? selectSignedMultiplier}) + {dynamic signedMultiplicand, dynamic signedMultiplier}) genPPG = PartialProductGenerator.new, super.name = 'partial_product', super.reserveName, @@ -354,21 +352,18 @@ class PartialProduct extends PartialProductMatrix { 'PartialProduct_W${multiplicand.width}x${multiplier.width}' '_${Multiplier.signedMD(signedMultiplicand)}_' '${Multiplier.signedML(signedMultiplier)}') { - final selectSignedMultiplicandInternal = selectSignedMultiplicand != null - ? addInput(selectSignedMultiplicand.name, selectSignedMultiplicand) - : null; - final selectSignedMultiplierInternal = selectSignedMultiplier != null - ? addInput(selectSignedMultiplier.name, selectSignedMultiplier) - : null; + final signedMultiplicandParameter = + StaticOrDynamicParameter.ofDynamic(signedMultiplicand); + + final signedMultiplierParameter = + StaticOrDynamicParameter.ofDynamic(signedMultiplier); final localMultiplicand = addInput('multiplicand', multiplicand, width: multiplicand.width); final localMultiplier = addInput('multiplier', multiplier, width: multiplier.width); _array = genPPG(localMultiplicand, localMultiplier, radixEncoder, - signedMultiplicand: signedMultiplicand, - signedMultiplier: signedMultiplier, - selectSignedMultiplicand: selectSignedMultiplicandInternal, - selectSignedMultiplier: selectSignedMultiplierInternal); + signedMultiplicand: signedMultiplicandParameter.clone(this), + signedMultiplier: signedMultiplierParameter.clone(this)); } } diff --git a/lib/src/arithmetic/multiplier_components/partial_product_sign_extend.dart b/lib/src/arithmetic/multiplier_components/partial_product_sign_extend.dart index 134010f35..02a90fb84 100644 --- a/lib/src/arithmetic/multiplier_components/partial_product_sign_extend.dart +++ b/lib/src/arithmetic/multiplier_components/partial_product_sign_extend.dart @@ -46,19 +46,11 @@ abstract class PartialProductSignExtension { /// The partial product generator we are sign extending. final PartialProductGeneratorBase ppg; - /// multiplicand operand is always signed. - bool get signedMultiplicand => ppg.signedMultiplicand; + /// The parameter controlling whether the multiplicand is signed. + dynamic get signedMultiplicand => ppg.signedMultiplicand; - /// multiplier operand is always signed. - bool get signedMultiplier => ppg.signedMultiplier; - - /// If not null, use this signal to select between signed and unsigned - /// multiplicand. - Logic? get selectSignedMultiplicand => ppg.selectSignedMultiplicand; - - /// If not null, use this signal to select between signed and unsigned - /// multiplier. - Logic? get selectSignedMultiplier => ppg.selectSignedMultiplier; + /// The parameter controlling whether the multiplier is signed. + dynamic get signedMultiplier => ppg.signedMultiplier; /// Number of rows of partial products. int get rows => ppg.rows; @@ -91,15 +83,7 @@ abstract class PartialProductSignExtension { /// Sign Extension class that operates on a [PartialProductGeneratorBase] /// and sign-extends the entries. - PartialProductSignExtension(this.ppg, {this.name = 'no_sign_extension'}) { - if (signedMultiplier && (selectSignedMultiplier != null)) { - throw RohdHclException('sign reconfiguration requires signed=false'); - } - if (signedMultiplicand && (selectSignedMultiplicand != null)) { - throw RohdHclException('multiplicand sign reconfiguration requires ' - 'signedMultiplicand=false'); - } - } + PartialProductSignExtension(this.ppg, {this.name = 'no_sign_extension'}); /// Execute the sign extension, overridden to specialize. void signExtend(); @@ -107,7 +91,7 @@ abstract class PartialProductSignExtension { /// Helper function for sign extension routines: /// For signed operands, set the MSB to [sign], otherwise add this [sign] bit. void addStopSign(List addend, SignBit sign) { - if (!signedMultiplicand) { + if (!StaticOrDynamicParameter.ofDynamic(signedMultiplicand).staticConfig) { addend.add(sign); } else { addend.last = sign; @@ -117,12 +101,15 @@ abstract class PartialProductSignExtension { /// Helper function for sign extension routines: /// For signed operands, flip the MSB, otherwise add this [sign] bit. void addStopSignFlip(List addend, SignBit sign) { - if (!signedMultiplicand) { - if (selectSignedMultiplicand == null) { + final signedMultiplicandParameter = + StaticOrDynamicParameter.ofDynamic(signedMultiplicand); + if (!signedMultiplicandParameter.staticConfig) { + if (signedMultiplicandParameter.dynamicConfig == null) { addend.add(sign); } else { - addend.add(SignBit(mux(selectSignedMultiplicand!, ~addend.last, sign), - inverted: selectSignedMultiplicand != null)); + addend.add(SignBit( + mux(signedMultiplicandParameter.dynamicConfig!, ~addend.last, sign), + inverted: signedMultiplicandParameter.dynamicConfig != null)); } } else { addend.last = SignBit(~addend.last, inverted: true); @@ -168,9 +155,7 @@ class PartialProductGenerator extends PartialProductGeneratorBase { super.multiplicand, super.multiplier, super.radixEncoder, {super.signedMultiplicand, super.signedMultiplier, - super.selectSignedMultiplicand, - super.selectSignedMultiplier, - super.name = 'none'}) { + super.name = 'partial_product_generator'}) { extender = NoneSignExtension(this); signExtend(); } @@ -189,10 +174,8 @@ class BruteSignExtension extends PartialProductSignExtension { /// Fully sign extend the PP array: useful for reference only @override void signExtend() { - if (signedMultiplicand && (selectSignedMultiplicand != null)) { - throw RohdHclException('multiplicand sign reconfiguration requires ' - 'signedMultiplicand=false'); - } + final signedMultiplicandParameter = + StaticOrDynamicParameter.ofDynamic(signedMultiplicand); if (isSignExtended) { throw RohdHclException('Partial Product array already sign-extended'); } @@ -207,10 +190,12 @@ class BruteSignExtension extends PartialProductSignExtension { for (var row = 0; row < rows; row++) { final addend = partialProducts[row]; final Logic sign; - if (selectSignedMultiplicand != null) { - sign = mux(selectSignedMultiplicand!, addend.last, signs[row]); + if (signedMultiplicandParameter.dynamicConfig != null) { + sign = mux(signedMultiplicandParameter.dynamicConfig!, addend.last, + signs[row]); } else { - sign = signedMultiplicand ? addend.last : signs[row]; + sign = + signedMultiplicandParameter.staticConfig ? addend.last : signs[row]; } addend.addAll(List.filled((rows - row) * shift, SignBit(sign))); if (row > 0) { @@ -241,8 +226,6 @@ class PartialProductGeneratorBruteSignExtension super.multiplicand, super.multiplier, super.radixEncoder, {super.signedMultiplicand, super.signedMultiplier, - super.selectSignedMultiplicand, - super.selectSignedMultiplier, super.name = 'brute'}) { extender = BruteSignExtension(this); signExtend(); @@ -265,20 +248,24 @@ class CompactSignExtension extends PartialProductSignExtension { // Mohanty, B.K., Choubey, A. Efficient Design for Radix-8 Booth Multiplier // and Its Application in Lifting 2-D DWT. Circuits Syst Signal Process 36, // 1129–1149 (2017). https://doi.org/10.1007/s00034-016-0349-9 - if (signedMultiplicand && (selectSignedMultiplicand != null)) { - throw RohdHclException('multiplicand sign reconfiguration requires ' - 'signedMultiplicand=false'); - } + // if (signedMultiplicand && (selectSignedMultiplicand != null)) { + // throw RohdHclException('multiplicand sign reconfiguration requires ' + // 'signedMultiplicand=false'); + // } if (isSignExtended) { throw RohdHclException('Partial Product array already sign-extended'); } isSignExtended = true; + final signedMultiplicandParameter = + StaticOrDynamicParameter.ofDynamic(signedMultiplicand); + final lastRow = rows - 1; final firstAddend = partialProducts[0]; final lastAddend = partialProducts[lastRow]; - final firstRowQStart = selector.width - (signedMultiplicand ? 1 : 0); + final firstRowQStart = + selector.width - (signedMultiplicandParameter.staticConfig ? 1 : 0); final lastRowSignPos = shift * lastRow; final alignRow0Sign = firstRowQStart - lastRowSignPos; @@ -341,12 +328,15 @@ class CompactSignExtension extends PartialProductSignExtension { // Compute Sign extension for row==0 final firstSign = Logic(name: 'firstsign', naming: Naming.mergeable); - if (selectSignedMultiplicand == null) { + if (signedMultiplicandParameter.dynamicConfig == null) { firstSign <= - (signedMultiplicand ? SignBit(firstAddend.last) : SignBit(signs[0])); + (signedMultiplicandParameter.staticConfig + ? SignBit(firstAddend.last) + : SignBit(signs[0])); } else { firstSign <= - SignBit(mux(selectSignedMultiplicand!, firstAddend.last, signs[0])); + SignBit(mux(signedMultiplicandParameter.dynamicConfig!, + firstAddend.last, signs[0])); } final q = [ (firstSign ^ remainders[lastRow]) @@ -372,7 +362,7 @@ class CompactSignExtension extends PartialProductSignExtension { for (var i = 0; i < shift - 1; i++) { firstAddend[i] = m[0][i]; } - if (!signedMultiplicand) { + if (!signedMultiplicandParameter.staticConfig) { firstAddend.add(q[0]); } else { firstAddend.last = q[0]; @@ -401,8 +391,6 @@ class PartialProductGeneratorCompactSignExtension super.multiplicand, super.multiplier, super.radixEncoder, {super.signedMultiplicand, super.signedMultiplier, - super.selectSignedMultiplicand, - super.selectSignedMultiplier, super.name = 'compact'}) { extender = CompactSignExtension(this); signExtend(); @@ -427,14 +415,16 @@ class StopBitsSignExtension extends PartialProductSignExtension { /// @override void signExtend() { - if (signedMultiplicand && (selectSignedMultiplicand != null)) { - throw RohdHclException('multiplicand sign reconfiguration requires ' - 'signedMultiplicand=false'); - } + // if (signedMultiplicand && (selectSignedMultiplicand != null)) { + // throw RohdHclException('multiplicand sign reconfiguration requires ' + // 'signedMultiplicand=false'); + // } if (isSignExtended) { throw RohdHclException('Partial Product array already sign-extended'); } isSignExtended = true; + final signedMultiplicandParameter = + StaticOrDynamicParameter.ofDynamic(signedMultiplicand); final finalCarryPos = shift * (rows - 1); final finalCarryRelPos = finalCarryPos - selector.width - shift; @@ -455,13 +445,15 @@ class StopBitsSignExtension extends PartialProductSignExtension { for (var row = 0; row < rows; row++) { final addend = partialProducts[row]; final Logic sign; - if (selectSignedMultiplicand != null) { - sign = mux(selectSignedMultiplicand!, addend.last, signs[row]); + if (signedMultiplicandParameter.dynamicConfig != null) { + sign = mux(signedMultiplicandParameter.dynamicConfig!, addend.last, + signs[row]); } else { - sign = signedMultiplicand ? addend.last : signs[row]; + sign = + signedMultiplicandParameter.staticConfig ? addend.last : signs[row]; } if (row == 0) { - if (!signedMultiplicand) { + if (!signedMultiplicandParameter.staticConfig) { addend.addAll(List.filled(shift, SignBit(sign))); } else { // either is signed? @@ -478,6 +470,8 @@ class StopBitsSignExtension extends PartialProductSignExtension { } } + final signedMultiplierParameter = + StaticOrDynamicParameter.ofDynamic(signedMultiplier); if (finalCarryRow > 0) { final extensionRow = partialProducts[finalCarryRow]; extensionRow @@ -485,7 +479,8 @@ class StopBitsSignExtension extends PartialProductSignExtension { finalCarryPos - (extensionRow.length + rowShift[finalCarryRow]), Const(0))) ..add(SignBit(signs[rows - 1])); - } else if (signedMultiplier | (selectSignedMultiplier != null)) { + } else if (signedMultiplierParameter.staticConfig | + (signedMultiplierParameter.dynamicConfig != null)) { // Create an extra row to hold the final carry bit partialProducts .add(List.filled(selector.width, Const(0), growable: true)); @@ -516,8 +511,6 @@ class PartialProductGeneratorStopBitsSignExtension super.multiplicand, super.multiplier, super.radixEncoder, {super.signedMultiplicand, super.signedMultiplier, - super.selectSignedMultiplicand, - super.selectSignedMultiplier, super.name = 'stop_bits'}) { extender = StopBitsSignExtension(this); signExtend(); @@ -545,8 +538,6 @@ class PartialProductGeneratorCompactRectSignExtension super.multiplicand, super.multiplier, super.radixEncoder, {super.signedMultiplicand, super.signedMultiplier, - super.selectSignedMultiplicand, - super.selectSignedMultiplier, super.name = 'compact_rect'}) { extender = CompactRectSignExtension(this); signExtend(); @@ -573,12 +564,15 @@ class CompactRectSignExtension extends PartialProductSignExtension { throw RohdHclException('Partial Product array already sign-extended'); } isSignExtended = true; + final signedMultiplicandParameter = + StaticOrDynamicParameter.ofDynamic(signedMultiplicand); final lastRow = rows - 1; final firstAddend = partialProducts[0]; final lastAddend = partialProducts[lastRow]; - final firstRowQStart = selector.width - (signedMultiplicand ? 1 : 0); + final firstRowQStart = + selector.width - (signedMultiplicandParameter.staticConfig ? 1 : 0); final lastRowSignPos = shift * lastRow; final align = firstRowQStart - lastRowSignPos; @@ -668,12 +662,15 @@ class CompactRectSignExtension extends PartialProductSignExtension { // Insert the lastRow sign: Either in firstRow's Q if there is a // collision or in another row if it lands beyond the Q sign extension final firstSign = Logic(name: 'firstsign', naming: Naming.mergeable); - if (selectSignedMultiplicand == null) { + if (signedMultiplicandParameter.dynamicConfig == null) { firstSign <= - (signedMultiplicand ? SignBit(firstAddend.last) : SignBit(signs[0])); + (signedMultiplicandParameter.staticConfig + ? SignBit(firstAddend.last) + : SignBit(signs[0])); } else { firstSign <= - (SignBit(mux(selectSignedMultiplicand!, firstAddend.last, signs[0]))); + (SignBit(mux(signedMultiplicandParameter.dynamicConfig!, + firstAddend.last, signs[0]))); } final lastSign = SignBit(remainders[lastRow]); // Compute Sign extension MSBs for firstRow @@ -704,7 +701,7 @@ class CompactRectSignExtension extends PartialProductSignExtension { final finalCarryRelPos = lastRowSignPos - selector.width - shift + - (signedMultiplicand ? 1 : 0); + (signedMultiplicandParameter.staticConfig ? 1 : 0); final finalCarryRow = (finalCarryRelPos / shift).floor(); final curRowLength = partialProducts[finalCarryRow].length + rowShift[finalCarryRow]; diff --git a/lib/src/arithmetic/multiply_accumulate.dart b/lib/src/arithmetic/multiply_accumulate.dart index 1e6585ba0..a02886713 100644 --- a/lib/src/arithmetic/multiply_accumulate.dart +++ b/lib/src/arithmetic/multiply_accumulate.dart @@ -47,44 +47,15 @@ abstract class MultiplyAccumulate extends Module { /// Configuration for signed multiplicand [a]. @protected - late final StaticOrRuntimeParameter signedMultiplicandParameter; + late final StaticOrDynamicParameter signedMultiplicandParameter; /// Configuration for signed multiplier [b]. @protected - late final StaticOrRuntimeParameter signedMultiplierParameter; + late final StaticOrDynamicParameter signedMultiplierParameter; /// Configuration for signed addend [c]. @protected - late final StaticOrRuntimeParameter signedAddendParameter; - - /// The MAC treats multiplicand [a] as always signed. - @protected - late bool signedMultiplicand; - - /// The MAC treats multiplier [b] as always signed. - @protected - late bool signedMultiplier; - - /// The MAC treats addend [c] as always signed. - @protected - late bool signedAddend; - - /// If not null, use this signal to select between signed and unsigned - /// multiplicand [a]. - @protected - Logic? get selectSignedMultiplicand => - signedMultiplicandParameter.tryRuntimeInput(this); - - /// If not null, use this signal to select between signed and unsigned - /// multiplier [b] - @protected - Logic? get selectSignedMultiplier => - signedMultiplierParameter.tryRuntimeInput(this); - - /// If not null, use this signal to select between signed and unsigned - /// multiplier [b] - @protected - Logic? get selectSignedAddend => signedAddendParameter.tryRuntimeInput(this); + late final StaticOrDynamicParameter signedAddendParameter; /// [Logic] that tells us [accumulate] is signed. @protected @@ -93,31 +64,21 @@ abstract class MultiplyAccumulate extends Module { /// Take input [a] and input [b], compute their product, add input [c] to /// produce the [accumulate] result. /// - /// The optional [signedMultiplicand] parameter configures the The optional - /// [signedMultiplicand] parameter configures the multiplicand [a] statically - /// using a [bool] as a signed multiplicand (default is `false`, or unsigned) - /// or dynamically with a 1-bit [Logic] [selectSignedMultiplicand] input. You - /// can pass either a [bool] (for static configuration) or a [Logic] - /// (dynamically configuring the type handled) with a signal to this - /// parameter, otherwise this constructor will throw. + /// The optional [signedMultiplicand] parameter configures the multiplicand + /// [a] statically using a `bool` to indicate a signed multiplicand (default + /// is `false`, or unsigned) or dynamically with a 1-bit [Logic] input. + /// Passing something other than null, `bool`, or [Logic] will result in a + /// throw. /// /// The optional [signedMultiplier] parameter configures the multiplier [b] - /// statically using a [bool] as a signed multiplier (default is `false`, or - /// unsigned) or dynamically with a 1-bit [Logic] [selectSignedMultiplier] - /// input. You can pass either a [bool] (for static configuration) or a - /// [Logic] (dynamically configuring the type handled with a signal) to this - /// parameter, otherwise this constructor will throw. + /// statically using a `bool` to indicate a signed multiplier (default is + /// `false`, or unsigned) or dynamically with a 1-bit [Logic] input. Passing + /// something other than null, `bool`, or [Logic] will result in a throw. /// - /// The optional [signedAddend] parameter configures the addend [c] as a - /// signed addend (default is unsigned) or with a runtime configurable - /// [selectSignedAddend] input. - /// - /// The optional [signedAddend] parameter configures the multiplicand [c] - /// statically using a [bool] as a signed multiplicand (default is `false`, or - /// unsigned) or dynamically with a 1-bit [Logic] [selectSignedAddend] input. - /// You can pass either a [bool] (for static configuration) or a [Logic] - /// (dynamically configuring the type handled) with a signal to this - /// parameter, otherwise this constructor will throw. + /// The optional [signedAddend] parameter configures the multiplier [c] + /// statically using a `bool` to indicate a signed addend (default is `false`, + /// or unsigned) or dynamically with a 1-bit [Logic] input. Passing something + /// other null, `bool`, or [Logic] will result in a throw. MultiplyAccumulate(Logic a, Logic b, Logic c, {Logic? clk, Logic? reset, @@ -141,13 +102,10 @@ abstract class MultiplyAccumulate extends Module { c = addInput('c', c, width: c.width); signedMultiplicandParameter = - StaticOrRuntimeParameter.ofDynamic(signedMultiplicand); - this.signedMultiplicand = signedMultiplicandParameter.staticConfig; + StaticOrDynamicParameter.ofDynamic(signedMultiplicand); signedMultiplierParameter = - StaticOrRuntimeParameter.ofDynamic(signedMultiplier); - this.signedMultiplier = signedMultiplierParameter.staticConfig; - signedAddendParameter = StaticOrRuntimeParameter.ofDynamic(signedAddend); - this.signedAddend = signedAddendParameter.staticConfig; + StaticOrDynamicParameter.ofDynamic(signedMultiplier); + signedAddendParameter = StaticOrDynamicParameter.ofDynamic(signedAddend); addOutput('accumulate', width: a.width + b.width + 1); @@ -158,16 +116,16 @@ abstract class MultiplyAccumulate extends Module { } /// This is a helper function that prints out the kind of addend (selected by - /// a [Logic] or set statically by a [bool]).) This supplements the + /// a [Logic] or set statically by a `bool`).) This supplements the /// [Multiplier] functions that can be used for multiplicand and multiplier as /// they are statics: [Multiplier.signedMD] and [Multiplier.signedML]. /// - UA: unsigned addend. /// - SA: signed addend. /// - SSA: dynamic selection of signed addend. static String signedAD(dynamic adConfig) => - ((adConfig is! StaticOrRuntimeParameter) | (adConfig == null)) + ((adConfig is! StaticOrDynamicParameter) | (adConfig == null)) ? 'UA' - : (adConfig as StaticOrRuntimeParameter).runtimeConfig != null + : (adConfig as StaticOrDynamicParameter).dynamicConfig != null ? 'SSA' : adConfig.staticConfig ? 'SA' @@ -210,15 +168,11 @@ class CompressionTreeMultiplyAccumulate extends MultiplyAccumulate { definitionName: definitionName ?? 'CompressionTreeMAC_W${a.width}x${b.width}_Acc${c.width}_' '${MultiplyAccumulate.signedAD(signedAddend)}') { - final ppg = PartialProductGenerator( - a, - b, - RadixEncoder(radix), - selectSignedMultiplicand: selectSignedMultiplicand, - signedMultiplicand: signedMultiplicand, - selectSignedMultiplier: selectSignedMultiplier, - signedMultiplier: signedMultiplier, - ); + // Build the partial product generator. + + final ppg = PartialProductGenerator(a, b, RadixEncoder(radix), + signedMultiplicand: super.signedMultiplicandParameter, + signedMultiplier: super.signedMultiplierParameter); seGen(ppg).signExtend(); @@ -227,9 +181,9 @@ class CompressionTreeMultiplyAccumulate extends MultiplyAccumulate { ppg.rowShift[ppg.partialProducts.length - 1]; final additionalRowSign = mux( - (selectSignedAddend != null) - ? selectSignedAddend! - : (signedAddend ? Const(1) : Const(0)), + (signedAddendParameter.dynamicConfig != null) + ? signedAddendParameter.dynamicConfig! + : (signedAddendParameter.staticConfig ? Const(1) : Const(0)), c[c.width - 1], Const(0)); @@ -238,6 +192,7 @@ class CompressionTreeMultiplyAccumulate extends MultiplyAccumulate { additionalRow.add(additionalRowSign); } additionalRow + ..add(~additionalRowSign) ..add(~additionalRowSign) ..add(Const(1)); @@ -292,27 +247,9 @@ class MultiplyOnly extends MultiplyAccumulate { // ignore: prefer_interpolation_to_compose_strings name: 'multiply_only_' + _genName(mulGen, a, b, signedMultiplicand, signedMultiplier)) { - // Here we need to copy the Config and make sure we access our module's - // input by calling .logic(this) on the runtimeConfig. - - // TODO(desmonddak): try using tryRuntimeInput instead of getLogic. final multiply = mulGen(a, b, - signedMultiplicand: StaticOrRuntimeParameter( - name: 'selectSignedMultiplicand', - runtimeConfig: signedMultiplicandParameter.runtimeConfig != null - ? signedMultiplicandParameter.getLogic(this) - : null, - staticConfig: signedMultiplicandParameter.runtimeConfig == null - ? signedMultiplicandParameter.staticConfig - : null), - signedMultiplier: StaticOrRuntimeParameter( - name: 'selectSignedMultiplier', - runtimeConfig: signedMultiplierParameter.runtimeConfig != null - ? signedMultiplierParameter.getLogic(this) - : null, - staticConfig: signedMultiplierParameter.runtimeConfig == null - ? signedMultiplierParameter.staticConfig - : null)); + signedMultiplicand: signedMultiplicandParameter.clone(this), + signedMultiplier: signedMultiplierParameter.clone(this)); accumulate <= mux( diff --git a/lib/src/arithmetic/ones_complement_adder.dart b/lib/src/arithmetic/ones_complement_adder.dart index 31bb05190..a52d7a7c9 100644 --- a/lib/src/arithmetic/ones_complement_adder.dart +++ b/lib/src/arithmetic/ones_complement_adder.dart @@ -30,7 +30,7 @@ class OnesComplementAdder extends Adder { /// Subtraction is happening @protected - late final Logic? subtractIn; + late final StaticOrDynamicParameter subtractIn; /// Generate an endAroundCarry signal instead of adding it to the /// [sum]. @@ -38,25 +38,28 @@ class OnesComplementAdder extends Adder { /// [OnesComplementAdder] constructor with an adder functor [adderGen]. /// - A subtractor is created if [subtract] is set to `true`. Alternatively, - /// if [subtract] configuration is `false`, and a Lgic control signal - /// [subtractIn] is provided, then subtraction can be dynamically selected. - /// Otherwise an adder is constructed. - /// - If [generateEndAroundCarry] is `true`, then the end-around - /// carry is not performed and is provided as output [endAroundCarry]. If + /// if [subtract] configuration is `false`, and a [Logic] control signal + /// [subtractIn] is provided, then subtraction can be dynamically selected. + /// Otherwise an adder is constructed. + /// + /// - The optional [subtract] parameter configures the adder to subtract [b] + /// from [a] statically using a `bool` to indicate a ssubtraction (default + /// is `false`, or addition) or dynamically with a 1-bit [Logic] input. + /// Passing something other null, `bool`, or [Logic] will result in a throw. + /// - If [generateEndAroundCarry] is `true`, then the end-around carry is not + /// performed and is provided as output [endAroundCarry]. If /// [generateEndAroundCarry] is `false`, extra hardware takes care of adding - /// the - /// end-around carry to [sum]. + /// the end-around carry to [sum]. /// - [carryIn] allows for another adder to chain into this one. /// - [chainable] tells this adder to not store the [endAroundCarry] in the - /// sign bit as well, but to zero that to allow adders to be chained such as - /// for use in the [CarrySelectCompoundAdder]. + /// sign bit as well, but to zero that to allow adders to be chained such as + /// for use in the [CarrySelectCompoundAdder]. OnesComplementAdder(super.a, super.b, {Adder Function(Logic, Logic, {Logic? carryIn}) adderGen = NativeAdder.new, - Logic? subtractIn, this.generateEndAroundCarry = false, super.carryIn, - bool subtract = false, + dynamic subtract, bool chainable = false, super.reserveName, super.reserveDefinitionName, @@ -68,19 +71,10 @@ class OnesComplementAdder extends Adder { if (generateEndAroundCarry) { addOutput('endAroundCarry'); } - if ((subtractIn != null) & subtract) { - throw RohdHclException( - "either provide a Logic signal 'subtractIn' for runtime " - " configuration, or a boolean parameter 'subtract' for " - 'generation time configuration, but not both.'); - } - this.subtractIn = - (subtractIn != null) ? addInput('subtractIn', subtractIn) : null; + subtractIn = StaticOrDynamicParameter.ofDynamic(subtract).clone(this); _sign = addOutput('sign'); - final doSubtract = - (this.subtractIn ?? (subtract ? Const(subtract) : Const(0))) - .named('dosubtract', naming: Naming.mergeable); + final doSubtract = subtractIn.getLogic(this); final adderSum = adderGen(a, mux(doSubtract, ~b, b), carryIn: carryIn ?? Const(0)) diff --git a/lib/src/arithmetic/sign_magnitude_adder.dart b/lib/src/arithmetic/sign_magnitude_adder.dart index e7c6c15d7..fc3cf7610 100644 --- a/lib/src/arithmetic/sign_magnitude_adder.dart +++ b/lib/src/arithmetic/sign_magnitude_adder.dart @@ -101,7 +101,7 @@ class SignMagnitudeAdder extends SignMagnitudeAdderBase { final adder = OnesComplementAdder( mux(_sign & sub, ~a, a), mux(_sign & sub, ~b, b), generateEndAroundCarry: largestMagnitudeFirst & generateEndAroundCarry, - subtractIn: sub, + subtract: sub, adderGen: adderGen); sum <= adder.sum; if (generateEndAroundCarry) { diff --git a/lib/src/component_config/components/config_compound_adder.dart b/lib/src/component_config/components/config_compound_adder.dart index b136739c4..91b88f417 100644 --- a/lib/src/component_config/components/config_compound_adder.dart +++ b/lib/src/component_config/components/config_compound_adder.dart @@ -36,7 +36,7 @@ class CompoundAdderConfigurator extends Configurator { ? CarrySelectCompoundAdder.splitSelectAdderAlgorithmNBit( blockWidthKnob.value) : CarrySelectCompoundAdder.splitSelectAdderAlgorithmSingleBlock, - adderGen: (a, b, {carryIn, subtractIn, name = 'default_adder'}) => + adderGen: (a, b, {carryIn, subtract, name = 'default_adder'}) => adderSelectionKnob.selectedAdder()(a, b, carryIn: carryIn, name: name)); diff --git a/lib/src/component_config/config_knobs/toggle_config_knob.dart b/lib/src/component_config/config_knobs/toggle_config_knob.dart index e3e251989..6e31fdcdb 100644 --- a/lib/src/component_config/config_knobs/toggle_config_knob.dart +++ b/lib/src/component_config/config_knobs/toggle_config_knob.dart @@ -8,7 +8,7 @@ import 'package:rohd_hcl/rohd_hcl.dart'; -/// A knob for holding a [bool]. +/// A knob for holding a `bool`. class ToggleConfigKnob extends ConfigKnob { /// Creates a new knob with the specified initial [value]. ToggleConfigKnob({required super.value}); diff --git a/lib/src/static_or_dynamic_parameter.dart b/lib/src/static_or_dynamic_parameter.dart new file mode 100644 index 000000000..46b93d43c --- /dev/null +++ b/lib/src/static_or_dynamic_parameter.dart @@ -0,0 +1,107 @@ +// Copyright (C) 2025 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// static_or_dynamic_parameter.dart +// Configuration classes for managing parameters that can be set statically or +// dynamically at hardware runtime using a [Logic] control signal. +// +// 2025 June 27 +// Author: Desmond Kirkpatrick + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// A general configuration class for specifying parameters that are +/// for both static or dynamic configurations of a component feature. +class StaticOrDynamicParameter { + /// The dynamic configuration logic that can be used to configure the + /// component at runtime. + final Logic? dynamicConfig; + + /// The static configuration flag that indicates whether the + /// feature is statically configured or not. + late final bool staticConfig; + + /// The name of the configuration, especially needed to add a [Logic] + /// configuration control signal as a module input. + final String name; + + /// Creates a new [StaticOrDynamicParameter] instance. Note that + /// [dynamicConfig] overrides [staticConfig]. Also, it is presumed that + /// [staticConfig] has a default value of `false` if not provided. + StaticOrDynamicParameter( + {required this.name, this.dynamicConfig, bool? staticConfig = false}) { + if (dynamicConfig == null && staticConfig != null) { + this.staticConfig = staticConfig; + } else { + this.staticConfig = false; + } + } + + /// Factory constructor to create a [StaticOrDynamicParameter] instance from a + /// dynamic. + factory StaticOrDynamicParameter.ofDynamic(dynamic config) { + if (config is StaticOrDynamicParameter) { + return config; + } else if (config is bool) { + return BooleanConfig(staticConfig: config); + } else if (config == null) { + return BooleanConfig(staticConfig: null); + } else if (config is Logic) { + return DynamicConfig(config, name: config.name); + } else { + throw RohdHclException( + 'Unsupported configuration type: ${config.runtimeType}'); + } + } + + /// Clone the parameter for use in submodules. + StaticOrDynamicParameter clone(Module module) { + if (dynamicConfig != null) { + return DynamicConfig(getLogic(module), name: name); + } else { + return this; + } + } + + /// Return a string representation of the configuration, including its name. + @override + String toString() => 'StaticOrDynamicParameter_${name}_static_$staticConfig' + '_dynamic_${dynamicConfig?.name ?? 'null'}'; + + /// Return a `bool` representing the value of the configuration. + // @visibleForTesting + bool get value => + staticConfig || + (dynamicConfig != null && dynamicConfig!.value == LogicValue.one); + + /// Return the internal [Logic] signal that represents the configuration, + /// either static or dynamically. + Logic getLogic(Module module) => + staticConfig ? Const(1) : (getDynamicInput(module) ?? Const(0)); + + /// Construct and return a [Logic]? that is a true input to the [module] + /// if this is a runtime configuration signal. + Logic? getDynamicInput(Module module) => (dynamicConfig != null) + ? tryDynamicInput(module) ?? module.addInput(name, dynamicConfig!) + : null; + + /// Returns a [Logic]? that represents the module internal control input. + Logic? tryDynamicInput(Module module) => + dynamicConfig != null ? module.tryInput(name) : null; +} + +/// A configuration class for boolean configurations, which can be used to +/// statically enable or disable features in a component. +class BooleanConfig extends StaticOrDynamicParameter { + /// Creates a new [BooleanConfig] instance. + BooleanConfig({super.staticConfig}) : super(name: 'boolean_config'); +} + +/// A configuration class for dynamic configurations, which can be used to +/// dynamically configure a component at runtime with a [Logic] signal. +class DynamicConfig extends StaticOrDynamicParameter { + /// Creates a new [DynamicConfig] instance. + DynamicConfig(Logic dynamicConfig, {required super.name}) + : super(dynamicConfig: dynamicConfig, staticConfig: null); +} diff --git a/lib/src/static_or_runtime_parameter.dart b/lib/src/static_or_runtime_parameter.dart deleted file mode 100644 index 064c38539..000000000 --- a/lib/src/static_or_runtime_parameter.dart +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (C) 2025 Intel Corporation -// SPDX-License-Identifier: BSD-3-Clause -// -// static_or_runtime_parameter.dart -// Configuration classes for managing parameters that can be set statically or -// at runtime. -// -// 2025 June 27 -// Author: Desmond Kirkpatrick - -import 'package:meta/meta.dart'; -import 'package:rohd/rohd.dart'; -import 'package:rohd_hcl/rohd_hcl.dart'; - -/// A general configuration class for specifying parameters that are -/// for both static or runtime configurations of a component feature. -class StaticOrRuntimeParameter { - /// The runtime configuration logic that can be used to configure the - /// component at runtime - final Logic? runtimeConfig; - - /// The static configuration flag that indicates whether the - /// feature is statically configured or not. - late final bool staticConfig; - - /// The name of the configuration, especially needed for runtime to add as - /// a module input. - final String name; - - /// Creates a new [StaticOrRuntimeParameter] instance. Note that - /// [runtimeConfig] overrides [staticConfig]. Also, it is presumed that - /// [staticConfig] has a default value of `false` if not provided. - StaticOrRuntimeParameter( - {required this.name, this.runtimeConfig, bool? staticConfig = false}) { - if (runtimeConfig == null && staticConfig != null) { - this.staticConfig = staticConfig; - } else { - this.staticConfig = false; - } - } - - /// Factory constructor to create a [StaticOrRuntimeParameter] instance from a - /// dynamic. - factory StaticOrRuntimeParameter.ofDynamic(dynamic config) { - if (config is StaticOrRuntimeParameter) { - return config; - } else if (config is bool) { - return BooleanConfig(staticConfig: config); - } else if (config == null) { - return BooleanConfig(staticConfig: null); - } else if (config is Logic) { - return RuntimeConfig(config, name: config.name); - } else { - throw RohdHclException( - 'Unsupported configuration type: ${config.runtimeType}'); - } - } - - /// Return a string representation of the configuration, including its name. - @override - String toString() => 'StaticOrRuntimeParameter_${name}_static_$staticConfig' - '_runtime_${runtimeConfig?.name ?? 'null'}'; - - /// Return a bool representing the value of the configuration. - @visibleForTesting - bool get value => - staticConfig || - (runtimeConfig != null && runtimeConfig!.value == LogicValue.one); - - /// Return the internal [Logic] signal that represents the configuration, - /// either static or runtime. - Logic getLogic(Module module) => - staticConfig ? Const(1) : (getRuntimeInput(module) ?? Const(0)); - - /// Construct and return a [Logic]? that is a `true` input to the [module] - /// if this is a runtime configuration signal. - Logic? getRuntimeInput(Module module) => (runtimeConfig != null) - ? tryRuntimeInput(module) ?? module.addInput(name, runtimeConfig!) - : null; - - /// Returns a [Logic]? that represents the module internalruntime input. - Logic? tryRuntimeInput(Module module) => - runtimeConfig != null ? module.tryInput(name) : null; -} - -/// A configuration class for boolean configurations, which can be used to -/// statically enable or disable features in a component. -class BooleanConfig extends StaticOrRuntimeParameter { - /// Creates a new [BooleanConfig] instance. - BooleanConfig({super.staticConfig}) : super(name: 'boolean_config'); -} - -/// A configuration class for runtime configurations, which can be used to -/// dynamically configure a component at runtime. -class RuntimeConfig extends StaticOrRuntimeParameter { - /// Creates a new [RuntimeConfig] instance. - RuntimeConfig(Logic runtimeConfig, {required super.name}) - : super(runtimeConfig: runtimeConfig, staticConfig: null); -} diff --git a/test/arithmetic/adder_test.dart b/test/arithmetic/adder_test.dart index f85f8960a..991a8a26c 100644 --- a/test/arithmetic/adder_test.dart +++ b/test/arithmetic/adder_test.dart @@ -269,7 +269,7 @@ void main() { a.put(av); b.put(bv); final adder = OnesComplementAdder(a, b, - subtractIn: subtractIn, + subtract: subtractIn, // endAroundCarry: carry, generateEndAroundCarry: true, adderGen: RippleCarryAdder.new); diff --git a/test/arithmetic/column_compressor_test.dart b/test/arithmetic/column_compressor_test.dart index 2aa429ae9..924d2a6f7 100644 --- a/test/arithmetic/column_compressor_test.dart +++ b/test/arithmetic/column_compressor_test.dart @@ -109,17 +109,26 @@ void main() { final encoder = RadixEncoder(radix); for (final useSelect in [false, true]) { final selectSignedMultiplicand = - useSelect ? Logic(name: 'mcand') : null; - final selectSignedMultiplier = useSelect ? Logic(name: 'mult') : null; + useSelect ? Logic(name: 'multiplicand') : null; + final selectSignedMultiplier = + useSelect ? Logic(name: 'multiplier') : null; if (useSelect) { selectSignedMultiplicand!.put(signed ? 1 : 0); selectSignedMultiplier!.put(signed ? 1 : 0); } - final pp = PartialProduct(a, b, encoder, - signedMultiplicand: !useSelect & signed, - signedMultiplier: !useSelect & signed, - selectSignedMultiplicand: selectSignedMultiplicand, - selectSignedMultiplier: selectSignedMultiplier); + final pp = PartialProduct( + a, + b, + encoder, + signedMultiplicand: StaticOrDynamicParameter( + name: 'signedMultiplicand', + staticConfig: !useSelect & signed, + dynamicConfig: selectSignedMultiplicand), + signedMultiplier: StaticOrDynamicParameter( + name: 'signedMultiplier', + staticConfig: !useSelect & signed, + dynamicConfig: selectSignedMultiplier), + ); CompactRectSignExtension(pp.array).signExtend(); pp.generateOutputs(); diff --git a/test/arithmetic/compound_adder_test.dart b/test/arithmetic/compound_adder_test.dart index 3219bfbc1..eee35057e 100644 --- a/test/arithmetic/compound_adder_test.dart +++ b/test/arithmetic/compound_adder_test.dart @@ -96,7 +96,7 @@ void main() { }); Adder defaultAdder(Logic a, Logic b, - {Logic? carryIn, Logic? subtractIn, String name = ''}) => + {Logic? carryIn, dynamic subtract, String name = ''}) => ParallelPrefixAdder(a, b, carryIn: carryIn, name: name); test('CarrySelectAdder: random inputs', () async { final a = Logic(name: 'a', width: 10); @@ -147,7 +147,7 @@ void main() { final refAdder = OnesComplementAdder(a, b, generateEndAroundCarry: true, - subtractIn: doSubtract ? Const(1) : Const(0)); + subtract: doSubtract ? Const(1) : Const(0)); final expectedVal = doSubtract ? ai - bi : ai + bi; final expectedValP1 = expectedVal + 1; @@ -183,16 +183,13 @@ void main() { final doSubtract = (useLogic == null) ? subtract : useLogic.value.toBool(); final adder = CarrySelectOnesComplementCompoundAdder(a, b, - subtractIn: useLogic, - subtract: subtract, + subtract: useLogic ?? subtract, generateCarryOut: true, generateCarryOutP1: true, widthGen: CarrySelectCompoundAdder.splitSelectAdderAlgorithmNBit(4)); final refAdder = OnesComplementAdder(a, b, - generateEndAroundCarry: true, - subtractIn: useLogic, - subtract: subtract); + generateEndAroundCarry: true, subtract: useLogic ?? subtract); for (var ai = 0; ai < pow(2, width); ai++) { for (var bi = 0; bi < pow(2, width); bi++) { final av = LogicValue.ofInt(ai, width); @@ -303,7 +300,7 @@ void main() { b.put(t.bMag); final adder = CarrySelectOnesComplementCompoundAdder(a, b, - subtractIn: t.subtractIn, + subtract: t.subtractIn, generateCarryOut: t.carry, generateCarryOutP1: t.carryP1); diff --git a/test/arithmetic/multiplier_encoder_test.dart b/test/arithmetic/multiplier_encoder_test.dart index 8022f058f..78890fe83 100644 --- a/test/arithmetic/multiplier_encoder_test.dart +++ b/test/arithmetic/multiplier_encoder_test.dart @@ -32,36 +32,39 @@ void testPartialProductExhaustive(PartialProductGeneratorBase pp) { final limitX = pow(2, widthX); final limitY = pow(2, widthY); - final multiplicandSigns = pp.signedMultiplicand - ? [true] - : (pp.selectSignedMultiplicand != null) - ? [false, true] - : [false]; - final multiplierSigns = pp.signedMultiplier - ? [true] - : (pp.selectSignedMultiplier != null) - ? [false, true] - : [false]; + final signedMultiplicandParameter = + StaticOrDynamicParameter.ofDynamic(pp.signedMultiplicand); + final signedMultiplierParameter = + StaticOrDynamicParameter.ofDynamic(pp.signedMultiplier); + + final multiplicandSigns = signedMultiplicandParameter.dynamicConfig != null + ? [false, true] + : [signedMultiplicandParameter.staticConfig]; + final multiplierSigns = signedMultiplierParameter.dynamicConfig != null + ? [false, true] + : [signedMultiplierParameter.staticConfig]; + test( 'exhaustive: ${pp.name} R${pp.selector.radix} ' 'WD=${pp.multiplicand.width} WM=${pp.multiplier.width} ' - 'SD=${pp.signedMultiplicand ? 1 : 0} ' - 'SM=${pp.signedMultiplier ? 1 : 0} ' - 'SelD=${pp.selectSignedMultiplicand != null ? 1 : 0} ' - 'SelM=${pp.selectSignedMultiplier != null ? 1 : 0}', () async { + 'MD=${Multiplier.signedMD(pp.signedMultiplicand)} ' + 'ML=${Multiplier.signedML(pp.signedMultiplicand)}', () async { for (var i = 0; i < limitX; i++) { for (var j = 0; j < limitY; j++) { for (final multiplicandSign in multiplicandSigns) { final X = SignedBigInt.fromSignedInt(i, widthX, signed: multiplicandSign); - if (pp.selectSignedMultiplicand != null) { - pp.selectSignedMultiplicand!.put(multiplicandSign ? 1 : 0); + + if (signedMultiplicandParameter.dynamicConfig != null) { + signedMultiplicandParameter.dynamicConfig! + .put(multiplicandSign ? 1 : 0); } for (final multiplierSign in multiplierSigns) { final Y = SignedBigInt.fromSignedInt(j, widthY, signed: multiplierSign); - if (pp.selectSignedMultiplier != null) { - pp.selectSignedMultiplier!.put(multiplierSign ? 1 : 0); + if (signedMultiplierParameter.dynamicConfig != null) { + signedMultiplierParameter.dynamicConfig! + .put(multiplierSign ? 1 : 0); } checkPartialProduct(pp, X, Y); } @@ -75,24 +78,23 @@ void testPartialProductRandom(PartialProductGeneratorBase pp, int iterations) { final widthX = pp.selector.multiplicand.width; final widthY = pp.encoder.multiplier.width; - final multiplicandSigns = pp.signedMultiplicand - ? [true] - : (pp.selectSignedMultiplicand != null) - ? [false, true] - : [false]; - final multiplierSigns = pp.signedMultiplier - ? [true] - : (pp.selectSignedMultiplier != null) - ? [false, true] - : [false]; + final signedMultiplicandParameter = + StaticOrDynamicParameter.ofDynamic(pp.signedMultiplicand); + final signedMultiplierParameter = + StaticOrDynamicParameter.ofDynamic(pp.signedMultiplier); + + final multiplicandSigns = signedMultiplicandParameter.dynamicConfig != null + ? [false, true] + : [signedMultiplicandParameter.staticConfig]; + final multiplierSigns = signedMultiplierParameter.dynamicConfig != null + ? [false, true] + : [signedMultiplierParameter.staticConfig]; test( 'random: ${pp.name} R${pp.selector.radix} ' 'WD=${pp.multiplicand.width} WM=${pp.multiplier.width} ' - 'SD=${pp.signedMultiplicand ? 1 : 0} ' - 'SM=${pp.signedMultiplier ? 1 : 0} ' - 'SelD=${pp.selectSignedMultiplicand != null ? 1 : 0} ' - 'SelM=${pp.selectSignedMultiplier != null ? 1 : 0}', () async { + 'MD=${Multiplier.signedMD(pp.signedMultiplicand)} ' + 'ML=${Multiplier.signedML(pp.signedMultiplicand)}', () async { final rand = Random(47); for (var i = 0; i < iterations; i++) { for (final multiplicandSign in multiplicandSigns) { @@ -100,16 +102,19 @@ void testPartialProductRandom(PartialProductGeneratorBase pp, int iterations) { .nextLogicValue(width: widthX) .toBigInt() .toCondSigned(widthX, signed: multiplicandSign); - if (pp.selectSignedMultiplicand != null) { - pp.selectSignedMultiplicand!.put(multiplicandSign ? 1 : 0); + + if (signedMultiplicandParameter.dynamicConfig != null) { + signedMultiplicandParameter.dynamicConfig! + .put(multiplicandSign ? 1 : 0); } for (final multiplierSign in multiplierSigns) { final Y = rand .nextLogicValue(width: widthY) .toBigInt() .toCondSigned(widthY, signed: multiplierSign); - if (pp.selectSignedMultiplier != null) { - pp.selectSignedMultiplier!.put(multiplierSign ? 1 : 0); + if (signedMultiplierParameter.dynamicConfig != null) { + signedMultiplierParameter.dynamicConfig! + .put(multiplierSign ? 1 : 0); } checkPartialProduct(pp, X, Y); } @@ -123,15 +128,17 @@ void testPartialProductSingle( test( 'single: ${pp.name} R${pp.selector.radix} ' 'WD=${pp.multiplicand.width} WM=${pp.multiplier.width} ' - 'SD=${pp.signedMultiplicand ? 1 : 0} ' - 'SM=${pp.signedMultiplier ? 1 : 0} ' - 'SelD=${pp.selectSignedMultiplicand != null ? 1 : 0} ' - 'SelM=${pp.selectSignedMultiplier != null ? 1 : 0}', () async { - if (pp.selectSignedMultiplicand != null) { - pp.selectSignedMultiplicand!.put(X.isNegative ? 1 : 0); + 'MD=${Multiplier.signedMD(pp.signedMultiplicand)} ' + 'ML=${Multiplier.signedML(pp.signedMultiplicand)}', () async { + final signedMultiplicandParameter = + StaticOrDynamicParameter.ofDynamic(pp.signedMultiplicand); + if (signedMultiplicandParameter.dynamicConfig != null) { + signedMultiplicandParameter.dynamicConfig!.put(X.isNegative ? 1 : 0); } - if (pp.selectSignedMultiplier != null) { - pp.selectSignedMultiplier!.put(Y.isNegative ? 1 : 0); + final signedMultiplierParameter = + StaticOrDynamicParameter.ofDynamic(pp.signedMultiplier); + if (signedMultiplierParameter.dynamicConfig != null) { + signedMultiplierParameter.dynamicConfig!.put(Y.isNegative ? 1 : 0); } checkPartialProduct(pp, X, Y); }); @@ -178,6 +185,7 @@ void main() { } } }); + group('PartialProduct: singleton fixed sign variants', () { const radix = 16; final encoder = RadixEncoder(radix); @@ -206,7 +214,7 @@ void main() { } }); - group('PartialProduct: fixed/select sign variants', () { + group('PartialProduct: fixed/select sign variants exhaustive', () { final selectSignMultiplicand = Logic(); final selectSignMultiplier = Logic(); for (final radix in [2, 4]) { @@ -225,12 +233,16 @@ void main() { final PartialProductGeneratorBase pp; pp = PartialProductGenerator(Logic(name: 'X', width: width), Logic(name: 'Y', width: width), encoder, - signedMultiplicand: signMultiplicand, - signedMultiplier: signMultiplier, - selectSignedMultiplicand: - selectMultiplicand ? selectSignMultiplicand : null, - selectSignedMultiplier: - selectMultiplier ? selectSignMultiplier : null); + signedMultiplicand: StaticOrDynamicParameter( + name: 'cand', + dynamicConfig: + selectMultiplicand ? selectSignMultiplicand : null, + staticConfig: signMultiplicand), + signedMultiplier: StaticOrDynamicParameter( + name: 'mult', + dynamicConfig: + selectMultiplier ? selectSignMultiplier : null, + staticConfig: signMultiplier)); currySignExtensionFunction(signExtension)(pp).signExtend(); testPartialProductExhaustive(pp); @@ -242,6 +254,46 @@ void main() { } }); + group('PartialProduct: fixed/select sign variants random', () { + final selectSignMultiplicand = Logic(); + final selectSignMultiplier = Logic(); + for (final radix in [2, 4]) { + final encoder = RadixEncoder(radix); + for (final selectMultiplicand in [false, true]) { + for (final signMultiplicand + in (!selectMultiplicand ? [false, true] : [false])) { + for (final selectMultiplier in [false, true]) { + for (final signMultiplier + in (!selectMultiplier ? [false, true] : [false])) { + selectSignMultiplicand.put(selectMultiplicand ? 1 : 0); + selectSignMultiplier.put(selectMultiplier ? 1 : 0); + for (final signExtension in SignExtension.values + .where((e) => e != SignExtension.none)) { + final width = log2Ceil(radix) + (signMultiplier ? 1 : 0); + final PartialProductGeneratorBase pp; + pp = PartialProductGenerator(Logic(name: 'X', width: width), + Logic(name: 'Y', width: width), encoder, + signedMultiplicand: StaticOrDynamicParameter( + name: 'cand', + dynamicConfig: + selectMultiplicand ? selectSignMultiplicand : null, + staticConfig: signMultiplicand), + signedMultiplier: StaticOrDynamicParameter( + name: 'mult', + dynamicConfig: + selectMultiplier ? selectSignMultiplier : null, + staticConfig: signMultiplier)); + currySignExtensionFunction(signExtension)(pp).signExtend(); + + testPartialProductRandom(pp, 10); + } + } + } + } + } + } + }); + group('PartialProduct: singleton fixed/select sign variants', () { const radix = 4; final encoder = RadixEncoder(radix); @@ -257,12 +309,15 @@ void main() { final PartialProductGeneratorBase pp; pp = PartialProductGenerator(Logic(name: 'X', width: width), Logic(name: 'Y', width: width), encoder, - signedMultiplicand: !selectMultiplicand, - signedMultiplier: !selectMultiplier, - selectSignedMultiplicand: - selectMultiplicand ? selectSignMultiplicand : null, - selectSignedMultiplier: - selectMultiplier ? selectSignMultiplier : null); + signedMultiplicand: StaticOrDynamicParameter( + name: 'cand', + dynamicConfig: + selectMultiplicand ? selectSignMultiplicand : null, + staticConfig: selectMultiplicand), + signedMultiplier: StaticOrDynamicParameter( + name: 'mult', + dynamicConfig: selectMultiplier ? selectSignMultiplier : null, + staticConfig: selectMultiplier)); CompactRectSignExtension(pp).signExtend(); @@ -285,7 +340,6 @@ void main() { for (var width = shift; width < min(5, 2 * shift); width++) { for (final signExtension in SignExtension.values.where((e) => e != SignExtension.none)) { - // final ppg = curryPartialProductGenerator(signExtension); final pp = PartialProductGenerator(Logic(name: 'X', width: width), Logic(name: 'Y', width: width), encoder); currySignExtensionFunction(signExtension)(pp).signExtend(); @@ -295,6 +349,7 @@ void main() { } } }); + group('PartialProduct: rectangle/radix/extension sweep', () { for (var radix = 2; radix < 8; radix *= 2) { final encoder = RadixEncoder(radix); @@ -446,9 +501,6 @@ void main() { final compress = ColumnCompressor(ppm.rows, ppm.rowShift); await ppm.build(); await compress.build(); - - File('ppg.sv').writeAsStringSync(ppm.generateSynth()); - File('compress.sv').writeAsStringSync(compress.generateSynth()); }); test('single MAC partial product test', () async { diff --git a/test/arithmetic/multiplier_test.dart b/test/arithmetic/multiplier_test.dart index 91b888039..02f84d92d 100644 --- a/test/arithmetic/multiplier_test.dart +++ b/test/arithmetic/multiplier_test.dart @@ -19,15 +19,11 @@ import 'package:test/test.dart'; /// The following routines are useful only during testing. extension TestMultiplierSignage on Multiplier { - /// Return `true` if multiplicand [a] is truly signed (fixed or runtime). - bool isSignedMultiplicand() => (selectSignedMultiplicand == null) - ? signedMultiplicand - : !selectSignedMultiplicand!.value.isZero; + /// Return true if multiplicand [a] is truly signed (fixed or runtime) + bool isSignedMultiplicand() => signedMultiplicandParameter.value; - /// Return `true` if multiplier [b] is truly signed (fixed or runtime). - bool isSignedMultiplier() => (selectSignedMultiplier == null) - ? signedMultiplier - : !selectSignedMultiplier!.value.isZero; + /// Return true if multiplier [b] is truly signed (fixed or runtime) + bool isSignedMultiplier() => signedMultiplierParameter.value; /// Return `true` if accumulate result is truly signed (fixed or runtime). bool isSignedResult() => isSignedMultiplicand() | isSignedMultiplier(); @@ -35,20 +31,14 @@ extension TestMultiplierSignage on Multiplier { /// The following routines are useful only during testing. extension TestMultiplierAccumulateSignage on MultiplyAccumulate { - /// Return `true` if multiplicand [a] is truly signed (fixed or runtime). - bool isSignedMultiplicand() => (selectSignedMultiplicand == null) - ? signedMultiplicand - : !selectSignedMultiplicand!.value.isZero; + /// Return true if multiplicand [a] is truly signed (fixed or runtime) + bool isSignedMultiplicand() => signedMultiplicandParameter.value; - /// Return `true` if multiplier [b] is truly signed (fixed or runtime). - bool isSignedMultiplier() => (selectSignedMultiplier == null) - ? signedMultiplier - : !selectSignedMultiplier!.value.isZero; + /// Return true if multiplier [b] is truly signed (fixed or runtime) + bool isSignedMultiplier() => signedMultiplierParameter.value; - /// Return `true` if addend [c] is truly signed (fixed or runtime). - bool isSignedAddend() => (selectSignedAddend == null) - ? signedAddend - : !selectSignedAddend!.value.isZero; + /// Return true if addend [c] is truly signed (fixed or runtime) + bool isSignedAddend() => signedAddendParameter.value; /// Return `true` if accumulate result is truly signed (fixed or runtime). bool isSignedResult() => @@ -85,7 +75,8 @@ void checkMultiplyAccumulate( final result = mod.accumulate.value .toBigInt() .toCondSigned(mod.accumulate.width, signed: mod.isSignedResult()); - expect(result, equals(golden)); + expect(result, equals(golden), + reason: '${mod.name} failed for A=$bA, B=$bB, C=$bC'); } void testMultiplyAccumulateSingle(int width, BigInt ibA, BigInt ibB, BigInt ibC, @@ -265,13 +256,13 @@ void main() { for (final selectSignedMultiplier in [null, Const(0), Const(1)]) { for (final signedMultiplier in (selectSignedMultiplier == null) ? [false, true] : [false]) { - final signedMultiplicandConfig = StaticOrRuntimeParameter( + final signedMultiplicandConfig = StaticOrDynamicParameter( name: 'signedMultiplicand', - runtimeConfig: selectSignedMultiplicand, + dynamicConfig: selectSignedMultiplicand, staticConfig: signedMultiplicand); - final signedMultiplierConfig = StaticOrRuntimeParameter( + final signedMultiplierConfig = StaticOrDynamicParameter( name: 'signedMultiplier', - runtimeConfig: selectSignedMultiplier, + dynamicConfig: selectSignedMultiplier, staticConfig: signedMultiplier); final mod = NativeMultiplier(a, b, signedMultiplicand: signedMultiplicandConfig, @@ -303,32 +294,32 @@ void main() { } }); - // TODO(desmonddak): must set variables in the enclosing module, so we can't - // really curry unless the enclosing module reads them off the passed in - // multiplier. group('Native multiplier check', () { for (final selectSignedMultiplicand in [null, Const(0), Const(1)]) { - // for (final selectSignedMultiplicand in [null]) { for (final signedMultiplicand in (selectSignedMultiplicand == null) ? [false, true] : [false]) { for (final selectSignedMultiplier in [null, Const(0), Const(1)]) { - // for (final selectSignedMultiplier in [null]) { for (final signedMultiplier in (selectSignedMultiplier == null) ? [false, true] : [false]) { + final signedMultiplicandConfig = StaticOrDynamicParameter( + name: 'signedMultiplicand', + dynamicConfig: selectSignedMultiplicand, + staticConfig: signedMultiplicand); + final signedMultiplierConfig = StaticOrDynamicParameter( + name: 'signedMultiplier', + dynamicConfig: selectSignedMultiplier, + staticConfig: signedMultiplier); + // Make sure multiplier generator lambda function passes the correct + // signage as these may contain Logic signals that may have been + // added to the enclosing module via [StaticOrRuntimeParameter]. testMultiplyAccumulateExhaustive( 5, (a, b, c) => MultiplyOnly( a, b, c, - signedMultiplicand: selectSignedMultiplicand != null - ? RuntimeConfig(selectSignedMultiplicand, - name: 'selectSignedMultiplicand') - : BooleanConfig(staticConfig: signedMultiplicand), - signedMultiplier: selectSignedMultiplier != null - ? RuntimeConfig(selectSignedMultiplier, - name: 'selectSignedMultiplier') - : BooleanConfig(staticConfig: signedMultiplier), + signedMultiplicand: signedMultiplicandConfig, + signedMultiplier: signedMultiplierConfig, (a, b, {signedMultiplicand, signedMultiplier}) => NativeMultiplier(a, b, signedMultiplicand: signedMultiplicand, @@ -381,13 +372,13 @@ void main() { in (selectSignedMultiplier == null) ? [false, true] : [false]) { for (final radix in [4]) { for (final width in [1 + log2Ceil(radix)]) { - final signedMultiplicandConfig = StaticOrRuntimeParameter( + final signedMultiplicandConfig = StaticOrDynamicParameter( name: 'signedMultiplicand', - runtimeConfig: selectSignedMultiplicand, + dynamicConfig: selectSignedMultiplicand, staticConfig: signedMultiplicand); - final signedMultiplierConfig = StaticOrRuntimeParameter( + final signedMultiplierConfig = StaticOrDynamicParameter( name: 'signedMultiplier', - runtimeConfig: selectSignedMultiplier, + dynamicConfig: selectSignedMultiplier, staticConfig: signedMultiplier); testMultiplyAccumulateRandom( width, @@ -404,6 +395,45 @@ void main() { } }); + test('Compression Tree MAC: curried random sign/select singleton', () { + const signedMultiplicand = true; + const signedMultiplier = true; + const signedAddend = true; + for (final radix in [4]) { + for (final width in [1 + log2Ceil(radix)]) { + final signedMultiplicandConfig = StaticOrDynamicParameter( + name: 'signedMultiplicand', staticConfig: signedMultiplicand); + final signedMultiplierConfig = StaticOrDynamicParameter( + name: 'signedMultiplier', staticConfig: signedMultiplier); + + final signedAddendConfig = StaticOrDynamicParameter( + name: 'signedAddend', staticConfig: signedAddend); + + final fn = curryMultiplyAccumulate( + radix, + adderGen: ParallelPrefixAdder.new, + signedMultiplicand: signedMultiplicandConfig, + signedMultiplier: signedMultiplierConfig, + signedAddend: signedAddendConfig, + ); + + final a = Logic(name: 'a', width: width); + final b = Logic(name: 'b', width: width); + final c = Logic(name: 'c', width: width * 2); + + final bA = BigInt.from(2).toCondSigned(width, signed: true); + final bB = BigInt.from(-2).toCondSigned(width, signed: true); + final bC = BigInt.from(-2).toCondSigned(width, signed: true); + + a.put(bA); + b.put(bB); + c.put(bC); + final mod = fn(a, b, c); + checkMultiplyAccumulate(mod, bA, bB, bC); + } + } + }); + group('Compression Tree MAC: curried random sign/select', () { for (final selectSignedMultiplicand in [null, Const(0), Const(1)]) { for (final signedMultiplicand @@ -416,18 +446,18 @@ void main() { in (selectSignedAddend == null) ? [false, true] : [false]) { for (final radix in [4]) { for (final width in [1 + log2Ceil(radix)]) { - final signedMultiplicandConfig = StaticOrRuntimeParameter( + final signedMultiplicandConfig = StaticOrDynamicParameter( name: 'signedMultiplicand', - runtimeConfig: selectSignedMultiplicand, + dynamicConfig: selectSignedMultiplicand, staticConfig: signedMultiplicand); - final signedMultiplierConfig = StaticOrRuntimeParameter( + final signedMultiplierConfig = StaticOrDynamicParameter( name: 'signedMultiplier', - runtimeConfig: selectSignedMultiplier, + dynamicConfig: selectSignedMultiplier, staticConfig: signedMultiplier); - final signedAddendConfig = StaticOrRuntimeParameter( + final signedAddendConfig = StaticOrDynamicParameter( name: 'signedAddend', - runtimeConfig: selectSignedAddend, + dynamicConfig: selectSignedAddend, staticConfig: signedAddend); testMultiplyAccumulateRandom( width, @@ -462,9 +492,9 @@ void main() { clk: clk, adderGen: ParallelPrefixAdder.new, signedMultiplicand: - RuntimeConfig(signedSelect, name: 'selectSignedMultiplicand'), + DynamicConfig(signedSelect, name: 'selectSignedMultiplicand'), signedMultiplier: - RuntimeConfig(signedSelect, name: 'selectSignedMultiplier')); + DynamicConfig(signedSelect, name: 'selectSignedMultiplier')); unawaited(Simulator.run()); a.put(bA); b.put(bB); @@ -497,11 +527,9 @@ void main() { final mod = CompressionTreeMultiplyAccumulate(a, b, c, clk: clk, adderGen: ParallelPrefixAdder.new, - signedMultiplicand: - RuntimeConfig(signedSelect, name: 'selectSignedMultiplicand'), - signedMultiplier: - RuntimeConfig(signedSelect, name: 'selectSignedMultiplier'), - signedAddend: RuntimeConfig(signedSelect, name: 'selectSignedAddend')); + signedMultiplicand: signedSelect, + signedMultiplier: signedSelect, + signedAddend: signedSelect); unawaited(Simulator.run()); a.put(bA); b.put(bB); @@ -545,13 +573,13 @@ void main() { final mod = CompressionTreeMultiplier(a, b, adderGen: ParallelPrefixAdder.new, signExtensionGen: StopBitsSignExtension.new, - signedMultiplicand: StaticOrRuntimeParameter( + signedMultiplicand: StaticOrDynamicParameter( name: 'signedMultiplicand', - runtimeConfig: signedSelect, + dynamicConfig: signedSelect, staticConfig: signed), - signedMultiplier: StaticOrRuntimeParameter( + signedMultiplier: StaticOrDynamicParameter( name: 'signedMultiplicand', - runtimeConfig: signedSelect, + dynamicConfig: signedSelect, staticConfig: signed)); final golden = bA * bB; final result = mod.isSignedResult() @@ -583,10 +611,8 @@ void main() { Logic(), (a, b, {signedMultiplicand, signedMultiplier}) => SimpleMultiplier(a, b, signedMultiplicand, signedMultiplier), - signedMultiplicand: - RuntimeConfig(signedOperands, name: 'selectSignedMultiplicand'), - signedMultiplier: - RuntimeConfig(signedOperands, name: 'selectSignedMultiplier')); + signedMultiplicand: signedOperands, + signedMultiplier: signedOperands); checkMultiplyAccumulate(mod, av, bv, BigInt.zero); }); @@ -614,12 +640,9 @@ void main() { c.put(bC); final mod = CompressionTreeMultiplyAccumulate(a, b, c, - signedMultiplicand: - RuntimeConfig(signedOperands, name: 'selectSignedMultiplicand'), - signedMultiplier: - RuntimeConfig(signedOperands, name: 'selectSignedMultiplier'), - signedAddend: - RuntimeConfig(signedOperands, name: 'selectSignedAddend')); + signedMultiplicand: signedOperands, + signedMultiplier: signedOperands, + signedAddend: signedOperands); checkMultiplyAccumulate(mod, bA, bB, bC); } @@ -647,9 +670,9 @@ void main() { c.put(bC); final mod = CompressionTreeMultiplyAccumulate(a, b, c, - signedMultiplicand: BooleanConfig(staticConfig: signed), - signedMultiplier: BooleanConfig(staticConfig: signed), - signedAddend: BooleanConfig(staticConfig: signed)); + signedMultiplicand: signed, + signedMultiplier: signed, + signedAddend: signed); checkMultiplyAccumulate(mod, bA, bB, bC); } }); @@ -695,8 +718,7 @@ void main() { c.put(bC); final multiplier = CompressionTreeMultiplyAccumulate(a, b, c, - signedMultiplicand: BooleanConfig(staticConfig: signed), - signedMultiplier: BooleanConfig(staticConfig: signed)); + signedMultiplicand: signed, signedMultiplier: signed); final accumulate = multiplier.accumulate; expect(accumulate.value.toBigInt(), equals(golden)); @@ -736,6 +758,64 @@ void main() { } }); + test('dot product select signed exhaustive', () async { + const width = 3; + final a = Logic(name: 'a', width: width); + final b = Logic(name: 'b', width: width); + final c = Logic(name: 'c', width: width); + final d = Logic(name: 'd', width: width); + + for (final mdSigned in [Const(0), Const(1)]) { + for (final mlSigned in [Const(0), Const(1)]) { + final ppG0 = PartialProductGenerator(a, b, RadixEncoder(4), + signedMultiplicand: mdSigned, signedMultiplier: mlSigned); + StopBitsSignExtension(ppG0).signExtend(); + final ppG1 = PartialProductGenerator(c, d, RadixEncoder(4), + signedMultiplicand: mdSigned, signedMultiplier: mlSigned); + StopBitsSignExtension(ppG1).signExtend(); + + ppG0.partialProducts.addAll(ppG1.partialProducts); + ppG0.rowShift.addAll(ppG1.rowShift); + final vec = []; + for (var row = 0; row < ppG0.rows; row++) { + vec.add(ppG0.partialProducts[row].rswizzle()); + } + final cc = ColumnCompressor(vec, ppG0.rowShift); + final sum = cc.add0 + cc.add1; + const limit = 1 << width; + for (var ai = 0; ai < limit; ai++) { + for (var bi = 0; bi < limit; bi++) { + for (var ci = 0; ci < limit; ci++) { + for (var di = 0; di < limit; di++) { + // By default these are unsigned + final aVal = SignedBigInt.fromSignedInt(ai, a.width, + signed: mdSigned.value.toBool()); + final bVal = SignedBigInt.fromSignedInt(bi, b.width, + signed: mlSigned.value.toBool()); + final cVal = SignedBigInt.fromSignedInt(ci, c.width, + signed: mdSigned.value.toBool()); + final dVal = SignedBigInt.fromSignedInt(di, d.width, + signed: mlSigned.value.toBool()); + + a.put(aVal); + b.put(bVal); + c.put(cVal); + d.put(dVal); + final expected = aVal * bVal + cVal * dVal; + final computed = sum.value.toBigInt().toCondSigned(sum.width, + signed: mdSigned.value.toBool() | mlSigned.value.toBool()); + expect(computed, equals(expected), reason: ''' + aVal=$aVal, bVal=$bVal, cVal=$cVal, dVal=$dVal + computed=$computed, expected=$expected + '''); + } + } + } + } + } + } + }); + test('setting PPG', () async { const width = 8; final a = Logic(name: 'a', width: width); diff --git a/test/arithmetic/ones_complement_adder_test.dart b/test/arithmetic/ones_complement_adder_test.dart index b3b7ea3db..886c09065 100644 --- a/test/arithmetic/ones_complement_adder_test.dart +++ b/test/arithmetic/ones_complement_adder_test.dart @@ -164,7 +164,7 @@ void main() { a.put(av); b.put(bv); final adder = OnesComplementAdder(a, b, - subtractIn: subtractIn, + subtract: subtractIn, generateEndAroundCarry: true, adderGen: RippleCarryAdder.new); final carry = adder.endAroundCarry!; diff --git a/test/arithmetic/values/floating_point_value_test.dart b/test/arithmetic/values/floating_point_value_test.dart index 94750c492..63ffac6ef 100644 --- a/test/arithmetic/values/floating_point_value_test.dart +++ b/test/arithmetic/values/floating_point_value_test.dart @@ -406,6 +406,7 @@ void main() { final fp2 = FloatingPoint16Value.populator().ofSpacedBinaryString(s); expect(fp, equals(fp2)); }); + test('FPV Value comparison', () { final fp = FloatingPointValue.populator(exponentWidth: 4, mantissaWidth: 4) .ofSpacedBinaryString('1 0101 0101'); @@ -433,6 +434,7 @@ void main() { .ofSpacedBinaryString('0 0000 0000')), equals(0)); }); + test('FPV: infinity/NaN conversion tests', () async { const exponentWidth = 4; const mantissaWidth = 4; @@ -462,6 +464,7 @@ void main() { .isNaN, equals(true)); }); + test('FPV: infinity/NaN unrounded conversion tests', () async { const exponentWidth = 4; const mantissaWidth = 4;