From 03fcfcce7c3faa7eb3321b4a38c41fc4631125d5 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Tue, 6 Aug 2024 21:00:23 -0700 Subject: [PATCH 01/38] integer arithmetic operations --- lib/rohd_hcl.dart | 11 +- lib/src/{ => arithmetic}/adder.dart | 27 +- lib/src/arithmetic/booth.dart | 744 ++++++++++++++++++ .../carry_save_mutiplier.dart | 0 lib/src/arithmetic/compressor.dart | 333 ++++++++ lib/src/arithmetic/multiplier.dart | 197 +++++ .../parallel_prefix_operations.dart | 65 +- .../{ => arithmetic}/ripple_carry_adder.dart | 30 +- lib/src/arithmetic/sign_magnitude_adder.dart | 69 ++ lib/src/multiplier.dart | 37 - test/arithmetic/adder_test.dart | 224 ++++++ test/arithmetic/booth_test.dart | 208 +++++ .../carry_save_multiplier_test.dart | 0 test/arithmetic/compressor_test.dart | 116 +++ test/arithmetic/multiplier_test.dart | 189 +++++ .../parallel_prefix_operations_test.dart | 92 +-- .../ripple_carry_adder_test.dart | 0 17 files changed, 2216 insertions(+), 126 deletions(-) rename lib/src/{ => arithmetic}/adder.dart (71%) create mode 100644 lib/src/arithmetic/booth.dart rename lib/src/{ => arithmetic}/carry_save_mutiplier.dart (100%) create mode 100644 lib/src/arithmetic/compressor.dart create mode 100644 lib/src/arithmetic/multiplier.dart rename lib/src/{ => arithmetic}/parallel_prefix_operations.dart (83%) rename lib/src/{ => arithmetic}/ripple_carry_adder.dart (62%) create mode 100644 lib/src/arithmetic/sign_magnitude_adder.dart delete mode 100644 lib/src/multiplier.dart create mode 100644 test/arithmetic/adder_test.dart create mode 100644 test/arithmetic/booth_test.dart rename test/{ => arithmetic}/carry_save_multiplier_test.dart (100%) create mode 100644 test/arithmetic/compressor_test.dart create mode 100644 test/arithmetic/multiplier_test.dart rename test/{ => arithmetic}/parallel_prefix_operations_test.dart (69%) rename test/{ => arithmetic}/ripple_carry_adder_test.dart (100%) diff --git a/lib/rohd_hcl.dart b/lib/rohd_hcl.dart index 40df8285..243ceb0f 100644 --- a/lib/rohd_hcl.dart +++ b/lib/rohd_hcl.dart @@ -1,10 +1,14 @@ // Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause -export 'src/adder.dart'; export 'src/arbiters/arbiters.dart'; +export 'src/arithmetic/adder.dart'; +export 'src/arithmetic/carry_save_mutiplier.dart'; +export 'src/arithmetic/multiplier.dart'; +export 'src/arithmetic/parallel_prefix_operations.dart'; +export 'src/arithmetic/ripple_carry_adder.dart'; +export 'src/arithmetic/sign_magnitude_adder.dart'; export 'src/binary_gray.dart'; -export 'src/carry_save_mutiplier.dart'; export 'src/component_config/component_config.dart'; export 'src/count.dart'; export 'src/edge_detector.dart'; @@ -16,9 +20,6 @@ export 'src/find.dart'; export 'src/interfaces/interfaces.dart'; export 'src/memory/memories.dart'; export 'src/models/models.dart'; -export 'src/multiplier.dart'; -export 'src/parallel_prefix_operations.dart'; -export 'src/ripple_carry_adder.dart'; export 'src/rotate.dart'; export 'src/shift_register.dart'; export 'src/sort.dart'; diff --git a/lib/src/adder.dart b/lib/src/arithmetic/adder.dart similarity index 71% rename from lib/src/adder.dart rename to lib/src/arithmetic/adder.dart index 80ea05ab..13b27cd5 100644 --- a/lib/src/adder.dart +++ b/lib/src/arithmetic/adder.dart @@ -21,9 +21,28 @@ abstract class Adder extends Module { @protected late final Logic b; - /// The addition results [sum]. + /// The addition results [out] including carry bit + Logic get out => output('out'); + + /// The carry results [carryOut]. + Logic get carryOut => output('carryOut'); + + /// The addition results [sum] including carry bit Logic get sum => output('sum'); + /// Implementation needs to provide a method for calculating the full sum + @protected + Logic calculateSum(); + + /// Implementation needs to provide a method for calculating the sum + /// without carry + @protected + Logic calculateOut(); + + /// Implementation needs to provide a method for calculating the carry out + @protected + Logic calculateCarry(); + /// Takes in input [a] and input [b] and return the [sum] of the addition /// result. The width of input [a] and [b] must be the same. Adder(Logic a, Logic b, {super.name}) { @@ -32,7 +51,13 @@ abstract class Adder extends Module { } this.a = addInput('a', a, width: a.width); this.b = addInput('b', b, width: b.width); + addOutput('out', width: a.width); + addOutput('carryOut'); addOutput('sum', width: a.width + 1); + + out <= calculateOut(); + carryOut <= calculateCarry(); + sum <= calculateSum(); } } diff --git a/lib/src/arithmetic/booth.dart b/lib/src/arithmetic/booth.dart new file mode 100644 index 00000000..229deba4 --- /dev/null +++ b/lib/src/arithmetic/booth.dart @@ -0,0 +1,744 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// booth.dart +// Generation of Booth Encoded partial products for multiplication +// +// 2024 May 15 +// Author: Desmond Kirkpatrick + +import 'dart:io'; +import 'dart:math'; + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/src/utils.dart'; + +/// Simplest version of bit string representation +String bitString(LogicValue value) => value.toString(includeWidth: false); + +/// A bundle for the leaf radix compute nodes +/// This holds the multiples of the multiplicand that are needed for encoding +class RadixEncode extends LogicStructure { + /// Which multiples need to be selected + final Logic multiples; + + /// 'sign' of multiple + final Logic sign; + + /// Structure for holding Radix Encoding + RadixEncode({required int numMultiples}) + : this._( + Logic(width: numMultiples, name: 'multiples'), Logic(name: 'sign')); + + RadixEncode._(this.multiples, this.sign, {String? name}) + : super([multiples, sign], name: name ?? 'RadixLogic'); + + @override + RadixEncode clone({String? name}) => + RadixEncode(numMultiples: multiples.width); +} + +/// Base interface for radix radixEncoder +class RadixEncoder { + /// The radix of the radixEncoder + int radix; + + /// Baseline call for setting up an empty radixEncoder + RadixEncoder(this.radix); + + /// Encode a multiplier slice into the Booth encoded value + RadixEncode encode(Logic multiplierSlice) { + assert( + multiplierSlice.width == log2Ceil(radix) + 1, + 'multiplier slice width ${multiplierSlice.width}' + 'must be same length as log(radix)+1=${log2Ceil(radix) + 1}'); + final width = log2Ceil(radix) + 1; + final inputXor = Logic(width: width); + inputXor <= + (multiplierSlice ^ (multiplierSlice >>> 1)) + .slice(multiplierSlice.width - 1, 0); + + final multiples = []; + for (var i = 2; i < radix + 1; i += 2) { + final variantA = LogicValue.ofInt(i - 1, width); + final xorA = variantA ^ (variantA >>> 1); + final variantB = LogicValue.ofInt(i, width); + final xorB = variantB ^ (variantB >>> 1); + // Multiples don't agree on a bit position so we will skip that position + final multiplesDisagree = xorA ^ xorB; + // Where multiples agree, we need the sense or direction (1 or 0) + final senseMultiples = xorA & xorB; + + multiples.add([ + for (var j = 0; j < width - 1; j++) + if (multiplesDisagree[j].isZero) + if (senseMultiples[j].isZero) ~inputXor[j] else inputXor[j] + ].swizzle().and()); + } + + return RadixEncode._( + multiples.rswizzle(), multiplierSlice[multiplierSlice.width - 1]); + } +} + +/// A class that generates the Booth encoding of the multipler +class MultiplierEncoder { + /// Access the multiplier + Logic multiplier = Logic(); + + /// Number of row radixEncoders + late final int rows; + + Logic _extendedMultiplier = Logic(); + late final RadixEncoder _encoder; + late final int _sliceWidth; + + /// Generate an encoding of the input multiplier + MultiplierEncoder(this.multiplier, RadixEncoder radixEncoder, + {bool signed = true}) + : _encoder = radixEncoder, + _sliceWidth = log2Ceil(radixEncoder.radix) + 1 { + // Unsigned encoding wants to overlap past the multipler + if (signed) { + rows = + ((multiplier.width + (signed ? 0 : 1)) / log2Ceil(radixEncoder.radix)) + .ceil(); + } else { + rows = (((multiplier.width + 1) % (_sliceWidth - 1) == 0) ? 0 : 1) + + ((multiplier.width + 1) ~/ log2Ceil(radixEncoder.radix)); + } + // slices overlap by 1 and start at -1 + _extendedMultiplier = (signed + ? multiplier.signExtend(rows * (_sliceWidth - 1)) + : multiplier.zeroExtend(rows * (_sliceWidth - 1))); + } + + /// Retrieve the Booth encoding for the row + RadixEncode getEncoding(int row) { + assert(row < rows, 'row $row is not < number of encoding rows $rows'); + final base = row * (_sliceWidth - 1); + final multiplierSlice = [ + if (row > 0) + _extendedMultiplier.slice(base + _sliceWidth - 2, base - 1) + else + [_extendedMultiplier.slice(base + _sliceWidth - 2, base), Const(0)] + .swizzle() + ]; + return _encoder.encode(multiplierSlice.first); + } +} + +/// A class accessing the multiples of the multiplicand at a position +class MultiplicandSelector { + /// radix of the selector + int radix; + + /// The bit shift of the selector (typically overlaps 1) + int shift; + + /// New width of partial products generated from the multiplicand + int get width => multiplicand.width + shift - 1; + + /// Access the multiplicand + Logic multiplicand = Logic(); + + /// Place to store multiples of the multiplicand + late LogicArray multiples; + + /// Generate required multiples of multiplicand + MultiplicandSelector(this.radix, this.multiplicand, {bool signed = true}) + : shift = log2Ceil(radix), + assert(radix <= 16, 'beyond radix 16 is not yet supported') { + final width = multiplicand.width + shift; + final numMultiples = radix ~/ 2; + multiples = LogicArray([numMultiples], width); + final extendedMultiplicand = signed + ? multiplicand.signExtend(width) + : multiplicand.zeroExtend(width); + + for (var pos = 0; pos < numMultiples; pos++) { + final ratio = pos + 1; + multiples.elements[pos] <= + switch (ratio) { + 1 => extendedMultiplicand, + 2 => extendedMultiplicand << 1, + 3 => (extendedMultiplicand << 2) - extendedMultiplicand, + 4 => extendedMultiplicand << 2, + 5 => (extendedMultiplicand << 2) + extendedMultiplicand, + 6 => (extendedMultiplicand << 3) - (extendedMultiplicand << 1), + 7 => (extendedMultiplicand << 3) - extendedMultiplicand, + 8 => extendedMultiplicand << 3, + _ => extendedMultiplicand + // TODO(desmonddak): generalize to support higher radix than 16 + }; + } + } + + /// Retrieve the multiples of the multiplicand at current bit position + Logic getMultiples(int col) => [ + for (var i = 0; i < multiples.elements.length; i++) + multiples.elements[i][col] + ].swizzle().reversed; + + Logic _select(Logic multiples, RadixEncode encode) => + (encode.multiples & multiples).or() ^ encode.sign; + + /// Select the partial product term from the multiples using a RadixEncode + Logic select(int col, RadixEncode encode) => + _select(getMultiples(col), encode); +} + +/// A class that generates a set of partial products +class PartialProductGenerator { + /// Get the shift increment between neighboring product rows + int get shift => selector.shift; + + /// The actual shift in each row + final rowShift = []; + + /// rows of partial products + int get rows => partialProducts.length; + + /// The multiplicand term (X) + Logic get multiplicand => selector.multiplicand; + + /// The multiplier term (Y) + Logic get multiplier => encoder.multiplier; + + /// Partial Products output + late List> partialProducts = []; + + /// Encoder for the full multiply operand + late final MultiplierEncoder encoder; + + /// Selector for the multiplicand which uses the encoder to index into + /// multiples of the multiplicand and generate partial products + late final MultiplicandSelector selector; + + /// Operands are signed + late bool signed = true; + + // Used to avoid sign extending more than once + var _signExtended = false; + + /// Construct the partial product matrix + PartialProductGenerator( + Logic multiplicand, Logic multiplier, RadixEncoder radixEncoder, + {this.signed = true}) { + encoder = MultiplierEncoder(multiplier, radixEncoder, signed: signed); + selector = + MultiplicandSelector(radixEncoder.radix, multiplicand, signed: signed); + _build(); + } + + /// Setup the partial products array (partialProducts and rowShift) + void _build() { + _signExtended = false; + partialProducts.clear(); + rowShift.clear(); + for (var row = 0; row < encoder.rows; row++) { + partialProducts.add(List.generate( + selector.width, (i) => selector.select(i, encoder.getEncoding(row)))); + } + for (var row = 0; row < rows; row++) { + rowShift.add(row * shift); + } + } + + /// Fully sign extend the PP array: useful for reference only + void bruteForceSignExtend() { + assert(!_signExtended, 'Partial Product array already sign-extended'); + _signExtended = true; + final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; + for (var row = 0; row < rows; row++) { + final addend = partialProducts[row]; + final sign = signed ? addend.last : signs[row]; + addend.addAll(List.filled((rows - row) * shift, sign)); + if (row > 0) { + addend + ..insertAll(0, List.filled(shift - 1, Const(0))) + ..insert(0, signs[row - 1]); + rowShift[row] -= shift; + } + } + // Insert carry bit in extra row + partialProducts.add(List.generate(selector.width, (i) => Const(0))); + partialProducts.last.insert(0, signs[rows - 2]); + rowShift.add((rows - 2) * shift); + } + + /// Sign extend the PP array using stop bits + /// If possible, fold the final carry into another row (only when rectangular + /// enough that carry bit lands outside another row). + /// This technique can then be combined with a first-row extension technique + /// for folding in the final carry. + void signExtendWithStopBitsRect() { + assert(!_signExtended, 'Partial Product array already sign-extended'); + _signExtended = true; + + final finalCarryPos = shift * (rows - 1); + final finalCarryRelPos = finalCarryPos - selector.width - shift; + final finalCarryRow = + ((encoder.multiplier.width > selector.multiplicand.width) && + (finalCarryRelPos > 0)) + ? (finalCarryRelPos / shift).floor() + : 0; + + final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; + for (var row = 0; row < rows; row++) { + final addend = partialProducts[row]; + final sign = signed ? addend.last : signs[row]; + if (row == 0) { + if (signed) { + addend.addAll(List.filled(shift - 1, sign)); // signed only? + } else { + addend.addAll(List.filled(shift, sign)); + } + addend.add(~sign); + } else { + if (signed) { + addend.last = ~sign; + } else { + addend.add(~sign); + } + addend + ..addAll(List.filled(shift - 1, Const(1))) + ..insertAll(0, List.filled(shift - 1, Const(0))) + ..insert(0, signs[row - 1]); + rowShift[row] -= shift; + } + } + + if (finalCarryRow > 0) { + final extensionRow = partialProducts[finalCarryRow]; + extensionRow + ..addAll(List.filled( + finalCarryPos - (extensionRow.length + rowShift[finalCarryRow]), + Const(0))) + ..add(signs[rows - 1]); + } else if (signed) { + // Create an extra row to hold the final carry bit + partialProducts + .add(List.filled(selector.width, Const(0), growable: true)); + partialProducts.last.insert(0, signs[rows - 2]); + rowShift.add((rows - 2) * shift); + + // Hack for radix-2 + if (shift == 1) { + partialProducts.last.last = ~partialProducts.last.last; + } + } + } + + void _addStopSignFlip(List addend, Logic sign) { + if (signed) { + addend.last = ~addend.last; + } else { + addend.add(sign); + } + } + + void _addStopSign(List addend, Logic sign) { + if (signed) { + addend.last = sign; + } else { + addend.add(sign); + } + } + + /// Sign extend the PP array using stop bits without adding a row + void signExtendCompact() { + assert(!_signExtended, 'Partial Product array already sign-extended'); + _signExtended = true; + + final lastRow = rows - 1; + final firstAddend = partialProducts[0]; + final lastAddend = partialProducts[lastRow]; + var alignRow0Sign = selector.width - + shift * lastRow - + ((shift > 1) + ? 1 + : signed + ? 1 + : 0); + + if (alignRow0Sign < 0) { + alignRow0Sign = 0; + } + + final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; + + final propagate = + List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); + for (var row = 0; row < rows; row++) { + propagate[row].add(signs[row]); + for (var col = 0; col < 2 * (shift - 1); col++) { + propagate[row].add(partialProducts[row][col]); + } + for (var col = 1; col < propagate[row].length; col++) { + propagate[row][col] = propagate[row][col] & propagate[row][col - 1]; + } + } + final m = + List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); + for (var row = 0; row < rows; row++) { + for (var c = 0; c < shift - 1; c++) { + m[row].add(partialProducts[row][c] ^ propagate[row][c]); + } + m[row].addAll(List.filled(shift - 1, Logic())); + } + + for (var i = shift - 1; i < m[lastRow].length; i++) { + m[lastRow][i] = lastAddend[i] ^ + (i < alignRow0Sign ? propagate[lastRow][i] : Const(0)); + } + + final remainders = List.filled(rows, Logic()); + for (var row = 0; row < lastRow; row++) { + remainders[row] = propagate[row][shift - 1]; + } + remainders[lastRow] <= propagate[lastRow][alignRow0Sign]; + + // Compute Sign extension for row==0 + final firstSign = signed ? firstAddend.last : signs[0]; + final q = [ + firstSign ^ remainders[lastRow], + ~(firstSign & ~remainders[lastRow]), + ]; + q.insertAll(1, List.filled(shift - 1, ~q[1])); + + for (var row = 0; row < rows; row++) { + final addend = partialProducts[row]; + if (row > 0) { + final mLimit = (row == lastRow) ? 2 * (shift - 1) : shift - 1; + for (var i = 0; i < mLimit; i++) { + addend[i] = m[row][i]; + } + // Stop bits + if (signed) { + addend.last = ~addend.last; + } else { + addend.add(~signs[row]); + } + addend + ..insert(0, remainders[row - 1]) + ..addAll(List.filled(shift - 1, Const(1))); + rowShift[row] -= 1; + } else { + for (var i = 0; i < shift - 1; i++) { + firstAddend[i] = m[0][i]; + } + if (signed) { + firstAddend.last = q[0]; + } else { + firstAddend.add(q[0]); + } + firstAddend.addAll(q.getRange(1, q.length)); + } + } + if (shift == 1) { + lastAddend.add(Const(1)); + } + } + + /// Sign extend the PP array using stop bits without adding a row + /// This routine works with different widths of multiplicand/multiplier + void signExtendCompactRect() { + assert(!_signExtended, 'Partial Product array already sign-extended'); + _signExtended = true; + + final lastRow = rows - 1; + final firstAddend = partialProducts[0]; + final lastAddend = partialProducts[lastRow]; + + final firstRowQStart = selector.width - (signed ? 1 : 0); + final lastRowSignPos = shift * lastRow; + + final align = firstRowQStart - lastRowSignPos; + + final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; + + // Compute propgation info for folding sign bits into main rows + final propagate = + List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); + + for (var row = 0; row < rows; row++) { + propagate[row].add(signs[row]); + for (var col = 0; col < 2 * (shift - 1); col++) { + propagate[row].add(partialProducts[row][col]); + } + // Last row has extend sign propagation to Q start + if (row == lastRow) { + var col = 2 * (shift - 1); + while (propagate[lastRow].length <= align) { + propagate[lastRow].add(partialProducts[row][col++]); + } + } + // Now compute the propagation logic + for (var col = 1; col < propagate[row].length; col++) { + propagate[row][col] = propagate[row][col] & propagate[row][col - 1]; + } + } + + // Compute 'm', the prefix of each row to carry the sign of the next row + final m = + List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); + for (var row = 0; row < rows; row++) { + for (var c = 0; c < shift - 1; c++) { + m[row].add(partialProducts[row][c] ^ propagate[row][c]); + } + m[row].addAll(List.filled(shift - 1, Logic())); + } + while (m[lastRow].length < align) { + m[lastRow].add(Logic()); + } + for (var i = shift - 1; i < m[lastRow].length; i++) { + m[lastRow][i] = + lastAddend[i] ^ (i < align ? propagate[lastRow][i] : Const(0)); + } + + final remainders = List.filled(rows, Logic()); + for (var row = 0; row < lastRow; row++) { + remainders[row] = propagate[row][shift - 1]; + } + remainders[lastRow] = propagate[lastRow][align > 0 ? align : 0]; + + // Merge 'm' into the LSBs of each addend + for (var row = 0; row < rows; row++) { + final addend = partialProducts[row]; + if (row > 0) { + final mLimit = (row == lastRow) ? align : shift - 1; + for (var i = 0; i < mLimit; i++) { + addend[i] = m[row][i]; + } + // Stop bits + _addStopSignFlip(addend, ~signs[row]); + addend + ..insert(0, remainders[row - 1]) + ..addAll(List.filled(shift - 1, Const(1))); + rowShift[row] -= 1; + } else { + // First row + for (var i = 0; i < shift - 1; i++) { + firstAddend[i] = m[0][i]; + } + } + } + + // 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 = signed ? firstAddend.last : signs[0]; + final lastSign = remainders[lastRow]; + // Compute Sign extension MSBs for firstRow + final qLen = shift + 1; + final insertSignPos = (align > 0) ? 0 : -align; + final q = List.filled(min(qLen, insertSignPos), firstSign, growable: true); + if (insertSignPos < qLen) { + // At sign insertion position + q.add(firstSign ^ lastSign); + if (insertSignPos == qLen - 1) { + q[insertSignPos] = ~q[insertSignPos]; + q.add(~(firstSign | q[insertSignPos])); + } else { + q + ..addAll(List.filled(qLen - insertSignPos - 2, firstSign & ~lastSign)) + ..add(~(firstSign & ~lastSign)); + } + } + + if (-align >= q.length) { + q.last = ~firstSign; + } + _addStopSign(firstAddend, q[0]); + firstAddend.addAll(q.getRange(1, q.length)); + + if (-align >= q.length) { + final finalCarryRelPos = + lastRowSignPos - selector.width - shift + (signed ? 1 : 0); + final finalCarryRow = (finalCarryRelPos / shift).floor(); + final curRowLength = + partialProducts[finalCarryRow].length + rowShift[finalCarryRow]; + + partialProducts[finalCarryRow] + ..addAll(List.filled(lastRowSignPos - curRowLength, Const(0))) + ..add(remainders[lastRow]); + } + if (shift == 1) { + lastAddend.add(Const(1)); + } + } + + /// Return the actual largest width of all rows + int maxWidth() { + var maxW = 0; + for (var row = 0; row < rows; row++) { + final entry = partialProducts[row]; + if (entry.length + rowShift[row] > maxW) { + maxW = entry.length + rowShift[row]; + } + } + return maxW; + } + + /// Accumulate the partial products and return as BigInt + BigInt evaluate() { + final maxW = maxWidth(); + var accum = BigInt.from(0); + for (var row = 0; row < rows; row++) { + final pp = partialProducts[row].rswizzle().value; + final value = pp.zeroExtend(maxW) << rowShift[row]; + if (pp.isValid) { + accum += value.toBigInt(); + } + } + final sum = LogicValue.ofBigInt(accum, maxW).toBigInt(); + return signed ? sum.toSigned(maxW) : sum; + } + + /// Print out the partial product matrix + @override + String toString() { + final str = StringBuffer(); + + final maxW = maxWidth(); + final nonSignExtendedPad = _signExtended + ? 0 + : shift > 2 + ? shift - 1 + : 1; + // We will print encoding(1-hot multiples and sign) before each row + final shortPrefix = + '99 ${'M='.padRight(2 + selector.radix ~/ 2)} S= : '.length + + 3 * nonSignExtendedPad; + + // print bit position header + str.write(' ' * shortPrefix); + for (var i = maxW - 1; i >= 0; i--) { + final bits = i > 9 ? 2 : 1; + str + ..write('$i') + ..write(' ' * (3 - bits)); + } + str.write('\n'); + // Partial product matrix: rows of multiplicand multiples shift by + // rowshift[row] + for (var row = 0; row < rows; row++) { + final rowStr = (row < 10) ? '0$row' : '$row'; + if (row < encoder.rows) { + final encoding = encoder.getEncoding(row); + if (encoding.multiples.value.isValid) { + str.write('$rowStr M=${bitString(encoding.multiples.reversed.value)} ' + 'S=${encoding.sign.value.toInt()}: '); + } else { + str.write(' ' * shortPrefix); + } + } else { + str.write('$rowStr ${'M='.padRight(2 + selector.radix ~/ 2)} S= : '); + } + final entry = partialProducts[row].reversed.toList(); + final prefixCnt = + maxW - (entry.length + rowShift[row]) + nonSignExtendedPad; + str.write(' ' * prefixCnt); + for (var col = 0; col < entry.length; col++) { + str.write('${bitString(entry[col].value)} '); + } + final suffixCnt = rowShift[row]; + final value = entry.swizzle().value.zeroExtend(maxW) << suffixCnt; + final intValue = value.isValid ? value.toBigInt() : BigInt.from(-1); + str + ..write(' ' * suffixCnt) + ..write(': ${bitString(value)}') + ..write(' = ${value.isValid ? intValue : ""}' + ' (${value.isValid ? intValue.toSigned(maxW) : ""})\n'); + } + // Compute and print binary representation from accumulated value + // Later: we will compare with a compression tree result + str + ..write('=' * (shortPrefix + 3 * maxW)) + ..write('\n') + ..write(' ' * shortPrefix); + + final sum = LogicValue.ofBigInt(evaluate(), maxW); + // print out the sum as a MSB-first bitvector + for (final elem in [for (var i = 0; i < maxW; i++) sum[i]].reversed) { + str.write('${elem.toInt()} '); + } + final val = evaluate(); + str.write(': ${bitString(sum)} = ' + '${val.toUnsigned(maxW)}'); + if (_signExtended) { + str.write(' ($val)\n\n'); + } + return str.toString(); + } +} + +// This routine is to reverse-engineer how to create booth encoders from +// XOR computations on the multiplier bits +// It is used to validate the RadixEncoder class +void main() { + for (var radix = 2; radix < 32; radix *= 2) { + stdout.write('Radix-$radix:\n'); + final encoder = RadixEncoder(radix); + + final width = log2Ceil(radix) + 1; + final inputXor = Logic(width: width); + final multiples = []; + for (var i = 2; i < radix + 1; i += 2) { + final pastX = LogicValue.ofInt(i - 1, width); + final x = LogicValue.ofInt(i, width); + final pastXor = pastX ^ (pastX >>> 1); + final xor = x ^ (x >>> 1); + // Multiples don't agree on a bit position so we will skip + final multiplesDisagree = xor ^ pastXor; + // Where multiples agree, we need the sense or direction (1 or 0) + final senseMultiples = xor & pastXor; + + final andOutput = [ + for (var j = 0; j < width - 1; j++) + if (multiplesDisagree[j].isZero) + if (senseMultiples[j].isZero) ~inputXor[j] else inputXor[j] + ].swizzle().and(); + final multPos = (i >>> 1) + i % 2; + stdout + ..write('\tM${(i >>> 1) + i % 2} x=${bitString(x)} ' + 'lx=${bitString(pastX)} ' + // 'm=$m xor=${bitString(xor)}(${xor.toInt()}) ' + 'dontcare=${bitString(multiplesDisagree)}' + ' agree=${bitString(senseMultiples)}') + ..write(': '); + for (var j = 0; j < width - 1; j++) { + if (multiplesDisagree[j].isZero) { + if (senseMultiples[j].isZero) { + stdout.write('~'); + } + stdout.write('xor[$j] '); + } + } + multiples.add(andOutput); + stdout.write('\n'); + final inLogic = Logic(width: width); + for (var k = 0; k < radix; k++) { + final inValue = LogicValue.ofInt(k, width); + inLogic.put(inValue); + final code = encoder.encode(inLogic).multiples[multPos - 1]; + final newCode = + RadixEncoder(radix).encode(inLogic).multiples[multPos - 1]; + inputXor.put(inValue ^ (inValue >>> 1)); + // stdout + // ..write('in=${bitString(inValue)} ') + // ..write('xor=${bitString(inputXor.value)} ') + // ..write('out=${bitString(andOutput.value)} ') + // ..write('code=${bitString(code.value)} ') + // ..write('ncode=${bitString(newCode.value)}') + // ..write('') + // ..write('\n'); + assert(andOutput.value == code.value, 'andOutput mismatches code'); + assert(newCode.value == code.value, 'newCode mismatches code'); + assert( + newCode.value == andOutput.value, 'andOutput mismatches newCode'); + } + } + } +} diff --git a/lib/src/carry_save_mutiplier.dart b/lib/src/arithmetic/carry_save_mutiplier.dart similarity index 100% rename from lib/src/carry_save_mutiplier.dart rename to lib/src/arithmetic/carry_save_mutiplier.dart diff --git a/lib/src/arithmetic/compressor.dart b/lib/src/arithmetic/compressor.dart new file mode 100644 index 00000000..b540b170 --- /dev/null +++ b/lib/src/arithmetic/compressor.dart @@ -0,0 +1,333 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// compressor.dart +// Column compression of partial prodcuts +// +// 2024 June 04 +// Author: Desmond Kirkpatrick + +import 'dart:io'; +import 'package:collection/collection.dart'; +import 'package:meta/meta.dart'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/src/arithmetic/booth.dart'; + +// TODO(desmonddak): Logic and LogicValue majority() functions + +/// Base class for column compressor function +class Compressor extends Module { + /// Input bits to compress + @protected + late final Logic compressBits; + + /// The addition results [sum] including carry bit + Logic get sum => output('sum'); + + /// The carry results [carry]. + Logic get carry => output('carry'); + + /// Construct a column compressor + Compressor(Logic compressBits) { + this.compressBits = addInput( + 'compressBits', + compressBits, + width: compressBits.width, + ); + addOutput('sum'); + addOutput('carry'); + } +} + +/// 2-input column compressor (half-adder) +class Compressor2 extends Compressor { + /// Construct a 2-input compressor (half-adder) + Compressor2(super.compressBits) { + sum <= compressBits.xor(); + carry <= compressBits.and(); + } +} + +/// 3-input column compressor (full-adder) +class Compressor3 extends Compressor { + /// Construct a 3-input column compressor (full-adder) + Compressor3(super.compressBits) { + sum <= compressBits.xor(); + carry <= + mux(compressBits[0], compressBits.slice(2, 1).or(), + compressBits.slice(2, 1).and()); + } +} + +// ignore: public_member_api_docs +enum CompressTermType { carry, sum, pp } + +/// A compression term +class CompressTerm implements Comparable { + /// The type of term we have + late final CompressTermType type; + + /// The inputs that drove this Term + late List inputs = []; + + /// The row of the terminal + final int row; + + /// The column of the term + final int col; + + /// The Logic wire of the term + final logic = Logic(); + + /// Estimated delay of the output of this CompessTerm + late double delay; + + /// Estimated delay of a Sum term + static const sumDelay = 1.0; + + /// Estimated delay of a Carry term + static const carryDelay = 0.75; + + /// CompressTerm constructor + CompressTerm(this.type, this.row, this.col) { + delay = 0.0; + } + + /// Create a sum Term + factory CompressTerm.sumTerm(List args, int row, int col) { + final term = CompressTerm(CompressTermType.sum, row, col); + // ignore: cascade_invocations + term.inputs = args; + for (final i in term.inputs) { + if (i.delay + sumDelay > term.delay) { + term.delay = i.delay + sumDelay; + } + } + return term; + } + + /// Create a carry Term + factory CompressTerm.carryTerm(List args, int row, int col) { + final term = CompressTerm(CompressTermType.carry, row, col); + // ignore: cascade_invocations + term.inputs = args; + for (final i in term.inputs) { + if (i.delay + carryDelay > term.delay) { + term.delay = i.delay + carryDelay; + } + } + return term; + } + @override + int compareTo(Object other) { + if (other is! CompressTerm) { + throw Exception('Input must be of type CompressTerm '); + } + return delay > other.delay ? 1 : (delay < other.delay ? -1 : 0); + } + + @override + String toString() { + final str = StringBuffer(); + final ts = switch (type) { + CompressTermType.pp => 'pp', + CompressTermType.carry => 'c', + CompressTermType.sum => 's' + }; + str + ..write(ts) + ..write('$row,$col'); + return str.toString(); + } +} + +/// A column of partial product terms +typedef ColumnQueue = PriorityQueue; + +/// A column compressor +class ColumnCompressor { + /// Columns of partial product CompressTerms + late final List columns; + + /// The partial product generator to be compressed + final PartialProductGenerator pp; + + /// Initialize a ColumnCompressor for a set of partial products + ColumnCompressor(this.pp) { + columns = List.generate(pp.maxWidth(), (i) => ColumnQueue()); + + for (var row = 0; row < pp.rows; row++) { + for (var col = 0; col < pp.partialProducts[row].length; col++) { + final trueColumn = pp.rowShift[row] + col; + final term = CompressTerm(CompressTermType.pp, row, trueColumn); + term.logic <= pp.partialProducts[row][col]; + columns[trueColumn].add(term); + } + } + } + +// TODO(desmonddak): This cannot run without real logic values due to toInt() +// which forces the user to assign values to the inputs first +// We need a way to build the CompressionTerm without actual values +// e.g., there needs to be a way to do the reductions with 'X' values + /// Evaluate the logic value of a given CompressTerm + LogicValue evaluateTerm(CompressTerm term) { + switch (term.type) { + case CompressTermType.pp: + return term.logic.value; + case CompressTermType.sum: + // xor the eval of the terms + final termValues = [for (term in term.inputs) evaluateTerm(term)]; + final sum = termValues.swizzle().xor(); + return sum; + case CompressTermType.carry: + final termValues = [for (term in term.inputs) evaluateTerm(term)]; + final termValuesInt = [ + for (var i = 0; i < termValues.length; i++) termValues[i].toInt() + ]; + + final count = (termValuesInt.isNotEmpty) + ? termValuesInt.reduce((c, term) => c + term) + : 0; + final majority = + (count > termValues.length ~/ 2 ? LogicValue.one : LogicValue.zero); + // Alternative method: + // final x = Logic(width: termValues.length); + // x.put(termValues.swizzle()); + // final newCount = Count(x).index.value.toInt(); + // stdout.write('count=$count newCount=$newCount\n'); + // assert(newCount == count, 'count=$count newCount=$newCount\n'); + return majority; + } + } + + /// Return the longest column length + int longestColumn() => + columns.reduce((a, b) => a.length > b.length ? a : b).length; + + @override + String toString() { + final ts = StringBuffer(); + for (var row = 0; row < longestColumn(); row++) { + for (var col = columns.length - 1; col >= 0; col--) { + final colList = columns[col].toList(); + if (row < colList.length) { + ts.write('\t${colList[row]}'); + } else { + ts.write('\t'); + } + } + ts.write('\n'); + } + return ts.toString(); + } + + /// Evaluate the (un)compressed partial product array + /// logic=true will read the logic gate outputs at each level + /// print=true will print out the array + BigInt evaluate({bool print = false, bool logic = false}) { + final ts = StringBuffer(); + final rows = longestColumn(); + final width = pp.maxWidth(); + + var accum = BigInt.zero; + for (var row = 0; row < rows; row++) { + final rowBits = []; + for (var col = columns.length - 1; col >= 0; col--) { + final colList = columns[col].toList(); + if (row < colList.length) { + final value = + logic ? colList[row].logic.value : evaluateTerm(colList[row]); + rowBits.add(value); + if (print) { + ts.write('\t${bitString(value)}'); + } + } else if (print) { + ts.write('\t'); + } + } + rowBits.addAll(List.filled(pp.rowShift[row], LogicValue.zero)); + final val = rowBits.swizzle().zeroExtend(width).toBigInt(); + accum += val; + if (print) { + ts.write('\t${bitString(rowBits.swizzle().zeroExtend(width))} ($val)'); + if (row == rows - 1) { + ts.write(' Total=${accum.toSigned(width)}\n'); + stdout.write(ts); + } else { + ts.write('\n'); + } + } + } + return accum.toSigned(width); + } + + /// Convert a row to a Logic bitvector + Logic extractRow(int row) { + final width = pp.maxWidth(); + + final rowBits = []; + for (var col = columns.length - 1; col >= 0; col--) { + final colList = columns[col].toList(); + if (row < colList.length) { + final value = colList[row].logic; + rowBits.add(value); + } + } + rowBits.addAll(List.filled(pp.rowShift[row], Const(0))); + return rowBits.swizzle().zeroExtend(width); + } + + /// Core iterator for column compressor routine + List _compressIter(int iteration) { + final terms = []; + for (var col = 0; col < columns.length; col++) { + final queue = columns[col]; + final depth = queue.length; + if (depth > iteration) { + if (depth > 2) { + final first = queue.removeFirst(); + final second = queue.removeFirst(); + final inputs = [first, second]; + Compressor compressor; + if (depth > 3) { + inputs.add(queue.removeFirst()); + compressor = + Compressor3([for (final i in inputs) i.logic].swizzle()); + } else { + compressor = + Compressor2([for (final i in inputs) i.logic].swizzle()); + } + final t = CompressTerm.sumTerm(inputs, 0, col); + t.logic <= compressor.sum; + // assert(t.logic.value == evaluateTerm(t), + // 'sum logic does not match evaluate'); + terms.add(t); + columns[col].add(t); + if (col < columns.length - 1) { + final t = CompressTerm.carryTerm(inputs, 0, col); + columns[col + 1].add(t); + terms.add(t); + t.logic <= compressor.carry; + // assert(t.logic.value == evaluateTerm(t), + // 'carry logic does not match evaluate.'); + } + } + } + } + return terms; + } + + /// Compress the partial products array to two addends + List compress() { + final terms = []; + var iterations = longestColumn(); + while (iterations > 0) { + terms.addAll(_compressIter(iterations--)); + if (longestColumn() <= 2) { + break; + } + } + return terms; + } +} diff --git a/lib/src/arithmetic/multiplier.dart b/lib/src/arithmetic/multiplier.dart new file mode 100644 index 00000000..efe8a471 --- /dev/null +++ b/lib/src/arithmetic/multiplier.dart @@ -0,0 +1,197 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// multiplier.dart +// Abstract class of of multiplier module implementation. All multiplier module +// need to inherit this module to ensure consistency. +// +// 2023 May 29 +// Author: Yao Jing Quek + +import 'package:meta/meta.dart'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_hcl/src/arithmetic/booth.dart'; +import 'package:rohd_hcl/src/arithmetic/compressor.dart'; + +/// An abstract class for all multiplier implementations. +abstract class Multiplier extends Module { + /// The input to the multiplier pin [a]. + @protected + late final Logic a; + + /// The input to the multiplier pin [b]. + @protected + late final Logic b; + + /// The multiplier treats operands and output as signed + bool get signed => _signed; + + @protected + bool _signed = false; + + /// The multiplication results of the multiplier. + Logic get product; + + /// Take input [a] and input [b] and return the + /// [product] of the multiplication result. + Multiplier(Logic a, Logic b, {bool signed = false, super.name}) { + if (a.width != b.width) { + throw RohdHclException('inputs of a and b should have same width.'); + } + _signed = signed; + this.a = addInput('a', a, width: a.width); + this.b = addInput('b', b, width: b.width); + } +} + +/// An abstract class for all multiply accumulate implementations. +abstract class MultiplyAccumulate extends Module { + /// The input to the multiplier pin [a]. + @protected + late final Logic a; + + /// The input to the multiplier pin [b]. + @protected + late final Logic b; + + /// The input to the addend pin [c]. + @protected + late final Logic c; + + /// The multiplier treats operands and output as signed + bool get signed => _signed; + + @protected + bool _signed = false; + + /// The multiplication results of the multiply-accumulate. + Logic get accumulate; + + /// Take input [a] and input [b], compute their + /// product, add input [c] to produce the [accumulate] result. + MultiplyAccumulate(Logic a, Logic b, Logic c, + {bool signed = false, super.name}) { + if (a.width != b.width) { + throw RohdHclException('inputs of a and b should have same width.'); + } + _signed = signed; + this.a = addInput('a', a, width: a.width); + this.b = addInput('b', b, width: b.width); + this.c = addInput('c', c, width: c.width); + } +} + +/// An implementation of an integer multiplier using compression trees +class CompressionTreeMultiplier extends Multiplier { + /// The final product of the multiplier module. + @override + Logic get product => output('product'); + + /// Construct a compression tree integer multipler with + /// a given radix and final adder functor + CompressionTreeMultiplier(super.a, super.b, int radix, + ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppTree, + {super.signed = false}) + : super( + name: 'Compression Tree Multiplier: ' + 'R${radix}_${ppTree.call([ + Logic() + ], (a, b) => Logic()).runtimeType}') { + final product = addOutput('product', width: a.width + b.width); + + final pp = + PartialProductGenerator(a, b, RadixEncoder(radix), signed: signed); + // ignore: cascade_invocations + pp.signExtendCompact(); + final compressor = ColumnCompressor(pp); + // ignore: cascade_invocations + compressor.compress(); + final adder = ParallelPrefixAdder( + compressor.extractRow(0), compressor.extractRow(1), ppTree); + product <= adder.out.slice(a.width + b.width - 1, 0); + } +} + +/// An implementation of an integer multiply accumulate using compression trees +class CompressionTreeMultiplyAccumulate extends MultiplyAccumulate { + /// The final product of the multiplier module. + @override + Logic get accumulate => output('accumulate'); + + /// Construct a compression tree integer multipler with + /// a given radix and final adder functor + CompressionTreeMultiplyAccumulate(super.a, super.b, super.c, int radix, + ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppTree, + {bool signed = false}) + : super( + name: 'Compression Tree Multiplier: ' + 'R${radix}_${ppTree.call([Logic()], (a, b) => Logic()).name}') { + final accumulate = addOutput('accumulate', width: a.width + b.width + 1); + _signed = signed; + + final pp = + PartialProductGenerator(a, b, RadixEncoder(radix), signed: signed); + // ignore: cascade_invocations + pp.signExtendCompact(); + + // Evaluate works only because the compressed rows have the same shape + // So the rowshift is valid. + // But this requires that we prefix the PP with the addend (not add) to + // keep the evaluate routine working. + + // TODO(desmonddak): This sign extension method for the additional + // addend may only work with signExtendCompact. + + final lastLength = + pp.partialProducts[pp.rows - 1].length + pp.rowShift[pp.rows - 1]; + + final sign = signed ? c[c.width - 1] : Const(0); + final l = [for (var i = 0; i < c.width; i++) c[i]]; + while (l.length < lastLength) { + l.add(sign); + } + l + ..add(~sign) + ..add(Const(1)); + + pp.partialProducts.insert(0, l); + pp.rowShift.insert(0, 0); + + // TODO(desmonddak): probably cleaner to add row at end, but + // compressor fails to properly handle, so we need to debug + // pp.partialProducts.add(l); + // pp.rowShift.add(0); + final compressor = ColumnCompressor(pp); + // ignore: cascade_invocations + compressor.compress(); + + final adder = ParallelPrefixAdder( + compressor.extractRow(0), compressor.extractRow(1), ppTree); + accumulate <= adder.out.slice(a.width + b.width, 0); + } +} + +/// A MultiplyAccumulate which ignores the [c] term and applies the +/// multiplier function +class MultiplyOnly extends MultiplyAccumulate { + @override + Logic get accumulate => output('accumulate'); + + /// Construct a MultiplyAccumulate that only multiplies to enable + /// using the same tester with zero addend. + MultiplyOnly(super.a, super.b, super.c, + Multiplier Function(Logic a, Logic b) multiplyGenerator) + : super(name: 'Multiply Only: ${multiplyGenerator.call(a, b).name}') { + final accumulate = addOutput('accumulate', width: a.width + b.width + 1); + + final multiply = multiplyGenerator(a, b); + _signed = multiply.signed; + + accumulate <= + (signed + ? [multiply.product[multiply.product.width - 1], multiply.product] + .swizzle() + : multiply.product.zeroExtend(accumulate.width)); + } +} diff --git a/lib/src/parallel_prefix_operations.dart b/lib/src/arithmetic/parallel_prefix_operations.dart similarity index 83% rename from lib/src/parallel_prefix_operations.dart rename to lib/src/arithmetic/parallel_prefix_operations.dart index 7f1d5b38..4081cd97 100644 --- a/lib/src/parallel_prefix_operations.dart +++ b/lib/src/arithmetic/parallel_prefix_operations.dart @@ -11,6 +11,7 @@ import 'dart:math'; import 'package:collection/collection.dart'; +import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; @@ -166,12 +167,28 @@ class ParallelPrefixOrScan extends Module { ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen) { inp = addInput('inp', inp, width: inp.width); - final u = - ppGen(List.generate(inp.width, (i) => inp[i]), (a, b) => a | b); + final u = ppGen(inp.elements, (a, b) => a | b); addOutput('out', width: inp.width) <= u.val.rswizzle(); } } +/// Priority Finder based on ParallelPrefix tree +class ParallelPrefixPriorityFinder extends Module { + /// Output [out] is the one-hot reduction to the first '1' in the Logic input + /// Search is from the LSB + Logic get out => output('out'); + + /// Priority Finder constructor + ParallelPrefixPriorityFinder( + Logic inp, + ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppGen) { + inp = addInput('inp', inp, width: inp.width); + final u = ParallelPrefixOrScan(inp, ppGen); + addOutput('out', width: inp.width) <= (u.out & ~(u.out << Const(1))); + } +} + /// Priority Encoder based on ParallelPrefix tree class ParallelPrefixPriorityEncoder extends Module { /// Output [out] is the bit position of the first '1' in the Logic input @@ -184,32 +201,46 @@ class ParallelPrefixPriorityEncoder extends Module { ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen) { inp = addInput('inp', inp, width: inp.width); - final u = ParallelPrefixOrScan(inp, ppGen); - addOutput('out', width: inp.width) <= (u.out & ~(u.out << Const(1))); + addOutput('out', width: log2Ceil(inp.width)); + final u = ParallelPrefixPriorityFinder(inp, ppGen); + out <= OneHotToBinary(u.out).binary; } } /// Adder based on ParallelPrefix tree -class ParallelPrefixAdder extends Module { - /// Output [out] the arithmetic sum of the two Logic inputs - Logic get out => output('out'); +class ParallelPrefixAdder extends Adder { + late final Logic _out; + late final Logic _carry = Logic(); /// Adder constructor - ParallelPrefixAdder(Logic a, Logic b, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen, - {super.definitionName}) { - a = addInput('a', a, width: a.width); - b = addInput('b', b, width: b.width); + ParallelPrefixAdder(super.a, super.b, + ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen) + : _out = Logic(width: a.width), + super( + name: 'ParallelPrefixAdder: ${ppGen.call([ + Logic() + ], (a, b) => Logic()).name}') { final u = ppGen( - // generate, propagate or generate List.generate( a.width, (i) => [a[i] & b[i], a[i] | b[i]].swizzle()), (lhs, rhs) => [rhs[1] | rhs[0] & lhs[1], rhs[0] & lhs[0]].swizzle()); - addOutput('out', width: a.width) <= + _carry <= u.val[a.width - 1][1]; + _out <= List.generate(a.width, (i) => (i == 0) ? a[i] ^ b[i] : a[i] ^ b[i] ^ u.val[i - 1][1]) .rswizzle(); } + @override + @protected + Logic calculateOut() => _out; + + @override + @protected + Logic calculateCarry() => _carry; + + @override + @protected + Logic calculateSum() => [_carry, _out].swizzle(); } /// Incrementer based on ParallelPrefix tree @@ -223,8 +254,7 @@ class ParallelPrefixIncr extends Module { ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen) { inp = addInput('inp', inp, width: inp.width); - final u = ppGen(List.generate(inp.width, (i) => inp[i]), - (lhs, rhs) => rhs & lhs); + final u = ppGen(inp.elements, (lhs, rhs) => rhs & lhs); addOutput('out', width: inp.width) <= (List.generate( inp.width, (i) => ((i == 0) ? ~inp[i] : inp[i] ^ u.val[i - 1])) @@ -243,8 +273,7 @@ class ParallelPrefixDecr extends Module { ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen) { inp = addInput('inp', inp, width: inp.width); - final u = ppGen(List.generate(inp.width, (i) => ~inp[i]), - (lhs, rhs) => rhs & lhs); + final u = ppGen((~inp).elements, (lhs, rhs) => rhs & lhs); addOutput('out', width: inp.width) <= (List.generate( inp.width, (i) => ((i == 0) ? ~inp[i] : inp[i] ^ u.val[i - 1])) diff --git a/lib/src/ripple_carry_adder.dart b/lib/src/arithmetic/ripple_carry_adder.dart similarity index 62% rename from lib/src/ripple_carry_adder.dart rename to lib/src/arithmetic/ripple_carry_adder.dart index 0c257512..454480b0 100644 --- a/lib/src/ripple_carry_adder.dart +++ b/lib/src/arithmetic/ripple_carry_adder.dart @@ -8,6 +8,7 @@ // Author: Yao Jing Quek // +import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; @@ -17,20 +18,37 @@ import 'package:rohd_hcl/rohd_hcl.dart'; /// from the least significant bit (LSB) to most significant bit (MSB), the /// adder sequentially adds corresponding bits of two binary numbers. class RippleCarryAdder extends Adder { - /// Constructs an n-bit adder based on inputs List of inputs. - RippleCarryAdder(super.a, super.b, {super.name = 'ripple_carry_adder'}) { - Logic carry = Const(0); + @protected + late final Logic _out; + late final Logic _carry = Logic(); + /// Constructs an n-bit adder based on inputs List of inputs. + RippleCarryAdder(super.a, super.b, + {super.name = 'ripple_carry_adder', Logic? carry}) + : _out = Logic(width: a.width) { final sumList = []; for (var i = 0; i < a.width; i++) { - final fullAdder = FullAdder(a: a[i], b: b[i], carryIn: carry); + final fullAdder = FullAdder(a: a[i], b: b[i], carryIn: carry ?? Const(0)); carry = fullAdder.carryOut; sumList.add(fullAdder.sum); } - sumList.add(carry); + sumList.add(carry!); - sum <= sumList.rswizzle(); + _out <= sumList.rswizzle().slice(_out.width - 1, 0); + _carry <= sumList[sumList.rswizzle().width - 1]; } + + @override + @protected + Logic calculateOut() => _out; + + @override + @protected + Logic calculateCarry() => _carry; + + @override + @protected + Logic calculateSum() => [_carry, _out].swizzle(); } diff --git a/lib/src/arithmetic/sign_magnitude_adder.dart b/lib/src/arithmetic/sign_magnitude_adder.dart new file mode 100644 index 00000000..d9b7845c --- /dev/null +++ b/lib/src/arithmetic/sign_magnitude_adder.dart @@ -0,0 +1,69 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +//sign_magnitude_adder +// Implementation of a One's Complement Adder +// +// 2024 April 12 +// Author: Desmond Kirkpatrick + +import 'package:meta/meta.dart'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// An Adder which performs one's complement arithmetic using an unsigned +/// adder that is passed in using a functor +/// -- Requires that if the larger magnitude number is negative it +/// must be the first 'a' argument +/// We cannot enforce because this may be a smaller mantissa in +/// a larger magnitude negative floating point number (no asserts please) +class SignMagnitudeAdder extends Adder { + /// The sign of the first input + @protected + late final Logic aSign; + + /// The sign of the second input + @protected + late final Logic bSign; + + /// The sign of the result + Logic get sign => output('sign'); + + late final Logic _out; + late final Logic _carry = Logic(); + + /// [SignMagnitudeAdder] constructor with an unsigned adder functor + SignMagnitudeAdder(Logic as, super.a, Logic bs, super.b, + Adder Function(Logic, Logic) adderGen) + : _out = Logic(width: a.width), + super( + name: 'Ones Complement Adder: ' + '${adderGen.call(Logic(), Logic()).name}') { + aSign = addInput('aSign', as); + bSign = addInput('bSign', bs); + final sign = addOutput('sign'); + + final aOnesComplement = mux(aSign, ~a, a); + final bOnesComplement = mux(bSign, ~b, b); + + final adder = adderGen(aOnesComplement, bOnesComplement); + final endAround = adder.carryOut & (aSign | bSign); + final localOut = mux(endAround, adder.sum + 1, adder.sum); + + sign <= aSign; + _out <= mux(sign, ~localOut, localOut).slice(_out.width - 1, 0); + _carry <= localOut.slice(localOut.width - 1, localOut.width - 1); + } + + @override + @protected + Logic calculateOut() => _out; + + @override + @protected + Logic calculateCarry() => _carry; + + @override + @protected + Logic calculateSum() => [_carry, _out].swizzle(); +} diff --git a/lib/src/multiplier.dart b/lib/src/multiplier.dart deleted file mode 100644 index 15a820aa..00000000 --- a/lib/src/multiplier.dart +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2023 Intel Corporation -// SPDX-License-Identifier: BSD-3-Clause -// -// multiplier.dart -// Abstract class of of multiplier module implementation. All multiplier module -// need to inherit this module to ensure consistency. -// -// 2023 May 29 -// Author: Yao Jing Quek - -import 'package:meta/meta.dart'; -import 'package:rohd/rohd.dart'; -import 'package:rohd_hcl/rohd_hcl.dart'; - -/// An abstract class for all multiplier implementation. -abstract class Multiplier extends Module { - /// The input to the multiplier pin [a]. - @protected - late final Logic a; - - /// The input to the multiplier pin [b]. - @protected - late final Logic b; - - /// The multiplication results of the multiplier. - Logic get product; - - /// Take input [a] and input [b] and return the - /// [product] of the multiplication result. - Multiplier(Logic a, Logic b, {super.name}) { - if (a.width != b.width) { - throw RohdHclException('inputs of a and b should have same width.'); - } - this.a = addInput('a', a, width: a.width); - this.b = addInput('b', b, width: b.width); - } -} diff --git a/test/arithmetic/adder_test.dart b/test/arithmetic/adder_test.dart new file mode 100644 index 00000000..85f26119 --- /dev/null +++ b/test/arithmetic/adder_test.dart @@ -0,0 +1,224 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// adder_test.dart +// Tests for the Adder interface. +// +// 2024 April 4 +// Author: Desmond Kirkpatrick + +import 'dart:math'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:test/test.dart'; + +Logic msb(Logic a) => a[a.width - 1]; +Logic lsb(Logic a) => a[0]; + +LogicValue msbV(LogicValue a) => a[a.width - 1]; +LogicValue lsbV(LogicValue a) => a[0]; + +void checkAdder(Adder adder, LogicValue av, LogicValue bv) { + final aB = av.toBigInt(); + final bB = bv.toBigInt(); + // ignore: invalid_use_of_protected_member + adder.a.put(av); + // ignore: invalid_use_of_protected_member + adder.b.put(bv); + + expect( + adder.out.value.toBigInt(), + // ignore: invalid_use_of_protected_member + equals((aB + bB) & ((BigInt.one << adder.a.width) - BigInt.one))); + expect(adder.sum.value.toBigInt(), equals(aB + bB)); +} + +void testExhaustive(int n, Adder Function(Logic a, Logic b) fn) { + final a = Logic(name: 'a', width: n); + final b = Logic(name: 'b', width: n); + + final mod = fn(a, b); + test( + 'exhaustive: ${mod.name}_W$n' + '_G${fn.call(a, b).name}', () async { + await mod.build(); + + for (var aa = 0; aa < (1 << n); ++aa) { + for (var bb = 0; bb < (1 << n); ++bb) { + final av = LogicValue.of(BigInt.from(aa), width: n); + final bv = LogicValue.of(BigInt.from(bb), width: n); + checkAdder(mod, av, bv); + } + } + }); +} + +void testAdderRandom(int n, int nSamples, Adder Function(Logic a, Logic b) fn) { + final a = Logic(name: 'a', width: n); + final b = Logic(name: 'b', width: n); + + final adder = fn(a, b); + test('random: ${adder.name}_W${a.width}_I$nSamples', () async { + await adder.build(); + + for (var i = 0; i < nSamples; ++i) { + final aa = Random().nextLogicValue(width: n); + final bb = Random().nextLogicValue(width: n); + checkAdder(adder, aa, bb); + } + }); +} + +void checkSignMagnitudeAdder(SignMagnitudeAdder adder, LogicValue aSign, + LogicValue aMagnitude, LogicValue bSign, LogicValue bMagnitude) { + // ignore: invalid_use_of_protected_member + adder.aSign.put(aSign); + // ignore: invalid_use_of_protected_member + adder.bSign.put(bSign); + + // ignore: invalid_use_of_protected_member + adder.a.put(aMagnitude); + // ignore: invalid_use_of_protected_member + adder.b.put(bMagnitude); + + final n = aMagnitude.width; + + final computedVal = (adder.sign.value == LogicValue.one) + ? -adder.out.value.toBigInt() + : adder.out.value.toBigInt(); + + final aValue = (aSign == LogicValue.one) + ? -aMagnitude.toBigInt() + : aMagnitude.toBigInt(); + final bValue = (bSign == LogicValue.one) + ? -bMagnitude.toBigInt() + : bMagnitude.toBigInt(); + final expectSign = (aValue + bValue).sign; + // Special modular arithmetic for 1's complement negative numbers + // Remember that there are two zeros in 1's complement + final expectedMag = (aValue + bValue).abs() % + ((expectSign == -1) + ? BigInt.from(pow(2, n) - 1) + : BigInt.from(pow(2, n))); + final expectVal = expectSign == -1 ? -expectedMag : expectedMag; + expect(computedVal, equals(expectVal)); +} + +void testExhaustiveSignMagnitude(int n, Adder Function(Logic a, Logic b) fn) { + final aSign = Logic(name: 'aSign'); + final a = Logic(name: 'a', width: n); + final bSign = Logic(name: 'bSign'); + final b = Logic(name: 'b', width: n); + + final adder = SignMagnitudeAdder(aSign, a, bSign, b, fn); + test('exhaustive Sign Magnitude: ${adder.name}_W${a.width}', () async { + await adder.build(); + + for (var i = 0; i < pow(2, n); i += 1) { + for (var j = 0; j < pow(2, n); j += 1) { + final bI = BigInt.from(i).toSigned(n); + final bJ = BigInt.from(j).toSigned(n); + final BigInt smaller; + final BigInt bigger; + // When equal, we want the negative one first to produce 1 1...1 + if ((bI.abs() > bJ.abs()) || (bI.abs() == bJ.abs() && (bI < bJ))) { + bigger = bI; + smaller = bJ; + } else { + bigger = bJ; + smaller = bI; + } + final biggerSign = bigger.abs() != bigger ? 1 : 0; + final smallerSign = smaller.abs() != smaller ? 1 : 0; + + final biggerSignLv = LogicValue.of(biggerSign, width: 1); + final smallerSignLv = LogicValue.of(smallerSign, width: 1); + + final biggerLv = LogicValue.of(bigger.abs(), width: n); + final smallerLv = LogicValue.of(smaller.abs(), width: n); + + checkSignMagnitudeAdder( + adder, biggerSignLv, biggerLv, smallerSignLv, smallerLv); + } + } + }); +} + +void testRandomSignMagnitude( + int width, int nSamples, Adder Function(Logic a, Logic b) fn) { + final aSign = Logic(name: 'aSign'); + final a = Logic(name: 'a', width: width); + final bSign = Logic(name: 'bSign'); + final b = Logic(name: 'b', width: width); + + final adder = SignMagnitudeAdder(aSign, a, bSign, b, fn); + test('random Sign Magnitude: ${adder.name}_W${a.width}', () async { + await adder.build(); + + for (var i = 0; i < nSamples; ++i) { + final aa = Random().nextLogicValue(width: width); + final av = aa.toBigInt().toSigned(width); + final bb = Random().nextLogicValue(width: width); + final bv = bb.toBigInt().toSigned(width); + + final BigInt smaller; + final BigInt bigger; + // When equal, we want the negative one first to produce 1 1...1 + if ((av.abs() > bv.abs()) || (av.abs() == bv.abs() && (av < bv))) { + bigger = av; + smaller = bv; + } else { + bigger = bv; + smaller = av; + } + final biggerSign = bigger.abs() != bigger ? 1 : 0; + final smallerSign = smaller.abs() != smaller ? 1 : 0; + + final biggerSignLv = LogicValue.of(biggerSign, width: 1); + final smallerSignLv = LogicValue.of(smallerSign, width: 1); + + final biggerLv = LogicValue.of(bigger.abs(), width: width); + final smallerLv = LogicValue.of(smaller.abs(), width: width); + + checkSignMagnitudeAdder( + adder, biggerSignLv, biggerLv, smallerSignLv, smallerLv); + } + }); +} + +void main() { + tearDown(() async { + await Simulator.reset(); + }); + + final generators = [Ripple.new, Sklansky.new, KoggeStone.new, BrentKung.new]; + + group('adderRandom', () { + for (final n in [64, 64, 65]) { + testAdderRandom(n, 30, RippleCarryAdder.new); + for (final ppGen in generators) { + testAdderRandom(n, 30, (a, b) => ParallelPrefixAdder(a, b, ppGen)); + } + } + }); + group('exhaustive', () { + testExhaustive(4, RippleCarryAdder.new); + for (final ppGen in generators) { + testExhaustive(4, (a, b) => ParallelPrefixAdder(a, b, ppGen)); + } + }); + group('SignMagnitude random', () { + for (final ppGen in generators) { + testRandomSignMagnitude(4, 30, RippleCarryAdder.new); + testRandomSignMagnitude( + 4, 30, (a, b) => ParallelPrefixAdder(a, b, ppGen)); + } + }); + group('SignMagnitude exhaustive', () { + for (final ppGen in generators) { + testExhaustiveSignMagnitude(4, RippleCarryAdder.new); + testExhaustiveSignMagnitude( + 4, (a, b) => ParallelPrefixAdder(a, b, ppGen)); + } + }); +} diff --git a/test/arithmetic/booth_test.dart b/test/arithmetic/booth_test.dart new file mode 100644 index 00000000..3d1b52b8 --- /dev/null +++ b/test/arithmetic/booth_test.dart @@ -0,0 +1,208 @@ +// Copxorright (C) 2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// booth_test.dart +// Tests for Booth encoding +// +// 2024 May 21 +// Author: Desmond Kirkpatrick + +import 'dart:io'; +import 'dart:math'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/src/arithmetic/booth.dart'; +import 'package:rohd_hcl/src/utils.dart'; +import 'package:test/test.dart'; + +enum SignExtension { brute, stop, compact, compactRect } + +void checkEvaluateExhaustive(PartialProductGenerator pp) { + final widthX = pp.selector.multiplicand.width; + final widthY = pp.encoder.multiplier.width; + + final limitX = pow(2, widthX); + final limitY = pow(2, widthY); + for (var i = BigInt.zero; i < BigInt.from(limitX); i += BigInt.one) { + for (var j = BigInt.zero; j < BigInt.from(limitY); j += BigInt.one) { + final X = pp.signed ? i.toSigned(widthX) : i.toUnsigned(widthX); + final Y = pp.signed ? j.toSigned(widthY) : j.toUnsigned(widthY); + pp.multiplicand.put(X); + pp.multiplier.put(Y); + // print('$X * $Y'); + expect(pp.evaluate(), equals(X * Y)); + } + } +} + +void checkEvaluateRandom(PartialProductGenerator pp, int nSamples) { + final widthX = pp.selector.multiplicand.width; + final widthY = pp.encoder.multiplier.width; + + for (var i = 0; i < nSamples; ++i) { + final rX = Random().nextLogicValue(width: widthX).toBigInt(); + final rY = Random().nextLogicValue(width: widthY).toBigInt(); + final X = pp.signed ? rX.toSigned(widthX) : rX; + final Y = pp.signed ? rY.toSigned(widthY) : rY; + pp.multiplicand.put(X); + pp.multiplier.put(Y); + // print('$X * $Y'); + expect(pp.evaluate(), equals(X * Y)); + } +} + +void main() { + test('single MAC partial product test', () async { + // stdout.write('\n'); + + final encoder = RadixEncoder(4); + const widthX = 4; + const widthY = 4; + + const i = 8; + var j = pow(2, widthY - 1).toInt(); + j = 2; + const k = 128; + + final X = BigInt.from(i).toSigned(widthX); + final Y = BigInt.from(j).toSigned(widthY); + final Z = BigInt.from(k).toSigned(widthX + widthY); + final product = X * Y + Z; + + final logicX = Logic(name: 'X', width: widthX); + final logicY = Logic(name: 'Y', width: widthY); + final logicZ = Logic(name: 'Z', width: widthX + widthY); + logicX.put(X); + logicY.put(Y); + logicZ.put(Z); + final pp = PartialProductGenerator(logicX, logicY, encoder); + // ignore: cascade_invocations + pp.signExtendCompact(); + // stdout.write(pp); + // Add a row for addend + final l = [for (var i = 0; i < logicZ.width; i++) logicZ[i]]; + // ignore: cascade_invocations + l + ..add(Const(0)) // ~Sign in our sign extension form + ..add(Const(1)); + pp.partialProducts.add(l); + pp.rowShift.add(0); + + // stdout.write('Test: $i($X) * $j($Y) + $k($Z)= $product vs ' + // '${pp.evaluate(signed: true)}\n'); + if (pp.evaluate() != product) { + stdout.write('Fail: $X * $Y: ${pp.evaluate()} vs expected $product\n'); + // ignore: cascade_invocations + // stdout.write(pp); + } + // expect(pp.evaluate(signed: true), equals(product)); + }); + + // TODO(dakdesmond): Figure out minimum widths! + + // This is a two-minute exhaustive but quick test + test('exhaustive partial product evaluate: square radix-4, all extension', + () async { + stdout.write('\n'); + for (final signed in [false, true]) { + for (var radix = 4; radix < 8; radix *= 2) { + final encoder = RadixEncoder(radix); + // stdout.write('encoding with radix=$radix\n'); + final shift = log2Ceil(encoder.radix); + for (var width = shift + 1; width < shift * 2 + 1; width++) { + for (final signExtension in SignExtension.values) { + final pp = PartialProductGenerator(Logic(name: 'X', width: width), + Logic(name: 'Y', width: width), encoder, + signed: signed); + switch (signExtension) { + case SignExtension.brute: + pp.bruteForceSignExtend(); + case SignExtension.stop: + pp.signExtendWithStopBitsRect(); + case SignExtension.compact: + pp.signExtendCompact(); + case SignExtension.compactRect: + pp.signExtendCompactRect(); + } + // testPartialProductExhaustive(pp); + checkEvaluateExhaustive(pp); + } + } + } + } + }); + + test('full rectangular test,', () async { + for (final signed in [false, true]) { + for (var radix = 2; radix < 32; radix *= 2) { + final encoder = RadixEncoder(radix); + final shift = log2Ceil(encoder.radix); + for (var width = 3 + shift + 1; width < 3 + shift * 2 + 1; width++) { + for (var skew = -3; skew < shift * 2; skew++) { + // Only some sign extension routines have rectangular support + for (final signExtension in [ + // SignExtension.brute, + // SignExtension.stop, + SignExtension.compactRect + ]) { + final pp = PartialProductGenerator(Logic(name: 'X', width: width), + Logic(name: 'Y', width: width + skew), encoder, + signed: signed); + + switch (signExtension) { + case SignExtension.brute: + pp.bruteForceSignExtend(); + case SignExtension.stop: + pp.signExtendWithStopBitsRect(); + case SignExtension.compact: + pp.signExtendCompact(); + case SignExtension.compactRect: + pp.signExtendCompactRect(); + } + checkEvaluateRandom(pp, 100); + } + } + } + } + } + }); + + test('Rectangle Q collision tests,', () async { + final alignTest = >[]; + + // These collide with the normal q extension bits + // These are unsigned tests + // ignore: cascade_invocations + alignTest + ..insert(0, [(2, 5, 0), (4, 7, 1), (8, 7, 2), (16, 9, 3)]) + ..insert(1, [(2, 5, 1), (4, 6, 2), (8, 9, 3), (16, 8, 4)]) + ..insert(2, [(2, 5, 2), (4, 7, 3), (8, 8, 4), (16, 11, 5)]) + ..insert(3, [(4, 6, 4), (8, 7, 5), (16, 10, 6)]) + ..insert(4, [(8, 9, 6), (16, 9, 7)]) + ..insert(5, [(16, 8, 8)]) + // + ; + + for (final alignList in alignTest) { + for (final align in alignList) { + final radix = align.$1; + final encoder = RadixEncoder(radix); + final width = align.$2; + + final skew = align.$3; + final X = BigInt.from(29).toUnsigned(width); + final Y = BigInt.from(2060).toUnsigned(width + skew); + final product = X * Y; + + final pp = PartialProductGenerator(Logic(name: 'X', width: width), + Logic(name: 'Y', width: width + skew), encoder, + signed: false); + + pp.multiplicand.put(X); + pp.multiplier.put(Y); + pp.signExtendCompactRect(); + expect(pp.evaluate(), equals(product)); + checkEvaluateRandom(pp, 100); + } + } + }); +} diff --git a/test/carry_save_multiplier_test.dart b/test/arithmetic/carry_save_multiplier_test.dart similarity index 100% rename from test/carry_save_multiplier_test.dart rename to test/arithmetic/carry_save_multiplier_test.dart diff --git a/test/arithmetic/compressor_test.dart b/test/arithmetic/compressor_test.dart new file mode 100644 index 00000000..da4dfc1c --- /dev/null +++ b/test/arithmetic/compressor_test.dart @@ -0,0 +1,116 @@ +// Copxorright (C) 2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// booth_test.dart +// Tests for the select interface of Booth encoding +// +// 2024 June 04 +// Author: Desmond Kirkpatrick + +import 'dart:io'; +import 'dart:math'; + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/src/arithmetic/booth.dart'; +import 'package:rohd_hcl/src/arithmetic/compressor.dart'; +import 'package:rohd_hcl/src/arithmetic/parallel_prefix_operations.dart'; +import 'package:rohd_hcl/src/utils.dart'; +import 'package:test/test.dart'; + +enum SignExtension { brute, stop, compact } + +void testCompressionExhaustive(PartialProductGenerator pp) { + final widthX = pp.selector.multiplicand.width; + final widthY = pp.encoder.multiplier.width; + + final compressor = ColumnCompressor(pp); + + final limitX = pow(2, widthX); + final limitY = pow(2, widthY); + for (var i = 0; i < limitX; i++) { + for (var j = 0; j < limitY; j++) { + final X = BigInt.from(i).toSigned(widthX); + final Y = BigInt.from(j).toSigned(widthY); + final product = X * Y; + + pp.multiplicand.put(X); + pp.multiplier.put(Y); + // stdout.write('$i($X) * $j($Y): should be $product\n'); + if (pp.evaluate() != product) { + stdout + ..write('Fail: $i($X) * $j($Y): ${pp.evaluate()} ' + 'vs expected $product\n') + ..write(pp); + } + expect(pp.evaluate(), equals(product)); + final evaluateValue = compressor.evaluate(); + if (evaluateValue != product) { + stdout + ..write('Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $evaluateValue ' + 'vs expected $product\n') + ..write(pp); + } + compressor.compress(); + final compressedValue = compressor.evaluate(); + if (compressedValue != product) { + stdout + ..write('Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $compressedValue ' + 'vs expected $product\n') + ..write(pp); + } + expect(compressedValue, equals(product)); + final compressedLogicValue = compressor.evaluate(logic: true); + if (compressedLogicValue != product) { + stdout + ..write( + 'Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $compressedLogicValue ' + 'vs expected $product\n') + ..write(pp); + } + expect(compressedLogicValue, equals(product)); + + final a = compressor.extractRow(0); + final b = compressor.extractRow(1); + final adder = ParallelPrefixAdder(a, b, KoggeStone.new); + final adderValue = + adder.out.value.toBigInt().toSigned(compressor.columns.length); + if (adderValue != product) { + stdout + ..write('Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $adderValue ' + 'vs expected $product\n') + ..write(pp); + } + expect(adderValue, equals(product)); + } + } +} + +void main() { + test('exhaustive compression evaluate: square radix-4, all SignExtension', + () async { + stdout.write('\n'); + + for (var radix = 4; radix < 8; radix *= 2) { + final encoder = RadixEncoder(2); + // stdout.write('encoding with radix=$radix\n'); + final shift = log2Ceil(encoder.radix); + for (var width = shift + 1; width < shift + 2; width++) { + // stdout.write('\tTesting width=$width\n'); + for (final signExtension in SignExtension.values) { + final pp = PartialProductGenerator(Logic(name: 'X', width: width), + Logic(name: 'Y', width: width), encoder); + switch (signExtension) { + case SignExtension.brute: + pp.bruteForceSignExtend(); + case SignExtension.stop: + // pp.signExtendWithStopBitsRect(); + case SignExtension.compact: + pp.signExtendCompact(); + } + // stdout.write('\tTesting extension=$signExtension\n'); + testCompressionExhaustive(pp); + } + } + } + }); +} diff --git a/test/arithmetic/multiplier_test.dart b/test/arithmetic/multiplier_test.dart new file mode 100644 index 00000000..32c3239f --- /dev/null +++ b/test/arithmetic/multiplier_test.dart @@ -0,0 +1,189 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// compressor.dart +// Column compression of partial prodcuts +// +// 2024 June 15 +// Author: Desmond Kirkpatrick + +import 'dart:math'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:test/test.dart'; + +// Inner test of a multipy accumulate unit +void checkMultiplyAccumulate( + MultiplyAccumulate mod, BigInt bA, BigInt bB, BigInt bC) { + final golden = bA * bB + bC; + // ignore: invalid_use_of_protected_member + mod.a.put(bA); + // ignore: invalid_use_of_protected_member + mod.b.put(bB); + // ignore: invalid_use_of_protected_member + mod.c.put(bC); + // print('$bA, $bB, $bC'); + + final result = mod.signed + ? mod.accumulate.value.toBigInt().toSigned(mod.accumulate.width) + : mod.accumulate.value.toBigInt().toUnsigned(mod.accumulate.width); + expect(result, equals(golden)); +} + +// Random testing of a mutiplier or multiplier/accumulate unit +void testMultiplyAccumulateRandom(int width, int iterations, + MultiplyAccumulate Function(Logic a, Logic b, Logic c) fn) { + final a = Logic(name: 'a', width: width); + final b = Logic(name: 'b', width: width); + final c = Logic(name: 'c', width: width * 2); + final mod = fn(a, b, c); + test('random_${mod.name}_S${mod.signed}_W${width}_I$iterations', () async { + final multiplyOnly = mod is MultiplyOnly; + await mod.build(); + final signed = mod.signed; + for (var i = 0; i < iterations; i++) { + final bA = signed + ? Random().nextLogicValue(width: width).toBigInt().toSigned(width) + : Random().nextLogicValue(width: width).toBigInt().toUnsigned(width); + final bB = signed + ? Random().nextLogicValue(width: width).toBigInt().toSigned(width) + : Random().nextLogicValue(width: width).toBigInt().toUnsigned(width); + final bC = multiplyOnly + ? BigInt.zero + : signed + ? Random().nextLogicValue(width: width).toBigInt().toSigned(width) + : Random() + .nextLogicValue(width: width) + .toBigInt() + .toUnsigned(width); + checkMultiplyAccumulate(mod, bA, bB, bC); + } + }); +} + +// Exhaustive testing of a mutiplier or multiplier/accumulate unit +void testMultiplyAccumulateExhaustive( + int width, MultiplyAccumulate Function(Logic a, Logic b, Logic c) fn) { + final a = Logic(name: 'a', width: width); + final b = Logic(name: 'b', width: width); + final c = Logic(name: 'c', width: 2 * width); + final mod = fn(a, b, c); + test('exhaustive_${mod.name}_S${mod.signed}_W$width', () async { + await mod.build(); + final signed = mod.signed; + final multiplyOnly = mod is MultiplyOnly; + + final cLimit = multiplyOnly ? 1 : (1 << (2 * width)); + + for (var aa = 0; aa < (1 << width); ++aa) { + for (var bb = 0; bb < (1 << width); ++bb) { + for (var cc = 0; cc < cLimit; ++cc) { + final bA = signed + ? BigInt.from(aa).toSigned(width) + : BigInt.from(aa).toUnsigned(width); + final bB = signed + ? BigInt.from(bb).toSigned(width) + : BigInt.from(bb).toUnsigned(width); + final bC = multiplyOnly + ? BigInt.zero + : signed + ? BigInt.from(cc).toSigned(2 * width) + : BigInt.from(cc).toUnsigned(2 * width); + + checkMultiplyAccumulate(mod, bA, bB, bC); + } + } + } + }); +} + +void main() { + tearDown(() async { + await Simulator.reset(); + }); + + // Use MAC tester for Multiply + + // First curry the Multiplier + Multiplier currySignedMultiplier(Logic a, Logic b) => + CompressionTreeMultiplier(a, b, 4, KoggeStone.new, signed: true); + + Multiplier curryUnsignedMultiplier(Logic a, Logic b) => + CompressionTreeMultiplier(a, b, 4, KoggeStone.new, signed: true); + + // Now treat the multiplier as a MAC with a zero input addend [c] + MultiplyAccumulate currySignedMultiplierAsMAC(Logic a, Logic b, Logic c) => + MultiplyOnly(a, b, c, currySignedMultiplier); + + MultiplyAccumulate curryUnsignedMultiplierAsMAC(Logic a, Logic b, Logic c) => + MultiplyOnly(a, b, c, curryUnsignedMultiplier); + + group('test Compression Tree Multiplier Randomly', () { + for (final width in [4, 5, 6, 11]) { + testMultiplyAccumulateRandom(width, 30, currySignedMultiplierAsMAC); + testMultiplyAccumulateRandom(width, 30, curryUnsignedMultiplierAsMAC); + } + }); + group('test Compression Tree Multiplier Exhaustive', () { + for (final width in [4, 5]) { + testMultiplyAccumulateExhaustive(width, currySignedMultiplierAsMAC); + testMultiplyAccumulateExhaustive(width, curryUnsignedMultiplierAsMAC); + } + }); + + MultiplyAccumulate currySignedCompressionTreeMultiplyAccumulate( + Logic a, Logic b, Logic c) => + CompressionTreeMultiplyAccumulate(a, b, c, 4, KoggeStone.new, + signed: true); + MultiplyAccumulate curryUnsignedCompressionTreeMultiplyAccumulate( + Logic a, Logic b, Logic c) => + CompressionTreeMultiplyAccumulate(a, b, c, 4, KoggeStone.new); + + group('test Multiply Accumulate Random', () { + for (final width in [4, 5, 6, 11]) { + testMultiplyAccumulateRandom( + width, 30, currySignedCompressionTreeMultiplyAccumulate); + testMultiplyAccumulateRandom( + width, 30, curryUnsignedCompressionTreeMultiplyAccumulate); + } + }); + group('test Multiply Accumulate Exhaustive', () { + for (final width in [3, 4]) { + testMultiplyAccumulateExhaustive( + width, currySignedCompressionTreeMultiplyAccumulate); + testMultiplyAccumulateExhaustive( + width, curryUnsignedCompressionTreeMultiplyAccumulate); + } + }); + + test('single mac', () async { + const width = 6; + final a = Logic(name: 'a', width: width); + final b = Logic(name: 'b', width: width); + final c = Logic(name: 'c', width: 2 * width); + + const av = 0; + const bv = 0; + const cv = -512; + for (final signed in [true, false]) { + final bA = signed + ? BigInt.from(av).toSigned(width) + : BigInt.from(av).toUnsigned(width); + final bB = signed + ? BigInt.from(bv).toSigned(width) + : BigInt.from(bv).toUnsigned(width); + final bC = signed + ? BigInt.from(cv).toSigned(2 * width) + : BigInt.from(cv).toUnsigned(width * 2); + + // Set these so that printing inside module build will have Logic values + a.put(bA); + b.put(bB); + c.put(bC); + + final mod = CompressionTreeMultiplyAccumulate(a, b, c, 4, KoggeStone.new, + signed: signed); + checkMultiplyAccumulate(mod, bA, bB, bC); + } + }); +} diff --git a/test/parallel_prefix_operations_test.dart b/test/arithmetic/parallel_prefix_operations_test.dart similarity index 69% rename from test/parallel_prefix_operations_test.dart rename to test/arithmetic/parallel_prefix_operations_test.dart index 09da5835..534393b0 100644 --- a/test/parallel_prefix_operations_test.dart +++ b/test/arithmetic/parallel_prefix_operations_test.dart @@ -7,9 +7,10 @@ // 2023 Sep 29 // Author: Desmond Kirkpatrick -import 'dart:math'; +// ignore_for_file: avoid_print + import 'package:rohd/rohd.dart'; -import 'package:rohd_hcl/src/parallel_prefix_operations.dart'; +import 'package:rohd_hcl/src/arithmetic/parallel_prefix_operations.dart'; import 'package:test/test.dart'; void testOrScan(int n, ParallelPrefixOrScan Function(Logic a) fn) { @@ -42,14 +43,14 @@ void testOrScan(int n, ParallelPrefixOrScan Function(Logic a) fn) { }); } -void testPriorityEncoder( - int n, ParallelPrefixPriorityEncoder Function(Logic a) fn) { +void testPriorityFinder( + int n, ParallelPrefixPriorityFinder Function(Logic a) fn) { test('priority_encoder_$n', () async { final inp = Logic(name: 'inp', width: n); final mod = fn(inp); await mod.build(); - int computePriorityEncoding(int j) { + int computePriorityLocation(int j) { for (var i = 0; i < n; ++i) { if (((1 << i) & j) != 0) { return 1 << i; @@ -61,60 +62,38 @@ void testPriorityEncoder( // put/expect testing for (var j = 0; j < (1 << n); ++j) { - final golden = computePriorityEncoding(j); + final golden = computePriorityLocation(j); inp.put(j); final result = mod.out.value.toInt(); - // print("priority_encoder: $j ${result} ${golden}"); + // print('priority_encoder: $j $result $golden'); expect(result, equals(golden)); } }); } -void testAdder(int n, ParallelPrefixAdder Function(Logic a, Logic b) fn) { - test('adder_$n', () async { - final a = Logic(name: 'a', width: n); - final b = Logic(name: 'b', width: n); - - final mod = fn(a, b); +void testPriorityEncoder( + int n, ParallelPrefixPriorityEncoder Function(Logic a) fn) { + test('priority_encoder_$n', () async { + final inp = Logic(name: 'inp', width: n); + final mod = fn(inp); await mod.build(); - int computeAdder(int aa, int bb) => (aa + bb) & ((1 << n) - 1); - - // put/expect testing - - for (var aa = 0; aa < (1 << n); ++aa) { - for (var bb = 0; bb < (1 << n); ++bb) { - final golden = computeAdder(aa, bb); - a.put(aa); - b.put(bb); - final result = mod.out.value.toInt(); - //print("adder: $aa $bb $result $golden"); - expect(result, equals(golden)); + int computePriorityEncoding(int j) { + for (var i = 0; i < n; ++i) { + if (((1 << i) & j) != 0) { + return i; + } } + return 0; } - }); -} - -void testAdderRandom( - int n, int nSamples, ParallelPrefixAdder Function(Logic a, Logic b) fn) { - test('adder_$n', () async { - final a = Logic(name: 'a', width: n); - final b = Logic(name: 'b', width: n); - - final mod = fn(a, b); - await mod.build(); - LogicValue computeAdder(LogicValue aa, LogicValue bb) => - (aa + bb) & LogicValue.ofBigInt(BigInt.from((1 << n) - 1), n); // put/expect testing - for (var i = 0; i < nSamples; ++i) { - final aa = Random().nextLogicValue(width: n); - final bb = Random().nextLogicValue(width: n); - final golden = computeAdder(aa, bb); - a.put(aa); - b.put(bb); - final result = mod.out.value; + for (var j = 0; j < (1 << n); ++j) { + final golden = computePriorityEncoding(j); + inp.put(j); + final result = mod.out.value.toInt(); + // print('priority_encoder: $j $result $golden'); expect(result, equals(golden)); } }); @@ -183,27 +162,20 @@ void main() { } }); - group('priority_encoder', () { + group('priority_finder', () { for (final n in [7, 8, 9]) { for (final ppGen in generators) { - testPriorityEncoder( - n, (inp) => ParallelPrefixPriorityEncoder(inp, ppGen)); + testPriorityFinder( + n, (inp) => ParallelPrefixPriorityFinder(inp, ppGen)); } } }); - group('adder', () { - for (final n in [3, 4, 5]) { - for (final ppGen in generators) { - testAdder(n, (a, b) => ParallelPrefixAdder(a, b, ppGen)); - } - } - }); - - group('adderRandom', () { - for (final n in [127, 128, 129]) { + group('priority_encoder', () { + for (final n in [7, 8, 9]) { for (final ppGen in generators) { - testAdderRandom(n, 10, (a, b) => ParallelPrefixAdder(a, b, ppGen)); + testPriorityEncoder( + n, (inp) => ParallelPrefixPriorityEncoder(inp, ppGen)); } } }); @@ -223,4 +195,6 @@ void main() { } } }); + + // Note: all ParallelPrefixAdders are tested in adder_test.dart } diff --git a/test/ripple_carry_adder_test.dart b/test/arithmetic/ripple_carry_adder_test.dart similarity index 100% rename from test/ripple_carry_adder_test.dart rename to test/arithmetic/ripple_carry_adder_test.dart From e34ec224198fa431f5e2f6a48d12cded8aa7f352 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Wed, 7 Aug 2024 10:12:12 -0700 Subject: [PATCH 02/38] multiplier documentation and generator --- doc/components/multiplier.md | 66 +++++++++++++++++-- doc/components/parallel_prefix_operations.md | 2 +- lib/src/arithmetic/multiplier.dart | 8 +-- .../components/component_registry.dart | 1 + .../components/components.dart | 1 + .../config_compression_tree_multiplier.dart | 58 ++++++++++++++++ test/arithmetic/multiplier_test.dart | 32 +++++++++ 7 files changed, 154 insertions(+), 14 deletions(-) create mode 100644 lib/src/component_config/components/config_compression_tree_multiplier.dart diff --git a/doc/components/multiplier.md b/doc/components/multiplier.md index 91eccfa8..10a51e60 100644 --- a/doc/components/multiplier.md +++ b/doc/components/multiplier.md @@ -1,21 +1,42 @@ # Multiplier -ROHD HCL provides a multiplier module to get the product from a list of logic. As of now, we have +ROHD HCL provides an abstract [Multiplier] module which multiplies two +numbers represented as two [Logic]s, potentially of different widths, +treating them as either signed (2s complement) or unsigned. It +produces the product as a [Logic] with width equal to the sum of the +widths of the inputs. As of now, we have the following implementations +of this abstract [Module]: - [Carry Save Multiplier](#carry-save-multiplier) +- [Compression Tree Multiplier](#compression-tree-multiplier) + +An additional kind of abstract module provided is a +[MultiplyAccumulate] module which multiplies two numbers represented +as two [Logic]s and adds the result to a third [Logic] with width +equal to the sum of the widths of the main inputs. We have a +high-performance implementation: + +- [Compression Tree Multipy Accumulate](#compression-tree-multiply-accumulate) ## Carry Save Multiplier -Carry save multiplier is a digital circuit used for performing multiplication operations. It is particularly useful in applications that require high speed multiplication, such as digital signal processing. +Carry save multiplier is a digital circuit used for performing multiplication operations. It +is particularly useful in applications that require high speed +multiplication, such as digital signal processing. -The [`CarrySaveMultiplier`](https://intel.github.io/rohd-hcl/rohd_hcl/CarrySaveMultiplier-class.html) module in ROHD-HCL accept input parameters the clock `clk` signal, reset `reset` signal, `Logic`s' a and b as the input pin and the name of the module `name`. Note that the width of the inputs must be the same or `RohdHclException` will be thrown. +The +[`CarrySaveMultiplier`](https://intel.github.io/rohd-hcl/rohd_hcl/CarrySaveMultiplier-class.html) +module in ROHD-HCL accept input parameters the clock `clk` signal, +reset `reset` signal, `Logic`s' a and b as the input pin and the name +of the module `name`. Note that the width of the inputs must be the +same or `RohdHclException` will be thrown. An example is shown below to multiply two inputs of signals that have 4-bits of width. ```dart -const widthLength = 4; -final a = Logic(name: 'a', width: widthLength); -final b = Logic(name: 'b', width: widthLength); +const bitWidth = 4; +final a = Logic(name: 'a', width: bitWidth); +final b = Logic(name: 'b', width: bitWidth); final reset = Logic(name: 'reset'); final clk = SimpleClockGenerator(10).clk; @@ -46,3 +67,36 @@ await waitCycles(csm.latency).then( Simulator.endSimulation(); ``` + +## Compression Tree Multiplier + +A compression tree multiplier is a digital circuit used for performing +multiplication operations, using Booth encoding to produce addends, a +compression tree for reducing addends to a final pair, and a final +adder generated from a parallel prefix tree option. It is particularly +useful in applications that require high speed multiplication, such as +digital signal processing. + +The parameters of the +[CompressionTreeMultiplier] are: + +- Two input terms a and b +- The radix used for Booth encoding (2, 4, 8, and 16 are currently supported) +- The type of [ParallelPrefix] tree used in the final [ParallelPrefixAdder] +- Whether the operands should be treated as signed (2s complement) or unsigned + +## Compression Tree Multiply Accumulate + +A compression tree multiply accumulate is similar to a compress tree +multiplier, but it inserts an additional addend into the compression +tree to allow for accumulation into this third input. + +The parameters of the +[CompressionTreeMultiplier] are: + +- Two input terms a and b +- The accumulate input term c +- The radix used for Booth encoding (2, 4, 8, and 16 are currently supported) +- The type of [ParallelPrefix] tree used in the final [ParallelPrefixAdder] +- Whether the operands should be treated as signed (2s complement) or unsigned + diff --git a/doc/components/parallel_prefix_operations.md b/doc/components/parallel_prefix_operations.md index 2c01f72a..d80492dc 100644 --- a/doc/components/parallel_prefix_operations.md +++ b/doc/components/parallel_prefix_operations.md @@ -4,7 +4,7 @@ Parallel prefix or 'scan' trees are useful for efficient implementation of computations which involve associative operators. They are used in computations like encoding, or-reduction, and addition. By leveraging advanced programming idioms, like -functors, allowing for passing of function that generates prefix trees +functors, allowing for passing of a function that generates prefix trees into an scan-based generator for that computation, we can have a wide variety of that computation supported by our component library. For example, we have tree patterns defined by ripple, Sklanksy, diff --git a/lib/src/arithmetic/multiplier.dart b/lib/src/arithmetic/multiplier.dart index efe8a471..61d9755b 100644 --- a/lib/src/arithmetic/multiplier.dart +++ b/lib/src/arithmetic/multiplier.dart @@ -36,11 +36,8 @@ abstract class Multiplier extends Module { /// Take input [a] and input [b] and return the /// [product] of the multiplication result. Multiplier(Logic a, Logic b, {bool signed = false, super.name}) { - if (a.width != b.width) { - throw RohdHclException('inputs of a and b should have same width.'); - } _signed = signed; - this.a = addInput('a', a, width: a.width); + this.a = addInput('a', a, width: a.width); this.b = addInput('b', b, width: b.width); } } @@ -72,9 +69,6 @@ abstract class MultiplyAccumulate extends Module { /// product, add input [c] to produce the [accumulate] result. MultiplyAccumulate(Logic a, Logic b, Logic c, {bool signed = false, super.name}) { - if (a.width != b.width) { - throw RohdHclException('inputs of a and b should have same width.'); - } _signed = signed; this.a = addInput('a', a, width: a.width); this.b = addInput('b', b, width: b.width); diff --git a/lib/src/component_config/components/component_registry.dart b/lib/src/component_config/components/component_registry.dart index f25d00b0..6d3665e1 100644 --- a/lib/src/component_config/components/component_registry.dart +++ b/lib/src/component_config/components/component_registry.dart @@ -24,4 +24,5 @@ List get componentRegistry => [ EdgeDetectorConfigurator(), FindConfigurator(), ParallelPrefixAdderConfigurator(), + CompressionTreeMultiplierConfigurator(), ]; diff --git a/lib/src/component_config/components/components.dart b/lib/src/component_config/components/components.dart index 899a8ec0..32c33fda 100644 --- a/lib/src/component_config/components/components.dart +++ b/lib/src/component_config/components/components.dart @@ -2,6 +2,7 @@ // SPDX-License-Identifier: BSD-3-Clause export 'config_carry_save_multiplier.dart'; +export 'config_compression_tree_multiplier.dart'; export 'config_ecc.dart'; export 'config_edge_detector.dart'; export 'config_fifo.dart'; diff --git a/lib/src/component_config/components/config_compression_tree_multiplier.dart b/lib/src/component_config/components/config_compression_tree_multiplier.dart new file mode 100644 index 00000000..e7d43058 --- /dev/null +++ b/lib/src/component_config/components/config_compression_tree_multiplier.dart @@ -0,0 +1,58 @@ +// Copyright (C) 2023-24 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// _adder.dart +// Configurator for a Parallel Prefix Adder. +// +// 2024 August 7 +// Author: Desmond Kirkpatrick + +import 'dart:collection'; + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +/// A [Configurator] for [CompressionTreeMultiplier]s. +class CompressionTreeMultiplierConfigurator extends Configurator { + /// Map from Type to Function for Parallel Prefix generator + static Map, Logic Function(Logic, Logic))> + generatorMap = { + Ripple: Ripple.new, + Sklansky: Sklansky.new, + KoggeStone: KoggeStone.new, + BrentKung: BrentKung.new + }; + + /// Controls the type of [ParallelPrefix] tree used in the adder. + final prefixTreeKnob = + ChoiceConfigKnob(generatorMap.keys.toList(), value: KoggeStone); + + /// Controls the Booth encoding radix of the multiplier.! + final radixKnob = ChoiceConfigKnob( + [2, 4, 8, 16], + value: 4, + ); + + /// Controls the width of the multiplicand.! + final IntConfigKnob multiplicandWidthKnob = IntConfigKnob(value: 5); + /// Controls the width of the multiplier.! + final IntConfigKnob multiplierWidthKnob = IntConfigKnob(value: 5); + + @override + Module createModule() => CompressionTreeMultiplier( + Logic(name: 'a', width: multiplicandWidthKnob.value), + Logic(name: 'b', width: multiplierWidthKnob.value), + radixKnob.value, + generatorMap[prefixTreeKnob.value]!); + + @override + late final Map> knobs = UnmodifiableMapView({ + 'Tree type': prefixTreeKnob, + 'Radix': radixKnob, + 'Multiplicand width': multiplicandWidthKnob, + 'Multiplier width': multiplierWidthKnob, + }); + + @override + final String name = 'Comp. Tree Multiplier'; +} diff --git a/test/arithmetic/multiplier_test.dart b/test/arithmetic/multiplier_test.dart index 32c3239f..c62ffe7c 100644 --- a/test/arithmetic/multiplier_test.dart +++ b/test/arithmetic/multiplier_test.dart @@ -186,4 +186,36 @@ void main() { checkMultiplyAccumulate(mod, bA, bB, bC); } }); + + test('single rectangular mac', () async { + const widthA = 6; + const widthB = 9; + final a = Logic(name: 'a', width: widthA); + final b = Logic(name: 'b', width: widthB); + final c = Logic(name: 'c', width: widthA + widthB); + + const av = 0; + const bv = 0; + const cv = -512; + for (final signed in [true, false]) { + final bA = signed + ? BigInt.from(av).toSigned(widthA) + : BigInt.from(av).toUnsigned(widthA); + final bB = signed + ? BigInt.from(bv).toSigned(widthB) + : BigInt.from(bv).toUnsigned(widthB); + final bC = signed + ? BigInt.from(cv).toSigned(widthA + widthB) + : BigInt.from(cv).toUnsigned(widthA + widthB); + + // Set these so that printing inside module build will have Logic values + a.put(bA); + b.put(bB); + c.put(bC); + + final mod = CompressionTreeMultiplyAccumulate(a, b, c, 4, KoggeStone.new, + signed: signed); + checkMultiplyAccumulate(mod, bA, bB, bC); + } + }); } From bb0326f975d03744760e36d8c23a5b3b60f70a79 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Wed, 7 Aug 2024 10:30:00 -0700 Subject: [PATCH 03/38] spacing issue in doc --- doc/components/multiplier.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/components/multiplier.md b/doc/components/multiplier.md index 10a51e60..2767d276 100644 --- a/doc/components/multiplier.md +++ b/doc/components/multiplier.md @@ -75,7 +75,7 @@ multiplication operations, using Booth encoding to produce addends, a compression tree for reducing addends to a final pair, and a final adder generated from a parallel prefix tree option. It is particularly useful in applications that require high speed multiplication, such as -digital signal processing. +digital signal processing. The parameters of the [CompressionTreeMultiplier] are: @@ -99,4 +99,3 @@ The parameters of the - The radix used for Booth encoding (2, 4, 8, and 16 are currently supported) - The type of [ParallelPrefix] tree used in the final [ParallelPrefixAdder] - Whether the operands should be treated as signed (2s complement) or unsigned - From e8fd111044adbc54ed7989ab152ffcd84bf2cc0e Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Wed, 7 Aug 2024 10:34:52 -0700 Subject: [PATCH 04/38] dart format --- .../components/config_compression_tree_multiplier.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/src/component_config/components/config_compression_tree_multiplier.dart b/lib/src/component_config/components/config_compression_tree_multiplier.dart index e7d43058..b860400f 100644 --- a/lib/src/component_config/components/config_compression_tree_multiplier.dart +++ b/lib/src/component_config/components/config_compression_tree_multiplier.dart @@ -11,6 +11,7 @@ import 'dart:collection'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; + /// A [Configurator] for [CompressionTreeMultiplier]s. class CompressionTreeMultiplierConfigurator extends Configurator { /// Map from Type to Function for Parallel Prefix generator @@ -29,12 +30,13 @@ class CompressionTreeMultiplierConfigurator extends Configurator { /// Controls the Booth encoding radix of the multiplier.! final radixKnob = ChoiceConfigKnob( - [2, 4, 8, 16], - value: 4, + [2, 4, 8, 16], + value: 4, ); - + /// Controls the width of the multiplicand.! final IntConfigKnob multiplicandWidthKnob = IntConfigKnob(value: 5); + /// Controls the width of the multiplier.! final IntConfigKnob multiplierWidthKnob = IntConfigKnob(value: 5); From 164a21521ca6ce97fcd75e7b9ed6191d291c5119 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Wed, 7 Aug 2024 10:44:42 -0700 Subject: [PATCH 05/38] dart format --- lib/src/arithmetic/multiplier.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/arithmetic/multiplier.dart b/lib/src/arithmetic/multiplier.dart index 61d9755b..7e7a15bf 100644 --- a/lib/src/arithmetic/multiplier.dart +++ b/lib/src/arithmetic/multiplier.dart @@ -37,7 +37,7 @@ abstract class Multiplier extends Module { /// [product] of the multiplication result. Multiplier(Logic a, Logic b, {bool signed = false, super.name}) { _signed = signed; - this.a = addInput('a', a, width: a.width); + this.a = addInput('a', a, width: a.width); this.b = addInput('b', b, width: b.width); } } From 31c2a8e3fdcbb2965ed3a107df913f174af197cc Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Wed, 7 Aug 2024 10:51:58 -0700 Subject: [PATCH 06/38] carry_save_multiplier cannot HANDLE different widths --- lib/src/arithmetic/carry_save_mutiplier.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/src/arithmetic/carry_save_mutiplier.dart b/lib/src/arithmetic/carry_save_mutiplier.dart index 36f29123..1b1a7c85 100644 --- a/lib/src/arithmetic/carry_save_mutiplier.dart +++ b/lib/src/arithmetic/carry_save_mutiplier.dart @@ -43,6 +43,9 @@ class CarrySaveMultiplier extends Multiplier { {required Logic clk, required Logic reset, super.name = 'carry_save_multiplier'}) { + if (a.width != b.width) { + throw RohdHclException('inputs of a and b should have same width.'); + } clk = addInput('clk', clk); reset = addInput('reset', reset); From 0b4dafc7d5fdaad0dec643e6a4adf165431c27c5 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Wed, 7 Aug 2024 14:47:38 -0700 Subject: [PATCH 07/38] proper headers --- lib/src/arithmetic/sign_magnitude_adder.dart | 4 ++-- test/arithmetic/compressor_test.dart | 2 +- test/arithmetic/multiplier_test.dart | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/src/arithmetic/sign_magnitude_adder.dart b/lib/src/arithmetic/sign_magnitude_adder.dart index d9b7845c..c26f983e 100644 --- a/lib/src/arithmetic/sign_magnitude_adder.dart +++ b/lib/src/arithmetic/sign_magnitude_adder.dart @@ -1,10 +1,10 @@ // Copyright (C) 2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // -//sign_magnitude_adder +// sign_magnitude_adder.dart // Implementation of a One's Complement Adder // -// 2024 April 12 +// 2024 August 8 // Author: Desmond Kirkpatrick import 'package:meta/meta.dart'; diff --git a/test/arithmetic/compressor_test.dart b/test/arithmetic/compressor_test.dart index da4dfc1c..d2703b29 100644 --- a/test/arithmetic/compressor_test.dart +++ b/test/arithmetic/compressor_test.dart @@ -1,7 +1,7 @@ // Copxorright (C) 2023 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // -// booth_test.dart +// compressor_test.dart // Tests for the select interface of Booth encoding // // 2024 June 04 diff --git a/test/arithmetic/multiplier_test.dart b/test/arithmetic/multiplier_test.dart index c62ffe7c..3d510757 100644 --- a/test/arithmetic/multiplier_test.dart +++ b/test/arithmetic/multiplier_test.dart @@ -1,10 +1,10 @@ // Copyright (C) 2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // -// compressor.dart -// Column compression of partial prodcuts +// multiplier_test.dart +// Test Multiplier and MultiplerAccumulate: CompressionTree implementations // -// 2024 June 15 +// 2024 August 7 // Author: Desmond Kirkpatrick import 'dart:math'; From 6f4ae416cd80b89b34fac8e8faa259c242987d67 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Wed, 7 Aug 2024 15:02:19 -0700 Subject: [PATCH 08/38] fixed header comments --- .../components/config_compression_tree_multiplier.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/component_config/components/config_compression_tree_multiplier.dart b/lib/src/component_config/components/config_compression_tree_multiplier.dart index b860400f..ee48bfdc 100644 --- a/lib/src/component_config/components/config_compression_tree_multiplier.dart +++ b/lib/src/component_config/components/config_compression_tree_multiplier.dart @@ -1,8 +1,8 @@ // Copyright (C) 2023-24 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // -// _adder.dart -// Configurator for a Parallel Prefix Adder. +// config_compression_tree_multiplier.dart +// Configurator for a Compression Tree Multiplier. // // 2024 August 7 // Author: Desmond Kirkpatrick From 976811372084a42410651f8f237adfbcb80decd5 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Thu, 8 Aug 2024 21:03:59 -0700 Subject: [PATCH 09/38] copyright updates --- README.md | 2 +- confapp/README.md | 2 +- confapp/lib/app.dart | 2 +- confapp/lib/hcl/cubit/component_cubit.dart | 2 +- confapp/lib/hcl/cubit/system_verilog_cubit.dart | 2 +- confapp/lib/hcl/hcl.dart | 2 +- confapp/lib/hcl/view/hcl_page.dart | 2 +- confapp/lib/hcl/view/hcl_view.dart | 2 +- confapp/lib/hcl/view/screen/content_widget.dart | 2 +- confapp/lib/hcl/view/screen/sidebar_widget.dart | 2 +- confapp/lib/hcl_bloc_observer.dart | 2 +- confapp/lib/main.dart | 2 +- confapp/test/example_component.dart | 2 +- confapp/test/hcl/view/hcl_page_test.dart | 2 +- example/example.dart | 2 +- gen/generate.dart | 2 +- lib/src/arbiters/arbiter.dart | 2 +- lib/src/arbiters/arbiters.dart | 2 +- lib/src/arbiters/mask_round_robin_arbiter.dart | 2 +- lib/src/arbiters/priority_arbiter.dart | 2 +- lib/src/arbiters/rotate_round_robin_arbiter.dart | 2 +- lib/src/arbiters/round_robin_arbiter.dart | 2 +- lib/src/arbiters/stateful_arbiter.dart | 2 +- lib/src/arithmetic/adder.dart | 2 +- lib/src/arithmetic/carry_save_mutiplier.dart | 2 +- lib/src/arithmetic/multiplier.dart | 2 +- lib/src/arithmetic/parallel_prefix_operations.dart | 2 +- lib/src/arithmetic/ripple_carry_adder.dart | 2 +- lib/src/binary_gray.dart | 2 +- lib/src/component_config/component_config.dart | 2 +- .../components/config_carry_save_multiplier.dart | 2 +- .../components/config_compression_tree_multiplier.dart | 2 +- lib/src/component_config/components/config_fifo.dart | 2 +- lib/src/component_config/components/config_one_hot.dart | 2 +- .../components/config_parallel_prefix_adder.dart | 2 +- .../components/config_priority_arbiter.dart | 2 +- lib/src/component_config/components/config_rf.dart | 2 +- .../components/config_ripple_carry_adder.dart | 2 +- lib/src/component_config/components/config_rotate.dart | 2 +- .../components/config_round_robin_arbiter.dart | 2 +- lib/src/component_config/components/config_sort.dart | 2 +- .../config_knobs/choice_config_knob.dart | 2 +- lib/src/component_config/config_knobs/config_knob.dart | 2 +- lib/src/component_config/config_knobs/config_knobs.dart | 2 +- .../config_knobs/group_of_knobs_knob.dart | 2 +- .../component_config/config_knobs/int_config_knob.dart | 2 +- .../config_knobs/list_of_knobs_knob.dart | 2 +- .../config_knobs/string_config_knob.dart | 2 +- .../config_knobs/toggle_config_knob.dart | 2 +- lib/src/component_config/configurator.dart | 2 +- lib/src/encodings/binary_to_one_hot.dart | 2 +- lib/src/encodings/case_one_hot_to_binary.dart | 2 +- lib/src/encodings/encodings.dart | 2 +- lib/src/encodings/one_hot_to_binary.dart | 2 +- lib/src/encodings/tree_one_hot_to_binary.dart | 2 +- lib/src/exceptions.dart | 2 +- lib/src/fifo.dart | 2 +- lib/src/find.dart | 2 +- lib/src/interfaces/apb.dart | 2 +- lib/src/interfaces/interfaces.dart | 2 +- lib/src/memory/memories.dart | 2 +- lib/src/memory/register_file.dart | 2 +- lib/src/models/apb_bfm/abp_completer.dart | 2 +- lib/src/models/apb_bfm/apb_bfm.dart | 2 +- lib/src/models/apb_bfm/apb_compliance_checker.dart | 2 +- lib/src/models/apb_bfm/apb_monitor.dart | 2 +- lib/src/models/apb_bfm/apb_packet.dart | 2 +- lib/src/models/apb_bfm/apb_requester.dart | 2 +- lib/src/models/apb_bfm/apb_requester_driver.dart | 2 +- .../ready_valid_bfm/ready_valid_transmitter_driver.dart | 9 +++++++++ lib/src/models/sparse_memory_storage.dart | 2 +- lib/src/rotate.dart | 2 +- lib/src/shift_register.dart | 2 +- lib/src/sort.dart | 2 +- test/apb_bfm_test.dart | 2 +- test/apb_test.dart | 2 +- test/arithmetic/adder_test.dart | 2 +- test/arithmetic/booth_test.dart | 2 +- test/arithmetic/carry_save_multiplier_test.dart | 2 +- test/arithmetic/compressor_test.dart | 2 +- test/arithmetic/parallel_prefix_operations_test.dart | 2 +- test/arithmetic/ripple_carry_adder_test.dart | 2 +- test/binary_gray_test.dart | 2 +- test/bitonic_sort_test.dart | 2 +- test/count_test.dart | 2 +- test/find_test.dart | 2 +- test/one_hot_test.dart | 2 +- test/ready_valid_bfm_test.dart | 9 +++++++++ test/rotate_test.dart | 2 +- test/sparse_memory_storage_test.dart | 2 +- tool/converters/json_html.sh | 2 +- tool/converters/verilog_html.sh | 2 +- tool/converters/verilog_json.sh | 2 +- tool/generate_coverage.sh | 2 +- tool/gh_actions/analyze_flutter_source.sh | 2 +- tool/gh_actions/analyze_source.sh | 2 +- tool/gh_actions/create_htmls.sh | 3 +++ tool/gh_actions/hcl_site_generation_build.sh | 2 +- tool/gh_actions/install_d3_hwschematic.sh | 2 +- tool/gh_actions/install_dependencies.sh | 2 +- tool/gh_actions/install_dependencies_generator_site.sh | 2 +- tool/gh_actions/install_opencadsuite.sh | 2 +- tool/gh_actions/run_flutter_tests.sh | 2 +- tool/gh_actions/run_tests.sh | 2 +- tool/gh_actions/verify_formatting.sh | 2 +- tool/gh_codespaces/install_dart.sh | 2 +- tool/gh_codespaces/install_flutter.sh | 2 +- tool/gh_codespaces/run_setup.sh | 2 +- tool/run_checks.sh | 2 +- tool/synthesize.sh | 2 +- 110 files changed, 128 insertions(+), 107 deletions(-) diff --git a/README.md b/README.md index 5e16d499..de8710db 100644 --- a/README.md +++ b/README.md @@ -52,5 +52,5 @@ Some examples of component categories include: ---------------- -Copyright (C) 2023 Intel Corporation +Copyright (C) 2023-2024 Intel Corporation SPDX-License-Identifier: BSD-3-Clause diff --git a/confapp/README.md b/confapp/README.md index e9511b07..fad42662 100644 --- a/confapp/README.md +++ b/confapp/README.md @@ -11,5 +11,5 @@ flowchart TD; ---------------- -Copyright (C) 2023 Intel Corporation +Copyright (C) 2023-2024 Intel Corporation SPDX-License-Identifier: BSD-3-Clause diff --git a/confapp/lib/app.dart b/confapp/lib/app.dart index 08abcd63..a24fefc9 100644 --- a/confapp/lib/app.dart +++ b/confapp/lib/app.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // app.dart diff --git a/confapp/lib/hcl/cubit/component_cubit.dart b/confapp/lib/hcl/cubit/component_cubit.dart index 596f21c0..f92d53ea 100644 --- a/confapp/lib/hcl/cubit/component_cubit.dart +++ b/confapp/lib/hcl/cubit/component_cubit.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // component_cubit.dart diff --git a/confapp/lib/hcl/cubit/system_verilog_cubit.dart b/confapp/lib/hcl/cubit/system_verilog_cubit.dart index 509cd1fc..e40eb814 100644 --- a/confapp/lib/hcl/cubit/system_verilog_cubit.dart +++ b/confapp/lib/hcl/cubit/system_verilog_cubit.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // system_verilog_cubit.dart diff --git a/confapp/lib/hcl/hcl.dart b/confapp/lib/hcl/hcl.dart index fb907b30..0c258afc 100644 --- a/confapp/lib/hcl/hcl.dart +++ b/confapp/lib/hcl/hcl.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause export 'view/hcl_page.dart'; diff --git a/confapp/lib/hcl/view/hcl_page.dart b/confapp/lib/hcl/view/hcl_page.dart index 9b070de1..fb0d236b 100644 --- a/confapp/lib/hcl/view/hcl_page.dart +++ b/confapp/lib/hcl/view/hcl_page.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // hcl_page.dart diff --git a/confapp/lib/hcl/view/hcl_view.dart b/confapp/lib/hcl/view/hcl_view.dart index ec5073cb..9cbc76cd 100644 --- a/confapp/lib/hcl/view/hcl_view.dart +++ b/confapp/lib/hcl/view/hcl_view.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // hcl_view.dart diff --git a/confapp/lib/hcl/view/screen/content_widget.dart b/confapp/lib/hcl/view/screen/content_widget.dart index 69e6d2b9..01172670 100644 --- a/confapp/lib/hcl/view/screen/content_widget.dart +++ b/confapp/lib/hcl/view/screen/content_widget.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // content_widget.dart diff --git a/confapp/lib/hcl/view/screen/sidebar_widget.dart b/confapp/lib/hcl/view/screen/sidebar_widget.dart index 135ca160..f4969a47 100644 --- a/confapp/lib/hcl/view/screen/sidebar_widget.dart +++ b/confapp/lib/hcl/view/screen/sidebar_widget.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // sidebar_widget.dart diff --git a/confapp/lib/hcl_bloc_observer.dart b/confapp/lib/hcl_bloc_observer.dart index ed8b9a77..a8c0eec0 100644 --- a/confapp/lib/hcl_bloc_observer.dart +++ b/confapp/lib/hcl_bloc_observer.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // hcl_bloc_observer.dart diff --git a/confapp/lib/main.dart b/confapp/lib/main.dart index 1422d02d..5eee9444 100644 --- a/confapp/lib/main.dart +++ b/confapp/lib/main.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // main.dart diff --git a/confapp/test/example_component.dart b/confapp/test/example_component.dart index 70307558..930e9de1 100644 --- a/confapp/test/example_component.dart +++ b/confapp/test/example_component.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // example_component.dart diff --git a/confapp/test/hcl/view/hcl_page_test.dart b/confapp/test/hcl/view/hcl_page_test.dart index 4e06abf4..716a5b8c 100644 --- a/confapp/test/hcl/view/hcl_page_test.dart +++ b/confapp/test/hcl/view/hcl_page_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // hcl_page_test.dart diff --git a/example/example.dart b/example/example.dart index 0dec04bc..ad4eabe0 100644 --- a/example/example.dart +++ b/example/example.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // example.dart diff --git a/gen/generate.dart b/gen/generate.dart index 8adda02b..dc7bca2b 100644 --- a/gen/generate.dart +++ b/gen/generate.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // generate.dart diff --git a/lib/src/arbiters/arbiter.dart b/lib/src/arbiters/arbiter.dart index 1de80395..8bbe3ccc 100644 --- a/lib/src/arbiters/arbiter.dart +++ b/lib/src/arbiters/arbiter.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // arbiter.dart diff --git a/lib/src/arbiters/arbiters.dart b/lib/src/arbiters/arbiters.dart index 79b4eaeb..d020c6b1 100644 --- a/lib/src/arbiters/arbiters.dart +++ b/lib/src/arbiters/arbiters.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause export 'arbiter.dart'; diff --git a/lib/src/arbiters/mask_round_robin_arbiter.dart b/lib/src/arbiters/mask_round_robin_arbiter.dart index b9bf01b6..766f9082 100644 --- a/lib/src/arbiters/mask_round_robin_arbiter.dart +++ b/lib/src/arbiters/mask_round_robin_arbiter.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // mask_round_robin_arbiter.dart diff --git a/lib/src/arbiters/priority_arbiter.dart b/lib/src/arbiters/priority_arbiter.dart index f535e089..df112920 100644 --- a/lib/src/arbiters/priority_arbiter.dart +++ b/lib/src/arbiters/priority_arbiter.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // priority_arbiter.dart diff --git a/lib/src/arbiters/rotate_round_robin_arbiter.dart b/lib/src/arbiters/rotate_round_robin_arbiter.dart index 65d98811..80556b93 100644 --- a/lib/src/arbiters/rotate_round_robin_arbiter.dart +++ b/lib/src/arbiters/rotate_round_robin_arbiter.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // rotate_round_robin_arbiter.dart diff --git a/lib/src/arbiters/round_robin_arbiter.dart b/lib/src/arbiters/round_robin_arbiter.dart index 82c69068..cc1d33b4 100644 --- a/lib/src/arbiters/round_robin_arbiter.dart +++ b/lib/src/arbiters/round_robin_arbiter.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // round_robin_arbiter.dart diff --git a/lib/src/arbiters/stateful_arbiter.dart b/lib/src/arbiters/stateful_arbiter.dart index c8bd2236..be62a012 100644 --- a/lib/src/arbiters/stateful_arbiter.dart +++ b/lib/src/arbiters/stateful_arbiter.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // stateful_arbiter.dart diff --git a/lib/src/arithmetic/adder.dart b/lib/src/arithmetic/adder.dart index 13b27cd5..3f50146d 100644 --- a/lib/src/arithmetic/adder.dart +++ b/lib/src/arithmetic/adder.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // adder.dart diff --git a/lib/src/arithmetic/carry_save_mutiplier.dart b/lib/src/arithmetic/carry_save_mutiplier.dart index 1b1a7c85..2e97686b 100644 --- a/lib/src/arithmetic/carry_save_mutiplier.dart +++ b/lib/src/arithmetic/carry_save_mutiplier.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // carry_save_multiplier.dart diff --git a/lib/src/arithmetic/multiplier.dart b/lib/src/arithmetic/multiplier.dart index 7e7a15bf..a3f11564 100644 --- a/lib/src/arithmetic/multiplier.dart +++ b/lib/src/arithmetic/multiplier.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // multiplier.dart diff --git a/lib/src/arithmetic/parallel_prefix_operations.dart b/lib/src/arithmetic/parallel_prefix_operations.dart index 4081cd97..ac9cb2c5 100644 --- a/lib/src/arithmetic/parallel_prefix_operations.dart +++ b/lib/src/arithmetic/parallel_prefix_operations.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // parallel_prefix_operations.dart diff --git a/lib/src/arithmetic/ripple_carry_adder.dart b/lib/src/arithmetic/ripple_carry_adder.dart index 454480b0..324c718a 100644 --- a/lib/src/arithmetic/ripple_carry_adder.dart +++ b/lib/src/arithmetic/ripple_carry_adder.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // ripple_carry_adder.dart diff --git a/lib/src/binary_gray.dart b/lib/src/binary_gray.dart index 77fb6253..ef280c24 100644 --- a/lib/src/binary_gray.dart +++ b/lib/src/binary_gray.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // binary_gray.dart diff --git a/lib/src/component_config/component_config.dart b/lib/src/component_config/component_config.dart index 2d7d78e1..a655c067 100644 --- a/lib/src/component_config/component_config.dart +++ b/lib/src/component_config/component_config.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause export 'components/components.dart'; diff --git a/lib/src/component_config/components/config_carry_save_multiplier.dart b/lib/src/component_config/components/config_carry_save_multiplier.dart index 9ab4fd4b..9abbfe23 100644 --- a/lib/src/component_config/components/config_carry_save_multiplier.dart +++ b/lib/src/component_config/components/config_carry_save_multiplier.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_carry_save_multiplier.dart diff --git a/lib/src/component_config/components/config_compression_tree_multiplier.dart b/lib/src/component_config/components/config_compression_tree_multiplier.dart index ee48bfdc..a30f4187 100644 --- a/lib/src/component_config/components/config_compression_tree_multiplier.dart +++ b/lib/src/component_config/components/config_compression_tree_multiplier.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023-24 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_compression_tree_multiplier.dart diff --git a/lib/src/component_config/components/config_fifo.dart b/lib/src/component_config/components/config_fifo.dart index 2750129d..a304bec1 100644 --- a/lib/src/component_config/components/config_fifo.dart +++ b/lib/src/component_config/components/config_fifo.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_priority_arbiter.dart diff --git a/lib/src/component_config/components/config_one_hot.dart b/lib/src/component_config/components/config_one_hot.dart index 322189b3..53689009 100644 --- a/lib/src/component_config/components/config_one_hot.dart +++ b/lib/src/component_config/components/config_one_hot.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_rotate.dart diff --git a/lib/src/component_config/components/config_parallel_prefix_adder.dart b/lib/src/component_config/components/config_parallel_prefix_adder.dart index 19307207..b759e143 100644 --- a/lib/src/component_config/components/config_parallel_prefix_adder.dart +++ b/lib/src/component_config/components/config_parallel_prefix_adder.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023-24 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_parallel-prefix_adder.dart diff --git a/lib/src/component_config/components/config_priority_arbiter.dart b/lib/src/component_config/components/config_priority_arbiter.dart index b9a4e950..480c16df 100644 --- a/lib/src/component_config/components/config_priority_arbiter.dart +++ b/lib/src/component_config/components/config_priority_arbiter.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_priority_arbiter.dart diff --git a/lib/src/component_config/components/config_rf.dart b/lib/src/component_config/components/config_rf.dart index 2c18de2d..939a719a 100644 --- a/lib/src/component_config/components/config_rf.dart +++ b/lib/src/component_config/components/config_rf.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_rf.dart diff --git a/lib/src/component_config/components/config_ripple_carry_adder.dart b/lib/src/component_config/components/config_ripple_carry_adder.dart index 7e87516a..8256ad3f 100644 --- a/lib/src/component_config/components/config_ripple_carry_adder.dart +++ b/lib/src/component_config/components/config_ripple_carry_adder.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_rippler_carry_adder.dart diff --git a/lib/src/component_config/components/config_rotate.dart b/lib/src/component_config/components/config_rotate.dart index d26b47a7..f31c0815 100644 --- a/lib/src/component_config/components/config_rotate.dart +++ b/lib/src/component_config/components/config_rotate.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_rotate.dart diff --git a/lib/src/component_config/components/config_round_robin_arbiter.dart b/lib/src/component_config/components/config_round_robin_arbiter.dart index 8d2c443e..1d9d7bfe 100644 --- a/lib/src/component_config/components/config_round_robin_arbiter.dart +++ b/lib/src/component_config/components/config_round_robin_arbiter.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_round_robin_arbiter.dart diff --git a/lib/src/component_config/components/config_sort.dart b/lib/src/component_config/components/config_sort.dart index d1a97859..3f49d7b6 100644 --- a/lib/src/component_config/components/config_sort.dart +++ b/lib/src/component_config/components/config_sort.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_sort.dart diff --git a/lib/src/component_config/config_knobs/choice_config_knob.dart b/lib/src/component_config/config_knobs/choice_config_knob.dart index 2a97fea4..68416137 100644 --- a/lib/src/component_config/config_knobs/choice_config_knob.dart +++ b/lib/src/component_config/config_knobs/choice_config_knob.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // choice_config_knob.dart diff --git a/lib/src/component_config/config_knobs/config_knob.dart b/lib/src/component_config/config_knobs/config_knob.dart index a8b3bb4a..b76c13bb 100644 --- a/lib/src/component_config/config_knobs/config_knob.dart +++ b/lib/src/component_config/config_knobs/config_knob.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_knob.dart diff --git a/lib/src/component_config/config_knobs/config_knobs.dart b/lib/src/component_config/config_knobs/config_knobs.dart index de14e743..6f33c5ac 100644 --- a/lib/src/component_config/config_knobs/config_knobs.dart +++ b/lib/src/component_config/config_knobs/config_knobs.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause export 'choice_config_knob.dart'; diff --git a/lib/src/component_config/config_knobs/group_of_knobs_knob.dart b/lib/src/component_config/config_knobs/group_of_knobs_knob.dart index f0e29700..c6e7363f 100644 --- a/lib/src/component_config/config_knobs/group_of_knobs_knob.dart +++ b/lib/src/component_config/config_knobs/group_of_knobs_knob.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // group_of_knobs_knob.dart diff --git a/lib/src/component_config/config_knobs/int_config_knob.dart b/lib/src/component_config/config_knobs/int_config_knob.dart index 8d0c018e..e5a70794 100644 --- a/lib/src/component_config/config_knobs/int_config_knob.dart +++ b/lib/src/component_config/config_knobs/int_config_knob.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // int_config_knob.dart diff --git a/lib/src/component_config/config_knobs/list_of_knobs_knob.dart b/lib/src/component_config/config_knobs/list_of_knobs_knob.dart index afb81690..5204e746 100644 --- a/lib/src/component_config/config_knobs/list_of_knobs_knob.dart +++ b/lib/src/component_config/config_knobs/list_of_knobs_knob.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // list_of_knobs_knob.dart diff --git a/lib/src/component_config/config_knobs/string_config_knob.dart b/lib/src/component_config/config_knobs/string_config_knob.dart index dcdcca76..fc90d668 100644 --- a/lib/src/component_config/config_knobs/string_config_knob.dart +++ b/lib/src/component_config/config_knobs/string_config_knob.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // string_config_knob.dart 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 58b5022e..e3e25198 100644 --- a/lib/src/component_config/config_knobs/toggle_config_knob.dart +++ b/lib/src/component_config/config_knobs/toggle_config_knob.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_knob.dart diff --git a/lib/src/component_config/configurator.dart b/lib/src/component_config/configurator.dart index da4751a3..b56492e6 100644 --- a/lib/src/component_config/configurator.dart +++ b/lib/src/component_config/configurator.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // configurator.dart diff --git a/lib/src/encodings/binary_to_one_hot.dart b/lib/src/encodings/binary_to_one_hot.dart index 08983c17..98b954b6 100644 --- a/lib/src/encodings/binary_to_one_hot.dart +++ b/lib/src/encodings/binary_to_one_hot.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // binary_to_one_hot.dart diff --git a/lib/src/encodings/case_one_hot_to_binary.dart b/lib/src/encodings/case_one_hot_to_binary.dart index d464ee71..f54a49bb 100644 --- a/lib/src/encodings/case_one_hot_to_binary.dart +++ b/lib/src/encodings/case_one_hot_to_binary.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // case_one_hot_to_binary.dart diff --git a/lib/src/encodings/encodings.dart b/lib/src/encodings/encodings.dart index 81595531..570c7ae2 100644 --- a/lib/src/encodings/encodings.dart +++ b/lib/src/encodings/encodings.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause export 'binary_to_one_hot.dart'; diff --git a/lib/src/encodings/one_hot_to_binary.dart b/lib/src/encodings/one_hot_to_binary.dart index 1038ecd8..c71042a1 100644 --- a/lib/src/encodings/one_hot_to_binary.dart +++ b/lib/src/encodings/one_hot_to_binary.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // one_hot_to_binary.dart diff --git a/lib/src/encodings/tree_one_hot_to_binary.dart b/lib/src/encodings/tree_one_hot_to_binary.dart index 9cf05cc8..d6df0ecb 100644 --- a/lib/src/encodings/tree_one_hot_to_binary.dart +++ b/lib/src/encodings/tree_one_hot_to_binary.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // tree_one_hot_to_binary.dart diff --git a/lib/src/exceptions.dart b/lib/src/exceptions.dart index d6d8e4e9..ba7b5254 100644 --- a/lib/src/exceptions.dart +++ b/lib/src/exceptions.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // exceptions.dart diff --git a/lib/src/fifo.dart b/lib/src/fifo.dart index 08f0d4dd..188ca9a5 100644 --- a/lib/src/fifo.dart +++ b/lib/src/fifo.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // fifo.dart diff --git a/lib/src/find.dart b/lib/src/find.dart index e9ad6e66..63243f19 100644 --- a/lib/src/find.dart +++ b/lib/src/find.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // find.dart diff --git a/lib/src/interfaces/apb.dart b/lib/src/interfaces/apb.dart index 2aa23858..b41df2bc 100644 --- a/lib/src/interfaces/apb.dart +++ b/lib/src/interfaces/apb.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb.dart diff --git a/lib/src/interfaces/interfaces.dart b/lib/src/interfaces/interfaces.dart index 14df1afe..45914619 100644 --- a/lib/src/interfaces/interfaces.dart +++ b/lib/src/interfaces/interfaces.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause export 'apb.dart'; diff --git a/lib/src/memory/memories.dart b/lib/src/memory/memories.dart index dc51cd89..dec8f210 100644 --- a/lib/src/memory/memories.dart +++ b/lib/src/memory/memories.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause export 'memory.dart'; diff --git a/lib/src/memory/register_file.dart b/lib/src/memory/register_file.dart index 6a59cb03..5d7742af 100644 --- a/lib/src/memory/register_file.dart +++ b/lib/src/memory/register_file.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // register_file.dart diff --git a/lib/src/models/apb_bfm/abp_completer.dart b/lib/src/models/apb_bfm/abp_completer.dart index 55052566..a0058c33 100644 --- a/lib/src/models/apb_bfm/abp_completer.dart +++ b/lib/src/models/apb_bfm/abp_completer.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb_completer.dart diff --git a/lib/src/models/apb_bfm/apb_bfm.dart b/lib/src/models/apb_bfm/apb_bfm.dart index 5c6f78b0..3acc6337 100644 --- a/lib/src/models/apb_bfm/apb_bfm.dart +++ b/lib/src/models/apb_bfm/apb_bfm.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause export 'abp_completer.dart'; diff --git a/lib/src/models/apb_bfm/apb_compliance_checker.dart b/lib/src/models/apb_bfm/apb_compliance_checker.dart index 20d6b965..a9aa311d 100644 --- a/lib/src/models/apb_bfm/apb_compliance_checker.dart +++ b/lib/src/models/apb_bfm/apb_compliance_checker.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb_completer.dart diff --git a/lib/src/models/apb_bfm/apb_monitor.dart b/lib/src/models/apb_bfm/apb_monitor.dart index 6f757bea..a1b679fb 100644 --- a/lib/src/models/apb_bfm/apb_monitor.dart +++ b/lib/src/models/apb_bfm/apb_monitor.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb_monitor.dart diff --git a/lib/src/models/apb_bfm/apb_packet.dart b/lib/src/models/apb_bfm/apb_packet.dart index 2daeb8f3..77e32649 100644 --- a/lib/src/models/apb_bfm/apb_packet.dart +++ b/lib/src/models/apb_bfm/apb_packet.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb_packet.dart diff --git a/lib/src/models/apb_bfm/apb_requester.dart b/lib/src/models/apb_bfm/apb_requester.dart index 1bfc0295..428c534c 100644 --- a/lib/src/models/apb_bfm/apb_requester.dart +++ b/lib/src/models/apb_bfm/apb_requester.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb_requester.dart diff --git a/lib/src/models/apb_bfm/apb_requester_driver.dart b/lib/src/models/apb_bfm/apb_requester_driver.dart index 5ac8b828..42a3eda1 100644 --- a/lib/src/models/apb_bfm/apb_requester_driver.dart +++ b/lib/src/models/apb_bfm/apb_requester_driver.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb_requester_driver.dart diff --git a/lib/src/models/ready_valid_bfm/ready_valid_transmitter_driver.dart b/lib/src/models/ready_valid_bfm/ready_valid_transmitter_driver.dart index 270d73de..9330446f 100644 --- a/lib/src/models/ready_valid_bfm/ready_valid_transmitter_driver.dart +++ b/lib/src/models/ready_valid_bfm/ready_valid_transmitter_driver.dart @@ -1,3 +1,12 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// ready_valid_transmitter_agent.dart +// An agent for transmitting over a ready/valid protocol. +// +// 2024 January 5 +// Author: Max Korbel + import 'dart:async'; import 'dart:math'; diff --git a/lib/src/models/sparse_memory_storage.dart b/lib/src/models/sparse_memory_storage.dart index 6aced455..21bf3d8d 100644 --- a/lib/src/models/sparse_memory_storage.dart +++ b/lib/src/models/sparse_memory_storage.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // sparse_memory_storage.dart diff --git a/lib/src/rotate.dart b/lib/src/rotate.dart index 1dd37950..291e6142 100644 --- a/lib/src/rotate.dart +++ b/lib/src/rotate.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // rotate.dart diff --git a/lib/src/shift_register.dart b/lib/src/shift_register.dart index 6757bfc2..e567ab6d 100644 --- a/lib/src/shift_register.dart +++ b/lib/src/shift_register.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // shift_register.dart diff --git a/lib/src/sort.dart b/lib/src/sort.dart index a5db2b3c..d350ea64 100644 --- a/lib/src/sort.dart +++ b/lib/src/sort.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // sort.dart diff --git a/test/apb_bfm_test.dart b/test/apb_bfm_test.dart index 151a8243..d80a6553 100644 --- a/test/apb_bfm_test.dart +++ b/test/apb_bfm_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb_bfm_test.dart diff --git a/test/apb_test.dart b/test/apb_test.dart index 2c344c5c..ea658a27 100644 --- a/test/apb_test.dart +++ b/test/apb_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb_test.dart diff --git a/test/arithmetic/adder_test.dart b/test/arithmetic/adder_test.dart index 85f26119..d6e00438 100644 --- a/test/arithmetic/adder_test.dart +++ b/test/arithmetic/adder_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // adder_test.dart diff --git a/test/arithmetic/booth_test.dart b/test/arithmetic/booth_test.dart index 3d1b52b8..74244361 100644 --- a/test/arithmetic/booth_test.dart +++ b/test/arithmetic/booth_test.dart @@ -1,4 +1,4 @@ -// Copxorright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // booth_test.dart diff --git a/test/arithmetic/carry_save_multiplier_test.dart b/test/arithmetic/carry_save_multiplier_test.dart index 614bea00..35778a80 100644 --- a/test/arithmetic/carry_save_multiplier_test.dart +++ b/test/arithmetic/carry_save_multiplier_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // carry_save_mutiplier_test.dart diff --git a/test/arithmetic/compressor_test.dart b/test/arithmetic/compressor_test.dart index d2703b29..fcd6385f 100644 --- a/test/arithmetic/compressor_test.dart +++ b/test/arithmetic/compressor_test.dart @@ -1,4 +1,4 @@ -// Copxorright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // compressor_test.dart diff --git a/test/arithmetic/parallel_prefix_operations_test.dart b/test/arithmetic/parallel_prefix_operations_test.dart index 534393b0..1073e481 100644 --- a/test/arithmetic/parallel_prefix_operations_test.dart +++ b/test/arithmetic/parallel_prefix_operations_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // parallel-prefix_operations.dart diff --git a/test/arithmetic/ripple_carry_adder_test.dart b/test/arithmetic/ripple_carry_adder_test.dart index 1856438d..0c26c4fe 100644 --- a/test/arithmetic/ripple_carry_adder_test.dart +++ b/test/arithmetic/ripple_carry_adder_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // ripple_carry_adder_test.dart diff --git a/test/binary_gray_test.dart b/test/binary_gray_test.dart index 685cbd77..b983d322 100644 --- a/test/binary_gray_test.dart +++ b/test/binary_gray_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // binary_gray_test.dart diff --git a/test/bitonic_sort_test.dart b/test/bitonic_sort_test.dart index edbe6c55..d5ebfc24 100644 --- a/test/bitonic_sort_test.dart +++ b/test/bitonic_sort_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // bitonic_sort_test.dart diff --git a/test/count_test.dart b/test/count_test.dart index 1e0c10ac..a744d973 100644 --- a/test/count_test.dart +++ b/test/count_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // count_test.dart diff --git a/test/find_test.dart b/test/find_test.dart index ad323da8..6eff6e21 100644 --- a/test/find_test.dart +++ b/test/find_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // find_test.dart diff --git a/test/one_hot_test.dart b/test/one_hot_test.dart index 86eda999..d25c143c 100644 --- a/test/one_hot_test.dart +++ b/test/one_hot_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // one_hot_test.dart diff --git a/test/ready_valid_bfm_test.dart b/test/ready_valid_bfm_test.dart index f43acec8..1acf33e7 100644 --- a/test/ready_valid_bfm_test.dart +++ b/test/ready_valid_bfm_test.dart @@ -1,3 +1,12 @@ +// Copyright (C) 2023-2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// rotate_test.dart +// Tests for rotating +// +// 2023 February 17 +// Author: Max Korbel + import 'dart:async'; import 'dart:convert'; import 'dart:io'; diff --git a/test/rotate_test.dart b/test/rotate_test.dart index ad9bfc1c..b9d6e74c 100644 --- a/test/rotate_test.dart +++ b/test/rotate_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // rotate_test.dart diff --git a/test/sparse_memory_storage_test.dart b/test/sparse_memory_storage_test.dart index f7f1edc5..b4d2147c 100644 --- a/test/sparse_memory_storage_test.dart +++ b/test/sparse_memory_storage_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // sparse_memory_storage_test.dart diff --git a/tool/converters/json_html.sh b/tool/converters/json_html.sh index a0270633..a966e761 100755 --- a/tool/converters/json_html.sh +++ b/tool/converters/json_html.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # jsonToHtml.sh diff --git a/tool/converters/verilog_html.sh b/tool/converters/verilog_html.sh index df8c718d..79cf0ea4 100755 --- a/tool/converters/verilog_html.sh +++ b/tool/converters/verilog_html.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # verilogToJSON.sh diff --git a/tool/converters/verilog_json.sh b/tool/converters/verilog_json.sh index f1ff0e75..81d03692 100755 --- a/tool/converters/verilog_json.sh +++ b/tool/converters/verilog_json.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # verilogToJSON.sh diff --git a/tool/generate_coverage.sh b/tool/generate_coverage.sh index d41964aa..ad1b0dc7 100755 --- a/tool/generate_coverage.sh +++ b/tool/generate_coverage.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2022-2023 Intel Corporation +# Copyright (C) 2022-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # generate_coverage.sh diff --git a/tool/gh_actions/analyze_flutter_source.sh b/tool/gh_actions/analyze_flutter_source.sh index 0279e78e..15381d46 100755 --- a/tool/gh_actions/analyze_flutter_source.sh +++ b/tool/gh_actions/analyze_flutter_source.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # analyze_flutter_source.sh diff --git a/tool/gh_actions/analyze_source.sh b/tool/gh_actions/analyze_source.sh index 8fc260b6..9a86dfc4 100755 --- a/tool/gh_actions/analyze_source.sh +++ b/tool/gh_actions/analyze_source.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2022 Intel Corporation +# Copyright (C) 2022-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # analyze_source.sh diff --git a/tool/gh_actions/create_htmls.sh b/tool/gh_actions/create_htmls.sh index fadd709b..2238da1b 100755 --- a/tool/gh_actions/create_htmls.sh +++ b/tool/gh_actions/create_htmls.sh @@ -1,5 +1,8 @@ #!/bin/bash +# Copyright (C) 2022-2024 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause + # Scavenged from Nic30:d3-hwschematic #cp -r doc/d3-hwschematic-assets doc/api/ diff --git a/tool/gh_actions/hcl_site_generation_build.sh b/tool/gh_actions/hcl_site_generation_build.sh index ff1a6315..52b7de54 100755 --- a/tool/gh_actions/hcl_site_generation_build.sh +++ b/tool/gh_actions/hcl_site_generation_build.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # hcl_site_generation_build.sh diff --git a/tool/gh_actions/install_d3_hwschematic.sh b/tool/gh_actions/install_d3_hwschematic.sh index 57a6c342..dba3a7cd 100755 --- a/tool/gh_actions/install_d3_hwschematic.sh +++ b/tool/gh_actions/install_d3_hwschematic.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # install_d3_hwschematic.sh diff --git a/tool/gh_actions/install_dependencies.sh b/tool/gh_actions/install_dependencies.sh index 7aef3408..ce2f1011 100755 --- a/tool/gh_actions/install_dependencies.sh +++ b/tool/gh_actions/install_dependencies.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2022-2023 Intel Corporation +# Copyright (C) 2022-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # install_dependencies.sh diff --git a/tool/gh_actions/install_dependencies_generator_site.sh b/tool/gh_actions/install_dependencies_generator_site.sh index a5c81e53..12026319 100755 --- a/tool/gh_actions/install_dependencies_generator_site.sh +++ b/tool/gh_actions/install_dependencies_generator_site.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # install_dependencies_generator_site.sh diff --git a/tool/gh_actions/install_opencadsuite.sh b/tool/gh_actions/install_opencadsuite.sh index 952f49e5..d48df7c4 100755 --- a/tool/gh_actions/install_opencadsuite.sh +++ b/tool/gh_actions/install_opencadsuite.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # install_opencadsuite.sh diff --git a/tool/gh_actions/run_flutter_tests.sh b/tool/gh_actions/run_flutter_tests.sh index 464c3cff..c71d4d34 100755 --- a/tool/gh_actions/run_flutter_tests.sh +++ b/tool/gh_actions/run_flutter_tests.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # run_flutter_tests.sh diff --git a/tool/gh_actions/run_tests.sh b/tool/gh_actions/run_tests.sh index e3435489..8ec0d557 100755 --- a/tool/gh_actions/run_tests.sh +++ b/tool/gh_actions/run_tests.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2022-2023 Intel Corporation +# Copyright (C) 2022-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # run_tests.sh diff --git a/tool/gh_actions/verify_formatting.sh b/tool/gh_actions/verify_formatting.sh index e69c5224..2947583a 100755 --- a/tool/gh_actions/verify_formatting.sh +++ b/tool/gh_actions/verify_formatting.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2022-2023 Intel Corporation +# Copyright (C) 2022-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # verify_formatting.sh diff --git a/tool/gh_codespaces/install_dart.sh b/tool/gh_codespaces/install_dart.sh index abbe39a0..0f013974 100755 --- a/tool/gh_codespaces/install_dart.sh +++ b/tool/gh_codespaces/install_dart.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # install_dart.sh diff --git a/tool/gh_codespaces/install_flutter.sh b/tool/gh_codespaces/install_flutter.sh index c850d89b..1d32ef57 100755 --- a/tool/gh_codespaces/install_flutter.sh +++ b/tool/gh_codespaces/install_flutter.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023-24 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # install_flutter.sh diff --git a/tool/gh_codespaces/run_setup.sh b/tool/gh_codespaces/run_setup.sh index 3e92b957..70a564d6 100755 --- a/tool/gh_codespaces/run_setup.sh +++ b/tool/gh_codespaces/run_setup.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # run_setup.sh diff --git a/tool/run_checks.sh b/tool/run_checks.sh index 9c449ec4..402a154b 100755 --- a/tool/run_checks.sh +++ b/tool/run_checks.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2022-2023 Intel Corporation +# Copyright (C) 2022-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # run_checks.sh diff --git a/tool/synthesize.sh b/tool/synthesize.sh index 1c625820..12998686 100755 --- a/tool/synthesize.sh +++ b/tool/synthesize.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # synthesize.sh From ac60259b4506ac91a041af14615853f263eaba5e Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Thu, 8 Aug 2024 22:24:49 -0700 Subject: [PATCH 10/38] convert asserts to exceptions --- CONTRIBUTING.md | 4 +- lib/src/arithmetic/booth.dart | 82 +++++++++++-------- lib/src/arithmetic/compressor.dart | 13 ++- .../config_knobs/choice_config_knob.dart | 14 ++-- lib/src/component_config/configurator.dart | 10 ++- lib/src/encodings/case_one_hot_to_binary.dart | 6 +- lib/src/encodings/one_hot_to_binary.dart | 6 +- lib/src/error_checking/error_checker.dart | 8 +- lib/src/fifo.dart | 7 +- lib/src/memory/memory.dart | 22 +++-- .../ready_valid_transmitter_driver.dart | 4 +- lib/src/rotate.dart | 9 +- lib/src/utils.dart | 9 +- test/memory_test.dart | 2 +- 14 files changed, 122 insertions(+), 74 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 46966b60..a74c0284 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,7 +48,7 @@ You must have [Dart](https://dart.dev/) installed on your system to use ROHD and #### On your own system -[Visual Studio Code (VSCode)](https://code.visualstudio.com/) is a great IDE for development. You can find installation instructions for VSCode here: +[Visual Studio Code (VSCode)](https://code.visualstudio.com/) is a great IDE for development. You can find installation instructions for VSCode here: The Dart extension extends VSCode with support for the Dart programming language and provides tools for effectively editing, refactoring and running. Check out the detailed information: @@ -131,7 +131,7 @@ Please include the SPDX tag near the top of any new files you create: Here is an example of a recommended file header template: ```dart -// Copyright (C) 2021-2023 Intel Corporation +// Copyright (C) 2021-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // example.dart diff --git a/lib/src/arithmetic/booth.dart b/lib/src/arithmetic/booth.dart index 229deba4..0762badd 100644 --- a/lib/src/arithmetic/booth.dart +++ b/lib/src/arithmetic/booth.dart @@ -11,10 +11,10 @@ import 'dart:io'; import 'dart:math'; import 'package:rohd/rohd.dart'; -import 'package:rohd_hcl/src/utils.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; /// Simplest version of bit string representation -String bitString(LogicValue value) => value.toString(includeWidth: false); +// String bitString(LogicValue value) => value.toString(includeWidth: false); /// A bundle for the leaf radix compute nodes /// This holds the multiples of the multiplicand that are needed for encoding @@ -48,10 +48,10 @@ class RadixEncoder { /// Encode a multiplier slice into the Booth encoded value RadixEncode encode(Logic multiplierSlice) { - assert( - multiplierSlice.width == log2Ceil(radix) + 1, - 'multiplier slice width ${multiplierSlice.width}' - 'must be same length as log(radix)+1=${log2Ceil(radix) + 1}'); + if (multiplierSlice.width != log2Ceil(radix) + 1) { + throw RohdHclException('multiplier slice width ${multiplierSlice.width}' + 'must be same length as log(radix)+1=${log2Ceil(radix) + 1}'); + } final width = log2Ceil(radix) + 1; final inputXor = Logic(width: width); inputXor <= @@ -115,7 +115,9 @@ class MultiplierEncoder { /// Retrieve the Booth encoding for the row RadixEncode getEncoding(int row) { - assert(row < rows, 'row $row is not < number of encoding rows $rows'); + if (row >= rows) { + throw RohdHclException('row $row is not < number of encoding rows $rows'); + } final base = row * (_sliceWidth - 1); final multiplierSlice = [ if (row > 0) @@ -147,8 +149,10 @@ class MultiplicandSelector { /// Generate required multiples of multiplicand MultiplicandSelector(this.radix, this.multiplicand, {bool signed = true}) - : shift = log2Ceil(radix), - assert(radix <= 16, 'beyond radix 16 is not yet supported') { + : shift = log2Ceil(radix) { + 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); @@ -168,8 +172,7 @@ class MultiplicandSelector { 6 => (extendedMultiplicand << 3) - (extendedMultiplicand << 1), 7 => (extendedMultiplicand << 3) - extendedMultiplicand, 8 => extendedMultiplicand << 3, - _ => extendedMultiplicand - // TODO(desmonddak): generalize to support higher radix than 16 + _ => throw RohdHclException('Radix is beyond 16') }; } } @@ -247,7 +250,9 @@ class PartialProductGenerator { /// Fully sign extend the PP array: useful for reference only void bruteForceSignExtend() { - assert(!_signExtended, 'Partial Product array already sign-extended'); + if (_signExtended) { + throw RohdHclException('Partial Product array already sign-extended'); + } _signExtended = true; final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; for (var row = 0; row < rows; row++) { @@ -273,7 +278,9 @@ class PartialProductGenerator { /// This technique can then be combined with a first-row extension technique /// for folding in the final carry. void signExtendWithStopBitsRect() { - assert(!_signExtended, 'Partial Product array already sign-extended'); + if (_signExtended) { + throw RohdHclException('Partial Product array already sign-extended'); + } _signExtended = true; final finalCarryPos = shift * (rows - 1); @@ -348,7 +355,9 @@ class PartialProductGenerator { /// Sign extend the PP array using stop bits without adding a row void signExtendCompact() { - assert(!_signExtended, 'Partial Product array already sign-extended'); + if (_signExtended) { + throw RohdHclException('Partial Product array already sign-extended'); + } _signExtended = true; final lastRow = rows - 1; @@ -444,7 +453,9 @@ class PartialProductGenerator { /// Sign extend the PP array using stop bits without adding a row /// This routine works with different widths of multiplicand/multiplier void signExtendCompactRect() { - assert(!_signExtended, 'Partial Product array already sign-extended'); + if (_signExtended) { + throw RohdHclException('Partial Product array already sign-extended'); + } _signExtended = true; final lastRow = rows - 1; @@ -628,7 +639,7 @@ class PartialProductGenerator { if (row < encoder.rows) { final encoding = encoder.getEncoding(row); if (encoding.multiples.value.isValid) { - str.write('$rowStr M=${bitString(encoding.multiples.reversed.value)} ' + str.write('$rowStr M=${encoding.multiples.reversed.value.bitString} ' 'S=${encoding.sign.value.toInt()}: '); } else { str.write(' ' * shortPrefix); @@ -641,14 +652,14 @@ class PartialProductGenerator { maxW - (entry.length + rowShift[row]) + nonSignExtendedPad; str.write(' ' * prefixCnt); for (var col = 0; col < entry.length; col++) { - str.write('${bitString(entry[col].value)} '); + str.write('${entry[col].value.bitString} '); } final suffixCnt = rowShift[row]; final value = entry.swizzle().value.zeroExtend(maxW) << suffixCnt; final intValue = value.isValid ? value.toBigInt() : BigInt.from(-1); str ..write(' ' * suffixCnt) - ..write(': ${bitString(value)}') + ..write(': ${value.bitString}') ..write(' = ${value.isValid ? intValue : ""}' ' (${value.isValid ? intValue.toSigned(maxW) : ""})\n'); } @@ -665,7 +676,7 @@ class PartialProductGenerator { str.write('${elem.toInt()} '); } final val = evaluate(); - str.write(': ${bitString(sum)} = ' + str.write(': ${sum.bitString} = ' '${val.toUnsigned(maxW)}'); if (_signExtended) { str.write(' ($val)\n\n'); @@ -702,11 +713,11 @@ void main() { ].swizzle().and(); final multPos = (i >>> 1) + i % 2; stdout - ..write('\tM${(i >>> 1) + i % 2} x=${bitString(x)} ' - 'lx=${bitString(pastX)} ' - // 'm=$m xor=${bitString(xor)}(${xor.toInt()}) ' - 'dontcare=${bitString(multiplesDisagree)}' - ' agree=${bitString(senseMultiples)}') + ..write('\tM${(i >>> 1) + i % 2} x=${x.bitString} ' + 'lx=${pastX.bitString} ' + // 'm=$m xor=${xor.bitString}(${xor.toInt()}) ' + 'dontcare=${multiplesDisagree.bitString}' + ' agree=${senseMultiples.bitString}') ..write(': '); for (var j = 0; j < width - 1; j++) { if (multiplesDisagree[j].isZero) { @@ -727,17 +738,22 @@ void main() { RadixEncoder(radix).encode(inLogic).multiples[multPos - 1]; inputXor.put(inValue ^ (inValue >>> 1)); // stdout - // ..write('in=${bitString(inValue)} ') - // ..write('xor=${bitString(inputXor.value)} ') - // ..write('out=${bitString(andOutput.value)} ') - // ..write('code=${bitString(code.value)} ') - // ..write('ncode=${bitString(newCode.value)}') + // ..write('in=${inValue.bitString} ') + // ..write('xor=${inputXor.value.bitString)} ') + // ..write('out=${andOutput.value.bitString} ') + // ..write('code=${code.value.bitString} ') + // ..write('ncode=${newCode.value.bitString}') // ..write('') // ..write('\n'); - assert(andOutput.value == code.value, 'andOutput mismatches code'); - assert(newCode.value == code.value, 'newCode mismatches code'); - assert( - newCode.value == andOutput.value, 'andOutput mismatches newCode'); + if (andOutput.value != code.value) { + throw RohdHclException('andOutput mismatches code'); + } + if (newCode.value != code.value) { + throw RohdHclException('andOutput mismatches code'); + } + if (andOutput.value != andOutput.value) { + throw RohdHclException('andOutput mismatches code'); + } } } } diff --git a/lib/src/arithmetic/compressor.dart b/lib/src/arithmetic/compressor.dart index b540b170..91552696 100644 --- a/lib/src/arithmetic/compressor.dart +++ b/lib/src/arithmetic/compressor.dart @@ -11,6 +11,7 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:rohd_hcl/src/arithmetic/booth.dart'; // TODO(desmonddak): Logic and LogicValue majority() functions @@ -196,7 +197,9 @@ class ColumnCompressor { // x.put(termValues.swizzle()); // final newCount = Count(x).index.value.toInt(); // stdout.write('count=$count newCount=$newCount\n'); - // assert(newCount == count, 'count=$count newCount=$newCount\n'); + // if (newCount 1= count) { + // throw RohdHclException('count=$count newCount=$newCount'); + // } return majority; } } @@ -240,7 +243,7 @@ class ColumnCompressor { logic ? colList[row].logic.value : evaluateTerm(colList[row]); rowBits.add(value); if (print) { - ts.write('\t${bitString(value)}'); + ts.write('\t${value.bitString}'); } } else if (print) { ts.write('\t'); @@ -250,7 +253,7 @@ class ColumnCompressor { final val = rowBits.swizzle().zeroExtend(width).toBigInt(); accum += val; if (print) { - ts.write('\t${bitString(rowBits.swizzle().zeroExtend(width))} ($val)'); + ts.write('\t${rowBits.swizzle().zeroExtend(width).bitString} ($val)'); if (row == rows - 1) { ts.write(' Total=${accum.toSigned(width)}\n'); stdout.write(ts); @@ -300,8 +303,6 @@ class ColumnCompressor { } final t = CompressTerm.sumTerm(inputs, 0, col); t.logic <= compressor.sum; - // assert(t.logic.value == evaluateTerm(t), - // 'sum logic does not match evaluate'); terms.add(t); columns[col].add(t); if (col < columns.length - 1) { @@ -309,8 +310,6 @@ class ColumnCompressor { columns[col + 1].add(t); terms.add(t); t.logic <= compressor.carry; - // assert(t.logic.value == evaluateTerm(t), - // 'carry logic does not match evaluate.'); } } } diff --git a/lib/src/component_config/config_knobs/choice_config_knob.dart b/lib/src/component_config/config_knobs/choice_config_knob.dart index 68416137..8ae85949 100644 --- a/lib/src/component_config/config_knobs/choice_config_knob.dart +++ b/lib/src/component_config/config_knobs/choice_config_knob.dart @@ -20,14 +20,18 @@ class ChoiceConfigKnob extends ConfigKnob { /// Creates a new knob to with the specified default [value] of the available /// [choices]. - ChoiceConfigKnob(this.choices, {required super.value}) - : assert(choices.contains(value), - 'Default value should be one of the choices.'); + ChoiceConfigKnob(this.choices, {required super.value}) { + if (!choices.contains(value)) { + throw RohdHclException('Default value should be one of the choices.'); + } + } @override set value(T newValue) { - assert(choices.contains(newValue), - 'New value should be one of the available choices.'); + if (!choices.contains(newValue)) { + throw RohdHclException( + 'New value should be one of the available choices.'); + } super.value = newValue; } diff --git a/lib/src/component_config/configurator.dart b/lib/src/component_config/configurator.dart index b56492e6..259eaea2 100644 --- a/lib/src/component_config/configurator.dart +++ b/lib/src/component_config/configurator.dart @@ -51,12 +51,14 @@ abstract class Configurator { /// Loads the configuration from a serialized JSON representation. void loadJson(String json) { final decoded = jsonDecode(json) as Map; - assert(decoded['name'] == name, 'Expect name to be the same.'); + if (decoded['name'] != name) { + throw RohdHclException('Expect name to be the same.'); + } for (final decodedKnob in (decoded['knobs'] as Map).entries) { - assert(knobs.containsKey(decodedKnob.key), - 'Expect knobs in JSON to exist in configurator.'); - + if (!knobs.containsKey(decodedKnob.key)) { + throw RohdHclException('Expect name to be the same.'); + } knobs[decodedKnob.key]! .loadJson(decodedKnob.value as Map); } diff --git a/lib/src/encodings/case_one_hot_to_binary.dart b/lib/src/encodings/case_one_hot_to_binary.dart index f54a49bb..27f61ff3 100644 --- a/lib/src/encodings/case_one_hot_to_binary.dart +++ b/lib/src/encodings/case_one_hot_to_binary.dart @@ -22,8 +22,10 @@ class CaseOneHotToBinary extends OneHotToBinary { /// the maximum width of an `int`. CaseOneHotToBinary(super.onehot, {super.generateError = false, super.name = 'one_hot_to_binary'}) - : assert(onehot.width < 32, 'Should not be used for large widths.'), - super.base() { + : super.base() { + if (onehot.width >= 32) { + throw RohdHclException('Should not be used for large widths.'); + } Combinational([ Case(onehot, conditionalType: ConditionalType.unique, [ for (var i = 0; i < onehot.width; i++) diff --git a/lib/src/encodings/one_hot_to_binary.dart b/lib/src/encodings/one_hot_to_binary.dart index c71042a1..51ccefef 100644 --- a/lib/src/encodings/one_hot_to_binary.dart +++ b/lib/src/encodings/one_hot_to_binary.dart @@ -32,8 +32,10 @@ abstract class OneHotToBinary extends Module { {bool generateError = false, String name = 'one_hot_to_binary'}) { final isSmall = onehot.width <= 8; - assert(!(!isSmall && generateError), - 'Tree implementation does not generate error signal.'); + if (!isSmall && generateError) { + throw RohdHclException( + 'Tree implementation does not generate error signal.'); + } return isSmall ? CaseOneHotToBinary(onehot, generateError: generateError, name: name) diff --git a/lib/src/error_checking/error_checker.dart b/lib/src/error_checking/error_checker.dart index ea1a5d62..3c6a61c9 100644 --- a/lib/src/error_checking/error_checker.dart +++ b/lib/src/error_checking/error_checker.dart @@ -8,6 +8,7 @@ import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; /// A transmitter for data which includes a [code] for checking and possibly /// correcting data at the receiving end. @@ -97,8 +98,11 @@ abstract class ErrorCheckingReceiver extends Module { {required int codeWidth, required this.supportsErrorCorrection, required super.name, - super.definitionName}) - : assert(codeWidth > 0, 'Must provide non-empty code.') { + super.definitionName}) { + if (codeWidth <= 0) { + throw RohdHclException('Must provide non-empty code.'); + } + this.transmission = addInput('transmission', transmission, width: transmission.width); diff --git a/lib/src/fifo.dart b/lib/src/fifo.dart index 188ca9a5..ce19e23f 100644 --- a/lib/src/fifo.dart +++ b/lib/src/fifo.dart @@ -92,9 +92,10 @@ class Fifo extends Module { if (depth <= 0) { throw RohdHclException('Depth must be at least 1.'); } - - assert(_addrWidth > 0, - 'Assumption that address width is non-zero in implementation'); + if (_addrWidth <= 0) { + throw RohdHclException( + 'Assumption that address width is non-zero in implementation'); + } addInput('clk', clk); addInput('reset', reset); diff --git a/lib/src/memory/memory.dart b/lib/src/memory/memory.dart index 80a6b35f..c0545c1e 100644 --- a/lib/src/memory/memory.dart +++ b/lib/src/memory/memory.dart @@ -27,8 +27,10 @@ class MaskedDataPortInterface extends DataPortInterface { Logic get mask => port('mask'); /// Constructs a [DataPortInterface] with mask. - MaskedDataPortInterface(super.dataWidth, super.addrWidth) - : assert(dataWidth % 8 == 0, 'The data width must be byte-granularity') { + MaskedDataPortInterface(super.dataWidth, super.addrWidth) { + if (dataWidth % 8 != 0) { + throw RohdHclException('The data width must be byte-granularity'); + } setPorts([ Port('mask', dataWidth ~/ 8), ], [ @@ -122,17 +124,23 @@ abstract class Memory extends Module { Memory(Logic clk, Logic reset, List writePorts, List readPorts, {super.name = 'memory'}) - : assert(!(writePorts.isEmpty && readPorts.isEmpty), - 'Must specify at least one read port or one write port.'), - numWrites = writePorts.length, + : numWrites = writePorts.length, numReads = readPorts.length, dataWidth = (writePorts.isNotEmpty) ? writePorts[0].dataWidth - : readPorts[0].dataWidth, // at least one of these must exist + : (readPorts.isNotEmpty) + ? readPorts[0].dataWidth + : 0, // at least one of these must exist addrWidth = (writePorts.isNotEmpty) ? writePorts[0].addrWidth - : readPorts[0].addrWidth // at least one of these must exist + : (readPorts.isNotEmpty) + ? readPorts[0].addrWidth + : 0 // at least one of these must exist { + if (writePorts.isEmpty && readPorts.isEmpty) { + throw RohdHclException( + 'Must specify at least one read port or one write port.'); + } if (readLatency < 0) { throw RohdHclException('Read latency must be non-negative.'); } diff --git a/lib/src/models/ready_valid_bfm/ready_valid_transmitter_driver.dart b/lib/src/models/ready_valid_bfm/ready_valid_transmitter_driver.dart index 9330446f..ea4cec46 100644 --- a/lib/src/models/ready_valid_bfm/ready_valid_transmitter_driver.dart +++ b/lib/src/models/ready_valid_bfm/ready_valid_transmitter_driver.dart @@ -79,7 +79,9 @@ class ReadyValidTransmitterDriver } else { valid.inject(1); - assert(pkt.data.width == data.width, 'Data widths should match.'); + if (pkt.data.width != data.width) { + throw RohdHclException('Data widths should match.'); + } data.inject(pkt.data); // wait for it to be accepted diff --git a/lib/src/rotate.dart b/lib/src/rotate.dart index 291e6142..f7ef3923 100644 --- a/lib/src/rotate.dart +++ b/lib/src/rotate.dart @@ -166,10 +166,11 @@ extension RotateLogic on Logic { Logic _rotate(dynamic rotateAmount, {required RotateDirection direction, int? maxAmount}) { if (rotateAmount is int) { - assert( - maxAmount == null || rotateAmount <= maxAmount, - 'If `maxAmount` is provided with an integer `amount`,' - ' it should meet the restriction.'); + if (!(maxAmount == null || rotateAmount <= maxAmount)) { + throw RohdHclException( + 'If `maxAmount` is provided with an integer `amount`,' + ' it should meet the restriction.'); + } return direction == RotateDirection.left ? RotateLeftFixed(this, rotateAmount).rotated diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 4e2c7081..548ed32e 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -1,10 +1,17 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // utils.dart // Various utilities helpful for working with the component library import 'dart:math'; +import 'package:rohd/rohd.dart'; /// Computes the bit width needed to store [w] addresses. int log2Ceil(int w) => (log(w) / log(2)).ceil(); + +/// This extension will eventually move to ROHD once it is proven useful +extension LogicValueBitString on LogicValue { + /// Simplest version of bit string representation as shorthand + String get bitString => toString(includeWidth: false); +} diff --git a/test/memory_test.dart b/test/memory_test.dart index 5e331831..7d6cf5e2 100644 --- a/test/memory_test.dart +++ b/test/memory_test.dart @@ -379,7 +379,7 @@ void main() { RegisterFile(Logic(), Logic(), [], []); fail('Should have failed'); // ignore: avoid_catching_errors - } on AssertionError catch (_) {} + } on RohdHclException catch (_) {} }); }); } From ae9055fe8564a60d1f68f48416afa2442be08f77 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Fri, 9 Aug 2024 00:21:31 -0700 Subject: [PATCH 11/38] split/reorganize integer multiplication files --- doc/components/multiplier.md | 20 +- lib/rohd_hcl.dart | 2 + lib/src/arithmetic/compressor.dart | 20 +- lib/src/arithmetic/multiplicand_selector.dart | 73 +++ lib/src/arithmetic/multiplier.dart | 42 +- .../{booth.dart => multiplier_encoder.dart} | 14 +- .../parallel_prefix_operations.dart | 4 +- .../arithmetic/partial_product_generator.dart | 516 ++++++++++++++++++ lib/src/arithmetic/sign_magnitude_adder.dart | 9 +- test/arithmetic/compressor_test.dart | 5 +- ...test.dart => multiplier_encoder_test.dart} | 36 +- test/arithmetic/multiplier_test.dart | 17 +- 12 files changed, 659 insertions(+), 99 deletions(-) create mode 100644 lib/src/arithmetic/multiplicand_selector.dart rename lib/src/arithmetic/{booth.dart => multiplier_encoder.dart} (98%) create mode 100644 lib/src/arithmetic/partial_product_generator.dart rename test/arithmetic/{booth_test.dart => multiplier_encoder_test.dart} (86%) diff --git a/doc/components/multiplier.md b/doc/components/multiplier.md index 2767d276..ab1d9426 100644 --- a/doc/components/multiplier.md +++ b/doc/components/multiplier.md @@ -1,18 +1,18 @@ # Multiplier -ROHD HCL provides an abstract [Multiplier] module which multiplies two -numbers represented as two [Logic]s, potentially of different widths, +ROHD HCL provides an abstract `Multiplier` module which multiplies two +numbers represented as two `Logic`s, potentially of different widths, treating them as either signed (2s complement) or unsigned. It -produces the product as a [Logic] with width equal to the sum of the +produces the product as a `Logic` with width equal to the sum of the widths of the inputs. As of now, we have the following implementations -of this abstract [Module]: +of this abstract `Module`: - [Carry Save Multiplier](#carry-save-multiplier) - [Compression Tree Multiplier](#compression-tree-multiplier) An additional kind of abstract module provided is a -[MultiplyAccumulate] module which multiplies two numbers represented -as two [Logic]s and adds the result to a third [Logic] with width +`MultiplyAccumulate` module which multiplies two numbers represented +as two `Logic`s and adds the result to a third `Logic` with width equal to the sum of the widths of the main inputs. We have a high-performance implementation: @@ -78,11 +78,11 @@ useful in applications that require high speed multiplication, such as digital signal processing. The parameters of the -[CompressionTreeMultiplier] are: +`CompressionTreeMultiplier` are: - Two input terms a and b - The radix used for Booth encoding (2, 4, 8, and 16 are currently supported) -- The type of [ParallelPrefix] tree used in the final [ParallelPrefixAdder] +- The type of `ParallelPrefix` tree used in the final `ParallelPrefixAdder` - Whether the operands should be treated as signed (2s complement) or unsigned ## Compression Tree Multiply Accumulate @@ -92,10 +92,10 @@ multiplier, but it inserts an additional addend into the compression tree to allow for accumulation into this third input. The parameters of the -[CompressionTreeMultiplier] are: +`CompressionTreeMultiplier` are: - Two input terms a and b - The accumulate input term c - The radix used for Booth encoding (2, 4, 8, and 16 are currently supported) -- The type of [ParallelPrefix] tree used in the final [ParallelPrefixAdder] +- The type of `ParallelPrefix` tree used in the final `ParallelPrefixAdder` - Whether the operands should be treated as signed (2s complement) or unsigned diff --git a/lib/rohd_hcl.dart b/lib/rohd_hcl.dart index 243ceb0f..0f9c6cd5 100644 --- a/lib/rohd_hcl.dart +++ b/lib/rohd_hcl.dart @@ -4,7 +4,9 @@ export 'src/arbiters/arbiters.dart'; export 'src/arithmetic/adder.dart'; export 'src/arithmetic/carry_save_mutiplier.dart'; +export 'src/arithmetic/compressor.dart'; export 'src/arithmetic/multiplier.dart'; +export 'src/arithmetic/multiplier_encoder.dart'; export 'src/arithmetic/parallel_prefix_operations.dart'; export 'src/arithmetic/ripple_carry_adder.dart'; export 'src/arithmetic/sign_magnitude_adder.dart'; diff --git a/lib/src/arithmetic/compressor.dart b/lib/src/arithmetic/compressor.dart index 91552696..5304dcd3 100644 --- a/lib/src/arithmetic/compressor.dart +++ b/lib/src/arithmetic/compressor.dart @@ -12,12 +12,11 @@ import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -import 'package:rohd_hcl/src/arithmetic/booth.dart'; // TODO(desmonddak): Logic and LogicValue majority() functions /// Base class for column compressor function -class Compressor extends Module { +abstract class Compressor extends Module { /// Input bits to compress @protected late final Logic compressBits; @@ -60,8 +59,17 @@ class Compressor3 extends Compressor { } } -// ignore: public_member_api_docs -enum CompressTermType { carry, sum, pp } +/// Compress terms +enum CompressTermType { + /// A carry term + carry, + + /// A sum term + sum, + + /// A partial product term (from the original matrix) + pp +} /// A compression term class CompressTerm implements Comparable { @@ -90,9 +98,7 @@ class CompressTerm implements Comparable { static const carryDelay = 0.75; /// CompressTerm constructor - CompressTerm(this.type, this.row, this.col) { - delay = 0.0; - } + CompressTerm(this.type, this.row, this.col) : delay = 0.0; /// Create a sum Term factory CompressTerm.sumTerm(List args, int row, int col) { diff --git a/lib/src/arithmetic/multiplicand_selector.dart b/lib/src/arithmetic/multiplicand_selector.dart new file mode 100644 index 00000000..fcc8727f --- /dev/null +++ b/lib/src/arithmetic/multiplicand_selector.dart @@ -0,0 +1,73 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// multiplicand_selector.dart +// Selection of muliples of the multiplicand for booth recoding +// +// 2024 May 15 +// Author: Desmond Kirkpatrick + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_hcl/src/arithmetic/multiplier_encoder.dart'; + +/// A class accessing the multiples of the multiplicand at a position +class MultiplicandSelector { + /// radix of the selector + int radix; + + /// The bit shift of the selector (typically overlaps 1) + int shift; + + /// New width of partial products generated from the multiplicand + int get width => multiplicand.width + shift - 1; + + /// Access the multiplicand + Logic multiplicand = Logic(); + + /// Place to store multiples of the multiplicand + late LogicArray multiples; + + /// Generate required multiples of multiplicand + MultiplicandSelector(this.radix, this.multiplicand, {bool signed = true}) + : shift = log2Ceil(radix) { + 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); + final extendedMultiplicand = signed + ? multiplicand.signExtend(width) + : multiplicand.zeroExtend(width); + + for (var pos = 0; pos < numMultiples; pos++) { + final ratio = pos + 1; + multiples.elements[pos] <= + switch (ratio) { + 1 => extendedMultiplicand, + 2 => extendedMultiplicand << 1, + 3 => (extendedMultiplicand << 2) - extendedMultiplicand, + 4 => extendedMultiplicand << 2, + 5 => (extendedMultiplicand << 2) + extendedMultiplicand, + 6 => (extendedMultiplicand << 3) - (extendedMultiplicand << 1), + 7 => (extendedMultiplicand << 3) - extendedMultiplicand, + 8 => extendedMultiplicand << 3, + _ => throw RohdHclException('Radix is beyond 16') + }; + } + } + + /// Retrieve the multiples of the multiplicand at current bit position + Logic getMultiples(int col) => [ + for (var i = 0; i < multiples.elements.length; i++) + multiples.elements[i][col] + ].swizzle().reversed; + + Logic _select(Logic multiples, RadixEncode encode) => + (encode.multiples & multiples).or() ^ encode.sign; + + /// Select the partial product term from the multiples using a RadixEncode + Logic select(int col, RadixEncode encode) => + _select(getMultiples(col), encode); +} diff --git a/lib/src/arithmetic/multiplier.dart b/lib/src/arithmetic/multiplier.dart index a3f11564..18f11c99 100644 --- a/lib/src/arithmetic/multiplier.dart +++ b/lib/src/arithmetic/multiplier.dart @@ -11,8 +11,6 @@ import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -import 'package:rohd_hcl/src/arithmetic/booth.dart'; -import 'package:rohd_hcl/src/arithmetic/compressor.dart'; /// An abstract class for all multiplier implementations. abstract class Multiplier extends Module { @@ -25,18 +23,14 @@ abstract class Multiplier extends Module { late final Logic b; /// The multiplier treats operands and output as signed - bool get signed => _signed; - - @protected - bool _signed = false; + bool signed; /// The multiplication results of the multiplier. Logic get product; /// Take input [a] and input [b] and return the /// [product] of the multiplication result. - Multiplier(Logic a, Logic b, {bool signed = false, super.name}) { - _signed = signed; + Multiplier(Logic a, Logic b, {this.signed = false, super.name}) { this.a = addInput('a', a, width: a.width); this.b = addInput('b', b, width: b.width); } @@ -57,10 +51,7 @@ abstract class MultiplyAccumulate extends Module { late final Logic c; /// The multiplier treats operands and output as signed - bool get signed => _signed; - - @protected - bool _signed = false; + bool signed; /// The multiplication results of the multiply-accumulate. Logic get accumulate; @@ -68,8 +59,7 @@ abstract class MultiplyAccumulate extends Module { /// Take input [a] and input [b], compute their /// product, add input [c] to produce the [accumulate] result. MultiplyAccumulate(Logic a, Logic b, Logic c, - {bool signed = false, super.name}) { - _signed = signed; + {this.signed = false, super.name}) { this.a = addInput('a', a, width: a.width); this.b = addInput('b', b, width: b.width); this.c = addInput('c', c, width: c.width); @@ -117,22 +107,14 @@ class CompressionTreeMultiplyAccumulate extends MultiplyAccumulate { /// a given radix and final adder functor CompressionTreeMultiplyAccumulate(super.a, super.b, super.c, int radix, ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppTree, - {bool signed = false}) + {super.signed = false}) : super( name: 'Compression Tree Multiplier: ' 'R${radix}_${ppTree.call([Logic()], (a, b) => Logic()).name}') { final accumulate = addOutput('accumulate', width: a.width + b.width + 1); - _signed = signed; - final pp = - PartialProductGenerator(a, b, RadixEncoder(radix), signed: signed); - // ignore: cascade_invocations - pp.signExtendCompact(); - - // Evaluate works only because the compressed rows have the same shape - // So the rowshift is valid. - // But this requires that we prefix the PP with the addend (not add) to - // keep the evaluate routine working. + PartialProductGenerator(a, b, RadixEncoder(radix), signed: signed) + ..signExtendCompact(); // TODO(desmonddak): This sign extension method for the additional // addend may only work with signExtendCompact. @@ -149,6 +131,10 @@ class CompressionTreeMultiplyAccumulate extends MultiplyAccumulate { ..add(~sign) ..add(Const(1)); + // Evaluate works only because the compressed rows have the same shape + // So the rowshift is valid. + // But this requires that we prefix the PP with the addend (not add) to + // keep the evaluate routine working. pp.partialProducts.insert(0, l); pp.rowShift.insert(0, 0); @@ -156,9 +142,7 @@ class CompressionTreeMultiplyAccumulate extends MultiplyAccumulate { // compressor fails to properly handle, so we need to debug // pp.partialProducts.add(l); // pp.rowShift.add(0); - final compressor = ColumnCompressor(pp); - // ignore: cascade_invocations - compressor.compress(); + final compressor = ColumnCompressor(pp)..compress(); final adder = ParallelPrefixAdder( compressor.extractRow(0), compressor.extractRow(1), ppTree); @@ -180,7 +164,7 @@ class MultiplyOnly extends MultiplyAccumulate { final accumulate = addOutput('accumulate', width: a.width + b.width + 1); final multiply = multiplyGenerator(a, b); - _signed = multiply.signed; + signed = multiply.signed; accumulate <= (signed diff --git a/lib/src/arithmetic/booth.dart b/lib/src/arithmetic/multiplier_encoder.dart similarity index 98% rename from lib/src/arithmetic/booth.dart rename to lib/src/arithmetic/multiplier_encoder.dart index 0762badd..ab0564e0 100644 --- a/lib/src/arithmetic/booth.dart +++ b/lib/src/arithmetic/multiplier_encoder.dart @@ -1,7 +1,7 @@ // Copyright (C) 2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // -// booth.dart +// muliplier_encoder.dart // Generation of Booth Encoded partial products for multiplication // // 2024 May 15 @@ -9,15 +9,11 @@ import 'dart:io'; import 'dart:math'; - import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -/// Simplest version of bit string representation -// String bitString(LogicValue value) => value.toString(includeWidth: false); - -/// A bundle for the leaf radix compute nodes -/// This holds the multiples of the multiplicand that are needed for encoding +/// A bundle for the leaf radix compute nodes. This holds the multiples +/// of the multiplicand that are needed for encoding class RadixEncode extends LogicStructure { /// Which multiples need to be selected final Logic multiples; @@ -84,11 +80,13 @@ class RadixEncoder { /// A class that generates the Booth encoding of the multipler class MultiplierEncoder { /// Access the multiplier - Logic multiplier = Logic(); + final Logic multiplier; /// Number of row radixEncoders late final int rows; + /// The multiplier value, sign extended as appropriate to be divisible + /// by the RadixEncoder overlapping bitslices. Logic _extendedMultiplier = Logic(); late final RadixEncoder _encoder; late final int _sliceWidth; diff --git a/lib/src/arithmetic/parallel_prefix_operations.dart b/lib/src/arithmetic/parallel_prefix_operations.dart index ac9cb2c5..28fa90d8 100644 --- a/lib/src/arithmetic/parallel_prefix_operations.dart +++ b/lib/src/arithmetic/parallel_prefix_operations.dart @@ -209,8 +209,8 @@ class ParallelPrefixPriorityEncoder extends Module { /// Adder based on ParallelPrefix tree class ParallelPrefixAdder extends Adder { - late final Logic _out; - late final Logic _carry = Logic(); + final Logic _out; + final Logic _carry = Logic(); /// Adder constructor ParallelPrefixAdder(super.a, super.b, diff --git a/lib/src/arithmetic/partial_product_generator.dart b/lib/src/arithmetic/partial_product_generator.dart new file mode 100644 index 00000000..3ba3d888 --- /dev/null +++ b/lib/src/arithmetic/partial_product_generator.dart @@ -0,0 +1,516 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// partial_product_generator.dart +// Partial Product matrix generation from Booth recoded multiplicand +// +// 2024 May 15 +// Author: Desmond Kirkpatrick + +import 'dart:math'; + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// A 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 +class PartialProductGenerator { + /// Get the shift increment between neighboring product rows + int get shift => selector.shift; + + /// 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 + final rowShift = []; + + /// rows of partial products + int get rows => partialProducts.length; + + /// The multiplicand term + Logic get multiplicand => selector.multiplicand; + + /// The multiplier term + Logic get multiplier => encoder.multiplier; + + /// Partial Products output. Generated by selector and extended by sign + /// extension routines + late final List> partialProducts; + + /// Encoder for the full multiply operand + late final MultiplierEncoder encoder; + + /// Selector for the multiplicand which uses the encoder to index into + /// multiples of the multiplicand and generate partial products + late final MultiplicandSelector selector; + + /// Operands are signed + late bool signed = true; + + // Used to avoid sign extending more than once + var _signExtended = false; + + /// Construct the partial product matrix + PartialProductGenerator( + Logic multiplicand, Logic multiplier, RadixEncoder radixEncoder, + {this.signed = true}) { + encoder = MultiplierEncoder(multiplier, radixEncoder, signed: signed); + selector = + MultiplicandSelector(radixEncoder.radix, multiplicand, signed: signed); + _build(); + } + + /// Setup the partial products array (partialProducts and rowShift) + void _build() { + partialProducts = >[]; + for (var row = 0; row < encoder.rows; row++) { + partialProducts.add(List.generate( + selector.width, (i) => selector.select(i, encoder.getEncoding(row)))); + } + for (var row = 0; row < rows; row++) { + rowShift.add(row * shift); + } + } + + /// Fully sign extend the PP array: useful for reference only + void bruteForceSignExtend() { + if (_signExtended) { + throw RohdHclException('Partial Product array already sign-extended'); + } + _signExtended = true; + final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; + for (var row = 0; row < rows; row++) { + final addend = partialProducts[row]; + final sign = signed ? addend.last : signs[row]; + addend.addAll(List.filled((rows - row) * shift, sign)); + if (row > 0) { + addend + ..insertAll(0, List.filled(shift - 1, Const(0))) + ..insert(0, signs[row - 1]); + rowShift[row] -= shift; + } + } + // Insert carry bit in extra row + partialProducts.add(List.generate(selector.width, (i) => Const(0))); + partialProducts.last.insert(0, signs[rows - 2]); + rowShift.add((rows - 2) * shift); + } + + /// Sign extend the PP array using stop bits + /// If possible, fold the final carry into another row (only when rectangular + /// enough that carry bit lands outside another row). + /// This technique can then be combined with a first-row extension technique + /// for folding in the final carry. + void signExtendWithStopBitsRect() { + if (_signExtended) { + throw RohdHclException('Partial Product array already sign-extended'); + } + _signExtended = true; + + final finalCarryPos = shift * (rows - 1); + final finalCarryRelPos = finalCarryPos - selector.width - shift; + final finalCarryRow = + ((encoder.multiplier.width > selector.multiplicand.width) && + (finalCarryRelPos > 0)) + ? (finalCarryRelPos / shift).floor() + : 0; + + final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; + for (var row = 0; row < rows; row++) { + final addend = partialProducts[row]; + final sign = signed ? addend.last : signs[row]; + if (row == 0) { + if (signed) { + addend.addAll(List.filled(shift - 1, sign)); // signed only? + } else { + addend.addAll(List.filled(shift, sign)); + } + addend.add(~sign); + } else { + if (signed) { + addend.last = ~sign; + } else { + addend.add(~sign); + } + addend + ..addAll(List.filled(shift - 1, Const(1))) + ..insertAll(0, List.filled(shift - 1, Const(0))) + ..insert(0, signs[row - 1]); + rowShift[row] -= shift; + } + } + + if (finalCarryRow > 0) { + final extensionRow = partialProducts[finalCarryRow]; + extensionRow + ..addAll(List.filled( + finalCarryPos - (extensionRow.length + rowShift[finalCarryRow]), + Const(0))) + ..add(signs[rows - 1]); + } else if (signed) { + // Create an extra row to hold the final carry bit + partialProducts + .add(List.filled(selector.width, Const(0), growable: true)); + partialProducts.last.insert(0, signs[rows - 2]); + rowShift.add((rows - 2) * shift); + + // Hack for radix-2 + if (shift == 1) { + partialProducts.last.last = ~partialProducts.last.last; + } + } + } + + void _addStopSignFlip(List addend, Logic sign) { + if (signed) { + addend.last = ~addend.last; + } else { + addend.add(sign); + } + } + + void _addStopSign(List addend, Logic sign) { + if (signed) { + addend.last = sign; + } else { + addend.add(sign); + } + } + + /// Sign extend the PP array using stop bits without adding a row. + void signExtendCompact() { + // An implementation of + // 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, 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 (_signExtended) { + throw RohdHclException('Partial Product array already sign-extended'); + } + _signExtended = true; + + final lastRow = rows - 1; + final firstAddend = partialProducts[0]; + final lastAddend = partialProducts[lastRow]; + var alignRow0Sign = selector.width - + shift * lastRow - + ((shift > 1) + ? 1 + : signed + ? 1 + : 0); + + if (alignRow0Sign < 0) { + alignRow0Sign = 0; + } + + final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; + + final propagate = + List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); + for (var row = 0; row < rows; row++) { + propagate[row].add(signs[row]); + for (var col = 0; col < 2 * (shift - 1); col++) { + propagate[row].add(partialProducts[row][col]); + } + for (var col = 1; col < propagate[row].length; col++) { + propagate[row][col] = propagate[row][col] & propagate[row][col - 1]; + } + } + final m = + List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); + for (var row = 0; row < rows; row++) { + for (var c = 0; c < shift - 1; c++) { + m[row].add(partialProducts[row][c] ^ propagate[row][c]); + } + m[row].addAll(List.filled(shift - 1, Logic())); + } + + for (var i = shift - 1; i < m[lastRow].length; i++) { + m[lastRow][i] = lastAddend[i] ^ + (i < alignRow0Sign ? propagate[lastRow][i] : Const(0)); + } + + final remainders = List.filled(rows, Logic()); + for (var row = 0; row < lastRow; row++) { + remainders[row] = propagate[row][shift - 1]; + } + remainders[lastRow] <= propagate[lastRow][alignRow0Sign]; + + // Compute Sign extension for row==0 + final firstSign = signed ? firstAddend.last : signs[0]; + final q = [ + firstSign ^ remainders[lastRow], + ~(firstSign & ~remainders[lastRow]), + ]; + q.insertAll(1, List.filled(shift - 1, ~q[1])); + + for (var row = 0; row < rows; row++) { + final addend = partialProducts[row]; + if (row > 0) { + final mLimit = (row == lastRow) ? 2 * (shift - 1) : shift - 1; + for (var i = 0; i < mLimit; i++) { + addend[i] = m[row][i]; + } + // Stop bits + if (signed) { + addend.last = ~addend.last; + } else { + addend.add(~signs[row]); + } + addend + ..insert(0, remainders[row - 1]) + ..addAll(List.filled(shift - 1, Const(1))); + rowShift[row] -= 1; + } else { + for (var i = 0; i < shift - 1; i++) { + firstAddend[i] = m[0][i]; + } + if (signed) { + firstAddend.last = q[0]; + } else { + firstAddend.add(q[0]); + } + firstAddend.addAll(q.getRange(1, q.length)); + } + } + if (shift == 1) { + lastAddend.add(Const(1)); + } + } + + /// Sign extend the PP array using stop bits without adding a row + /// This routine works with different widths of multiplicand/multiplier, + /// an extension of Mohanty, B.K., Choubey designed by + /// Desmond A. Kirkpatrick + void signExtendCompactRect() { + if (_signExtended) { + throw RohdHclException('Partial Product array already sign-extended'); + } + _signExtended = true; + + final lastRow = rows - 1; + final firstAddend = partialProducts[0]; + final lastAddend = partialProducts[lastRow]; + + final firstRowQStart = selector.width - (signed ? 1 : 0); + final lastRowSignPos = shift * lastRow; + + final align = firstRowQStart - lastRowSignPos; + + final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; + + // Compute propgation info for folding sign bits into main rows + final propagate = + List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); + + for (var row = 0; row < rows; row++) { + propagate[row].add(signs[row]); + for (var col = 0; col < 2 * (shift - 1); col++) { + propagate[row].add(partialProducts[row][col]); + } + // Last row has extend sign propagation to Q start + if (row == lastRow) { + var col = 2 * (shift - 1); + while (propagate[lastRow].length <= align) { + propagate[lastRow].add(partialProducts[row][col++]); + } + } + // Now compute the propagation logic + for (var col = 1; col < propagate[row].length; col++) { + propagate[row][col] = propagate[row][col] & propagate[row][col - 1]; + } + } + + // Compute 'm', the prefix of each row to carry the sign of the next row + final m = + List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); + for (var row = 0; row < rows; row++) { + for (var c = 0; c < shift - 1; c++) { + m[row].add(partialProducts[row][c] ^ propagate[row][c]); + } + m[row].addAll(List.filled(shift - 1, Logic())); + } + while (m[lastRow].length < align) { + m[lastRow].add(Logic()); + } + for (var i = shift - 1; i < m[lastRow].length; i++) { + m[lastRow][i] = + lastAddend[i] ^ (i < align ? propagate[lastRow][i] : Const(0)); + } + + final remainders = List.filled(rows, Logic()); + for (var row = 0; row < lastRow; row++) { + remainders[row] = propagate[row][shift - 1]; + } + remainders[lastRow] = propagate[lastRow][align > 0 ? align : 0]; + + // Merge 'm' into the LSBs of each addend + for (var row = 0; row < rows; row++) { + final addend = partialProducts[row]; + if (row > 0) { + final mLimit = (row == lastRow) ? align : shift - 1; + for (var i = 0; i < mLimit; i++) { + addend[i] = m[row][i]; + } + // Stop bits + _addStopSignFlip(addend, ~signs[row]); + addend + ..insert(0, remainders[row - 1]) + ..addAll(List.filled(shift - 1, Const(1))); + rowShift[row] -= 1; + } else { + // First row + for (var i = 0; i < shift - 1; i++) { + firstAddend[i] = m[0][i]; + } + } + } + + // 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 = signed ? firstAddend.last : signs[0]; + final lastSign = remainders[lastRow]; + // Compute Sign extension MSBs for firstRow + final qLen = shift + 1; + final insertSignPos = (align > 0) ? 0 : -align; + final q = List.filled(min(qLen, insertSignPos), firstSign, growable: true); + if (insertSignPos < qLen) { + // At sign insertion position + q.add(firstSign ^ lastSign); + if (insertSignPos == qLen - 1) { + q[insertSignPos] = ~q[insertSignPos]; + q.add(~(firstSign | q[insertSignPos])); + } else { + q + ..addAll(List.filled(qLen - insertSignPos - 2, firstSign & ~lastSign)) + ..add(~(firstSign & ~lastSign)); + } + } + + if (-align >= q.length) { + q.last = ~firstSign; + } + _addStopSign(firstAddend, q[0]); + firstAddend.addAll(q.getRange(1, q.length)); + + if (-align >= q.length) { + final finalCarryRelPos = + lastRowSignPos - selector.width - shift + (signed ? 1 : 0); + final finalCarryRow = (finalCarryRelPos / shift).floor(); + final curRowLength = + partialProducts[finalCarryRow].length + rowShift[finalCarryRow]; + + partialProducts[finalCarryRow] + ..addAll(List.filled(lastRowSignPos - curRowLength, Const(0))) + ..add(remainders[lastRow]); + } + if (shift == 1) { + lastAddend.add(Const(1)); + } + } + + /// Return the actual largest width of all rows + int maxWidth() { + var maxW = 0; + for (var row = 0; row < rows; row++) { + final entry = partialProducts[row]; + if (entry.length + rowShift[row] > maxW) { + maxW = entry.length + rowShift[row]; + } + } + return maxW; + } + + /// Accumulate the partial products and return as BigInt + BigInt evaluate() { + final maxW = maxWidth(); + var accum = BigInt.from(0); + for (var row = 0; row < rows; row++) { + final pp = partialProducts[row].rswizzle().value; + final value = pp.zeroExtend(maxW) << rowShift[row]; + if (pp.isValid) { + accum += value.toBigInt(); + } + } + final sum = LogicValue.ofBigInt(accum, maxW).toBigInt(); + return signed ? sum.toSigned(maxW) : sum; + } + + /// Print out the partial product matrix + @override + String toString() { + final str = StringBuffer(); + + final maxW = maxWidth(); + final nonSignExtendedPad = _signExtended + ? 0 + : shift > 2 + ? shift - 1 + : 1; + // We will print encoding(1-hot multiples and sign) before each row + final shortPrefix = + '99 ${'M='.padRight(2 + selector.radix ~/ 2)} S= : '.length + + 3 * nonSignExtendedPad; + + // print bit position header + str.write(' ' * shortPrefix); + for (var i = maxW - 1; i >= 0; i--) { + final bits = i > 9 ? 2 : 1; + str + ..write('$i') + ..write(' ' * (3 - bits)); + } + str.write('\n'); + // Partial product matrix: rows of multiplicand multiples shift by + // rowshift[row] + for (var row = 0; row < rows; row++) { + final rowStr = (row < 10) ? '0$row' : '$row'; + if (row < encoder.rows) { + final encoding = encoder.getEncoding(row); + if (encoding.multiples.value.isValid) { + str.write('$rowStr M=${encoding.multiples.reversed.value.bitString} ' + 'S=${encoding.sign.value.toInt()}: '); + } else { + str.write(' ' * shortPrefix); + } + } else { + str.write('$rowStr ${'M='.padRight(2 + selector.radix ~/ 2)} S= : '); + } + final entry = partialProducts[row].reversed.toList(); + final prefixCnt = + maxW - (entry.length + rowShift[row]) + nonSignExtendedPad; + str.write(' ' * prefixCnt); + for (var col = 0; col < entry.length; col++) { + str.write('${entry[col].value.bitString} '); + } + final suffixCnt = rowShift[row]; + final value = entry.swizzle().value.zeroExtend(maxW) << suffixCnt; + final intValue = value.isValid ? value.toBigInt() : BigInt.from(-1); + str + ..write(' ' * suffixCnt) + ..write(': ${value.bitString}') + ..write(' = ${value.isValid ? intValue : ""}' + ' (${value.isValid ? intValue.toSigned(maxW) : ""})\n'); + } + // Compute and print binary representation from accumulated value + // Later: we will compare with a compression tree result + str + ..write('=' * (shortPrefix + 3 * maxW)) + ..write('\n') + ..write(' ' * shortPrefix); + + final sum = LogicValue.ofBigInt(evaluate(), maxW); + // print out the sum as a MSB-first bitvector + for (final elem in [for (var i = 0; i < maxW; i++) sum[i]].reversed) { + str.write('${elem.toInt()} '); + } + final val = evaluate(); + str.write(': ${sum.bitString} = ' + '${val.toUnsigned(maxW)}'); + if (_signExtended) { + str.write(' ($val)\n\n'); + } + return str.toString(); + } +} diff --git a/lib/src/arithmetic/sign_magnitude_adder.dart b/lib/src/arithmetic/sign_magnitude_adder.dart index c26f983e..b4db5295 100644 --- a/lib/src/arithmetic/sign_magnitude_adder.dart +++ b/lib/src/arithmetic/sign_magnitude_adder.dart @@ -12,11 +12,10 @@ import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; /// An Adder which performs one's complement arithmetic using an unsigned -/// adder that is passed in using a functor -/// -- Requires that if the larger magnitude number is negative it -/// must be the first 'a' argument -/// We cannot enforce because this may be a smaller mantissa in -/// a larger magnitude negative floating point number (no asserts please) +/// adder that is passed in using a functor. This requires that a larger +/// magnitude negative argumnet mus be the first 'a' argument. Enfording this +/// is challenging in floating point as a smaller mantissa may be larger +/// in magnitude due to the mantissa. class SignMagnitudeAdder extends Adder { /// The sign of the first input @protected diff --git a/test/arithmetic/compressor_test.dart b/test/arithmetic/compressor_test.dart index fcd6385f..a2a19289 100644 --- a/test/arithmetic/compressor_test.dart +++ b/test/arithmetic/compressor_test.dart @@ -11,10 +11,7 @@ import 'dart:io'; import 'dart:math'; import 'package:rohd/rohd.dart'; -import 'package:rohd_hcl/src/arithmetic/booth.dart'; -import 'package:rohd_hcl/src/arithmetic/compressor.dart'; -import 'package:rohd_hcl/src/arithmetic/parallel_prefix_operations.dart'; -import 'package:rohd_hcl/src/utils.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:test/test.dart'; enum SignExtension { brute, stop, compact } diff --git a/test/arithmetic/booth_test.dart b/test/arithmetic/multiplier_encoder_test.dart similarity index 86% rename from test/arithmetic/booth_test.dart rename to test/arithmetic/multiplier_encoder_test.dart index 74244361..cbd661d2 100644 --- a/test/arithmetic/booth_test.dart +++ b/test/arithmetic/multiplier_encoder_test.dart @@ -1,7 +1,7 @@ // Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // -// booth_test.dart +// multiplier_encoder_test.dart // Tests for Booth encoding // // 2024 May 21 @@ -9,9 +9,9 @@ import 'dart:io'; import 'dart:math'; + import 'package:rohd/rohd.dart'; -import 'package:rohd_hcl/src/arithmetic/booth.dart'; -import 'package:rohd_hcl/src/utils.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:test/test.dart'; enum SignExtension { brute, stop, compact, compactRect } @@ -28,7 +28,6 @@ void checkEvaluateExhaustive(PartialProductGenerator pp) { final Y = pp.signed ? j.toSigned(widthY) : j.toUnsigned(widthY); pp.multiplicand.put(X); pp.multiplier.put(Y); - // print('$X * $Y'); expect(pp.evaluate(), equals(X * Y)); } } @@ -45,7 +44,6 @@ void checkEvaluateRandom(PartialProductGenerator pp, int nSamples) { final Y = pp.signed ? rY.toSigned(widthY) : rY; pp.multiplicand.put(X); pp.multiplier.put(Y); - // print('$X * $Y'); expect(pp.evaluate(), equals(X * Y)); } } @@ -77,7 +75,6 @@ void main() { final pp = PartialProductGenerator(logicX, logicY, encoder); // ignore: cascade_invocations pp.signExtendCompact(); - // stdout.write(pp); // Add a row for addend final l = [for (var i = 0; i < logicZ.width; i++) logicZ[i]]; // ignore: cascade_invocations @@ -87,14 +84,10 @@ void main() { pp.partialProducts.add(l); pp.rowShift.add(0); - // stdout.write('Test: $i($X) * $j($Y) + $k($Z)= $product vs ' - // '${pp.evaluate(signed: true)}\n'); if (pp.evaluate() != product) { stdout.write('Fail: $X * $Y: ${pp.evaluate()} vs expected $product\n'); - // ignore: cascade_invocations - // stdout.write(pp); } - // expect(pp.evaluate(signed: true), equals(product)); + expect(pp.evaluate(), equals(product)); }); // TODO(dakdesmond): Figure out minimum widths! @@ -123,7 +116,6 @@ void main() { case SignExtension.compactRect: pp.signExtendCompactRect(); } - // testPartialProductExhaustive(pp); checkEvaluateExhaustive(pp); } } @@ -139,6 +131,7 @@ void main() { for (var width = 3 + shift + 1; width < 3 + shift * 2 + 1; width++) { for (var skew = -3; skew < shift * 2; skew++) { // Only some sign extension routines have rectangular support + // Commented out rectangular extension routines for speedup for (final signExtension in [ // SignExtension.brute, // SignExtension.stop, @@ -167,20 +160,15 @@ void main() { }); test('Rectangle Q collision tests,', () async { - final alignTest = >[]; - // These collide with the normal q extension bits // These are unsigned tests - // ignore: cascade_invocations - alignTest - ..insert(0, [(2, 5, 0), (4, 7, 1), (8, 7, 2), (16, 9, 3)]) - ..insert(1, [(2, 5, 1), (4, 6, 2), (8, 9, 3), (16, 8, 4)]) - ..insert(2, [(2, 5, 2), (4, 7, 3), (8, 8, 4), (16, 11, 5)]) - ..insert(3, [(4, 6, 4), (8, 7, 5), (16, 10, 6)]) - ..insert(4, [(8, 9, 6), (16, 9, 7)]) - ..insert(5, [(16, 8, 8)]) - // - ; + final alignTest = >[] + ..insert(0, [(2, 5, 0), (4, 7, 1), (8, 7, 2), (16, 9, 3)]) + ..insert(1, [(2, 5, 1), (4, 6, 2), (8, 9, 3), (16, 8, 4)]) + ..insert(2, [(2, 5, 2), (4, 7, 3), (8, 8, 4), (16, 11, 5)]) + ..insert(3, [(4, 6, 4), (8, 7, 5), (16, 10, 6)]) + ..insert(4, [(8, 9, 6), (16, 9, 7)]) + ..insert(5, [(16, 8, 8)]); for (final alignList in alignTest) { for (final align in alignList) { diff --git a/test/arithmetic/multiplier_test.dart b/test/arithmetic/multiplier_test.dart index 3d510757..0e440b74 100644 --- a/test/arithmetic/multiplier_test.dart +++ b/test/arithmetic/multiplier_test.dart @@ -22,7 +22,6 @@ void checkMultiplyAccumulate( mod.b.put(bB); // ignore: invalid_use_of_protected_member mod.c.put(bC); - // print('$bA, $bB, $bC'); final result = mod.signed ? mod.accumulate.value.toBigInt().toSigned(mod.accumulate.width) @@ -41,21 +40,19 @@ void testMultiplyAccumulateRandom(int width, int iterations, final multiplyOnly = mod is MultiplyOnly; await mod.build(); final signed = mod.signed; + final value = Random(47); for (var i = 0; i < iterations; i++) { final bA = signed - ? Random().nextLogicValue(width: width).toBigInt().toSigned(width) - : Random().nextLogicValue(width: width).toBigInt().toUnsigned(width); + ? value.nextLogicValue(width: width).toBigInt().toSigned(width) + : value.nextLogicValue(width: width).toBigInt().toUnsigned(width); final bB = signed - ? Random().nextLogicValue(width: width).toBigInt().toSigned(width) - : Random().nextLogicValue(width: width).toBigInt().toUnsigned(width); + ? value.nextLogicValue(width: width).toBigInt().toSigned(width) + : value.nextLogicValue(width: width).toBigInt().toUnsigned(width); final bC = multiplyOnly ? BigInt.zero : signed - ? Random().nextLogicValue(width: width).toBigInt().toSigned(width) - : Random() - .nextLogicValue(width: width) - .toBigInt() - .toUnsigned(width); + ? value.nextLogicValue(width: width).toBigInt().toSigned(width) + : value.nextLogicValue(width: width).toBigInt().toUnsigned(width); checkMultiplyAccumulate(mod, bA, bB, bC); } }); From 77a47a4e6fb5f84184e3b1d81ff6fb2dca5ec1f5 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Fri, 9 Aug 2024 08:31:43 -0700 Subject: [PATCH 12/38] reorganized multiplier code --- .../{compressor.dart => addend_compressor.dart} | 15 ++++++++------- lib/src/arithmetic/multiplicand_selector.dart | 2 +- lib/src/arithmetic/multiplier.dart | 1 + lib/src/arithmetic/multiplier_lib.dart | 15 +++++++++++++++ lib/src/arithmetic/partial_product_generator.dart | 1 + 5 files changed, 26 insertions(+), 8 deletions(-) rename lib/src/arithmetic/{compressor.dart => addend_compressor.dart} (96%) create mode 100644 lib/src/arithmetic/multiplier_lib.dart diff --git a/lib/src/arithmetic/compressor.dart b/lib/src/arithmetic/addend_compressor.dart similarity index 96% rename from lib/src/arithmetic/compressor.dart rename to lib/src/arithmetic/addend_compressor.dart index 5304dcd3..8852607f 100644 --- a/lib/src/arithmetic/compressor.dart +++ b/lib/src/arithmetic/addend_compressor.dart @@ -1,7 +1,7 @@ // Copyright (C) 2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // -// compressor.dart +// addend_compressor.dart // Column compression of partial prodcuts // // 2024 June 04 @@ -11,12 +11,13 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; -import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; +import 'package:rohd_hcl/src/utils.dart'; // TODO(desmonddak): Logic and LogicValue majority() functions /// Base class for column compressor function -abstract class Compressor extends Module { +abstract class AddendCompressor extends Module { /// Input bits to compress @protected late final Logic compressBits; @@ -28,7 +29,7 @@ abstract class Compressor extends Module { Logic get carry => output('carry'); /// Construct a column compressor - Compressor(Logic compressBits) { + AddendCompressor(Logic compressBits) { this.compressBits = addInput( 'compressBits', compressBits, @@ -40,7 +41,7 @@ abstract class Compressor extends Module { } /// 2-input column compressor (half-adder) -class Compressor2 extends Compressor { +class Compressor2 extends AddendCompressor { /// Construct a 2-input compressor (half-adder) Compressor2(super.compressBits) { sum <= compressBits.xor(); @@ -49,7 +50,7 @@ class Compressor2 extends Compressor { } /// 3-input column compressor (full-adder) -class Compressor3 extends Compressor { +class Compressor3 extends AddendCompressor { /// Construct a 3-input column compressor (full-adder) Compressor3(super.compressBits) { sum <= compressBits.xor(); @@ -298,7 +299,7 @@ class ColumnCompressor { final first = queue.removeFirst(); final second = queue.removeFirst(); final inputs = [first, second]; - Compressor compressor; + AddendCompressor compressor; if (depth > 3) { inputs.add(queue.removeFirst()); compressor = diff --git a/lib/src/arithmetic/multiplicand_selector.dart b/lib/src/arithmetic/multiplicand_selector.dart index fcc8727f..47671ed6 100644 --- a/lib/src/arithmetic/multiplicand_selector.dart +++ b/lib/src/arithmetic/multiplicand_selector.dart @@ -9,7 +9,7 @@ import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -import 'package:rohd_hcl/src/arithmetic/multiplier_encoder.dart'; +import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; /// A class accessing the multiples of the multiplicand at a position class MultiplicandSelector { diff --git a/lib/src/arithmetic/multiplier.dart b/lib/src/arithmetic/multiplier.dart index 18f11c99..1538a7a6 100644 --- a/lib/src/arithmetic/multiplier.dart +++ b/lib/src/arithmetic/multiplier.dart @@ -11,6 +11,7 @@ import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; /// An abstract class for all multiplier implementations. abstract class Multiplier extends Module { diff --git a/lib/src/arithmetic/multiplier_lib.dart b/lib/src/arithmetic/multiplier_lib.dart new file mode 100644 index 00000000..47760e5f --- /dev/null +++ b/lib/src/arithmetic/multiplier_lib.dart @@ -0,0 +1,15 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// multiplier_lib.dart +// Consolidate exports of the main components for multiplication: encoder, +// selector, partial_product_generator, compressor +// +// 2024 August 9 +// Author: Desmond Kirkpatrick +// +// + +// Just exporting encoder exports all the symbols we use +export './compressor.dart'; +export './multiplier_encoder.dart'; diff --git a/lib/src/arithmetic/partial_product_generator.dart b/lib/src/arithmetic/partial_product_generator.dart index 3ba3d888..e87199af 100644 --- a/lib/src/arithmetic/partial_product_generator.dart +++ b/lib/src/arithmetic/partial_product_generator.dart @@ -11,6 +11,7 @@ import 'dart:math'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; /// A class that generates a set of partial products. Essentially a set of /// shifted rows of [Logic] addends generated by Booth recoding and From a7527c0a7ec500eb8911604c01ae687502c88a2b Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Fri, 9 Aug 2024 08:38:47 -0700 Subject: [PATCH 13/38] git failed to add all changes. --- lib/rohd_hcl.dart | 2 -- .../models/ready_valid_bfm/ready_valid_transmitter_driver.dart | 1 - test/arithmetic/compressor_test.dart | 1 + test/arithmetic/multiplier_encoder_test.dart | 1 + 4 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/rohd_hcl.dart b/lib/rohd_hcl.dart index 0f9c6cd5..243ceb0f 100644 --- a/lib/rohd_hcl.dart +++ b/lib/rohd_hcl.dart @@ -4,9 +4,7 @@ export 'src/arbiters/arbiters.dart'; export 'src/arithmetic/adder.dart'; export 'src/arithmetic/carry_save_mutiplier.dart'; -export 'src/arithmetic/compressor.dart'; export 'src/arithmetic/multiplier.dart'; -export 'src/arithmetic/multiplier_encoder.dart'; export 'src/arithmetic/parallel_prefix_operations.dart'; export 'src/arithmetic/ripple_carry_adder.dart'; export 'src/arithmetic/sign_magnitude_adder.dart'; diff --git a/lib/src/models/ready_valid_bfm/ready_valid_transmitter_driver.dart b/lib/src/models/ready_valid_bfm/ready_valid_transmitter_driver.dart index ea4cec46..f1ecdcc5 100644 --- a/lib/src/models/ready_valid_bfm/ready_valid_transmitter_driver.dart +++ b/lib/src/models/ready_valid_bfm/ready_valid_transmitter_driver.dart @@ -12,7 +12,6 @@ import 'dart:math'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -import 'package:rohd_hcl/src/models/ready_valid_bfm/ready_valid_packet.dart'; import 'package:rohd_vf/rohd_vf.dart'; /// An [Agent] for transmitting over a ready/valid protocol. diff --git a/test/arithmetic/compressor_test.dart b/test/arithmetic/compressor_test.dart index a2a19289..f991367f 100644 --- a/test/arithmetic/compressor_test.dart +++ b/test/arithmetic/compressor_test.dart @@ -12,6 +12,7 @@ import 'dart:math'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; import 'package:test/test.dart'; enum SignExtension { brute, stop, compact } diff --git a/test/arithmetic/multiplier_encoder_test.dart b/test/arithmetic/multiplier_encoder_test.dart index cbd661d2..a08adf19 100644 --- a/test/arithmetic/multiplier_encoder_test.dart +++ b/test/arithmetic/multiplier_encoder_test.dart @@ -12,6 +12,7 @@ import 'dart:math'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; import 'package:test/test.dart'; enum SignExtension { brute, stop, compact, compactRect } From fe188799fe6f9b3da88758febfc780a17d76a4d5 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Fri, 9 Aug 2024 08:46:36 -0700 Subject: [PATCH 14/38] save issue or git issue --- lib/src/arithmetic/multiplier_lib.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/arithmetic/multiplier_lib.dart b/lib/src/arithmetic/multiplier_lib.dart index 47760e5f..34270f6c 100644 --- a/lib/src/arithmetic/multiplier_lib.dart +++ b/lib/src/arithmetic/multiplier_lib.dart @@ -11,5 +11,5 @@ // // Just exporting encoder exports all the symbols we use -export './compressor.dart'; +export './addend_compressor.dart'; export './multiplier_encoder.dart'; From 3e29ad13e2eed996d9a7a1603b099a24f90839da Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Fri, 9 Aug 2024 09:31:06 -0700 Subject: [PATCH 15/38] test cleanup and more reorg of multiplier code --- test/arithmetic/addend_compressor_test.dart | 106 +++++++++++++++++ test/arithmetic/compressor_test.dart | 114 ------------------- test/arithmetic/multiplier_encoder_test.dart | 23 ++-- 3 files changed, 118 insertions(+), 125 deletions(-) create mode 100644 test/arithmetic/addend_compressor_test.dart delete mode 100644 test/arithmetic/compressor_test.dart diff --git a/test/arithmetic/addend_compressor_test.dart b/test/arithmetic/addend_compressor_test.dart new file mode 100644 index 00000000..b48da59d --- /dev/null +++ b/test/arithmetic/addend_compressor_test.dart @@ -0,0 +1,106 @@ +// Copyright (C) 2023-2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// compressor_test.dart +// Tests for the select interface of Booth encoding +// +// 2024 June 04 +// Author: Desmond Kirkpatrick + +import 'dart:io'; +import 'dart:math'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; +import 'package:test/test.dart'; + +enum SignExtension { brute, stop, compact } + +void testCompressionExhaustive(PartialProductGenerator pp) { + final widthX = pp.selector.multiplicand.width; + final widthY = pp.encoder.multiplier.width; + + final compressor = ColumnCompressor(pp); + + final limitX = pow(2, widthX); + final limitY = pow(2, widthY); + for (var i = 0; i < limitX; i++) { + for (var j = 0; j < limitY; j++) { + final X = pp.signed + ? BigInt.from(i).toSigned(widthX) + : BigInt.from(i).toUnsigned(widthX); + final Y = pp.signed + ? BigInt.from(j).toSigned(widthY) + : BigInt.from(j).toUnsigned(widthY); + final product = X * Y; + + pp.multiplicand.put(X); + pp.multiplier.put(Y); + final value = pp.evaluate(); + expect(value, equals(product), + reason: 'Fail: $i($X) * $j($Y): $value ' + 'vs expected $product' + '\n$pp'); + final evaluateValue = compressor.evaluate(); + if (evaluateValue != product) { + stdout + ..write('Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $evaluateValue ' + 'vs expected $product\n') + ..write(pp); + } + compressor.compress(); + final compressedValue = compressor.evaluate(); + expect(compressedValue, equals(product), + reason: 'Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $compressedValue ' + 'vs expected $product' + '\n$pp'); + final compressedLogicValue = compressor.evaluate(logic: true); + expect(compressedLogicValue, equals(product), + reason: + 'Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $compressedLogicValue ' + 'vs expected $product' + '\n$pp'); + + final a = compressor.extractRow(0); + final b = compressor.extractRow(1); + final adder = ParallelPrefixAdder(a, b, KoggeStone.new); + final adderValue = + adder.out.value.toBigInt().toSigned(compressor.columns.length); + expect(adderValue, equals(product), + reason: 'Fail: $i($X)[$widthX] * $j($Y)[$widthY]: ' + '$adderValue vs expected $product' + '\n$pp'); + } + } +} + +void main() { + test('exhaustive compression evaluate: square radix-4, all SignExtension', + () async { + stdout.write('\n'); + + for (final signed in [false, true]) { + for (var radix = 4; radix < 32; radix *= 2) { + final encoder = RadixEncoder(2); + // stdout.write('encoding with radix=$radix\n'); + final shift = log2Ceil(encoder.radix); + for (var width = shift + 1; width < 2 * shift + 1; width++) { + for (final signExtension in SignExtension.values) { + final pp = PartialProductGenerator(Logic(name: 'X', width: width), + Logic(name: 'Y', width: width), encoder, + signed: signed); + switch (signExtension) { + case SignExtension.brute: + pp.bruteForceSignExtend(); + case SignExtension.stop: + pp.signExtendWithStopBitsRect(); + case SignExtension.compact: + pp.signExtendCompact(); + } + testCompressionExhaustive(pp); + } + } + } + } + }); +} diff --git a/test/arithmetic/compressor_test.dart b/test/arithmetic/compressor_test.dart deleted file mode 100644 index f991367f..00000000 --- a/test/arithmetic/compressor_test.dart +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (C) 2023-2024 Intel Corporation -// SPDX-License-Identifier: BSD-3-Clause -// -// compressor_test.dart -// Tests for the select interface of Booth encoding -// -// 2024 June 04 -// Author: Desmond Kirkpatrick - -import 'dart:io'; -import 'dart:math'; - -import 'package:rohd/rohd.dart'; -import 'package:rohd_hcl/rohd_hcl.dart'; -import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; -import 'package:test/test.dart'; - -enum SignExtension { brute, stop, compact } - -void testCompressionExhaustive(PartialProductGenerator pp) { - final widthX = pp.selector.multiplicand.width; - final widthY = pp.encoder.multiplier.width; - - final compressor = ColumnCompressor(pp); - - final limitX = pow(2, widthX); - final limitY = pow(2, widthY); - for (var i = 0; i < limitX; i++) { - for (var j = 0; j < limitY; j++) { - final X = BigInt.from(i).toSigned(widthX); - final Y = BigInt.from(j).toSigned(widthY); - final product = X * Y; - - pp.multiplicand.put(X); - pp.multiplier.put(Y); - // stdout.write('$i($X) * $j($Y): should be $product\n'); - if (pp.evaluate() != product) { - stdout - ..write('Fail: $i($X) * $j($Y): ${pp.evaluate()} ' - 'vs expected $product\n') - ..write(pp); - } - expect(pp.evaluate(), equals(product)); - final evaluateValue = compressor.evaluate(); - if (evaluateValue != product) { - stdout - ..write('Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $evaluateValue ' - 'vs expected $product\n') - ..write(pp); - } - compressor.compress(); - final compressedValue = compressor.evaluate(); - if (compressedValue != product) { - stdout - ..write('Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $compressedValue ' - 'vs expected $product\n') - ..write(pp); - } - expect(compressedValue, equals(product)); - final compressedLogicValue = compressor.evaluate(logic: true); - if (compressedLogicValue != product) { - stdout - ..write( - 'Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $compressedLogicValue ' - 'vs expected $product\n') - ..write(pp); - } - expect(compressedLogicValue, equals(product)); - - final a = compressor.extractRow(0); - final b = compressor.extractRow(1); - final adder = ParallelPrefixAdder(a, b, KoggeStone.new); - final adderValue = - adder.out.value.toBigInt().toSigned(compressor.columns.length); - if (adderValue != product) { - stdout - ..write('Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $adderValue ' - 'vs expected $product\n') - ..write(pp); - } - expect(adderValue, equals(product)); - } - } -} - -void main() { - test('exhaustive compression evaluate: square radix-4, all SignExtension', - () async { - stdout.write('\n'); - - for (var radix = 4; radix < 8; radix *= 2) { - final encoder = RadixEncoder(2); - // stdout.write('encoding with radix=$radix\n'); - final shift = log2Ceil(encoder.radix); - for (var width = shift + 1; width < shift + 2; width++) { - // stdout.write('\tTesting width=$width\n'); - for (final signExtension in SignExtension.values) { - final pp = PartialProductGenerator(Logic(name: 'X', width: width), - Logic(name: 'Y', width: width), encoder); - switch (signExtension) { - case SignExtension.brute: - pp.bruteForceSignExtend(); - case SignExtension.stop: - // pp.signExtendWithStopBitsRect(); - case SignExtension.compact: - pp.signExtendCompact(); - } - // stdout.write('\tTesting extension=$signExtension\n'); - testCompressionExhaustive(pp); - } - } - } - }); -} diff --git a/test/arithmetic/multiplier_encoder_test.dart b/test/arithmetic/multiplier_encoder_test.dart index a08adf19..ecf9bd44 100644 --- a/test/arithmetic/multiplier_encoder_test.dart +++ b/test/arithmetic/multiplier_encoder_test.dart @@ -29,7 +29,9 @@ void checkEvaluateExhaustive(PartialProductGenerator pp) { final Y = pp.signed ? j.toSigned(widthY) : j.toUnsigned(widthY); pp.multiplicand.put(X); pp.multiplier.put(Y); - expect(pp.evaluate(), equals(X * Y)); + final value = pp.evaluate(); + expect(value, equals(X * Y), + reason: '$X * $Y = $value should be ${X * Y}'); } } } @@ -45,7 +47,8 @@ void checkEvaluateRandom(PartialProductGenerator pp, int nSamples) { final Y = pp.signed ? rY.toSigned(widthY) : rY; pp.multiplicand.put(X); pp.multiplier.put(Y); - expect(pp.evaluate(), equals(X * Y)); + final value = pp.evaluate(); + expect(value, equals(X * Y), reason: '$X * $Y = $value should be ${X * Y}'); } } @@ -73,15 +76,14 @@ void main() { logicX.put(X); logicY.put(Y); logicZ.put(Z); - final pp = PartialProductGenerator(logicX, logicY, encoder); - // ignore: cascade_invocations - pp.signExtendCompact(); + final pp = PartialProductGenerator(logicX, logicY, encoder) + ..signExtendCompact(); // Add a row for addend - final l = [for (var i = 0; i < logicZ.width; i++) logicZ[i]]; - // ignore: cascade_invocations - l - ..add(Const(0)) // ~Sign in our sign extension form - ..add(Const(1)); + final l = [ + for (var i = 0; i < logicZ.width; i++) logicZ[i], + Const(0), + Const(1) + ]; pp.partialProducts.add(l); pp.rowShift.add(0); @@ -100,7 +102,6 @@ void main() { for (final signed in [false, true]) { for (var radix = 4; radix < 8; radix *= 2) { final encoder = RadixEncoder(radix); - // stdout.write('encoding with radix=$radix\n'); final shift = log2Ceil(encoder.radix); for (var width = shift + 1; width < shift * 2 + 1; width++) { for (final signExtension in SignExtension.values) { From 2a29ceb6681a2e99a266fbe1fdcdcfcacbbd8e4c Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Fri, 9 Aug 2024 15:05:53 -0700 Subject: [PATCH 16/38] cleanup compressors --- lib/src/arithmetic/addend_compressor.dart | 139 ++++++++---------- lib/src/arithmetic/multiplier.dart | 10 +- .../arithmetic/partial_product_generator.dart | 9 ++ test/arithmetic/addend_compressor_test.dart | 47 +++++- test/arithmetic/adder_test.dart | 3 +- test/arithmetic/multiplier_encoder_test.dart | 76 +++++++++- 6 files changed, 190 insertions(+), 94 deletions(-) diff --git a/lib/src/arithmetic/addend_compressor.dart b/lib/src/arithmetic/addend_compressor.dart index 8852607f..60db65f8 100644 --- a/lib/src/arithmetic/addend_compressor.dart +++ b/lib/src/arithmetic/addend_compressor.dart @@ -14,8 +14,6 @@ import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; import 'package:rohd_hcl/src/utils.dart'; -// TODO(desmonddak): Logic and LogicValue majority() functions - /// Base class for column compressor function abstract class AddendCompressor extends Module { /// Input bits to compress @@ -78,7 +76,7 @@ class CompressTerm implements Comparable { late final CompressTermType type; /// The inputs that drove this Term - late List inputs = []; + late final List inputs; /// The row of the terminal final int row; @@ -87,7 +85,7 @@ class CompressTerm implements Comparable { final int col; /// The Logic wire of the term - final logic = Logic(); + final Logic logic; /// Estimated delay of the output of this CompessTerm late double delay; @@ -99,32 +97,18 @@ class CompressTerm implements Comparable { static const carryDelay = 0.75; /// CompressTerm constructor - CompressTerm(this.type, this.row, this.col) : delay = 0.0; - - /// Create a sum Term - factory CompressTerm.sumTerm(List args, int row, int col) { - final term = CompressTerm(CompressTermType.sum, row, col); - // ignore: cascade_invocations - term.inputs = args; - for (final i in term.inputs) { - if (i.delay + sumDelay > term.delay) { - term.delay = i.delay + sumDelay; - } - } - return term; - } - - /// Create a carry Term - factory CompressTerm.carryTerm(List args, int row, int col) { - final term = CompressTerm(CompressTermType.carry, row, col); - // ignore: cascade_invocations - term.inputs = args; - for (final i in term.inputs) { - if (i.delay + carryDelay > term.delay) { - term.delay = i.delay + carryDelay; + CompressTerm(this.type, this.logic, this.inputs, this.row, this.col) { + delay = 0.0; + final deltaDelay = switch (type) { + CompressTermType.carry => carryDelay, + CompressTermType.sum => sumDelay, + CompressTermType.pp => 0.0 + }; + for (final i in inputs) { + if (i.delay + deltaDelay > delay) { + delay = i.delay + deltaDelay; } } - return term; } @override int compareTo(Object other) { @@ -134,6 +118,41 @@ class CompressTerm implements Comparable { return delay > other.delay ? 1 : (delay < other.delay ? -1 : 0); } + /// Evaluate the logic value of a given CompressTerm. + LogicValue evaluate() { + late LogicValue value; + switch (type) { + case CompressTermType.pp: + value = logic.value; + case CompressTermType.sum: + // xor the eval of the terms + final termValues = [for (final term in inputs) term.evaluate()]; + final sum = termValues.swizzle().xor(); + value = sum; + case CompressTermType.carry: + final termValues = [for (final term in inputs) term.evaluate()]; + final termValuesInt = [ + for (var i = 0; i < termValues.length; i++) termValues[i].toInt() + ]; + + final count = (termValuesInt.isNotEmpty) + ? termValuesInt.reduce((c, term) => c + term) + : 0; + final majority = + (count > termValues.length ~/ 2 ? LogicValue.one : LogicValue.zero); + // Alternative method: + // final x = Logic(width: termValues.length); + // x.put(termValues.swizzle()); + // final newCount = Count(x).index.value.toInt(); + // stdout.write('count=$count newCount=$newCount\n'); + // if (newCount != count) { + // throw RohdHclException('count=$count newCount=$newCount'); + // } + value = majority; + } + return value; + } + @override String toString() { final str = StringBuffer(); @@ -167,50 +186,13 @@ class ColumnCompressor { for (var row = 0; row < pp.rows; row++) { for (var col = 0; col < pp.partialProducts[row].length; col++) { final trueColumn = pp.rowShift[row] + col; - final term = CompressTerm(CompressTermType.pp, row, trueColumn); - term.logic <= pp.partialProducts[row][col]; + final term = CompressTerm(CompressTermType.pp, + pp.partialProducts[row][col], [], row, trueColumn); columns[trueColumn].add(term); } } } -// TODO(desmonddak): This cannot run without real logic values due to toInt() -// which forces the user to assign values to the inputs first -// We need a way to build the CompressionTerm without actual values -// e.g., there needs to be a way to do the reductions with 'X' values - /// Evaluate the logic value of a given CompressTerm - LogicValue evaluateTerm(CompressTerm term) { - switch (term.type) { - case CompressTermType.pp: - return term.logic.value; - case CompressTermType.sum: - // xor the eval of the terms - final termValues = [for (term in term.inputs) evaluateTerm(term)]; - final sum = termValues.swizzle().xor(); - return sum; - case CompressTermType.carry: - final termValues = [for (term in term.inputs) evaluateTerm(term)]; - final termValuesInt = [ - for (var i = 0; i < termValues.length; i++) termValues[i].toInt() - ]; - - final count = (termValuesInt.isNotEmpty) - ? termValuesInt.reduce((c, term) => c + term) - : 0; - final majority = - (count > termValues.length ~/ 2 ? LogicValue.one : LogicValue.zero); - // Alternative method: - // final x = Logic(width: termValues.length); - // x.put(termValues.swizzle()); - // final newCount = Count(x).index.value.toInt(); - // stdout.write('count=$count newCount=$newCount\n'); - // if (newCount 1= count) { - // throw RohdHclException('count=$count newCount=$newCount'); - // } - return majority; - } - } - /// Return the longest column length int longestColumn() => columns.reduce((a, b) => a.length > b.length ? a : b).length; @@ -234,8 +216,8 @@ class ColumnCompressor { /// Evaluate the (un)compressed partial product array /// logic=true will read the logic gate outputs at each level - /// print=true will print out the array - BigInt evaluate({bool print = false, bool logic = false}) { + /// printOut=true will print out the array + BigInt evaluate({bool printOut = false, bool logic = false}) { final ts = StringBuffer(); final rows = longestColumn(); final width = pp.maxWidth(); @@ -247,19 +229,19 @@ class ColumnCompressor { final colList = columns[col].toList(); if (row < colList.length) { final value = - logic ? colList[row].logic.value : evaluateTerm(colList[row]); + logic ? colList[row].logic.value : (colList[row].evaluate()); rowBits.add(value); - if (print) { + if (printOut) { ts.write('\t${value.bitString}'); } - } else if (print) { + } else if (printOut) { ts.write('\t'); } } rowBits.addAll(List.filled(pp.rowShift[row], LogicValue.zero)); final val = rowBits.swizzle().zeroExtend(width).toBigInt(); accum += val; - if (print) { + if (printOut) { ts.write('\t${rowBits.swizzle().zeroExtend(width).bitString} ($val)'); if (row == rows - 1) { ts.write(' Total=${accum.toSigned(width)}\n'); @@ -269,6 +251,9 @@ class ColumnCompressor { } } } + if (printOut) { + print(ts); + } return accum.toSigned(width); } @@ -308,15 +293,15 @@ class ColumnCompressor { compressor = Compressor2([for (final i in inputs) i.logic].swizzle()); } - final t = CompressTerm.sumTerm(inputs, 0, col); - t.logic <= compressor.sum; + final t = CompressTerm( + CompressTermType.sum, compressor.sum, inputs, 0, col); terms.add(t); columns[col].add(t); if (col < columns.length - 1) { - final t = CompressTerm.carryTerm(inputs, 0, col); + final t = CompressTerm( + CompressTermType.carry, compressor.carry, inputs, 0, col); columns[col + 1].add(t); terms.add(t); - t.logic <= compressor.carry; } } } diff --git a/lib/src/arithmetic/multiplier.dart b/lib/src/arithmetic/multiplier.dart index 1538a7a6..9bc85c3d 100644 --- a/lib/src/arithmetic/multiplier.dart +++ b/lib/src/arithmetic/multiplier.dart @@ -84,14 +84,10 @@ class CompressionTreeMultiplier extends Multiplier { Logic() ], (a, b) => Logic()).runtimeType}') { final product = addOutput('product', width: a.width + b.width); - final pp = - PartialProductGenerator(a, b, RadixEncoder(radix), signed: signed); - // ignore: cascade_invocations - pp.signExtendCompact(); - final compressor = ColumnCompressor(pp); - // ignore: cascade_invocations - compressor.compress(); + PartialProductGenerator(a, b, RadixEncoder(radix), signed: signed) + ..signExtendCompact(); + final compressor = ColumnCompressor(pp)..compress(); final adder = ParallelPrefixAdder( compressor.extractRow(0), compressor.extractRow(1), ppTree); product <= adder.out.slice(a.width + b.width - 1, 0); diff --git a/lib/src/arithmetic/partial_product_generator.dart b/lib/src/arithmetic/partial_product_generator.dart index e87199af..95655a17 100644 --- a/lib/src/arithmetic/partial_product_generator.dart +++ b/lib/src/arithmetic/partial_product_generator.dart @@ -58,6 +58,15 @@ class PartialProductGenerator { encoder = MultiplierEncoder(multiplier, radixEncoder, signed: signed); selector = MultiplicandSelector(radixEncoder.radix, multiplicand, signed: signed); + + if (multiplicand.width < selector.shift) { + throw RohdHclException('multiplicand width must be greater than ' + '${selector.shift}'); + } + if (multiplier.width < (selector.shift + (signed ? 1 : 0))) { + throw RohdHclException('multiplier width must be greater than ' + '${selector.shift + (signed ? 1 : 0)}'); + } _build(); } diff --git a/test/arithmetic/addend_compressor_test.dart b/test/arithmetic/addend_compressor_test.dart index b48da59d..2ba06a62 100644 --- a/test/arithmetic/addend_compressor_test.dart +++ b/test/arithmetic/addend_compressor_test.dart @@ -80,8 +80,8 @@ void main() { stdout.write('\n'); for (final signed in [false, true]) { - for (var radix = 4; radix < 32; radix *= 2) { - final encoder = RadixEncoder(2); + for (var radix = 4; radix < 4; radix *= 2) { + final encoder = RadixEncoder(radix); // stdout.write('encoding with radix=$radix\n'); final shift = log2Ceil(encoder.radix); for (var width = shift + 1; width < 2 * shift + 1; width++) { @@ -103,4 +103,47 @@ void main() { } } }); + test('single compressor evaluate mac', () async { + const widthX = 6; + const widthY = 9; + final a = Logic(name: 'a', width: widthX); + final b = Logic(name: 'b', width: widthY); + + const av = 4; + const bv = 14; + for (final signed in [true, false]) { + final bA = signed + ? BigInt.from(av).toSigned(widthX) + : BigInt.from(av).toUnsigned(widthX); + final bB = signed + ? BigInt.from(bv).toSigned(widthY) + : BigInt.from(bv).toUnsigned(widthY); + + // Set these so that printing inside module build will have Logic values + a.put(bA); + b.put(bB); + const radix = 4; + final encoder = RadixEncoder(radix); + final pp = PartialProductGenerator(a, b, encoder, signed: signed) + ..signExtendCompactRect(); + // Turn on printing by using widthX == 6 (we are fooling the dead code + // checking linter here) + const output = widthX == 7; + if (output) { + print(pp); + } + expect(pp.evaluate(), equals(BigInt.from(av * bv))); + final compressor = ColumnCompressor(pp); + if (output) { + print('eval: ${compressor.evaluate(printOut: output)}'); + } + expect(compressor.evaluate(), equals(BigInt.from(av * bv))); + + compressor.compress(); + if (output) { + print('eval: ${compressor.evaluate(printOut: true)}'); + } + expect(compressor.evaluate(), equals(BigInt.from(av * bv))); + } + }); } diff --git a/test/arithmetic/adder_test.dart b/test/arithmetic/adder_test.dart index d6e00438..88f07cf7 100644 --- a/test/arithmetic/adder_test.dart +++ b/test/arithmetic/adder_test.dart @@ -125,8 +125,7 @@ void testExhaustiveSignMagnitude(int n, Adder Function(Logic a, Logic b) fn) { bigger = bI; smaller = bJ; } else { - bigger = bJ; - smaller = bI; + continue; } final biggerSign = bigger.abs() != bigger ? 1 : 0; final smallerSign = smaller.abs() != smaller ? 1 : 0; diff --git a/test/arithmetic/multiplier_encoder_test.dart b/test/arithmetic/multiplier_encoder_test.dart index ecf9bd44..d1052ce3 100644 --- a/test/arithmetic/multiplier_encoder_test.dart +++ b/test/arithmetic/multiplier_encoder_test.dart @@ -54,8 +54,6 @@ void checkEvaluateRandom(PartialProductGenerator pp, int nSamples) { void main() { test('single MAC partial product test', () async { - // stdout.write('\n'); - final encoder = RadixEncoder(4); const widthX = 4; const widthY = 4; @@ -93,17 +91,16 @@ void main() { expect(pp.evaluate(), equals(product)); }); - // TODO(dakdesmond): Figure out minimum widths! - // This is a two-minute exhaustive but quick test test('exhaustive partial product evaluate: square radix-4, all extension', () async { - stdout.write('\n'); for (final signed in [false, true]) { for (var radix = 4; radix < 8; radix *= 2) { + const radix = 4; final encoder = RadixEncoder(radix); final shift = log2Ceil(encoder.radix); - for (var width = shift + 1; width < shift * 2 + 1; width++) { + final minWidth = shift + (signed ? 1 : 0); + for (var width = minWidth; width < shift * 2 + 1; width++) { for (final signExtension in SignExtension.values) { final pp = PartialProductGenerator(Logic(name: 'X', width: width), Logic(name: 'Y', width: width), encoder, @@ -195,4 +192,71 @@ void main() { } } }); + + test('minimum width verification,', () async { + for (final signed in [false, true]) { + for (var radix = 2; radix < 32; radix *= 2) { + final encoder = RadixEncoder(radix); + final shift = log2Ceil(encoder.radix); + final width = shift + (signed ? 1 : 0); + const skew = 0; + // Only some sign extension routines have rectangular support + // Commented out rectangular extension routines for speedup + for (final signExtension in SignExtension.values) { + { + final pp = PartialProductGenerator(Logic(name: 'X', width: width), + Logic(name: 'Y', width: width + skew), encoder, + signed: signed); + + switch (signExtension) { + case SignExtension.brute: + pp.bruteForceSignExtend(); + case SignExtension.stop: + pp.signExtendWithStopBitsRect(); + case SignExtension.compact: + pp.signExtendCompact(); + case SignExtension.compactRect: + pp.signExtendCompactRect(); + } + checkEvaluateRandom(pp, 100); + } + } + } + } + }); + test('minimum rectangular width verification,', () async { + for (final signed in [false, true]) { + for (var radix = 2; radix < 32; radix *= 2) { + final encoder = RadixEncoder(radix); + final shift = log2Ceil(encoder.radix); + final width = shift; + final skew = (signed ? 1 : 0); + // Only some sign extension routines have rectangular support + // Commented out rectangular extension routines for speedup + for (final signExtension in [ + SignExtension.brute, + SignExtension.stop, + SignExtension.compactRect + ]) { + { + final pp = PartialProductGenerator(Logic(name: 'X', width: width), + Logic(name: 'Y', width: width + skew), encoder, + signed: signed); + + switch (signExtension) { + case SignExtension.brute: + pp.bruteForceSignExtend(); + case SignExtension.stop: + pp.signExtendWithStopBitsRect(); + case SignExtension.compact: + pp.signExtendCompact(); + case SignExtension.compactRect: + pp.signExtendCompactRect(); + } + checkEvaluateExhaustive(pp); + } + } + } + } + }); } From fd4633607e3b6434d8b7573cc37a98c7cd38b929 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Fri, 9 Aug 2024 15:13:20 -0700 Subject: [PATCH 17/38] kill print statements --- lib/src/arithmetic/addend_compressor.dart | 3 ++- test/arithmetic/addend_compressor_test.dart | 13 +++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/lib/src/arithmetic/addend_compressor.dart b/lib/src/arithmetic/addend_compressor.dart index 60db65f8..12a41f1b 100644 --- a/lib/src/arithmetic/addend_compressor.dart +++ b/lib/src/arithmetic/addend_compressor.dart @@ -252,7 +252,8 @@ class ColumnCompressor { } } if (printOut) { - print(ts); + // We need this to be able to debug, but git lint flunks print + // print(ts); } return accum.toSigned(width); } diff --git a/test/arithmetic/addend_compressor_test.dart b/test/arithmetic/addend_compressor_test.dart index 2ba06a62..f1b07050 100644 --- a/test/arithmetic/addend_compressor_test.dart +++ b/test/arithmetic/addend_compressor_test.dart @@ -128,21 +128,14 @@ void main() { ..signExtendCompactRect(); // Turn on printing by using widthX == 6 (we are fooling the dead code // checking linter here) - const output = widthX == 7; - if (output) { - print(pp); - } + // print(pp); expect(pp.evaluate(), equals(BigInt.from(av * bv))); final compressor = ColumnCompressor(pp); - if (output) { - print('eval: ${compressor.evaluate(printOut: output)}'); - } + // print('eval: ${compressor.evaluate(printOut: output)}'); expect(compressor.evaluate(), equals(BigInt.from(av * bv))); compressor.compress(); - if (output) { - print('eval: ${compressor.evaluate(printOut: true)}'); - } + // print('eval: ${compressor.evaluate(printOut: true)}'); expect(compressor.evaluate(), equals(BigInt.from(av * bv))); } }); From e8b96e80b0a5bc7311391083f40e1ee73f42eb93 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Sat, 10 Aug 2024 09:35:23 -0700 Subject: [PATCH 18/38] fixed broken multiplier_lib --- lib/src/arithmetic/multiplier_encoder.dart | 570 --------------------- lib/src/arithmetic/multiplier_lib.dart | 2 +- 2 files changed, 1 insertion(+), 571 deletions(-) diff --git a/lib/src/arithmetic/multiplier_encoder.dart b/lib/src/arithmetic/multiplier_encoder.dart index ab0564e0..5a624707 100644 --- a/lib/src/arithmetic/multiplier_encoder.dart +++ b/lib/src/arithmetic/multiplier_encoder.dart @@ -7,8 +7,6 @@ // 2024 May 15 // Author: Desmond Kirkpatrick -import 'dart:io'; -import 'dart:math'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; @@ -188,571 +186,3 @@ class MultiplicandSelector { Logic select(int col, RadixEncode encode) => _select(getMultiples(col), encode); } - -/// A class that generates a set of partial products -class PartialProductGenerator { - /// Get the shift increment between neighboring product rows - int get shift => selector.shift; - - /// The actual shift in each row - final rowShift = []; - - /// rows of partial products - int get rows => partialProducts.length; - - /// The multiplicand term (X) - Logic get multiplicand => selector.multiplicand; - - /// The multiplier term (Y) - Logic get multiplier => encoder.multiplier; - - /// Partial Products output - late List> partialProducts = []; - - /// Encoder for the full multiply operand - late final MultiplierEncoder encoder; - - /// Selector for the multiplicand which uses the encoder to index into - /// multiples of the multiplicand and generate partial products - late final MultiplicandSelector selector; - - /// Operands are signed - late bool signed = true; - - // Used to avoid sign extending more than once - var _signExtended = false; - - /// Construct the partial product matrix - PartialProductGenerator( - Logic multiplicand, Logic multiplier, RadixEncoder radixEncoder, - {this.signed = true}) { - encoder = MultiplierEncoder(multiplier, radixEncoder, signed: signed); - selector = - MultiplicandSelector(radixEncoder.radix, multiplicand, signed: signed); - _build(); - } - - /// Setup the partial products array (partialProducts and rowShift) - void _build() { - _signExtended = false; - partialProducts.clear(); - rowShift.clear(); - for (var row = 0; row < encoder.rows; row++) { - partialProducts.add(List.generate( - selector.width, (i) => selector.select(i, encoder.getEncoding(row)))); - } - for (var row = 0; row < rows; row++) { - rowShift.add(row * shift); - } - } - - /// Fully sign extend the PP array: useful for reference only - void bruteForceSignExtend() { - if (_signExtended) { - throw RohdHclException('Partial Product array already sign-extended'); - } - _signExtended = true; - final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; - for (var row = 0; row < rows; row++) { - final addend = partialProducts[row]; - final sign = signed ? addend.last : signs[row]; - addend.addAll(List.filled((rows - row) * shift, sign)); - if (row > 0) { - addend - ..insertAll(0, List.filled(shift - 1, Const(0))) - ..insert(0, signs[row - 1]); - rowShift[row] -= shift; - } - } - // Insert carry bit in extra row - partialProducts.add(List.generate(selector.width, (i) => Const(0))); - partialProducts.last.insert(0, signs[rows - 2]); - rowShift.add((rows - 2) * shift); - } - - /// Sign extend the PP array using stop bits - /// If possible, fold the final carry into another row (only when rectangular - /// enough that carry bit lands outside another row). - /// This technique can then be combined with a first-row extension technique - /// for folding in the final carry. - void signExtendWithStopBitsRect() { - if (_signExtended) { - throw RohdHclException('Partial Product array already sign-extended'); - } - _signExtended = true; - - final finalCarryPos = shift * (rows - 1); - final finalCarryRelPos = finalCarryPos - selector.width - shift; - final finalCarryRow = - ((encoder.multiplier.width > selector.multiplicand.width) && - (finalCarryRelPos > 0)) - ? (finalCarryRelPos / shift).floor() - : 0; - - final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; - for (var row = 0; row < rows; row++) { - final addend = partialProducts[row]; - final sign = signed ? addend.last : signs[row]; - if (row == 0) { - if (signed) { - addend.addAll(List.filled(shift - 1, sign)); // signed only? - } else { - addend.addAll(List.filled(shift, sign)); - } - addend.add(~sign); - } else { - if (signed) { - addend.last = ~sign; - } else { - addend.add(~sign); - } - addend - ..addAll(List.filled(shift - 1, Const(1))) - ..insertAll(0, List.filled(shift - 1, Const(0))) - ..insert(0, signs[row - 1]); - rowShift[row] -= shift; - } - } - - if (finalCarryRow > 0) { - final extensionRow = partialProducts[finalCarryRow]; - extensionRow - ..addAll(List.filled( - finalCarryPos - (extensionRow.length + rowShift[finalCarryRow]), - Const(0))) - ..add(signs[rows - 1]); - } else if (signed) { - // Create an extra row to hold the final carry bit - partialProducts - .add(List.filled(selector.width, Const(0), growable: true)); - partialProducts.last.insert(0, signs[rows - 2]); - rowShift.add((rows - 2) * shift); - - // Hack for radix-2 - if (shift == 1) { - partialProducts.last.last = ~partialProducts.last.last; - } - } - } - - void _addStopSignFlip(List addend, Logic sign) { - if (signed) { - addend.last = ~addend.last; - } else { - addend.add(sign); - } - } - - void _addStopSign(List addend, Logic sign) { - if (signed) { - addend.last = sign; - } else { - addend.add(sign); - } - } - - /// Sign extend the PP array using stop bits without adding a row - void signExtendCompact() { - if (_signExtended) { - throw RohdHclException('Partial Product array already sign-extended'); - } - _signExtended = true; - - final lastRow = rows - 1; - final firstAddend = partialProducts[0]; - final lastAddend = partialProducts[lastRow]; - var alignRow0Sign = selector.width - - shift * lastRow - - ((shift > 1) - ? 1 - : signed - ? 1 - : 0); - - if (alignRow0Sign < 0) { - alignRow0Sign = 0; - } - - final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; - - final propagate = - List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); - for (var row = 0; row < rows; row++) { - propagate[row].add(signs[row]); - for (var col = 0; col < 2 * (shift - 1); col++) { - propagate[row].add(partialProducts[row][col]); - } - for (var col = 1; col < propagate[row].length; col++) { - propagate[row][col] = propagate[row][col] & propagate[row][col - 1]; - } - } - final m = - List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); - for (var row = 0; row < rows; row++) { - for (var c = 0; c < shift - 1; c++) { - m[row].add(partialProducts[row][c] ^ propagate[row][c]); - } - m[row].addAll(List.filled(shift - 1, Logic())); - } - - for (var i = shift - 1; i < m[lastRow].length; i++) { - m[lastRow][i] = lastAddend[i] ^ - (i < alignRow0Sign ? propagate[lastRow][i] : Const(0)); - } - - final remainders = List.filled(rows, Logic()); - for (var row = 0; row < lastRow; row++) { - remainders[row] = propagate[row][shift - 1]; - } - remainders[lastRow] <= propagate[lastRow][alignRow0Sign]; - - // Compute Sign extension for row==0 - final firstSign = signed ? firstAddend.last : signs[0]; - final q = [ - firstSign ^ remainders[lastRow], - ~(firstSign & ~remainders[lastRow]), - ]; - q.insertAll(1, List.filled(shift - 1, ~q[1])); - - for (var row = 0; row < rows; row++) { - final addend = partialProducts[row]; - if (row > 0) { - final mLimit = (row == lastRow) ? 2 * (shift - 1) : shift - 1; - for (var i = 0; i < mLimit; i++) { - addend[i] = m[row][i]; - } - // Stop bits - if (signed) { - addend.last = ~addend.last; - } else { - addend.add(~signs[row]); - } - addend - ..insert(0, remainders[row - 1]) - ..addAll(List.filled(shift - 1, Const(1))); - rowShift[row] -= 1; - } else { - for (var i = 0; i < shift - 1; i++) { - firstAddend[i] = m[0][i]; - } - if (signed) { - firstAddend.last = q[0]; - } else { - firstAddend.add(q[0]); - } - firstAddend.addAll(q.getRange(1, q.length)); - } - } - if (shift == 1) { - lastAddend.add(Const(1)); - } - } - - /// Sign extend the PP array using stop bits without adding a row - /// This routine works with different widths of multiplicand/multiplier - void signExtendCompactRect() { - if (_signExtended) { - throw RohdHclException('Partial Product array already sign-extended'); - } - _signExtended = true; - - final lastRow = rows - 1; - final firstAddend = partialProducts[0]; - final lastAddend = partialProducts[lastRow]; - - final firstRowQStart = selector.width - (signed ? 1 : 0); - final lastRowSignPos = shift * lastRow; - - final align = firstRowQStart - lastRowSignPos; - - final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; - - // Compute propgation info for folding sign bits into main rows - final propagate = - List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); - - for (var row = 0; row < rows; row++) { - propagate[row].add(signs[row]); - for (var col = 0; col < 2 * (shift - 1); col++) { - propagate[row].add(partialProducts[row][col]); - } - // Last row has extend sign propagation to Q start - if (row == lastRow) { - var col = 2 * (shift - 1); - while (propagate[lastRow].length <= align) { - propagate[lastRow].add(partialProducts[row][col++]); - } - } - // Now compute the propagation logic - for (var col = 1; col < propagate[row].length; col++) { - propagate[row][col] = propagate[row][col] & propagate[row][col - 1]; - } - } - - // Compute 'm', the prefix of each row to carry the sign of the next row - final m = - List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); - for (var row = 0; row < rows; row++) { - for (var c = 0; c < shift - 1; c++) { - m[row].add(partialProducts[row][c] ^ propagate[row][c]); - } - m[row].addAll(List.filled(shift - 1, Logic())); - } - while (m[lastRow].length < align) { - m[lastRow].add(Logic()); - } - for (var i = shift - 1; i < m[lastRow].length; i++) { - m[lastRow][i] = - lastAddend[i] ^ (i < align ? propagate[lastRow][i] : Const(0)); - } - - final remainders = List.filled(rows, Logic()); - for (var row = 0; row < lastRow; row++) { - remainders[row] = propagate[row][shift - 1]; - } - remainders[lastRow] = propagate[lastRow][align > 0 ? align : 0]; - - // Merge 'm' into the LSBs of each addend - for (var row = 0; row < rows; row++) { - final addend = partialProducts[row]; - if (row > 0) { - final mLimit = (row == lastRow) ? align : shift - 1; - for (var i = 0; i < mLimit; i++) { - addend[i] = m[row][i]; - } - // Stop bits - _addStopSignFlip(addend, ~signs[row]); - addend - ..insert(0, remainders[row - 1]) - ..addAll(List.filled(shift - 1, Const(1))); - rowShift[row] -= 1; - } else { - // First row - for (var i = 0; i < shift - 1; i++) { - firstAddend[i] = m[0][i]; - } - } - } - - // 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 = signed ? firstAddend.last : signs[0]; - final lastSign = remainders[lastRow]; - // Compute Sign extension MSBs for firstRow - final qLen = shift + 1; - final insertSignPos = (align > 0) ? 0 : -align; - final q = List.filled(min(qLen, insertSignPos), firstSign, growable: true); - if (insertSignPos < qLen) { - // At sign insertion position - q.add(firstSign ^ lastSign); - if (insertSignPos == qLen - 1) { - q[insertSignPos] = ~q[insertSignPos]; - q.add(~(firstSign | q[insertSignPos])); - } else { - q - ..addAll(List.filled(qLen - insertSignPos - 2, firstSign & ~lastSign)) - ..add(~(firstSign & ~lastSign)); - } - } - - if (-align >= q.length) { - q.last = ~firstSign; - } - _addStopSign(firstAddend, q[0]); - firstAddend.addAll(q.getRange(1, q.length)); - - if (-align >= q.length) { - final finalCarryRelPos = - lastRowSignPos - selector.width - shift + (signed ? 1 : 0); - final finalCarryRow = (finalCarryRelPos / shift).floor(); - final curRowLength = - partialProducts[finalCarryRow].length + rowShift[finalCarryRow]; - - partialProducts[finalCarryRow] - ..addAll(List.filled(lastRowSignPos - curRowLength, Const(0))) - ..add(remainders[lastRow]); - } - if (shift == 1) { - lastAddend.add(Const(1)); - } - } - - /// Return the actual largest width of all rows - int maxWidth() { - var maxW = 0; - for (var row = 0; row < rows; row++) { - final entry = partialProducts[row]; - if (entry.length + rowShift[row] > maxW) { - maxW = entry.length + rowShift[row]; - } - } - return maxW; - } - - /// Accumulate the partial products and return as BigInt - BigInt evaluate() { - final maxW = maxWidth(); - var accum = BigInt.from(0); - for (var row = 0; row < rows; row++) { - final pp = partialProducts[row].rswizzle().value; - final value = pp.zeroExtend(maxW) << rowShift[row]; - if (pp.isValid) { - accum += value.toBigInt(); - } - } - final sum = LogicValue.ofBigInt(accum, maxW).toBigInt(); - return signed ? sum.toSigned(maxW) : sum; - } - - /// Print out the partial product matrix - @override - String toString() { - final str = StringBuffer(); - - final maxW = maxWidth(); - final nonSignExtendedPad = _signExtended - ? 0 - : shift > 2 - ? shift - 1 - : 1; - // We will print encoding(1-hot multiples and sign) before each row - final shortPrefix = - '99 ${'M='.padRight(2 + selector.radix ~/ 2)} S= : '.length + - 3 * nonSignExtendedPad; - - // print bit position header - str.write(' ' * shortPrefix); - for (var i = maxW - 1; i >= 0; i--) { - final bits = i > 9 ? 2 : 1; - str - ..write('$i') - ..write(' ' * (3 - bits)); - } - str.write('\n'); - // Partial product matrix: rows of multiplicand multiples shift by - // rowshift[row] - for (var row = 0; row < rows; row++) { - final rowStr = (row < 10) ? '0$row' : '$row'; - if (row < encoder.rows) { - final encoding = encoder.getEncoding(row); - if (encoding.multiples.value.isValid) { - str.write('$rowStr M=${encoding.multiples.reversed.value.bitString} ' - 'S=${encoding.sign.value.toInt()}: '); - } else { - str.write(' ' * shortPrefix); - } - } else { - str.write('$rowStr ${'M='.padRight(2 + selector.radix ~/ 2)} S= : '); - } - final entry = partialProducts[row].reversed.toList(); - final prefixCnt = - maxW - (entry.length + rowShift[row]) + nonSignExtendedPad; - str.write(' ' * prefixCnt); - for (var col = 0; col < entry.length; col++) { - str.write('${entry[col].value.bitString} '); - } - final suffixCnt = rowShift[row]; - final value = entry.swizzle().value.zeroExtend(maxW) << suffixCnt; - final intValue = value.isValid ? value.toBigInt() : BigInt.from(-1); - str - ..write(' ' * suffixCnt) - ..write(': ${value.bitString}') - ..write(' = ${value.isValid ? intValue : ""}' - ' (${value.isValid ? intValue.toSigned(maxW) : ""})\n'); - } - // Compute and print binary representation from accumulated value - // Later: we will compare with a compression tree result - str - ..write('=' * (shortPrefix + 3 * maxW)) - ..write('\n') - ..write(' ' * shortPrefix); - - final sum = LogicValue.ofBigInt(evaluate(), maxW); - // print out the sum as a MSB-first bitvector - for (final elem in [for (var i = 0; i < maxW; i++) sum[i]].reversed) { - str.write('${elem.toInt()} '); - } - final val = evaluate(); - str.write(': ${sum.bitString} = ' - '${val.toUnsigned(maxW)}'); - if (_signExtended) { - str.write(' ($val)\n\n'); - } - return str.toString(); - } -} - -// This routine is to reverse-engineer how to create booth encoders from -// XOR computations on the multiplier bits -// It is used to validate the RadixEncoder class -void main() { - for (var radix = 2; radix < 32; radix *= 2) { - stdout.write('Radix-$radix:\n'); - final encoder = RadixEncoder(radix); - - final width = log2Ceil(radix) + 1; - final inputXor = Logic(width: width); - final multiples = []; - for (var i = 2; i < radix + 1; i += 2) { - final pastX = LogicValue.ofInt(i - 1, width); - final x = LogicValue.ofInt(i, width); - final pastXor = pastX ^ (pastX >>> 1); - final xor = x ^ (x >>> 1); - // Multiples don't agree on a bit position so we will skip - final multiplesDisagree = xor ^ pastXor; - // Where multiples agree, we need the sense or direction (1 or 0) - final senseMultiples = xor & pastXor; - - final andOutput = [ - for (var j = 0; j < width - 1; j++) - if (multiplesDisagree[j].isZero) - if (senseMultiples[j].isZero) ~inputXor[j] else inputXor[j] - ].swizzle().and(); - final multPos = (i >>> 1) + i % 2; - stdout - ..write('\tM${(i >>> 1) + i % 2} x=${x.bitString} ' - 'lx=${pastX.bitString} ' - // 'm=$m xor=${xor.bitString}(${xor.toInt()}) ' - 'dontcare=${multiplesDisagree.bitString}' - ' agree=${senseMultiples.bitString}') - ..write(': '); - for (var j = 0; j < width - 1; j++) { - if (multiplesDisagree[j].isZero) { - if (senseMultiples[j].isZero) { - stdout.write('~'); - } - stdout.write('xor[$j] '); - } - } - multiples.add(andOutput); - stdout.write('\n'); - final inLogic = Logic(width: width); - for (var k = 0; k < radix; k++) { - final inValue = LogicValue.ofInt(k, width); - inLogic.put(inValue); - final code = encoder.encode(inLogic).multiples[multPos - 1]; - final newCode = - RadixEncoder(radix).encode(inLogic).multiples[multPos - 1]; - inputXor.put(inValue ^ (inValue >>> 1)); - // stdout - // ..write('in=${inValue.bitString} ') - // ..write('xor=${inputXor.value.bitString)} ') - // ..write('out=${andOutput.value.bitString} ') - // ..write('code=${code.value.bitString} ') - // ..write('ncode=${newCode.value.bitString}') - // ..write('') - // ..write('\n'); - if (andOutput.value != code.value) { - throw RohdHclException('andOutput mismatches code'); - } - if (newCode.value != code.value) { - throw RohdHclException('andOutput mismatches code'); - } - if (andOutput.value != andOutput.value) { - throw RohdHclException('andOutput mismatches code'); - } - } - } - } -} diff --git a/lib/src/arithmetic/multiplier_lib.dart b/lib/src/arithmetic/multiplier_lib.dart index 34270f6c..d3706319 100644 --- a/lib/src/arithmetic/multiplier_lib.dart +++ b/lib/src/arithmetic/multiplier_lib.dart @@ -10,6 +10,6 @@ // // -// Just exporting encoder exports all the symbols we use export './addend_compressor.dart'; export './multiplier_encoder.dart'; +export './partial_product_generator.dart'; From 61521b5e55dd8fbafc75b72d6b8378399221e215 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Sat, 10 Aug 2024 12:07:39 -0700 Subject: [PATCH 19/38] integrated sign extension into PP generation --- lib/src/arithmetic/multiplier.dart | 6 +- .../arithmetic/partial_product_generator.dart | 33 +++++++- test/arithmetic/addend_compressor_test.dart | 19 ++--- test/arithmetic/multiplier_encoder_test.dart | 76 +++++-------------- test/arithmetic/multiplier_test.dart | 2 +- 5 files changed, 58 insertions(+), 78 deletions(-) diff --git a/lib/src/arithmetic/multiplier.dart b/lib/src/arithmetic/multiplier.dart index 9bc85c3d..c8899b00 100644 --- a/lib/src/arithmetic/multiplier.dart +++ b/lib/src/arithmetic/multiplier.dart @@ -85,8 +85,7 @@ class CompressionTreeMultiplier extends Multiplier { ], (a, b) => Logic()).runtimeType}') { final product = addOutput('product', width: a.width + b.width); final pp = - PartialProductGenerator(a, b, RadixEncoder(radix), signed: signed) - ..signExtendCompact(); + PartialProductGenerator(a, b, RadixEncoder(radix), signed: signed); final compressor = ColumnCompressor(pp)..compress(); final adder = ParallelPrefixAdder( compressor.extractRow(0), compressor.extractRow(1), ppTree); @@ -110,8 +109,7 @@ class CompressionTreeMultiplyAccumulate extends MultiplyAccumulate { 'R${radix}_${ppTree.call([Logic()], (a, b) => Logic()).name}') { final accumulate = addOutput('accumulate', width: a.width + b.width + 1); final pp = - PartialProductGenerator(a, b, RadixEncoder(radix), signed: signed) - ..signExtendCompact(); + PartialProductGenerator(a, b, RadixEncoder(radix), signed: signed); // TODO(desmonddak): This sign extension method for the additional // addend may only work with signExtendCompact. diff --git a/lib/src/arithmetic/partial_product_generator.dart b/lib/src/arithmetic/partial_product_generator.dart index 95655a17..f71764a4 100644 --- a/lib/src/arithmetic/partial_product_generator.dart +++ b/lib/src/arithmetic/partial_product_generator.dart @@ -13,6 +13,24 @@ import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; +/// Methods for sign extending the [PartialProductGenerator] +enum SignExtension { + /// No sign extension + none, + + /// Brute force sign extend each row to the full width of the product + brute, + + /// Extend using stop bits in each row (and an extra row for final sign) + stop, + + /// Fold in last row sign bit (Mohanty, B.K., Choubey, A.) + compact, + + /// Sign folding that works for rectangular partial products + compactRect +} + /// A 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 @@ -54,7 +72,8 @@ class PartialProductGenerator { /// Construct the partial product matrix PartialProductGenerator( Logic multiplicand, Logic multiplier, RadixEncoder radixEncoder, - {this.signed = true}) { + {this.signed = true, + SignExtension signExtension = SignExtension.compactRect}) { encoder = MultiplierEncoder(multiplier, radixEncoder, signed: signed); selector = MultiplicandSelector(radixEncoder.radix, multiplicand, signed: signed); @@ -68,6 +87,18 @@ class PartialProductGenerator { '${selector.shift + (signed ? 1 : 0)}'); } _build(); + switch (signExtension) { + case SignExtension.none: + ; + case SignExtension.brute: + bruteForceSignExtend(); + case SignExtension.stop: + signExtendWithStopBitsRect(); + case SignExtension.compact: + signExtendCompact(); + case SignExtension.compactRect: + signExtendCompactRect(); + } } /// Setup the partial products array (partialProducts and rowShift) diff --git a/test/arithmetic/addend_compressor_test.dart b/test/arithmetic/addend_compressor_test.dart index f1b07050..602f0610 100644 --- a/test/arithmetic/addend_compressor_test.dart +++ b/test/arithmetic/addend_compressor_test.dart @@ -14,8 +14,6 @@ import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; import 'package:test/test.dart'; -enum SignExtension { brute, stop, compact } - void testCompressionExhaustive(PartialProductGenerator pp) { final widthX = pp.selector.multiplicand.width; final widthY = pp.encoder.multiplier.width; @@ -86,17 +84,13 @@ void main() { final shift = log2Ceil(encoder.radix); for (var width = shift + 1; width < 2 * shift + 1; width++) { for (final signExtension in SignExtension.values) { + if (signExtension == SignExtension.none) { + continue; + } final pp = PartialProductGenerator(Logic(name: 'X', width: width), Logic(name: 'Y', width: width), encoder, - signed: signed); - switch (signExtension) { - case SignExtension.brute: - pp.bruteForceSignExtend(); - case SignExtension.stop: - pp.signExtendWithStopBitsRect(); - case SignExtension.compact: - pp.signExtendCompact(); - } + signed: signed, signExtension: signExtension); + testCompressionExhaustive(pp); } } @@ -124,8 +118,7 @@ void main() { b.put(bB); const radix = 4; final encoder = RadixEncoder(radix); - final pp = PartialProductGenerator(a, b, encoder, signed: signed) - ..signExtendCompactRect(); + final pp = PartialProductGenerator(a, b, encoder, signed: signed); // Turn on printing by using widthX == 6 (we are fooling the dead code // checking linter here) // print(pp); diff --git a/test/arithmetic/multiplier_encoder_test.dart b/test/arithmetic/multiplier_encoder_test.dart index d1052ce3..2bbecdff 100644 --- a/test/arithmetic/multiplier_encoder_test.dart +++ b/test/arithmetic/multiplier_encoder_test.dart @@ -15,8 +15,6 @@ import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; import 'package:test/test.dart'; -enum SignExtension { brute, stop, compact, compactRect } - void checkEvaluateExhaustive(PartialProductGenerator pp) { final widthX = pp.selector.multiplicand.width; final widthY = pp.encoder.multiplier.width; @@ -74,8 +72,7 @@ void main() { logicX.put(X); logicY.put(Y); logicZ.put(Z); - final pp = PartialProductGenerator(logicX, logicY, encoder) - ..signExtendCompact(); + final pp = PartialProductGenerator(logicX, logicY, encoder); // Add a row for addend final l = [ for (var i = 0; i < logicZ.width; i++) logicZ[i], @@ -102,19 +99,13 @@ void main() { final minWidth = shift + (signed ? 1 : 0); for (var width = minWidth; width < shift * 2 + 1; width++) { for (final signExtension in SignExtension.values) { + if (signExtension == SignExtension.none) { + continue; + } final pp = PartialProductGenerator(Logic(name: 'X', width: width), Logic(name: 'Y', width: width), encoder, - signed: signed); - switch (signExtension) { - case SignExtension.brute: - pp.bruteForceSignExtend(); - case SignExtension.stop: - pp.signExtendWithStopBitsRect(); - case SignExtension.compact: - pp.signExtendCompact(); - case SignExtension.compactRect: - pp.signExtendCompactRect(); - } + signed: signed, signExtension: signExtension); + checkEvaluateExhaustive(pp); } } @@ -132,25 +123,14 @@ void main() { // Only some sign extension routines have rectangular support // Commented out rectangular extension routines for speedup for (final signExtension in [ - // SignExtension.brute, - // SignExtension.stop, + SignExtension.brute, + SignExtension.stop, SignExtension.compactRect ]) { final pp = PartialProductGenerator(Logic(name: 'X', width: width), Logic(name: 'Y', width: width + skew), encoder, - signed: signed); - - switch (signExtension) { - case SignExtension.brute: - pp.bruteForceSignExtend(); - case SignExtension.stop: - pp.signExtendWithStopBitsRect(); - case SignExtension.compact: - pp.signExtendCompact(); - case SignExtension.compactRect: - pp.signExtendCompactRect(); - } - checkEvaluateRandom(pp, 100); + signed: signed, signExtension: signExtension); + checkEvaluateRandom(pp, 20); } } } @@ -186,7 +166,6 @@ void main() { pp.multiplicand.put(X); pp.multiplier.put(Y); - pp.signExtendCompactRect(); expect(pp.evaluate(), equals(product)); checkEvaluateRandom(pp, 100); } @@ -203,23 +182,13 @@ void main() { // Only some sign extension routines have rectangular support // Commented out rectangular extension routines for speedup for (final signExtension in SignExtension.values) { - { - final pp = PartialProductGenerator(Logic(name: 'X', width: width), - Logic(name: 'Y', width: width + skew), encoder, - signed: signed); - - switch (signExtension) { - case SignExtension.brute: - pp.bruteForceSignExtend(); - case SignExtension.stop: - pp.signExtendWithStopBitsRect(); - case SignExtension.compact: - pp.signExtendCompact(); - case SignExtension.compactRect: - pp.signExtendCompactRect(); - } - checkEvaluateRandom(pp, 100); + if (signExtension == SignExtension.none) { + continue; } + final pp = PartialProductGenerator(Logic(name: 'X', width: width), + Logic(name: 'Y', width: width + skew), encoder, + signed: signed, signExtension: signExtension); + checkEvaluateRandom(pp, 100); } } } @@ -241,18 +210,7 @@ void main() { { final pp = PartialProductGenerator(Logic(name: 'X', width: width), Logic(name: 'Y', width: width + skew), encoder, - signed: signed); - - switch (signExtension) { - case SignExtension.brute: - pp.bruteForceSignExtend(); - case SignExtension.stop: - pp.signExtendWithStopBitsRect(); - case SignExtension.compact: - pp.signExtendCompact(); - case SignExtension.compactRect: - pp.signExtendCompactRect(); - } + signed: signed, signExtension: signExtension); checkEvaluateExhaustive(pp); } } diff --git a/test/arithmetic/multiplier_test.dart b/test/arithmetic/multiplier_test.dart index 0e440b74..0562458a 100644 --- a/test/arithmetic/multiplier_test.dart +++ b/test/arithmetic/multiplier_test.dart @@ -106,7 +106,7 @@ void main() { CompressionTreeMultiplier(a, b, 4, KoggeStone.new, signed: true); Multiplier curryUnsignedMultiplier(Logic a, Logic b) => - CompressionTreeMultiplier(a, b, 4, KoggeStone.new, signed: true); + CompressionTreeMultiplier(a, b, 4, KoggeStone.new, signed: false); // Now treat the multiplier as a MAC with a zero input addend [c] MultiplyAccumulate currySignedMultiplierAsMAC(Logic a, Logic b, Logic c) => From 857b7b76782fee31f11e212036dce63f6538dfc0 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Sat, 10 Aug 2024 12:19:31 -0700 Subject: [PATCH 20/38] fixed default arg warning --- test/arithmetic/multiplier_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/arithmetic/multiplier_test.dart b/test/arithmetic/multiplier_test.dart index 0562458a..503de362 100644 --- a/test/arithmetic/multiplier_test.dart +++ b/test/arithmetic/multiplier_test.dart @@ -106,7 +106,7 @@ void main() { CompressionTreeMultiplier(a, b, 4, KoggeStone.new, signed: true); Multiplier curryUnsignedMultiplier(Logic a, Logic b) => - CompressionTreeMultiplier(a, b, 4, KoggeStone.new, signed: false); + CompressionTreeMultiplier(a, b, 4, KoggeStone.new); // Now treat the multiplier as a MAC with a zero input addend [c] MultiplyAccumulate currySignedMultiplierAsMAC(Logic a, Logic b, Logic c) => From e449c12d05e31f655543acb4391de13247e16908 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Sun, 11 Aug 2024 09:24:25 -0700 Subject: [PATCH 21/38] tuned up PP printout with a new LogicValue firstOne extension --- .../arithmetic/partial_product_generator.dart | 10 +++-- lib/src/utils.dart | 36 +++++++++++++++ test/arithmetic/multiplier_encoder_test.dart | 44 +++++++++++++------ test/arithmetic/multiplier_test.dart | 2 +- 4 files changed, 73 insertions(+), 19 deletions(-) diff --git a/lib/src/arithmetic/partial_product_generator.dart b/lib/src/arithmetic/partial_product_generator.dart index f71764a4..5234377b 100644 --- a/lib/src/arithmetic/partial_product_generator.dart +++ b/lib/src/arithmetic/partial_product_generator.dart @@ -221,7 +221,7 @@ class PartialProductGenerator { /// Sign extend the PP array using stop bits without adding a row. void signExtendCompact() { // An implementation of - // 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, A. Efficient Design for Radix-8 Booth Multiplier + // 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 (_signExtended) { @@ -491,7 +491,7 @@ class PartialProductGenerator { : 1; // We will print encoding(1-hot multiples and sign) before each row final shortPrefix = - '99 ${'M='.padRight(2 + selector.radix ~/ 2)} S= : '.length + + '99 ${'M='.padRight(2 + selector.radix ~/ 2)}(99) S= : '.length + 3 * nonSignExtendedPad; // print bit position header @@ -510,13 +510,15 @@ class PartialProductGenerator { if (row < encoder.rows) { final encoding = encoder.getEncoding(row); if (encoding.multiples.value.isValid) { - str.write('$rowStr M=${encoding.multiples.reversed.value.bitString} ' + str.write('$rowStr M=${encoding.multiples.reversed.value.bitString}' + '(${(encoding.multiples.value.firstOne() + 1).toString().padLeft(2)}) ' 'S=${encoding.sign.value.toInt()}: '); } else { str.write(' ' * shortPrefix); } } else { - str.write('$rowStr ${'M='.padRight(2 + selector.radix ~/ 2)} S= : '); + str.write( + '$rowStr ${'M='.padRight(2 + selector.radix ~/ 2)} S= : '); } final entry = partialProducts[row].reversed.toList(); final prefixCnt = diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 548ed32e..0a21dca0 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -15,3 +15,39 @@ extension LogicValueBitString on LogicValue { /// Simplest version of bit string representation as shorthand String get bitString => toString(includeWidth: false); } + +/// This extension will eventually move to ROHD once it is proven useful +extension LogicValueMajority on LogicValue { + /// Compute the unary majority on LogicValue + bool majority() { + if (!isValid) { + return false; + } + final zero = LogicValue.filled(width, LogicValue.zero); + var shiftedValue = this; + var result = 0; + while (shiftedValue != zero) { + result++; + shiftedValue >>>= 1; + } + return result > (width ~/ 2); + } + + /// Compute the first One find operation on LogicValue, returning its position + int firstOne() { + if (!isValid) { + return -1; + } + var shiftedValue = this; + var result = 0; + while (shiftedValue[0] != LogicValue.one) { + result++; + if (result == width) { + result = -1; + break; + } + shiftedValue >>>= 1; + } + return result; + } +} diff --git a/test/arithmetic/multiplier_encoder_test.dart b/test/arithmetic/multiplier_encoder_test.dart index 2bbecdff..72da1249 100644 --- a/test/arithmetic/multiplier_encoder_test.dart +++ b/test/arithmetic/multiplier_encoder_test.dart @@ -52,18 +52,24 @@ void checkEvaluateRandom(PartialProductGenerator pp, int nSamples) { void main() { test('single MAC partial product test', () async { - final encoder = RadixEncoder(4); - const widthX = 4; - const widthY = 4; + final encoder = RadixEncoder(16); + const widthX = 8; + const widthY = 18; - const i = 8; - var j = pow(2, widthY - 1).toInt(); - j = 2; - const k = 128; + const signed = true; + + // const i = 8; + // var j = pow(2, widthY - 1).toInt(); + // j = 2; + // const k = 128; + const i = 1478; + const j = 9; + const k = 0; final X = BigInt.from(i).toSigned(widthX); final Y = BigInt.from(j).toSigned(widthY); final Z = BigInt.from(k).toSigned(widthX + widthY); + print('X=$X Y=$Y, Z=$Z'); final product = X * Y + Z; final logicX = Logic(name: 'X', width: widthX); @@ -73,14 +79,24 @@ void main() { logicY.put(Y); logicZ.put(Z); final pp = PartialProductGenerator(logicX, logicY, encoder); + + final lastLength = + pp.partialProducts[pp.rows - 1].length + pp.rowShift[pp.rows - 1]; + + final sign = signed ? logicZ[logicZ.width - 1] : Const(0); + final l = [for (var i = 0; i < logicZ.width; i++) logicZ[i]]; + while (l.length < lastLength) { + l.add(sign); + } + l + ..add(~sign) + ..add(Const(1)); + print('lastL=$lastLength'); // Add a row for addend - final l = [ - for (var i = 0; i < logicZ.width; i++) logicZ[i], - Const(0), - Const(1) - ]; - pp.partialProducts.add(l); - pp.rowShift.add(0); + print(pp); + pp.partialProducts.insert(0, l); + pp.rowShift.insert(0, 0); + print(pp); if (pp.evaluate() != product) { stdout.write('Fail: $X * $Y: ${pp.evaluate()} vs expected $product\n'); diff --git a/test/arithmetic/multiplier_test.dart b/test/arithmetic/multiplier_test.dart index 503de362..c19f4e33 100644 --- a/test/arithmetic/multiplier_test.dart +++ b/test/arithmetic/multiplier_test.dart @@ -154,7 +154,7 @@ void main() { }); test('single mac', () async { - const width = 6; + const width = 8; final a = Logic(name: 'a', width: width); final b = Logic(name: 'b', width: width); final c = Logic(name: 'c', width: 2 * width); From 5a4cad700e28ca9dd3b84f3533414b13a7629b03 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Sun, 11 Aug 2024 09:40:09 -0700 Subject: [PATCH 22/38] code cleanup, remove print, long lines --- lib/src/arithmetic/partial_product_generator.dart | 3 ++- test/arithmetic/multiplier_encoder_test.dart | 14 +++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/src/arithmetic/partial_product_generator.dart b/lib/src/arithmetic/partial_product_generator.dart index 5234377b..810b1cd7 100644 --- a/lib/src/arithmetic/partial_product_generator.dart +++ b/lib/src/arithmetic/partial_product_generator.dart @@ -510,8 +510,9 @@ class PartialProductGenerator { if (row < encoder.rows) { final encoding = encoder.getEncoding(row); if (encoding.multiples.value.isValid) { + final multiple = encoding.multiples.value.firstOne() + 1; str.write('$rowStr M=${encoding.multiples.reversed.value.bitString}' - '(${(encoding.multiples.value.firstOne() + 1).toString().padLeft(2)}) ' + '(${multiple.toString().padLeft(2)}) ' 'S=${encoding.sign.value.toInt()}: '); } else { str.write(' ' * shortPrefix); diff --git a/test/arithmetic/multiplier_encoder_test.dart b/test/arithmetic/multiplier_encoder_test.dart index 72da1249..805834bd 100644 --- a/test/arithmetic/multiplier_encoder_test.dart +++ b/test/arithmetic/multiplier_encoder_test.dart @@ -56,8 +56,6 @@ void main() { const widthX = 8; const widthY = 18; - const signed = true; - // const i = 8; // var j = pow(2, widthY - 1).toInt(); // j = 2; @@ -69,7 +67,7 @@ void main() { final X = BigInt.from(i).toSigned(widthX); final Y = BigInt.from(j).toSigned(widthY); final Z = BigInt.from(k).toSigned(widthX + widthY); - print('X=$X Y=$Y, Z=$Z'); + // print('X=$X Y=$Y, Z=$Z'); final product = X * Y + Z; final logicX = Logic(name: 'X', width: widthX); @@ -83,7 +81,9 @@ void main() { final lastLength = pp.partialProducts[pp.rows - 1].length + pp.rowShift[pp.rows - 1]; - final sign = signed ? logicZ[logicZ.width - 1] : Const(0); + final sign = logicZ[logicZ.width - 1]; + // for unsigned versus signed testing + // final sign = signed ? logicZ[logicZ.width - 1] : Const(0); final l = [for (var i = 0; i < logicZ.width; i++) logicZ[i]]; while (l.length < lastLength) { l.add(sign); @@ -91,12 +91,12 @@ void main() { l ..add(~sign) ..add(Const(1)); - print('lastL=$lastLength'); + // print('lastL=$lastLength'); // Add a row for addend - print(pp); + // print(pp); pp.partialProducts.insert(0, l); pp.rowShift.insert(0, 0); - print(pp); + // print(pp); if (pp.evaluate() != product) { stdout.write('Fail: $X * $Y: ${pp.evaluate()} vs expected $product\n'); From 32dde502af488ea7a4cd69c7b4d7f984f7ce7ebf Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Sun, 11 Aug 2024 23:30:21 -0700 Subject: [PATCH 23/38] much cleaner multiply test, sign args --- lib/src/arithmetic/carry_save_mutiplier.dart | 1 + lib/src/arithmetic/multiplicand_selector.dart | 2 +- lib/src/arithmetic/multiplier.dart | 8 +- lib/src/arithmetic/multiplier_encoder.dart | 4 +- .../arithmetic/partial_product_generator.dart | 4 +- .../components/component_registry.dart | 3 +- .../config_carry_save_multiplier.dart | 11 +- .../carry_save_multiplier_test.dart | 5 +- test/arithmetic/multiplier_encoder_test.dart | 2 +- test/arithmetic/multiplier_test.dart | 102 +++++++++--------- test/configurator_test.dart | 6 +- 11 files changed, 77 insertions(+), 71 deletions(-) diff --git a/lib/src/arithmetic/carry_save_mutiplier.dart b/lib/src/arithmetic/carry_save_mutiplier.dart index 2e97686b..3a8363d1 100644 --- a/lib/src/arithmetic/carry_save_mutiplier.dart +++ b/lib/src/arithmetic/carry_save_mutiplier.dart @@ -42,6 +42,7 @@ class CarrySaveMultiplier extends Multiplier { CarrySaveMultiplier(super.a, super.b, {required Logic clk, required Logic reset, + required super.signed, super.name = 'carry_save_multiplier'}) { if (a.width != b.width) { throw RohdHclException('inputs of a and b should have same width.'); diff --git a/lib/src/arithmetic/multiplicand_selector.dart b/lib/src/arithmetic/multiplicand_selector.dart index 47671ed6..06bb4043 100644 --- a/lib/src/arithmetic/multiplicand_selector.dart +++ b/lib/src/arithmetic/multiplicand_selector.dart @@ -29,7 +29,7 @@ class MultiplicandSelector { late LogicArray multiples; /// Generate required multiples of multiplicand - MultiplicandSelector(this.radix, this.multiplicand, {bool signed = true}) + MultiplicandSelector(this.radix, this.multiplicand, {required bool signed}) : shift = log2Ceil(radix) { if (radix > 16) { throw RohdHclException('Radices beyond 16 are not yet supported'); diff --git a/lib/src/arithmetic/multiplier.dart b/lib/src/arithmetic/multiplier.dart index c8899b00..3f482afa 100644 --- a/lib/src/arithmetic/multiplier.dart +++ b/lib/src/arithmetic/multiplier.dart @@ -31,7 +31,7 @@ abstract class Multiplier extends Module { /// Take input [a] and input [b] and return the /// [product] of the multiplication result. - Multiplier(Logic a, Logic b, {this.signed = false, super.name}) { + Multiplier(Logic a, Logic b, {required this.signed, super.name}) { this.a = addInput('a', a, width: a.width); this.b = addInput('b', b, width: b.width); } @@ -105,7 +105,7 @@ class CompressionTreeMultiplyAccumulate extends MultiplyAccumulate { ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppTree, {super.signed = false}) : super( - name: 'Compression Tree Multiplier: ' + name: 'Compression Tree Multiply Accumulate: ' 'R${radix}_${ppTree.call([Logic()], (a, b) => Logic()).name}') { final accumulate = addOutput('accumulate', width: a.width + b.width + 1); final pp = @@ -147,13 +147,13 @@ class CompressionTreeMultiplyAccumulate extends MultiplyAccumulate { /// A MultiplyAccumulate which ignores the [c] term and applies the /// multiplier function -class MultiplyOnly extends MultiplyAccumulate { +class MutiplyOnly extends MultiplyAccumulate { @override Logic get accumulate => output('accumulate'); /// Construct a MultiplyAccumulate that only multiplies to enable /// using the same tester with zero addend. - MultiplyOnly(super.a, super.b, super.c, + MutiplyOnly(super.a, super.b, super.c, Multiplier Function(Logic a, Logic b) multiplyGenerator) : super(name: 'Multiply Only: ${multiplyGenerator.call(a, b).name}') { final accumulate = addOutput('accumulate', width: a.width + b.width + 1); diff --git a/lib/src/arithmetic/multiplier_encoder.dart b/lib/src/arithmetic/multiplier_encoder.dart index 5a624707..f5707ea2 100644 --- a/lib/src/arithmetic/multiplier_encoder.dart +++ b/lib/src/arithmetic/multiplier_encoder.dart @@ -91,7 +91,7 @@ class MultiplierEncoder { /// Generate an encoding of the input multiplier MultiplierEncoder(this.multiplier, RadixEncoder radixEncoder, - {bool signed = true}) + {required bool signed}) : _encoder = radixEncoder, _sliceWidth = log2Ceil(radixEncoder.radix) + 1 { // Unsigned encoding wants to overlap past the multipler @@ -144,7 +144,7 @@ class MultiplicandSelector { late LogicArray multiples; /// Generate required multiples of multiplicand - MultiplicandSelector(this.radix, this.multiplicand, {bool signed = true}) + MultiplicandSelector(this.radix, this.multiplicand, {required bool signed}) : shift = log2Ceil(radix) { if (radix > 16) { throw RohdHclException('Radices beyond 16 are not yet supported'); diff --git a/lib/src/arithmetic/partial_product_generator.dart b/lib/src/arithmetic/partial_product_generator.dart index 810b1cd7..16dd4770 100644 --- a/lib/src/arithmetic/partial_product_generator.dart +++ b/lib/src/arithmetic/partial_product_generator.dart @@ -64,7 +64,7 @@ class PartialProductGenerator { late final MultiplicandSelector selector; /// Operands are signed - late bool signed = true; + final bool signed; // Used to avoid sign extending more than once var _signExtended = false; @@ -72,7 +72,7 @@ class PartialProductGenerator { /// Construct the partial product matrix PartialProductGenerator( Logic multiplicand, Logic multiplier, RadixEncoder radixEncoder, - {this.signed = true, + {required this.signed, SignExtension signExtension = SignExtension.compactRect}) { encoder = MultiplierEncoder(multiplier, radixEncoder, signed: signed); selector = diff --git a/lib/src/component_config/components/component_registry.dart b/lib/src/component_config/components/component_registry.dart index 6d3665e1..c6c336b4 100644 --- a/lib/src/component_config/components/component_registry.dart +++ b/lib/src/component_config/components/component_registry.dart @@ -24,5 +24,6 @@ List get componentRegistry => [ EdgeDetectorConfigurator(), FindConfigurator(), ParallelPrefixAdderConfigurator(), - CompressionTreeMultiplierConfigurator(), + // TODO(desmonddak): fix conversion path to schematic + // CompressionTreeMultiplierConfigurator(), ]; diff --git a/lib/src/component_config/components/config_carry_save_multiplier.dart b/lib/src/component_config/components/config_carry_save_multiplier.dart index 9abbfe23..96374d80 100644 --- a/lib/src/component_config/components/config_carry_save_multiplier.dart +++ b/lib/src/component_config/components/config_carry_save_multiplier.dart @@ -16,19 +16,20 @@ class CarrySaveMultiplierConfigurator extends Configurator { /// A knob controlling the width of the inputs to a [CarrySaveMultiplier]. final IntConfigKnob logicWidthKnob = IntConfigKnob(value: 8); + /// A knob controling the sign of a [CarrySaveMultiplier] + final signChoiceKnob = ChoiceConfigKnob([false, true], value: false); + @override final String name = 'Carry Save Multiplier'; @override CarrySaveMultiplier createModule() => CarrySaveMultiplier( - Logic(width: logicWidthKnob.value), - Logic(width: logicWidthKnob.value), - clk: Logic(), - reset: Logic(), - ); + Logic(width: logicWidthKnob.value), Logic(width: logicWidthKnob.value), + clk: Logic(), reset: Logic(), signed: true); @override late final Map> knobs = UnmodifiableMapView({ 'Width': logicWidthKnob, + 'Sign': signChoiceKnob, }); } diff --git a/test/arithmetic/carry_save_multiplier_test.dart b/test/arithmetic/carry_save_multiplier_test.dart index 35778a80..9cffff90 100644 --- a/test/arithmetic/carry_save_multiplier_test.dart +++ b/test/arithmetic/carry_save_multiplier_test.dart @@ -25,7 +25,8 @@ void main() { final clk = SimpleClockGenerator(10).clk; final reset = Logic(name: 'reset'); - expect(() => CarrySaveMultiplier(clk: clk, reset: reset, a, b), + expect( + () => CarrySaveMultiplier(clk: clk, reset: reset, signed: true, a, b), throwsA(const TypeMatcher())); }); @@ -36,7 +37,7 @@ void main() { final reset = Logic(name: 'reset'); final clk = SimpleClockGenerator(10).clk; - final csm = CarrySaveMultiplier(clk: clk, reset: reset, a, b); + final csm = CarrySaveMultiplier(clk: clk, reset: reset, signed: true, a, b); await csm.build(); diff --git a/test/arithmetic/multiplier_encoder_test.dart b/test/arithmetic/multiplier_encoder_test.dart index 805834bd..c3826398 100644 --- a/test/arithmetic/multiplier_encoder_test.dart +++ b/test/arithmetic/multiplier_encoder_test.dart @@ -76,7 +76,7 @@ void main() { logicX.put(X); logicY.put(Y); logicZ.put(Z); - final pp = PartialProductGenerator(logicX, logicY, encoder); + final pp = PartialProductGenerator(logicX, logicY, encoder, signed: true); final lastLength = pp.partialProducts[pp.rows - 1].length + pp.rowShift[pp.rows - 1]; diff --git a/test/arithmetic/multiplier_test.dart b/test/arithmetic/multiplier_test.dart index c19f4e33..007d6ffc 100644 --- a/test/arithmetic/multiplier_test.dart +++ b/test/arithmetic/multiplier_test.dart @@ -37,7 +37,7 @@ void testMultiplyAccumulateRandom(int width, int iterations, final c = Logic(name: 'c', width: width * 2); final mod = fn(a, b, c); test('random_${mod.name}_S${mod.signed}_W${width}_I$iterations', () async { - final multiplyOnly = mod is MultiplyOnly; + final multiplyOnly = mod is MutiplyOnly; await mod.build(); final signed = mod.signed; final value = Random(47); @@ -68,7 +68,7 @@ void testMultiplyAccumulateExhaustive( test('exhaustive_${mod.name}_S${mod.signed}_W$width', () async { await mod.build(); final signed = mod.signed; - final multiplyOnly = mod is MultiplyOnly; + final multiplyOnly = mod is MutiplyOnly; final cLimit = multiplyOnly ? 1 : (1 << (2 * width)); @@ -86,7 +86,6 @@ void testMultiplyAccumulateExhaustive( : signed ? BigInt.from(cc).toSigned(2 * width) : BigInt.from(cc).toUnsigned(2 * width); - checkMultiplyAccumulate(mod, bA, bB, bC); } } @@ -94,62 +93,65 @@ void testMultiplyAccumulateExhaustive( }); } +typedef MultiplyAccumulateCallback = MultiplyAccumulate Function( + Logic a, Logic b, Logic c); + +typedef MultiplierCallback = Multiplier Function(Logic a, Logic b); + void main() { tearDown(() async { await Simulator.reset(); }); - // Use MAC tester for Multiply - - // First curry the Multiplier - Multiplier currySignedMultiplier(Logic a, Logic b) => - CompressionTreeMultiplier(a, b, 4, KoggeStone.new, signed: true); - - Multiplier curryUnsignedMultiplier(Logic a, Logic b) => - CompressionTreeMultiplier(a, b, 4, KoggeStone.new); - - // Now treat the multiplier as a MAC with a zero input addend [c] - MultiplyAccumulate currySignedMultiplierAsMAC(Logic a, Logic b, Logic c) => - MultiplyOnly(a, b, c, currySignedMultiplier); - - MultiplyAccumulate curryUnsignedMultiplierAsMAC(Logic a, Logic b, Logic c) => - MultiplyOnly(a, b, c, curryUnsignedMultiplier); + MultiplierCallback curryCompressionTreeMultiplier( + int radix, + ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppTree, + {required bool signed}) => + (a, b) => CompressionTreeMultiplier(a, b, radix, ppTree, signed: signed); + + MultiplyAccumulateCallback curryMultiplierasMultiplyAccumulate( + int radix, + ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppTree, + {required bool signed}) => + (a, b, c) => MutiplyOnly(a, b, c, + curryCompressionTreeMultiplier(radix, ppTree, signed: signed)); + + MultiplyAccumulateCallback curryMultiplyAccumulate( + int radix, + ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppTree, + {required bool signed}) => + (a, b, c) => CompressionTreeMultiplyAccumulate(a, b, c, radix, ppTree, + signed: signed); - group('test Compression Tree Multiplier Randomly', () { - for (final width in [4, 5, 6, 11]) { - testMultiplyAccumulateRandom(width, 30, currySignedMultiplierAsMAC); - testMultiplyAccumulateRandom(width, 30, curryUnsignedMultiplierAsMAC); - } - }); - group('test Compression Tree Multiplier Exhaustive', () { - for (final width in [4, 5]) { - testMultiplyAccumulateExhaustive(width, currySignedMultiplierAsMAC); - testMultiplyAccumulateExhaustive(width, curryUnsignedMultiplierAsMAC); + group('Curried Test of Compression Tree Multiplier', () { + for (final signed in [false, true]) { + for (final radix in [2, 16]) { + for (final width in [5, 6]) { + for (final ppTree in [KoggeStone.new, BrentKung.new]) { + testMultiplyAccumulateRandom( + width, + 10, + curryMultiplierasMultiplyAccumulate(radix, ppTree, + signed: signed)); + } + } + } } }); - MultiplyAccumulate currySignedCompressionTreeMultiplyAccumulate( - Logic a, Logic b, Logic c) => - CompressionTreeMultiplyAccumulate(a, b, c, 4, KoggeStone.new, - signed: true); - MultiplyAccumulate curryUnsignedCompressionTreeMultiplyAccumulate( - Logic a, Logic b, Logic c) => - CompressionTreeMultiplyAccumulate(a, b, c, 4, KoggeStone.new); - - group('test Multiply Accumulate Random', () { - for (final width in [4, 5, 6, 11]) { - testMultiplyAccumulateRandom( - width, 30, currySignedCompressionTreeMultiplyAccumulate); - testMultiplyAccumulateRandom( - width, 30, curryUnsignedCompressionTreeMultiplyAccumulate); - } - }); - group('test Multiply Accumulate Exhaustive', () { - for (final width in [3, 4]) { - testMultiplyAccumulateExhaustive( - width, currySignedCompressionTreeMultiplyAccumulate); - testMultiplyAccumulateExhaustive( - width, curryUnsignedCompressionTreeMultiplyAccumulate); + group('Curried Test of Compression Tree Multiplier Accumulate', () { + for (final signed in [false, true]) { + for (final radix in [2, 16]) { + for (final width in [5, 6]) { + for (final ppTree in [KoggeStone.new, BrentKung.new]) { + testMultiplyAccumulateRandom(width, 10, + curryMultiplyAccumulate(radix, ppTree, signed: signed)); + } + } + } } }); diff --git a/test/configurator_test.dart b/test/configurator_test.dart index 8cd6155a..4a4c2328 100644 --- a/test/configurator_test.dart +++ b/test/configurator_test.dart @@ -125,9 +125,9 @@ void main() { test('should return both Int knobs to be configured', () { final multiplier = CarrySaveMultiplierConfigurator(); - for (final element in multiplier.knobs.values.toList()) { - expect(element, isA()); - } + expect(multiplier.knobs.values.whereType().length, 1); + expect(multiplier.knobs.values.whereType>().length, + 1); }); test('should return rtl code when invoke generate() with default value', From d61c8b05ac3d7c5c0582b823f145a3bf68647f74 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Mon, 12 Aug 2024 13:07:09 -0700 Subject: [PATCH 24/38] added test for majority() and fixed. updated LogicValue.firstOne() to use null --- lib/src/arithmetic/partial_product_generator.dart | 2 +- lib/src/utils.dart | 9 ++++----- test/arithmetic/multiplier_encoder_test.dart | 8 ++++++++ test/arithmetic/multiplier_test.dart | 4 ++-- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/src/arithmetic/partial_product_generator.dart b/lib/src/arithmetic/partial_product_generator.dart index 16dd4770..9a3b9426 100644 --- a/lib/src/arithmetic/partial_product_generator.dart +++ b/lib/src/arithmetic/partial_product_generator.dart @@ -510,7 +510,7 @@ class PartialProductGenerator { if (row < encoder.rows) { final encoding = encoder.getEncoding(row); if (encoding.multiples.value.isValid) { - final multiple = encoding.multiples.value.firstOne() + 1; + final multiple = encoding.multiples.value.firstOne()! + 1; str.write('$rowStr M=${encoding.multiples.reversed.value.bitString}' '(${multiple.toString().padLeft(2)}) ' 'S=${encoding.sign.value.toInt()}: '); diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 0a21dca0..e27b7f08 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -27,24 +27,23 @@ extension LogicValueMajority on LogicValue { var shiftedValue = this; var result = 0; while (shiftedValue != zero) { - result++; + result += (shiftedValue[0] & LogicValue.one == LogicValue.one) ? 1 : 0; shiftedValue >>>= 1; } return result > (width ~/ 2); } /// Compute the first One find operation on LogicValue, returning its position - int firstOne() { + int? firstOne() { if (!isValid) { - return -1; + return null; } var shiftedValue = this; var result = 0; while (shiftedValue[0] != LogicValue.one) { result++; if (result == width) { - result = -1; - break; + return null; } shiftedValue >>>= 1; } diff --git a/test/arithmetic/multiplier_encoder_test.dart b/test/arithmetic/multiplier_encoder_test.dart index c3826398..86c41427 100644 --- a/test/arithmetic/multiplier_encoder_test.dart +++ b/test/arithmetic/multiplier_encoder_test.dart @@ -104,6 +104,14 @@ void main() { expect(pp.evaluate(), equals(product)); }); + test('majority function', () async { + expect(LogicValue.ofBigInt(BigInt.from(7), 5).majority(), true); + expect(LogicValue.ofBigInt(BigInt.from(7) << 1, 5).majority(), true); + expect(LogicValue.ofBigInt(BigInt.from(11) << 1, 5).majority(), true); + expect(LogicValue.ofBigInt(BigInt.from(9) << 1, 5).majority(), false); + expect(LogicValue.ofBigInt(BigInt.from(7) << 3, 7).majority(), false); + }); + // This is a two-minute exhaustive but quick test test('exhaustive partial product evaluate: square radix-4, all extension', () async { diff --git a/test/arithmetic/multiplier_test.dart b/test/arithmetic/multiplier_test.dart index 007d6ffc..b47c8a82 100644 --- a/test/arithmetic/multiplier_test.dart +++ b/test/arithmetic/multiplier_test.dart @@ -110,7 +110,7 @@ void main() { {required bool signed}) => (a, b) => CompressionTreeMultiplier(a, b, radix, ppTree, signed: signed); - MultiplyAccumulateCallback curryMultiplierasMultiplyAccumulate( + MultiplyAccumulateCallback curryMultiplierAsMultiplyAccumulate( int radix, ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppTree, @@ -134,7 +134,7 @@ void main() { testMultiplyAccumulateRandom( width, 10, - curryMultiplierasMultiplyAccumulate(radix, ppTree, + curryMultiplierAsMultiplyAccumulate(radix, ppTree, signed: signed)); } } From a26a7099313d729432a51ce1900f9642da8e806c Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Mon, 12 Aug 2024 23:27:15 -0700 Subject: [PATCH 25/38] cleanup of API making more methods private, fixing SignMag adder API --- lib/src/arithmetic/adder.dart | 6 +---- lib/src/arithmetic/multiplier.dart | 7 +++--- .../parallel_prefix_operations.dart | 4 ---- .../arithmetic/partial_product_generator.dart | 23 +++++++++++-------- lib/src/arithmetic/ripple_carry_adder.dart | 4 ---- lib/src/arithmetic/sign_magnitude_adder.dart | 16 ++++++++----- 6 files changed, 28 insertions(+), 32 deletions(-) diff --git a/lib/src/arithmetic/adder.dart b/lib/src/arithmetic/adder.dart index 3f50146d..49629560 100644 --- a/lib/src/arithmetic/adder.dart +++ b/lib/src/arithmetic/adder.dart @@ -30,10 +30,6 @@ abstract class Adder extends Module { /// The addition results [sum] including carry bit Logic get sum => output('sum'); - /// Implementation needs to provide a method for calculating the full sum - @protected - Logic calculateSum(); - /// Implementation needs to provide a method for calculating the sum /// without carry @protected @@ -57,7 +53,7 @@ abstract class Adder extends Module { out <= calculateOut(); carryOut <= calculateCarry(); - sum <= calculateSum(); + sum <= [carryOut, out].swizzle(); } } diff --git a/lib/src/arithmetic/multiplier.dart b/lib/src/arithmetic/multiplier.dart index 3f482afa..b29b01a4 100644 --- a/lib/src/arithmetic/multiplier.dart +++ b/lib/src/arithmetic/multiplier.dart @@ -60,7 +60,7 @@ abstract class MultiplyAccumulate extends Module { /// Take input [a] and input [b], compute their /// product, add input [c] to produce the [accumulate] result. MultiplyAccumulate(Logic a, Logic b, Logic c, - {this.signed = false, super.name}) { + {required this.signed, super.name}) { this.a = addInput('a', a, width: a.width); this.b = addInput('b', b, width: b.width); this.c = addInput('c', c, width: c.width); @@ -103,7 +103,7 @@ class CompressionTreeMultiplyAccumulate extends MultiplyAccumulate { /// a given radix and final adder functor CompressionTreeMultiplyAccumulate(super.a, super.b, super.c, int radix, ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppTree, - {super.signed = false}) + {required super.signed}) : super( name: 'Compression Tree Multiply Accumulate: ' 'R${radix}_${ppTree.call([Logic()], (a, b) => Logic()).name}') { @@ -154,7 +154,8 @@ class MutiplyOnly extends MultiplyAccumulate { /// Construct a MultiplyAccumulate that only multiplies to enable /// using the same tester with zero addend. MutiplyOnly(super.a, super.b, super.c, - Multiplier Function(Logic a, Logic b) multiplyGenerator) + Multiplier Function(Logic a, Logic b) multiplyGenerator, + {super.signed = false}) // Will be overrwridden by multiplyGenerator : super(name: 'Multiply Only: ${multiplyGenerator.call(a, b).name}') { final accumulate = addOutput('accumulate', width: a.width + b.width + 1); diff --git a/lib/src/arithmetic/parallel_prefix_operations.dart b/lib/src/arithmetic/parallel_prefix_operations.dart index 28fa90d8..59a9959f 100644 --- a/lib/src/arithmetic/parallel_prefix_operations.dart +++ b/lib/src/arithmetic/parallel_prefix_operations.dart @@ -237,10 +237,6 @@ class ParallelPrefixAdder extends Adder { @override @protected Logic calculateCarry() => _carry; - - @override - @protected - Logic calculateSum() => [_carry, _out].swizzle(); } /// Incrementer based on ParallelPrefix tree diff --git a/lib/src/arithmetic/partial_product_generator.dart b/lib/src/arithmetic/partial_product_generator.dart index 9a3b9426..393e90cd 100644 --- a/lib/src/arithmetic/partial_product_generator.dart +++ b/lib/src/arithmetic/partial_product_generator.dart @@ -91,13 +91,13 @@ class PartialProductGenerator { case SignExtension.none: ; case SignExtension.brute: - bruteForceSignExtend(); + _bruteForceSignExtend(); case SignExtension.stop: - signExtendWithStopBitsRect(); + _signExtendWithStopBitsRect(); case SignExtension.compact: - signExtendCompact(); + _signExtendCompact(); case SignExtension.compactRect: - signExtendCompactRect(); + _signExtendCompactRect(); } } @@ -114,7 +114,7 @@ class PartialProductGenerator { } /// Fully sign extend the PP array: useful for reference only - void bruteForceSignExtend() { + void _bruteForceSignExtend() { if (_signExtended) { throw RohdHclException('Partial Product array already sign-extended'); } @@ -142,7 +142,7 @@ class PartialProductGenerator { /// enough that carry bit lands outside another row). /// This technique can then be combined with a first-row extension technique /// for folding in the final carry. - void signExtendWithStopBitsRect() { + void _signExtendWithStopBitsRect() { if (_signExtended) { throw RohdHclException('Partial Product array already sign-extended'); } @@ -219,7 +219,7 @@ class PartialProductGenerator { } /// Sign extend the PP array using stop bits without adding a row. - void signExtendCompact() { + void _signExtendCompact() { // An implementation of // 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, @@ -323,7 +323,7 @@ class PartialProductGenerator { /// This routine works with different widths of multiplicand/multiplier, /// an extension of Mohanty, B.K., Choubey designed by /// Desmond A. Kirkpatrick - void signExtendCompactRect() { + void _signExtendCompactRect() { if (_signExtended) { throw RohdHclException('Partial Product array already sign-extended'); } @@ -462,7 +462,11 @@ class PartialProductGenerator { } return maxW; } +} +/// Debug routines for printing out partial product matrix during +/// simulation with live logic values +extension EvaluateLivePartialProduct on PartialProductGenerator { /// Accumulate the partial products and return as BigInt BigInt evaluate() { final maxW = maxWidth(); @@ -479,8 +483,7 @@ class PartialProductGenerator { } /// Print out the partial product matrix - @override - String toString() { + String representation() { final str = StringBuffer(); final maxW = maxWidth(); diff --git a/lib/src/arithmetic/ripple_carry_adder.dart b/lib/src/arithmetic/ripple_carry_adder.dart index 324c718a..a6d9665b 100644 --- a/lib/src/arithmetic/ripple_carry_adder.dart +++ b/lib/src/arithmetic/ripple_carry_adder.dart @@ -47,8 +47,4 @@ class RippleCarryAdder extends Adder { @override @protected Logic calculateCarry() => _carry; - - @override - @protected - Logic calculateSum() => [_carry, _out].swizzle(); } diff --git a/lib/src/arithmetic/sign_magnitude_adder.dart b/lib/src/arithmetic/sign_magnitude_adder.dart index b4db5295..c4487927 100644 --- a/lib/src/arithmetic/sign_magnitude_adder.dart +++ b/lib/src/arithmetic/sign_magnitude_adder.dart @@ -28,12 +28,17 @@ class SignMagnitudeAdder extends Adder { /// The sign of the result Logic get sign => output('sign'); + /// If largest magnitude negative is first argument, no compare and + /// swap is needed. + bool largestNegativeFirst; + late final Logic _out; late final Logic _carry = Logic(); /// [SignMagnitudeAdder] constructor with an unsigned adder functor SignMagnitudeAdder(Logic as, super.a, Logic bs, super.b, - Adder Function(Logic, Logic) adderGen) + Adder Function(Logic, Logic) adderGen, + {this.largestNegativeFirst = false}) : _out = Logic(width: a.width), super( name: 'Ones Complement Adder: ' @@ -42,6 +47,9 @@ class SignMagnitudeAdder extends Adder { bSign = addInput('bSign', bs); final sign = addOutput('sign'); + final computeSign = mux(largestNegativeFirst ? Const(1) : Const(0), aSign, + mux(a.lt(b) & aSign, bSign, aSign)); + final aOnesComplement = mux(aSign, ~a, a); final bOnesComplement = mux(bSign, ~b, b); @@ -49,7 +57,7 @@ class SignMagnitudeAdder extends Adder { final endAround = adder.carryOut & (aSign | bSign); final localOut = mux(endAround, adder.sum + 1, adder.sum); - sign <= aSign; + sign <= computeSign; _out <= mux(sign, ~localOut, localOut).slice(_out.width - 1, 0); _carry <= localOut.slice(localOut.width - 1, localOut.width - 1); } @@ -61,8 +69,4 @@ class SignMagnitudeAdder extends Adder { @override @protected Logic calculateCarry() => _carry; - - @override - @protected - Logic calculateSum() => [_carry, _out].swizzle(); } From 7da50039ddf57749b2f616726b415479cf32cb63 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Fri, 16 Aug 2024 16:11:09 -0700 Subject: [PATCH 26/38] missed some final fixes --- .../components/component_registry.dart | 3 +-- test/arithmetic/addend_compressor_test.dart | 2 +- test/arithmetic/adder_test.dart | 10 +++++----- test/arithmetic/multiplier_encoder_test.dart | 5 +++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/src/component_config/components/component_registry.dart b/lib/src/component_config/components/component_registry.dart index c6c336b4..6d3665e1 100644 --- a/lib/src/component_config/components/component_registry.dart +++ b/lib/src/component_config/components/component_registry.dart @@ -24,6 +24,5 @@ List get componentRegistry => [ EdgeDetectorConfigurator(), FindConfigurator(), ParallelPrefixAdderConfigurator(), - // TODO(desmonddak): fix conversion path to schematic - // CompressionTreeMultiplierConfigurator(), + CompressionTreeMultiplierConfigurator(), ]; diff --git a/test/arithmetic/addend_compressor_test.dart b/test/arithmetic/addend_compressor_test.dart index 602f0610..e873a222 100644 --- a/test/arithmetic/addend_compressor_test.dart +++ b/test/arithmetic/addend_compressor_test.dart @@ -121,7 +121,7 @@ void main() { final pp = PartialProductGenerator(a, b, encoder, signed: signed); // Turn on printing by using widthX == 6 (we are fooling the dead code // checking linter here) - // print(pp); + // print(pp.representation()); expect(pp.evaluate(), equals(BigInt.from(av * bv))); final compressor = ColumnCompressor(pp); // print('eval: ${compressor.evaluate(printOut: output)}'); diff --git a/test/arithmetic/adder_test.dart b/test/arithmetic/adder_test.dart index 88f07cf7..a9b52fb3 100644 --- a/test/arithmetic/adder_test.dart +++ b/test/arithmetic/adder_test.dart @@ -110,10 +110,9 @@ void testExhaustiveSignMagnitude(int n, Adder Function(Logic a, Logic b) fn) { final bSign = Logic(name: 'bSign'); final b = Logic(name: 'b', width: n); - final adder = SignMagnitudeAdder(aSign, a, bSign, b, fn); - test('exhaustive Sign Magnitude: ${adder.name}_W${a.width}', () async { - await adder.build(); - + final adder = + SignMagnitudeAdder(aSign, a, bSign, b, fn, largestNegativeFirst: true); + test('exhaustive Sign Magnitude: ${adder.name}_W${a.width}', () { for (var i = 0; i < pow(2, n); i += 1) { for (var j = 0; j < pow(2, n); j += 1) { final bI = BigInt.from(i).toSigned(n); @@ -150,7 +149,8 @@ void testRandomSignMagnitude( final bSign = Logic(name: 'bSign'); final b = Logic(name: 'b', width: width); - final adder = SignMagnitudeAdder(aSign, a, bSign, b, fn); + final adder = + SignMagnitudeAdder(aSign, a, bSign, b, fn, largestNegativeFirst: true); test('random Sign Magnitude: ${adder.name}_W${a.width}', () async { await adder.build(); diff --git a/test/arithmetic/multiplier_encoder_test.dart b/test/arithmetic/multiplier_encoder_test.dart index 86c41427..c81b7429 100644 --- a/test/arithmetic/multiplier_encoder_test.dart +++ b/test/arithmetic/multiplier_encoder_test.dart @@ -93,10 +93,11 @@ void main() { ..add(Const(1)); // print('lastL=$lastLength'); // Add a row for addend - // print(pp); + // print(pp.representation()); + pp.partialProducts.insert(0, l); pp.rowShift.insert(0, 0); - // print(pp); + // print(pp.representation()); if (pp.evaluate() != product) { stdout.write('Fail: $X * $Y: ${pp.evaluate()} vs expected $product\n'); From cde1c365674703b170ad2e90077ec3cf66621d60 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Sat, 17 Aug 2024 16:47:24 -0700 Subject: [PATCH 27/38] fixed sign-mag to be correct width, fixed tests to be uniquely named --- lib/src/arithmetic/adder.dart | 31 +----- lib/src/arithmetic/multiplier.dart | 4 +- .../parallel_prefix_operations.dart | 78 ++++++------- lib/src/arithmetic/ripple_carry_adder.dart | 21 +--- lib/src/arithmetic/sign_magnitude_adder.dart | 50 ++++----- test/arithmetic/addend_compressor_test.dart | 2 +- test/arithmetic/adder_test.dart | 105 +++++++++--------- .../parallel_prefix_operations_test.dart | 30 ++--- 8 files changed, 140 insertions(+), 181 deletions(-) diff --git a/lib/src/arithmetic/adder.dart b/lib/src/arithmetic/adder.dart index 49629560..d1967be1 100644 --- a/lib/src/arithmetic/adder.dart +++ b/lib/src/arithmetic/adder.dart @@ -15,45 +15,24 @@ import 'package:rohd_hcl/rohd_hcl.dart'; abstract class Adder extends Module { /// The input to the adder pin [a]. @protected - late final Logic a; + Logic get a => input('a'); /// The input to the adder pin [b]. @protected - late final Logic b; + Logic get b => input('b'); - /// The addition results [out] including carry bit - Logic get out => output('out'); - - /// The carry results [carryOut]. - Logic get carryOut => output('carryOut'); - - /// The addition results [sum] including carry bit + /// The addition results in 2s complement form as [sum] Logic get sum => output('sum'); - /// Implementation needs to provide a method for calculating the sum - /// without carry - @protected - Logic calculateOut(); - - /// Implementation needs to provide a method for calculating the carry out - @protected - Logic calculateCarry(); - /// Takes in input [a] and input [b] and return the [sum] of the addition /// result. The width of input [a] and [b] must be the same. Adder(Logic a, Logic b, {super.name}) { if (a.width != b.width) { throw RohdHclException('inputs of a and b should have same width.'); } - this.a = addInput('a', a, width: a.width); - this.b = addInput('b', b, width: b.width); - addOutput('out', width: a.width); - addOutput('carryOut'); + addInput('a', a, width: a.width); + addInput('b', b, width: b.width); addOutput('sum', width: a.width + 1); - - out <= calculateOut(); - carryOut <= calculateCarry(); - sum <= [carryOut, out].swizzle(); } } diff --git a/lib/src/arithmetic/multiplier.dart b/lib/src/arithmetic/multiplier.dart index b29b01a4..342520e8 100644 --- a/lib/src/arithmetic/multiplier.dart +++ b/lib/src/arithmetic/multiplier.dart @@ -89,7 +89,7 @@ class CompressionTreeMultiplier extends Multiplier { final compressor = ColumnCompressor(pp)..compress(); final adder = ParallelPrefixAdder( compressor.extractRow(0), compressor.extractRow(1), ppTree); - product <= adder.out.slice(a.width + b.width - 1, 0); + product <= adder.sum.slice(a.width + b.width - 1, 0); } } @@ -141,7 +141,7 @@ class CompressionTreeMultiplyAccumulate extends MultiplyAccumulate { final adder = ParallelPrefixAdder( compressor.extractRow(0), compressor.extractRow(1), ppTree); - accumulate <= adder.out.slice(a.width + b.width, 0); + accumulate <= adder.sum.slice(a.width + b.width, 0); } } diff --git a/lib/src/arithmetic/parallel_prefix_operations.dart b/lib/src/arithmetic/parallel_prefix_operations.dart index 59a9959f..0cc2b695 100644 --- a/lib/src/arithmetic/parallel_prefix_operations.dart +++ b/lib/src/arithmetic/parallel_prefix_operations.dart @@ -11,7 +11,6 @@ import 'dart:math'; import 'package:collection/collection.dart'; -import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; @@ -162,10 +161,12 @@ class ParallelPrefixOrScan extends Module { Logic get out => output('out'); /// OrScan constructor - ParallelPrefixOrScan( - Logic inp, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) - ppGen) { + ParallelPrefixOrScan(Logic inp, + ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen) + : super( + name: 'ParallelPrefixOrScan: ${ppGen.call([ + Logic() + ], (a, b) => Logic()).name}') { inp = addInput('inp', inp, width: inp.width); final u = ppGen(inp.elements, (a, b) => a | b); addOutput('out', width: inp.width) <= u.val.rswizzle(); @@ -179,10 +180,12 @@ class ParallelPrefixPriorityFinder extends Module { Logic get out => output('out'); /// Priority Finder constructor - ParallelPrefixPriorityFinder( - Logic inp, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) - ppGen) { + ParallelPrefixPriorityFinder(Logic inp, + ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen) + : super( + name: 'ParallelPrefixFinder: ${ppGen.call([ + Logic() + ], (a, b) => Logic()).name}') { inp = addInput('inp', inp, width: inp.width); final u = ParallelPrefixOrScan(inp, ppGen); addOutput('out', width: inp.width) <= (u.out & ~(u.out << Const(1))); @@ -196,10 +199,12 @@ class ParallelPrefixPriorityEncoder extends Module { Logic get out => output('out'); /// PriorityEncoder constructor - ParallelPrefixPriorityEncoder( - Logic inp, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) - ppGen) { + ParallelPrefixPriorityEncoder(Logic inp, + ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen) + : super( + name: 'ParallelPrefixEncoder: ${ppGen.call([ + Logic() + ], (a, b) => Logic()).name}') { inp = addInput('inp', inp, width: inp.width); addOutput('out', width: log2Ceil(inp.width)); final u = ParallelPrefixPriorityFinder(inp, ppGen); @@ -209,14 +214,11 @@ class ParallelPrefixPriorityEncoder extends Module { /// Adder based on ParallelPrefix tree class ParallelPrefixAdder extends Adder { - final Logic _out; - final Logic _carry = Logic(); - /// Adder constructor ParallelPrefixAdder(super.a, super.b, ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen) - : _out = Logic(width: a.width), - super( + // : _out = Logic(width: a.width), + : super( name: 'ParallelPrefixAdder: ${ppGen.call([ Logic() ], (a, b) => Logic()).name}') { @@ -224,19 +226,15 @@ class ParallelPrefixAdder extends Adder { List.generate( a.width, (i) => [a[i] & b[i], a[i] | b[i]].swizzle()), (lhs, rhs) => [rhs[1] | rhs[0] & lhs[1], rhs[0] & lhs[0]].swizzle()); - _carry <= u.val[a.width - 1][1]; - _out <= - List.generate(a.width, - (i) => (i == 0) ? a[i] ^ b[i] : a[i] ^ b[i] ^ u.val[i - 1][1]) - .rswizzle(); + // carryOut <= u.val[a.width - 1][1]; + sum <= + [ + u.val[a.width - 1][1], + List.generate(a.width, + (i) => (i == 0) ? a[i] ^ b[i] : a[i] ^ b[i] ^ u.val[i - 1][1]) + .rswizzle() + ].swizzle(); } - @override - @protected - Logic calculateOut() => _out; - - @override - @protected - Logic calculateCarry() => _carry; } /// Incrementer based on ParallelPrefix tree @@ -245,10 +243,12 @@ class ParallelPrefixIncr extends Module { Logic get out => output('out'); /// Increment constructor - ParallelPrefixIncr( - Logic inp, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) - ppGen) { + ParallelPrefixIncr(Logic inp, + ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen) + : super( + name: 'ParallelPrefixIncr: ${ppGen.call([ + Logic() + ], (a, b) => Logic()).name}') { inp = addInput('inp', inp, width: inp.width); final u = ppGen(inp.elements, (lhs, rhs) => rhs & lhs); addOutput('out', width: inp.width) <= @@ -264,10 +264,12 @@ class ParallelPrefixDecr extends Module { Logic get out => output('out'); /// Decrement constructor - ParallelPrefixDecr( - Logic inp, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) - ppGen) { + ParallelPrefixDecr(Logic inp, + ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen) + : super( + name: 'ParallelPrefixDecr: ${ppGen.call([ + Logic() + ], (a, b) => Logic()).name}') { inp = addInput('inp', inp, width: inp.width); final u = ppGen((~inp).elements, (lhs, rhs) => rhs & lhs); addOutput('out', width: inp.width) <= diff --git a/lib/src/arithmetic/ripple_carry_adder.dart b/lib/src/arithmetic/ripple_carry_adder.dart index a6d9665b..a5fe3d38 100644 --- a/lib/src/arithmetic/ripple_carry_adder.dart +++ b/lib/src/arithmetic/ripple_carry_adder.dart @@ -8,7 +8,6 @@ // Author: Yao Jing Quek // -import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; @@ -18,14 +17,9 @@ import 'package:rohd_hcl/rohd_hcl.dart'; /// from the least significant bit (LSB) to most significant bit (MSB), the /// adder sequentially adds corresponding bits of two binary numbers. class RippleCarryAdder extends Adder { - @protected - late final Logic _out; - late final Logic _carry = Logic(); - /// Constructs an n-bit adder based on inputs List of inputs. - RippleCarryAdder(super.a, super.b, - {super.name = 'ripple_carry_adder', Logic? carry}) - : _out = Logic(width: a.width) { + RippleCarryAdder(super.a, super.b, {super.name = 'ripple_carry_adder'}) { + Logic? carry; final sumList = []; for (var i = 0; i < a.width; i++) { final fullAdder = FullAdder(a: a[i], b: b[i], carryIn: carry ?? Const(0)); @@ -36,15 +30,6 @@ class RippleCarryAdder extends Adder { sumList.add(carry!); - _out <= sumList.rswizzle().slice(_out.width - 1, 0); - _carry <= sumList[sumList.rswizzle().width - 1]; + sum <= sumList.rswizzle(); } - - @override - @protected - Logic calculateOut() => _out; - - @override - @protected - Logic calculateCarry() => _carry; } diff --git a/lib/src/arithmetic/sign_magnitude_adder.dart b/lib/src/arithmetic/sign_magnitude_adder.dart index c4487927..df0b9fdb 100644 --- a/lib/src/arithmetic/sign_magnitude_adder.dart +++ b/lib/src/arithmetic/sign_magnitude_adder.dart @@ -11,11 +11,10 @@ import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -/// An Adder which performs one's complement arithmetic using an unsigned -/// adder that is passed in using a functor. This requires that a larger -/// magnitude negative argumnet mus be the first 'a' argument. Enfording this -/// is challenging in floating point as a smaller mantissa may be larger -/// in magnitude due to the mantissa. +/// An Adder which performs one's complement arithmetic using an +/// adder that is passed in using a functor. If the caller can guarantee +/// that the larger magnitude or negative value is provided first in 'a', then +/// they can set 'largestMagnitudeFirst' to 'true' to avoid a comparator. class SignMagnitudeAdder extends Adder { /// The sign of the first input @protected @@ -28,45 +27,36 @@ class SignMagnitudeAdder extends Adder { /// The sign of the result Logic get sign => output('sign'); - /// If largest magnitude negative is first argument, no compare and - /// swap is needed. - bool largestNegativeFirst; - - late final Logic _out; - late final Logic _carry = Logic(); + /// Largest magnitude argument is provided in [a] or if equal + /// the argument with a negative sign. + bool largestMagnitudeFirst; /// [SignMagnitudeAdder] constructor with an unsigned adder functor SignMagnitudeAdder(Logic as, super.a, Logic bs, super.b, Adder Function(Logic, Logic) adderGen, - {this.largestNegativeFirst = false}) - : _out = Logic(width: a.width), - super( + {this.largestMagnitudeFirst = false}) + : super( name: 'Ones Complement Adder: ' '${adderGen.call(Logic(), Logic()).name}') { aSign = addInput('aSign', as); bSign = addInput('bSign', bs); final sign = addOutput('sign'); - final computeSign = mux(largestNegativeFirst ? Const(1) : Const(0), aSign, - mux(a.lt(b) & aSign, bSign, aSign)); + final bLarger = a.lt(b) | (a.eq(b) & bSign.gt(aSign)); + + final computeSign = mux(largestMagnitudeFirst ? Const(1) : Const(0), aSign, + mux(bLarger, bSign, aSign)); - final aOnesComplement = mux(aSign, ~a, a); - final bOnesComplement = mux(bSign, ~b, b); + final ax = a.zeroExtend(a.width + 1); + final bx = b.zeroExtend(b.width + 1); + + final aOnesComplement = mux(aSign, ~ax, ax); + final bOnesComplement = mux(bSign, ~bx, bx); final adder = adderGen(aOnesComplement, bOnesComplement); - final endAround = adder.carryOut & (aSign | bSign); + final endAround = adder.sum[-1] & (aSign | bSign); final localOut = mux(endAround, adder.sum + 1, adder.sum); - sign <= computeSign; - _out <= mux(sign, ~localOut, localOut).slice(_out.width - 1, 0); - _carry <= localOut.slice(localOut.width - 1, localOut.width - 1); + sum <= mux(sign, ~localOut, localOut).slice(ax.width - 1, 0); } - - @override - @protected - Logic calculateOut() => _out; - - @override - @protected - Logic calculateCarry() => _carry; } diff --git a/test/arithmetic/addend_compressor_test.dart b/test/arithmetic/addend_compressor_test.dart index e873a222..8d35612f 100644 --- a/test/arithmetic/addend_compressor_test.dart +++ b/test/arithmetic/addend_compressor_test.dart @@ -63,7 +63,7 @@ void testCompressionExhaustive(PartialProductGenerator pp) { final b = compressor.extractRow(1); final adder = ParallelPrefixAdder(a, b, KoggeStone.new); final adderValue = - adder.out.value.toBigInt().toSigned(compressor.columns.length); + adder.sum.value.toBigInt().toSigned(compressor.columns.length); expect(adderValue, equals(product), reason: 'Fail: $i($X)[$widthX] * $j($Y)[$widthY]: ' '$adderValue vs expected $product' diff --git a/test/arithmetic/adder_test.dart b/test/arithmetic/adder_test.dart index a9b52fb3..c2020f18 100644 --- a/test/arithmetic/adder_test.dart +++ b/test/arithmetic/adder_test.dart @@ -27,9 +27,10 @@ void checkAdder(Adder adder, LogicValue av, LogicValue bv) { adder.b.put(bv); expect( - adder.out.value.toBigInt(), + adder.sum.value.toBigInt(), // ignore: invalid_use_of_protected_member - equals((aB + bB) & ((BigInt.one << adder.a.width) - BigInt.one))); + // equals((aB + bB) & ((BigInt.one << adder.a.width) - BigInt.one))); + equals(aB + bB)); expect(adder.sum.value.toBigInt(), equals(aB + bB)); } @@ -81,11 +82,9 @@ void checkSignMagnitudeAdder(SignMagnitudeAdder adder, LogicValue aSign, // ignore: invalid_use_of_protected_member adder.b.put(bMagnitude); - final n = aMagnitude.width; - final computedVal = (adder.sign.value == LogicValue.one) - ? -adder.out.value.toBigInt() - : adder.out.value.toBigInt(); + ? -adder.sum.value.toBigInt() + : adder.sum.value.toBigInt(); final aValue = (aSign == LogicValue.one) ? -aMagnitude.toBigInt() @@ -94,64 +93,64 @@ void checkSignMagnitudeAdder(SignMagnitudeAdder adder, LogicValue aSign, ? -bMagnitude.toBigInt() : bMagnitude.toBigInt(); final expectSign = (aValue + bValue).sign; - // Special modular arithmetic for 1's complement negative numbers - // Remember that there are two zeros in 1's complement - final expectedMag = (aValue + bValue).abs() % - ((expectSign == -1) - ? BigInt.from(pow(2, n) - 1) - : BigInt.from(pow(2, n))); + final expectedMag = (aValue + bValue).abs(); + final expectVal = expectSign == -1 ? -expectedMag : expectedMag; expect(computedVal, equals(expectVal)); } -void testExhaustiveSignMagnitude(int n, Adder Function(Logic a, Logic b) fn) { +void testExhaustiveSignMagnitude(int n, Adder Function(Logic a, Logic b) fn, + {bool sortOperands = true}) { final aSign = Logic(name: 'aSign'); final a = Logic(name: 'a', width: n); final bSign = Logic(name: 'bSign'); final b = Logic(name: 'b', width: n); - final adder = - SignMagnitudeAdder(aSign, a, bSign, b, fn, largestNegativeFirst: true); - test('exhaustive Sign Magnitude: ${adder.name}_W${a.width}', () { + final adder = SignMagnitudeAdder(aSign, a, bSign, b, fn, + largestMagnitudeFirst: sortOperands); + test('exhaustive Sign Magnitude: ${adder.name}_W${a.width}_N$sortOperands', + () { for (var i = 0; i < pow(2, n); i += 1) { for (var j = 0; j < pow(2, n); j += 1) { final bI = BigInt.from(i).toSigned(n); final bJ = BigInt.from(j).toSigned(n); - final BigInt smaller; - final BigInt bigger; + + final bigger = bI; + final smaller = bJ; // When equal, we want the negative one first to produce 1 1...1 - if ((bI.abs() > bJ.abs()) || (bI.abs() == bJ.abs() && (bI < bJ))) { - bigger = bI; - smaller = bJ; - } else { + if (sortOperands & + ((bI.abs() < bJ.abs()) || (bI.abs() == bJ.abs() && (bI > bJ)))) { continue; - } - final biggerSign = bigger.abs() != bigger ? 1 : 0; - final smallerSign = smaller.abs() != smaller ? 1 : 0; + } else { + final biggerSign = bigger.abs() != bigger ? 1 : 0; + final smallerSign = smaller.abs() != smaller ? 1 : 0; - final biggerSignLv = LogicValue.of(biggerSign, width: 1); - final smallerSignLv = LogicValue.of(smallerSign, width: 1); + final biggerSignLv = LogicValue.of(biggerSign, width: 1); + final smallerSignLv = LogicValue.of(smallerSign, width: 1); - final biggerLv = LogicValue.of(bigger.abs(), width: n); - final smallerLv = LogicValue.of(smaller.abs(), width: n); + final biggerLv = LogicValue.of(bigger.abs(), width: n); + final smallerLv = LogicValue.of(smaller.abs(), width: n); - checkSignMagnitudeAdder( - adder, biggerSignLv, biggerLv, smallerSignLv, smallerLv); + checkSignMagnitudeAdder( + adder, biggerSignLv, biggerLv, smallerSignLv, smallerLv); + } } } }); } void testRandomSignMagnitude( - int width, int nSamples, Adder Function(Logic a, Logic b) fn) { + int width, int nSamples, Adder Function(Logic a, Logic b) fn, + {bool sortOperands = true}) { final aSign = Logic(name: 'aSign'); final a = Logic(name: 'a', width: width); final bSign = Logic(name: 'bSign'); final b = Logic(name: 'b', width: width); - final adder = - SignMagnitudeAdder(aSign, a, bSign, b, fn, largestNegativeFirst: true); - test('random Sign Magnitude: ${adder.name}_W${a.width}', () async { + final adder = SignMagnitudeAdder(aSign, a, bSign, b, fn, + largestMagnitudeFirst: sortOperands); + test('random Sign Magnitude: ${adder.name}_W${a.width}_N$sortOperands', + () async { await adder.build(); for (var i = 0; i < nSamples; ++i) { @@ -160,27 +159,25 @@ void testRandomSignMagnitude( final bb = Random().nextLogicValue(width: width); final bv = bb.toBigInt().toSigned(width); - final BigInt smaller; - final BigInt bigger; + final bigger = av; + final smaller = bv; // When equal, we want the negative one first to produce 1 1...1 - if ((av.abs() > bv.abs()) || (av.abs() == bv.abs() && (av < bv))) { - bigger = av; - smaller = bv; + if (sortOperands & + ((av.abs() < bv.abs()) || (av.abs() == bv.abs() && (av > bv)))) { + continue; } else { - bigger = bv; - smaller = av; - } - final biggerSign = bigger.abs() != bigger ? 1 : 0; - final smallerSign = smaller.abs() != smaller ? 1 : 0; + final biggerSign = bigger.abs() != bigger ? 1 : 0; + final smallerSign = smaller.abs() != smaller ? 1 : 0; - final biggerSignLv = LogicValue.of(biggerSign, width: 1); - final smallerSignLv = LogicValue.of(smallerSign, width: 1); + final biggerSignLv = LogicValue.of(biggerSign, width: 1); + final smallerSignLv = LogicValue.of(smallerSign, width: 1); - final biggerLv = LogicValue.of(bigger.abs(), width: width); - final smallerLv = LogicValue.of(smaller.abs(), width: width); + final biggerLv = LogicValue.of(bigger.abs(), width: width); + final smallerLv = LogicValue.of(smaller.abs(), width: width); - checkSignMagnitudeAdder( - adder, biggerSignLv, biggerLv, smallerSignLv, smallerLv); + checkSignMagnitudeAdder( + adder, biggerSignLv, biggerLv, smallerSignLv, smallerLv); + } } }); } @@ -209,15 +206,21 @@ void main() { group('SignMagnitude random', () { for (final ppGen in generators) { testRandomSignMagnitude(4, 30, RippleCarryAdder.new); + testRandomSignMagnitude(4, 30, RippleCarryAdder.new, sortOperands: false); testRandomSignMagnitude( 4, 30, (a, b) => ParallelPrefixAdder(a, b, ppGen)); + testRandomSignMagnitude(4, 30, (a, b) => ParallelPrefixAdder(a, b, ppGen), + sortOperands: false); } }); group('SignMagnitude exhaustive', () { for (final ppGen in generators) { testExhaustiveSignMagnitude(4, RippleCarryAdder.new); + testExhaustiveSignMagnitude(4, RippleCarryAdder.new, sortOperands: false); testExhaustiveSignMagnitude( 4, (a, b) => ParallelPrefixAdder(a, b, ppGen)); + testExhaustiveSignMagnitude(4, (a, b) => ParallelPrefixAdder(a, b, ppGen), + sortOperands: false); } }); } diff --git a/test/arithmetic/parallel_prefix_operations_test.dart b/test/arithmetic/parallel_prefix_operations_test.dart index 1073e481..2d82416d 100644 --- a/test/arithmetic/parallel_prefix_operations_test.dart +++ b/test/arithmetic/parallel_prefix_operations_test.dart @@ -14,9 +14,9 @@ import 'package:rohd_hcl/src/arithmetic/parallel_prefix_operations.dart'; import 'package:test/test.dart'; void testOrScan(int n, ParallelPrefixOrScan Function(Logic a) fn) { - test('or_scan_$n', () async { - final inp = Logic(name: 'inp', width: n); - final mod = fn(inp); + final inp = Logic(name: 'inp', width: n); + final mod = fn(inp); + test('or_scan_${n}_${mod.name}', () async { await mod.build(); int computeOrScan(int j) { @@ -45,9 +45,9 @@ void testOrScan(int n, ParallelPrefixOrScan Function(Logic a) fn) { void testPriorityFinder( int n, ParallelPrefixPriorityFinder Function(Logic a) fn) { - test('priority_encoder_$n', () async { - final inp = Logic(name: 'inp', width: n); - final mod = fn(inp); + final inp = Logic(name: 'inp', width: n); + final mod = fn(inp); + test('priority_finder_${n}_${mod.name}', () async { await mod.build(); int computePriorityLocation(int j) { @@ -73,9 +73,9 @@ void testPriorityFinder( void testPriorityEncoder( int n, ParallelPrefixPriorityEncoder Function(Logic a) fn) { - test('priority_encoder_$n', () async { - final inp = Logic(name: 'inp', width: n); - final mod = fn(inp); + final inp = Logic(name: 'inp', width: n); + final mod = fn(inp); + test('priority_encoder_${n}_${mod.name}', () async { await mod.build(); int computePriorityEncoding(int j) { @@ -100,9 +100,9 @@ void testPriorityEncoder( } void testIncr(int n, ParallelPrefixIncr Function(Logic a) fn) { - test('incr_$n', () async { - final inp = Logic(name: 'inp', width: n); - final mod = fn(inp); + final inp = Logic(name: 'inp', width: n); + final mod = fn(inp); + test('incr_${n}_${mod.name}', () async { await mod.build(); int computeIncr(int aa) => (aa + 1) & ((1 << n) - 1); @@ -120,9 +120,9 @@ void testIncr(int n, ParallelPrefixIncr Function(Logic a) fn) { } void testDecr(int n, ParallelPrefixDecr Function(Logic a) fn) { - test('decr_$n', () async { - final inp = Logic(name: 'inp', width: n); - final mod = fn(inp); + final inp = Logic(name: 'inp', width: n); + final mod = fn(inp); + test('decr_${n}_${mod.name}', () async { await mod.build(); int computeDecr(int aa) => (aa - 1) % (1 << n); From bdd5f9aa5a78603b3eb0e37a40812430f44653dd Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Sun, 18 Aug 2024 18:36:01 -0700 Subject: [PATCH 28/38] reorg and add major multiplier internals documentation --- doc/components/adder.md | 53 ++++- doc/components/multiplier.md | 47 +++- doc/components/multiplier_components.md | 202 ++++++++++++++++++ lib/rohd_hcl.dart | 1 + lib/src/arithmetic/addend_compressor.dart | 179 ++++++++-------- lib/src/arithmetic/multiplicand_selector.dart | 1 - lib/src/arithmetic/multiplier.dart | 1 - lib/src/arithmetic/multiplier_encoder.dart | 10 +- .../arithmetic/partial_product_generator.dart | 20 +- test/arithmetic/addend_compressor_test.dart | 83 +++++-- test/arithmetic/adder_test.dart | 31 +++ test/arithmetic/multiplier_encoder_test.dart | 7 - test/arithmetic/multiplier_test.dart | 18 ++ 13 files changed, 527 insertions(+), 126 deletions(-) create mode 100644 doc/components/multiplier_components.md diff --git a/doc/components/adder.md b/doc/components/adder.md index 4206fe63..3dacb0f7 100644 --- a/doc/components/adder.md +++ b/doc/components/adder.md @@ -1,8 +1,10 @@ # Adder -ROHD HCL provides an adder module to get the sum from a list of logic. As of now, we have +ROHD HCL provides a set of adder modules to get the sum from a list of logic. As of now, we have - [Ripple Carry Adder](#ripple-carry-adder) +- [Parallel Prefix Adder](#parallel-prefix-adder) +- [Sign Magnitude Adder](#sign-magnitude-adder) ## Ripple Carry Adder @@ -22,3 +24,52 @@ b.put(5); final rippleCarryAdder = RippleCarryAdder(a, b); final sum = rippleCarryAdder.sum; ``` + +## Parallel Prefix Adder + +A parallel prefix adder is an adder that uses n instance of a parallel prefix tree (see `Parallel Prefix Operations`) to efficiently connect a set of `Full Adder` circuits to form a complete adder. + +Here is an example of instantiating a `ParallelPrefixAdder`: + +```dart + const width = 6; + final a = Logic(name: 'a', width: width); + final b = Logic(name: 'b', width: width); + + a.put(18); + b.put(24); + + final adder = ParallelPrefixAdder(a, b, BrentKung.new); + + final sum = adder.sum; + + print('${sum.value.toBigInt()}'); +``` + +## Sign Magnitude Adder + +A sign magnitude adder is useful in situations where the sign of the addends is seperated from their magnitude (e.g., not 2s complement), such as in floating point multipliers. The `SignMagnitudeAdder` inherits from `Adder` but adds the `Logic` inputs for the two operands. + +If you can supply the largest magnitude number first, then you can disable a comparator generation inside by declaring the `largestMagnitudeFirst` option as true. + +Here is an example of instantiating a `SignMagnitudeAdder`: + +```dart + const width = 6; + final aSign = Logic(name: 'aSign'); + final a = Logic(name: 'a', width: width); + final bSign = Logic(name: 'bSign'); + final b = Logic(name: 'b', width: width); + + aSign.put(1); + a.put(24); + b.put(18); + bSign.put(0); + + final adder = SignMagnitudeAdder(aSign, a, bSign, b, RippleCarryAdder.new, + largestMagnitudeFirst: true); + + final sum = adder.sum; + + print('${sum.value.toBigInt()}'); +``` diff --git a/doc/components/multiplier.md b/doc/components/multiplier.md index ab1d9426..c7716962 100644 --- a/doc/components/multiplier.md +++ b/doc/components/multiplier.md @@ -18,6 +18,8 @@ high-performance implementation: - [Compression Tree Multipy Accumulate](#compression-tree-multiply-accumulate) +The compression tree based arithmetic units are built from a set of components for Booth-encoding, column compression, and parallel prefix adders described in the [`Booth Encoding Multiplier Building Blocks`](./multiplier_components.md#booth-encoding-multiplier-building-blocks) section. + ## Carry Save Multiplier Carry save multiplier is a digital circuit used for performing multiplication operations. It @@ -85,6 +87,26 @@ The parameters of the - The type of `ParallelPrefix` tree used in the final `ParallelPrefixAdder` - Whether the operands should be treated as signed (2s complement) or unsigned +Here is an example of use of the `CompressionTreeMultiplier`: + +```dart + const widthA = 6; + const widthB = 9; + const radix = 8; + final a = Logic(name: 'a', width: widthA); + final b = Logic(name: 'b', width: widthB); + + a.put(15); + b.put(3); + + final multiplier = + CompressionTreeMultiplier(a, b, radix, KoggeStone.new, signed: true); + + final product = multiplier.product; + + print('${product.value.toBigInt()}'); +``` + ## Compression Tree Multiply Accumulate A compression tree multiply accumulate is similar to a compress tree @@ -92,10 +114,33 @@ multiplier, but it inserts an additional addend into the compression tree to allow for accumulation into this third input. The parameters of the -`CompressionTreeMultiplier` are: +`CompressionTreeMultiplyAccumulate` are: - Two input terms a and b - The accumulate input term c - The radix used for Booth encoding (2, 4, 8, and 16 are currently supported) - The type of `ParallelPrefix` tree used in the final `ParallelPrefixAdder` - Whether the operands should be treated as signed (2s complement) or unsigned + +Here is an example of using the `CompressionTreeMultiplyAccumulate`: + +```dart + const widthA = 6; + const widthB = 9; + const radix = 8; + final a = Logic(name: 'a', width: widthA); + final b = Logic(name: 'b', width: widthB); + final c = Logic(name: 'c', width: widthA + widthB); + + a.put(15); + b.put(3); + c.put(5); + + final multiplier = CompressionTreeMultiplyAccumulate( + a, b, c, radix, KoggeStone.new, + signed: true); + + final accumulate = multiplier.accumulate; + + print('${accumulate.value.toBigInt()}'); +``` diff --git a/doc/components/multiplier_components.md b/doc/components/multiplier_components.md new file mode 100644 index 00000000..6b2d8699 --- /dev/null +++ b/doc/components/multiplier_components.md @@ -0,0 +1,202 @@ +# Booth Encoding Multiplier Building Blocks + +The Compression Tree multipliers [`CompressionTreeMultiplier`]() and [`CompressionTreeMultiplyAccumulate`]() use a set of building blocks that can also be used for building up other multipliers and arithmetic circuits. These are from the family of Booth-encoding multipliers which are comprised of three major stages: + +1) Booth radix encoding (typically radix-4) generating partial products +2) Partial product array column compression to two addends +3) Final adder + +Each of these stages can be implemented with different algorithms and in ROHD-HCL, we provide the flexible building blocks for these three stages. + +- [Partial Product Generator](#partial-product-generator) +- [Compression Tree](#compression-tree) +- [Final Adder](#final-adder) + +## Introduction to Booth Encoding + +Think of the hand-multiplication process where you write down the multiplicand and the multiplier. Then, starting with the LSB of the multiplier (6), you would take each bit (0 1 1 0), and created a shifted version of the multiplicand (3 = 0 0 1 1) to write down as an addend row, shifting left 1 position per row. Once complete, you would add up all the rows. In the example below, the bits of the multiplier (6) select single multiples of multiplicand (3) shifted into a partial product matrix, which adds up to their product (18). + +```text + 0 0 1 1 (3) + 0 1 1 0 (6) + ========== + 0 0 0 0 + 0 0 1 1 + 0 0 1 1 + 0 0 0 0 + ==================== + 0 0 1 0 0 1 0 (18) +``` + +With Booth encoding, we take multiple adjacent bits of the multiplier (6) to form these rows. In the case that most closely matches hand-multiplication, radix-2 Booth encoding, we take two adjacent bit slices to create multiples [-1,-0, +0, +1] where a leading bit in the slice would indicate negation. These then select the appropriate multiple to shift into the row. So (6) = [0 1 1 0] gets sliced left-to-right (leading with a 0) to the create multiple selectors: [0 0], [1 0], [1 1], [0 1]. These slices are radix encoded into multiple (+-0, +-1) selectors as follows according to radix-2: + +| bit_i | bit_i-1 | multiple| +|:-----:|:-------:|:-------:| +| 0 | 0 | +0 | +| 0 | 1 | +1 | +| 1 | 0 | -1 | +| 1 | 1 | -0 | + +: Radix-2 Table + +These slices then select shifted multiples of the multiplicand (3 = 0 0 1 1) as follows. + +```text +row slice mult +00 [0 0] = +0 0 0 0 0 +01 [1 0] = -1 1 1 0 0 +02 [1 1] = -0 1 1 1 1 +03 [0 1] = +1 0 0 1 1 +``` + +A few things to note: first, that we are negating by 1s complement (so we need a -0) and second, these rows do not add up to (18: 10010). For Booth encoded rows to add up properly, they need to be in 2s complement form and they need to be sign-extended. + + Here is the matrix with crude sign extension (this formatting is available from our [`PartialProductGenerator`]() component). With 2s complementation, and sign bits folded in (note the LSB of each row has a sign term from the previous row), these addends are correclty formed and add to (18: 10010). + +```text + 7 6 5 4 3 2 1 0 +00 M=0 S=0: 0 0 0 0 0 0 0 0 : 00000000 = 0 (0) +01 M=1 S=1: 1 1 1 1 1 0 0 0 : 11111000 = 248 (-8) +02 M=0 S=1: 1 1 1 1 1 1 1 : 11111110 = 254 (-2) +03 M=1 S=0: 0 0 0 1 1 1 : 00011100 = 28 (28) +04 M= S= : 0 0 0 0 0 : 00000000 = 0 (0) +==================================== + 0 0 0 1 0 0 1 0 : 00010010 = 18 (18) + ``` + + There are more compact ways of doing sign-extension which result in far fewer additions. Here is an example of compact sign-extension: + +```text + 7 6 5 4 3 2 1 0 +00 M=0 S=0: 1 0 0 0 0 : 00010000 = 16 (16) +01 M=1 S=1: 0 1 0 0 0 : 00001000 = 8 (8) +02 M=0 S=1: 0 1 1 1 1 : 00011110 = 30 (30) +03 M=1 S=0: 1 1 0 1 1 1 : 11011100 = 220 (-36) +==================================== + 0 0 0 1 0 0 1 0 : 00010010 = 18 (18) +``` + +And of course, with higher radix-encoding, we select more bits at a time from the multiplier and therefore have fewer rows to add. Here is radix-4 Booth encoding for our example, slicing (6: 0110) radix$_{4}$[100]=-2 and radix$_{4}$[011]=2 as multiples: + +```text + 7 6 5 4 3 2 1 0 +00 M=2 S=1: 0 1 1 1 0 0 0 : 00111000 = 56 (56) +01 M=2 S=0: 1 1 0 1 1 0 1 : 11011010 = 218 (-38) +==================================== + 0 0 0 1 0 0 1 0 : 00010010 = 18 (18) +``` + +Note that radix-4 shifts by 2 positions each row, but with only two rows and with sign-extension adding an LSB bit, you only see a shift of 1 in row 1. + +## Partial Product Generator + +This building block creates a set of rows of partial products from a multiplicand and a multiplier. It maintains the partial products as a list of rows, which are themselves lists of Logic as well as a row shift value for each row to represent the starting column of the row's least-significant bit. Its primary inputs are the multiplicand, multplier, [`RadixEncoder`](), whether the operands are signed, and the type of [`SignExtension`]() to use in generating the partial product rows. + +The partial product generator produces a set of addends in shifted position to be added. The main output of the component is + +```dart + - List> partialProducts; + - rowShift = []; +``` + +### Radix Encoding + +An argument to the [`PartialProductGenerator`]() is the [`RadixEncoder`]() to be used. The [`RadixEncoder`]() takes a single argument which is the radix (power of 2) to be used. + +Instead of using the 1's in the multiplier to select shifted versions of the multiplicand to add in a partial product matrix, radix-encoding will encoding multiples of the multiplicand by examining adjacent bits of the multiplier. For radix-4, for example, for a multiplier of size M, instead of M rows of partial products, M/2 rows are formed by selecting from multiples [-2, -1, 0, 1, 2] of the multiplicand. These multiples are computed from an 3 bit slices, overlapped by 1 bit, of the multiplier. Higher radices use wider slices of the multiplier to encode fewer multiples and therefore fewer rows. + +| bit_i | bit_i-1 | bit_i-2 | multiple| +|:-----:|:-------:|:-------:|:-------:| +| 0 | 0 | 0 | +0 | +| 0 | 0 | 1 | 1 | +| 0 | 1 | 0 | 1 | +| 0 | 1 | 1 | 2 | +| 1 | 0 | 0 | 2 | +| 1 | 0 | 1 | -1 | +| 1 | 1 | 0 | -1 | +| 1 | 1 | 1 | -0 | + +: Radix-4 Table + +Our [`RadixEncoder`]() module is general, creating selection tables for arbitrary Booth radices of powers of 2. Currently we are limited to radix-16 because of challenges in creating the odd multiples efficiently, and there are more advanced techniques for efficiently generating higher radices than 16 than our current encoding/selection/partial-product generation scheme. + +### Sign Extension Option + +The [`PartialProductGenerator`]() class also provides for sign extension with multiple options including `SignExtension.none` which is no sign extension for help in debugging, as well as `SignExtension.compactRect` which is a compact form which works for rectangular products where the multiplicand and multiplier can be of different widths. + +### Partial Proeduct Visualization + +Creating new arithmetic building blocks from these components is tricky and visualizing intermediate results really helps. To that end, our [`PartialProductGenerator`]() class has visualization extension [`EvaluatePartialProduct`]() which help evaluate the current `Logic` values in array form during simulation to help with debug. The evaluation routine with the extension also adds the addends for you to help sanity check the partial product generation. The routine is `EvaluateLivePartialProduct.representation`. + +```text + 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +00 M= 2 S=1: 0 1 1 1 1 1 1 1 0 1 0 : 0000000001111111010 = 1018 (1018) +01 M= 1 S=0: 1 1 1 0 0 0 0 0 1 1 0 : 0000001110000011000 = 7192 (7192) +02 M= 0 S=0: 1 1 1 0 0 0 0 0 0 0 0 : 0001110000000000000 = 57344 (57344) +03 M= 0 S=0: 1 1 1 0 0 0 0 0 0 0 0 : 1110000000000000000 = 458752 (-65536) +====================================================================== + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 : 0000000000000010010 = 18 (18) +``` + +## Compression Tree + +Once you have a partial product matrix, you would like to add up the addends. Traditionally this is done using compression trees which instantate 2:1 and 3:2 column compressors (or carry-save adders) to reduce the matrix to two addends. The final two addends are often added with an efficient final adder. + +Our [`ColumnCompressor`]() class uses a delay-driven approach to efficiently compress the rows of the partial product matrix. Its only arguement is a [`PartialProductGenerator`]() , and it creates a list of ColumnQueues containing the final two addends stored by column after compression. An extractRow() routine can be used to extract the columns. [`ColumnCompressor`]() currently has an extension [`EvaluateColumnCompressor`]() which can be used to print out the compression progress. Here is the legend for these printouts. + +- ppR,C = partial product entry at row R, column C +- sR,C = sum term coming last from row R, column C +- cR,C = carry term coming last from row R, column C + +Compression Tree before: + +```text + pp5,11 pp5,10 pp5,9 pp5,8 pp5,7 pp5,6 pp5,5 pp5,4 pp4,3 pp3,2 pp2,1 pp1,0 + pp4,9 pp3,8 pp4,7 pp3,6 pp3,5 pp3,4 pp3,3 pp2,2 pp0,1 pp0,0 + pp4,8 pp3,7 pp4,6 pp4,5 pp4,4 pp1,3 pp1,2 pp1,1 + pp2,7 pp0,6 pp0,5 pp0,4 pp0,3 pp0,2 + pp2,6 pp2,5 pp2,4 pp2,3 + pp1,6 pp1,5 pp1,4 + + 1 1 0 0 0 0 0 0 0 1 1 0 110000000110 (3078) + 1 1 0 0 0 1 1 1 0 0 001100011100 (796) + 0 0 0 0 0 1 0 0 000000001000 (8) + 0 1 0 0 0 0 000001000000 (64) + 1 1 1 1 000001111000 (120) + 0 1 1 000000110000 (48) Total=18 +``` + +Compression Tree after compression: + +```text + pp5,11 pp5,10 s0,9 s0,8 s0,7 c0,5 c0,4 c0,3 s0,3 s0,2 pp0,1 pp1,0 + c0,9 c0,8 c0,7 c0,6 s0,6 s0,5 s0,4 s0,3 s0,2 s0,1 pp0,0 + + 1 1 1 1 1 0 1 0 0 1 0 0 111110100100 (4004) + 0 0 0 0 1 1 0 1 1 1 0 000001101110 (110) Total=18 +``` + +## Final Adder + +Any adder can be used as the final adder of the final two addends produced from compression. Typically, we use some for of parallel prefix adder for performance. + +## Compress Tree Multiplier Example + +Here is a code snippet that shows how these components can be used to create a multiplier. + +First the partial product generator is used, which we pass in the [`RadixEncoder`](), whether the operands are signed, and the kind of sign extension to use on the partial products. + +Next, we use the [`ColumnCompressor`]() to compress the partial products into two final addends. + +We then choose a[`ParallelPrefixAdder`]() using the [`BrentKung`]() tree style to do the addition. We pass in the two extracted rows of the compressor. +Finally, we produce the product. + +```dart + final pp = + PartialProductGenerator(a, b, RadixEncoder(radix), signed: true, + signExtension: SignExtension.compactRect); + final compressor = ColumnCompressor(pp)..compress(); + final adder = ParallelPrefixAdder( + compressor.extractRow(0), compressor.extractRow(1), BrentKung.new); + product <= adder.sum.slice(a.width + b.width - 1, 0); +``` diff --git a/lib/rohd_hcl.dart b/lib/rohd_hcl.dart index 243ceb0f..c6956cc4 100644 --- a/lib/rohd_hcl.dart +++ b/lib/rohd_hcl.dart @@ -5,6 +5,7 @@ export 'src/arbiters/arbiters.dart'; export 'src/arithmetic/adder.dart'; export 'src/arithmetic/carry_save_mutiplier.dart'; export 'src/arithmetic/multiplier.dart'; +export 'src/arithmetic/multiplier_lib.dart'; export 'src/arithmetic/parallel_prefix_operations.dart'; export 'src/arithmetic/ripple_carry_adder.dart'; export 'src/arithmetic/sign_magnitude_adder.dart'; diff --git a/lib/src/arithmetic/addend_compressor.dart b/lib/src/arithmetic/addend_compressor.dart index 12a41f1b..ac101db9 100644 --- a/lib/src/arithmetic/addend_compressor.dart +++ b/lib/src/arithmetic/addend_compressor.dart @@ -59,7 +59,7 @@ class Compressor3 extends AddendCompressor { } /// Compress terms -enum CompressTermType { +enum _CompressTermType { /// A carry term carry, @@ -71,12 +71,12 @@ enum CompressTermType { } /// A compression term -class CompressTerm implements Comparable { +class _CompressTerm implements Comparable<_CompressTerm> { /// The type of term we have - late final CompressTermType type; + late final _CompressTermType type; /// The inputs that drove this Term - late final List inputs; + late final List<_CompressTerm> inputs; /// The row of the terminal final int row; @@ -97,12 +97,12 @@ class CompressTerm implements Comparable { static const carryDelay = 0.75; /// CompressTerm constructor - CompressTerm(this.type, this.logic, this.inputs, this.row, this.col) { + _CompressTerm(this.type, this.logic, this.inputs, this.row, this.col) { delay = 0.0; final deltaDelay = switch (type) { - CompressTermType.carry => carryDelay, - CompressTermType.sum => sumDelay, - CompressTermType.pp => 0.0 + _CompressTermType.carry => carryDelay, + _CompressTermType.sum => sumDelay, + _CompressTermType.pp => 0.0 }; for (final i in inputs) { if (i.delay + deltaDelay > delay) { @@ -112,7 +112,7 @@ class CompressTerm implements Comparable { } @override int compareTo(Object other) { - if (other is! CompressTerm) { + if (other is! _CompressTerm) { throw Exception('Input must be of type CompressTerm '); } return delay > other.delay ? 1 : (delay < other.delay ? -1 : 0); @@ -122,14 +122,14 @@ class CompressTerm implements Comparable { LogicValue evaluate() { late LogicValue value; switch (type) { - case CompressTermType.pp: + case _CompressTermType.pp: value = logic.value; - case CompressTermType.sum: + case _CompressTermType.sum: // xor the eval of the terms final termValues = [for (final term in inputs) term.evaluate()]; final sum = termValues.swizzle().xor(); value = sum; - case CompressTermType.carry: + case _CompressTermType.carry: final termValues = [for (final term in inputs) term.evaluate()]; final termValuesInt = [ for (var i = 0; i < termValues.length; i++) termValues[i].toInt() @@ -157,9 +157,9 @@ class CompressTerm implements Comparable { String toString() { final str = StringBuffer(); final ts = switch (type) { - CompressTermType.pp => 'pp', - CompressTermType.carry => 'c', - CompressTermType.sum => 's' + _CompressTermType.pp => 'pp', + _CompressTermType.carry => 'c', + _CompressTermType.sum => 's' }; str ..write(ts) @@ -169,7 +169,7 @@ class CompressTerm implements Comparable { } /// A column of partial product terms -typedef ColumnQueue = PriorityQueue; +typedef ColumnQueue = PriorityQueue<_CompressTerm>; /// A column compressor class ColumnCompressor { @@ -186,7 +186,7 @@ class ColumnCompressor { for (var row = 0; row < pp.rows; row++) { for (var col = 0; col < pp.partialProducts[row].length; col++) { final trueColumn = pp.rowShift[row] + col; - final term = CompressTerm(CompressTermType.pp, + final term = _CompressTerm(_CompressTermType.pp, pp.partialProducts[row][col], [], row, trueColumn); columns[trueColumn].add(term); } @@ -197,67 +197,6 @@ class ColumnCompressor { int longestColumn() => columns.reduce((a, b) => a.length > b.length ? a : b).length; - @override - String toString() { - final ts = StringBuffer(); - for (var row = 0; row < longestColumn(); row++) { - for (var col = columns.length - 1; col >= 0; col--) { - final colList = columns[col].toList(); - if (row < colList.length) { - ts.write('\t${colList[row]}'); - } else { - ts.write('\t'); - } - } - ts.write('\n'); - } - return ts.toString(); - } - - /// Evaluate the (un)compressed partial product array - /// logic=true will read the logic gate outputs at each level - /// printOut=true will print out the array - BigInt evaluate({bool printOut = false, bool logic = false}) { - final ts = StringBuffer(); - final rows = longestColumn(); - final width = pp.maxWidth(); - - var accum = BigInt.zero; - for (var row = 0; row < rows; row++) { - final rowBits = []; - for (var col = columns.length - 1; col >= 0; col--) { - final colList = columns[col].toList(); - if (row < colList.length) { - final value = - logic ? colList[row].logic.value : (colList[row].evaluate()); - rowBits.add(value); - if (printOut) { - ts.write('\t${value.bitString}'); - } - } else if (printOut) { - ts.write('\t'); - } - } - rowBits.addAll(List.filled(pp.rowShift[row], LogicValue.zero)); - final val = rowBits.swizzle().zeroExtend(width).toBigInt(); - accum += val; - if (printOut) { - ts.write('\t${rowBits.swizzle().zeroExtend(width).bitString} ($val)'); - if (row == rows - 1) { - ts.write(' Total=${accum.toSigned(width)}\n'); - stdout.write(ts); - } else { - ts.write('\n'); - } - } - } - if (printOut) { - // We need this to be able to debug, but git lint flunks print - // print(ts); - } - return accum.toSigned(width); - } - /// Convert a row to a Logic bitvector Logic extractRow(int row) { final width = pp.maxWidth(); @@ -275,8 +214,8 @@ class ColumnCompressor { } /// Core iterator for column compressor routine - List _compressIter(int iteration) { - final terms = []; + List<_CompressTerm> _compressIter(int iteration) { + final terms = <_CompressTerm>[]; for (var col = 0; col < columns.length; col++) { final queue = columns[col]; final depth = queue.length; @@ -284,7 +223,7 @@ class ColumnCompressor { if (depth > 2) { final first = queue.removeFirst(); final second = queue.removeFirst(); - final inputs = [first, second]; + final inputs = <_CompressTerm>[first, second]; AddendCompressor compressor; if (depth > 3) { inputs.add(queue.removeFirst()); @@ -294,13 +233,13 @@ class ColumnCompressor { compressor = Compressor2([for (final i in inputs) i.logic].swizzle()); } - final t = CompressTerm( - CompressTermType.sum, compressor.sum, inputs, 0, col); + final t = _CompressTerm( + _CompressTermType.sum, compressor.sum, inputs, 0, col); terms.add(t); columns[col].add(t); if (col < columns.length - 1) { - final t = CompressTerm( - CompressTermType.carry, compressor.carry, inputs, 0, col); + final t = _CompressTerm( + _CompressTermType.carry, compressor.carry, inputs, 0, col); columns[col + 1].add(t); terms.add(t); } @@ -311,8 +250,8 @@ class ColumnCompressor { } /// Compress the partial products array to two addends - List compress() { - final terms = []; + void compress() { + final terms = <_CompressTerm>[]; var iterations = longestColumn(); while (iterations > 0) { terms.addAll(_compressIter(iterations--)); @@ -320,6 +259,70 @@ class ColumnCompressor { break; } } - return terms; + } +} + +/// Debug routines for printing out ColumnCompressor during +/// simulation with live logic values +extension EvaluateLiveColumnCompressor on ColumnCompressor { + /// Evaluate the (un)compressed partial product array + /// logic=true will read the logic gate outputs at each level + /// printOut=true will print out the array in the StringBuffer + (BigInt, StringBuffer) evaluate({bool printOut = false, bool logic = false}) { + final ts = StringBuffer(); + final rows = longestColumn(); + final width = pp.maxWidth(); + + var accum = BigInt.zero; + for (var row = 0; row < rows; row++) { + final rowBits = []; + for (var col = columns.length - 1; col >= 0; col--) { + final colList = columns[col].toList(); + if (row < colList.length) { + final value = + logic ? colList[row].logic.value : (colList[row].evaluate()); + rowBits.add(value); + if (printOut) { + ts.write('\t${value.bitString}'); + } + } else if (printOut) { + ts.write('\t'); + } + } + rowBits.addAll(List.filled(pp.rowShift[row], LogicValue.zero)); + final val = rowBits.swizzle().zeroExtend(width).toBigInt(); + accum += val; + if (printOut) { + ts.write('\t${rowBits.swizzle().zeroExtend(width).bitString} ($val)'); + if (row == rows - 1) { + ts.write(' Total=${accum.toSigned(width)}\n'); + stdout.write(ts); + } else { + ts.write('\n'); + } + } + } + if (printOut) { + // We need this to be able to debug, but git lint flunks print + // print(ts); + } + return (accum.toSigned(width), ts); + } + + /// Return a string representing the compression tree in its current state + String representation() { + final ts = StringBuffer(); + for (var row = 0; row < longestColumn(); row++) { + for (var col = columns.length - 1; col >= 0; col--) { + final colList = columns[col].toList(); + if (row < colList.length) { + ts.write('\t${colList[row]}'); + } else { + ts.write('\t'); + } + } + ts.write('\n'); + } + return ts.toString(); } } diff --git a/lib/src/arithmetic/multiplicand_selector.dart b/lib/src/arithmetic/multiplicand_selector.dart index 06bb4043..b49d2217 100644 --- a/lib/src/arithmetic/multiplicand_selector.dart +++ b/lib/src/arithmetic/multiplicand_selector.dart @@ -9,7 +9,6 @@ import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; /// A class accessing the multiples of the multiplicand at a position class MultiplicandSelector { diff --git a/lib/src/arithmetic/multiplier.dart b/lib/src/arithmetic/multiplier.dart index 342520e8..e69184ad 100644 --- a/lib/src/arithmetic/multiplier.dart +++ b/lib/src/arithmetic/multiplier.dart @@ -11,7 +11,6 @@ import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; /// An abstract class for all multiplier implementations. abstract class Multiplier extends Module { diff --git a/lib/src/arithmetic/multiplier_encoder.dart b/lib/src/arithmetic/multiplier_encoder.dart index f5707ea2..cfe7727e 100644 --- a/lib/src/arithmetic/multiplier_encoder.dart +++ b/lib/src/arithmetic/multiplier_encoder.dart @@ -7,6 +7,8 @@ // 2024 May 15 // Author: Desmond Kirkpatrick +import 'dart:math'; + import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; @@ -37,8 +39,12 @@ class RadixEncoder { /// The radix of the radixEncoder int radix; - /// Baseline call for setting up an empty radixEncoder - RadixEncoder(this.radix); + /// Constructor for setting up a radix encoding block + RadixEncoder(this.radix) { + if (pow(2.0, log2Ceil(radix)) != radix) { + throw RohdHclException('radix must be a power of 2'); + } + } /// Encode a multiplier slice into the Booth encoded value RadixEncode encode(Logic multiplierSlice) { diff --git a/lib/src/arithmetic/partial_product_generator.dart b/lib/src/arithmetic/partial_product_generator.dart index 393e90cd..d30d06ef 100644 --- a/lib/src/arithmetic/partial_product_generator.dart +++ b/lib/src/arithmetic/partial_product_generator.dart @@ -11,7 +11,6 @@ import 'dart:math'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; /// Methods for sign extending the [PartialProductGenerator] enum SignExtension { @@ -31,7 +30,8 @@ enum SignExtension { compactRect } -/// A class that generates a set of partial products. Essentially a set of +/// A [PartialProductGenerator] 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 class PartialProductGenerator { @@ -69,7 +69,7 @@ class PartialProductGenerator { // Used to avoid sign extending more than once var _signExtended = false; - /// Construct the partial product matrix + /// Construct a [PartialProductGenerator] -- the partial product matrix PartialProductGenerator( Logic multiplicand, Logic multiplier, RadixEncoder radixEncoder, {required this.signed, @@ -493,9 +493,7 @@ extension EvaluateLivePartialProduct on PartialProductGenerator { ? shift - 1 : 1; // We will print encoding(1-hot multiples and sign) before each row - final shortPrefix = - '99 ${'M='.padRight(2 + selector.radix ~/ 2)}(99) S= : '.length + - 3 * nonSignExtendedPad; + final shortPrefix = '99 ${'M='}99 S= : '.length + 3 * nonSignExtendedPad; // print bit position header str.write(' ' * shortPrefix); @@ -513,16 +511,16 @@ extension EvaluateLivePartialProduct on PartialProductGenerator { if (row < encoder.rows) { final encoding = encoder.getEncoding(row); if (encoding.multiples.value.isValid) { - final multiple = encoding.multiples.value.firstOne()! + 1; - str.write('$rowStr M=${encoding.multiples.reversed.value.bitString}' - '(${multiple.toString().padLeft(2)}) ' + final first = encoding.multiples.value.firstOne() ?? -1; + final multiple = first + 1; + str.write('$rowStr M=' + '${multiple.toString().padLeft(2)} ' 'S=${encoding.sign.value.toInt()}: '); } else { str.write(' ' * shortPrefix); } } else { - str.write( - '$rowStr ${'M='.padRight(2 + selector.radix ~/ 2)} S= : '); + str.write('$rowStr ${'M='} S= : '); } final entry = partialProducts[row].reversed.toList(); final prefixCnt = diff --git a/test/arithmetic/addend_compressor_test.dart b/test/arithmetic/addend_compressor_test.dart index 8d35612f..be2ebeee 100644 --- a/test/arithmetic/addend_compressor_test.dart +++ b/test/arithmetic/addend_compressor_test.dart @@ -11,7 +11,6 @@ import 'dart:io'; import 'dart:math'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; import 'package:test/test.dart'; void testCompressionExhaustive(PartialProductGenerator pp) { @@ -40,19 +39,19 @@ void testCompressionExhaustive(PartialProductGenerator pp) { 'vs expected $product' '\n$pp'); final evaluateValue = compressor.evaluate(); - if (evaluateValue != product) { + if (evaluateValue.$1 != product) { stdout ..write('Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $evaluateValue ' 'vs expected $product\n') ..write(pp); } compressor.compress(); - final compressedValue = compressor.evaluate(); + final compressedValue = compressor.evaluate().$1; expect(compressedValue, equals(product), reason: 'Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $compressedValue ' 'vs expected $product' '\n$pp'); - final compressedLogicValue = compressor.evaluate(logic: true); + final compressedLogicValue = compressor.evaluate(logic: true).$1; expect(compressedLogicValue, equals(product), reason: 'Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $compressedLogicValue ' @@ -78,7 +77,7 @@ void main() { stdout.write('\n'); for (final signed in [false, true]) { - for (var radix = 4; radix < 4; radix *= 2) { + for (var radix = 2; radix < 4; radix *= 2) { final encoder = RadixEncoder(radix); // stdout.write('encoding with radix=$radix\n'); final shift = log2Ceil(encoder.radix); @@ -97,7 +96,7 @@ void main() { } } }); - test('single compressor evaluate mac', () async { + test('single compressor evaluate multiply', () async { const widthX = 6; const widthY = 9; final a = Logic(name: 'a', width: widthX); @@ -116,20 +115,76 @@ void main() { // Set these so that printing inside module build will have Logic values a.put(bA); b.put(bB); - const radix = 4; + const radix = 2; final encoder = RadixEncoder(radix); final pp = PartialProductGenerator(a, b, encoder, signed: signed); - // Turn on printing by using widthX == 6 (we are fooling the dead code - // checking linter here) - // print(pp.representation()); expect(pp.evaluate(), equals(BigInt.from(av * bv))); final compressor = ColumnCompressor(pp); - // print('eval: ${compressor.evaluate(printOut: output)}'); - expect(compressor.evaluate(), equals(BigInt.from(av * bv))); + expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); compressor.compress(); - // print('eval: ${compressor.evaluate(printOut: true)}'); - expect(compressor.evaluate(), equals(BigInt.from(av * bv))); + expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); + } + }); + test('single compressor evaluate', () async { + const widthX = 6; + const widthY = 6; + final a = Logic(name: 'a', width: widthX); + final b = Logic(name: 'b', width: widthY); + + const av = 3; + const bv = 6; + for (final signed in [true]) { + final bA = signed + ? BigInt.from(av).toSigned(widthX) + : BigInt.from(av).toUnsigned(widthX); + final bB = signed + ? BigInt.from(bv).toSigned(widthY) + : BigInt.from(bv).toUnsigned(widthY); + + // Set these so that printing inside module build will have Logic values + a.put(bA); + b.put(bB); + const radix = 2; + final encoder = RadixEncoder(radix); + + final pp = PartialProductGenerator(a, b, encoder, signed: signed); + expect(pp.evaluate(), equals(BigInt.from(av * bv))); + final compressor = ColumnCompressor(pp); + expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); + compressor.compress(); + expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); + } + }); + + test('example multiplier', () async { + const widthX = 10; + const widthY = 10; + final a = Logic(name: 'a', width: widthX); + final b = Logic(name: 'b', width: widthY); + + const av = 37; + const bv = 6; + for (final signed in [true]) { + final bA = signed + ? BigInt.from(av).toSigned(widthX) + : BigInt.from(av).toUnsigned(widthX); + final bB = signed + ? BigInt.from(bv).toSigned(widthY) + : BigInt.from(bv).toUnsigned(widthY); + + // Set these so that printing inside module build will have Logic values + a.put(bA); + b.put(bB); + const radix = 8; + final encoder = RadixEncoder(radix); + + final pp = PartialProductGenerator(a, b, encoder, signed: signed); + expect(pp.evaluate(), equals(BigInt.from(av * bv))); + final compressor = ColumnCompressor(pp); + expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); + compressor.compress(); + expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); } }); } diff --git a/test/arithmetic/adder_test.dart b/test/arithmetic/adder_test.dart index c2020f18..0288a8ab 100644 --- a/test/arithmetic/adder_test.dart +++ b/test/arithmetic/adder_test.dart @@ -223,4 +223,35 @@ void main() { sortOperands: false); } }); + test('trivial parallel prefix adder test', () async { + const width = 6; + final a = Logic(name: 'a', width: width); + final b = Logic(name: 'b', width: width); + + a.put(18); + b.put(24); + + final adder = ParallelPrefixAdder(a, b, BrentKung.new); + + final sum = adder.sum; + expect(sum.value.toBigInt(), equals(BigInt.from(18 + 24))); + }); + test('trivial sign magnitude adder test', () async { + const width = 6; + final aSign = Logic(name: 'aSign'); + final a = Logic(name: 'a', width: width); + final bSign = Logic(name: 'bSign'); + final b = Logic(name: 'b', width: width); + + aSign.put(1); + a.put(24); + b.put(18); + bSign.put(0); + + final adder = SignMagnitudeAdder(aSign, a, bSign, b, RippleCarryAdder.new, + largestMagnitudeFirst: true); + + final sum = adder.sum; + expect(sum.value.toBigInt(), equals(BigInt.from(24 - 18))); + }); } diff --git a/test/arithmetic/multiplier_encoder_test.dart b/test/arithmetic/multiplier_encoder_test.dart index c81b7429..f2380772 100644 --- a/test/arithmetic/multiplier_encoder_test.dart +++ b/test/arithmetic/multiplier_encoder_test.dart @@ -12,7 +12,6 @@ import 'dart:math'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; import 'package:test/test.dart'; void checkEvaluateExhaustive(PartialProductGenerator pp) { @@ -56,10 +55,6 @@ void main() { const widthX = 8; const widthY = 18; - // const i = 8; - // var j = pow(2, widthY - 1).toInt(); - // j = 2; - // const k = 128; const i = 1478; const j = 9; const k = 0; @@ -91,8 +86,6 @@ void main() { l ..add(~sign) ..add(Const(1)); - // print('lastL=$lastLength'); - // Add a row for addend // print(pp.representation()); pp.partialProducts.insert(0, l); diff --git a/test/arithmetic/multiplier_test.dart b/test/arithmetic/multiplier_test.dart index b47c8a82..bb8e7a38 100644 --- a/test/arithmetic/multiplier_test.dart +++ b/test/arithmetic/multiplier_test.dart @@ -217,4 +217,22 @@ void main() { checkMultiplyAccumulate(mod, bA, bB, bC); } }); + test('trivial compression tree multiply-accumulate test', () async { + const widthA = 6; + const widthB = 6; + const radix = 8; + final a = Logic(name: 'a', width: widthA); + final b = Logic(name: 'b', width: widthB); + final c = Logic(name: 'c', width: widthA + widthB); + + a.put(15); + b.put(3); + c.put(5); + + final multiplier = CompressionTreeMultiplyAccumulate( + a, b, c, radix, KoggeStone.new, + signed: true); + final accumulate = multiplier.accumulate; + expect(accumulate.value.toBigInt(), equals(BigInt.from(15 * 3 + 5))); + }); } From 22da93f76524bc918e06142b74d23882fb5500ce Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Sun, 18 Aug 2024 20:49:37 -0700 Subject: [PATCH 29/38] remove links for now to pass lint --- doc/components/multiplier_components.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/components/multiplier_components.md b/doc/components/multiplier_components.md index 6b2d8699..d502d401 100644 --- a/doc/components/multiplier_components.md +++ b/doc/components/multiplier_components.md @@ -1,6 +1,6 @@ # Booth Encoding Multiplier Building Blocks -The Compression Tree multipliers [`CompressionTreeMultiplier`]() and [`CompressionTreeMultiplyAccumulate`]() use a set of building blocks that can also be used for building up other multipliers and arithmetic circuits. These are from the family of Booth-encoding multipliers which are comprised of three major stages: +The Compression Tree multipliers [`CompressionTreeMultiplier`] and [`CompressionTreeMultiplyAccumulate`] use a set of building blocks that can also be used for building up other multipliers and arithmetic circuits. These are from the family of Booth-encoding multipliers which are comprised of three major stages: 1) Booth radix encoding (typically radix-4) generating partial products 2) Partial product array column compression to two addends @@ -51,7 +51,7 @@ row slice mult A few things to note: first, that we are negating by 1s complement (so we need a -0) and second, these rows do not add up to (18: 10010). For Booth encoded rows to add up properly, they need to be in 2s complement form and they need to be sign-extended. - Here is the matrix with crude sign extension (this formatting is available from our [`PartialProductGenerator`]() component). With 2s complementation, and sign bits folded in (note the LSB of each row has a sign term from the previous row), these addends are correclty formed and add to (18: 10010). + Here is the matrix with crude sign extension (this formatting is available from our [`PartialProductGenerator`] component). With 2s complementation, and sign bits folded in (note the LSB of each row has a sign term from the previous row), these addends are correclty formed and add to (18: 10010). ```text 7 6 5 4 3 2 1 0 @@ -90,7 +90,7 @@ Note that radix-4 shifts by 2 positions each row, but with only two rows and wit ## Partial Product Generator -This building block creates a set of rows of partial products from a multiplicand and a multiplier. It maintains the partial products as a list of rows, which are themselves lists of Logic as well as a row shift value for each row to represent the starting column of the row's least-significant bit. Its primary inputs are the multiplicand, multplier, [`RadixEncoder`](), whether the operands are signed, and the type of [`SignExtension`]() to use in generating the partial product rows. +This building block creates a set of rows of partial products from a multiplicand and a multiplier. It maintains the partial products as a list of rows, which are themselves lists of Logic as well as a row shift value for each row to represent the starting column of the row's least-significant bit. Its primary inputs are the multiplicand, multplier, [`RadixEncoder`], whether the operands are signed, and the type of [`SignExtension`] to use in generating the partial product rows. The partial product generator produces a set of addends in shifted position to be added. The main output of the component is @@ -101,7 +101,7 @@ The partial product generator produces a set of addends in shifted position to b ### Radix Encoding -An argument to the [`PartialProductGenerator`]() is the [`RadixEncoder`]() to be used. The [`RadixEncoder`]() takes a single argument which is the radix (power of 2) to be used. +An argument to the [`PartialProductGenerator`] is the [`RadixEncoder`] to be used. The [`RadixEncoder`] takes a single argument which is the radix (power of 2) to be used. Instead of using the 1's in the multiplier to select shifted versions of the multiplicand to add in a partial product matrix, radix-encoding will encoding multiples of the multiplicand by examining adjacent bits of the multiplier. For radix-4, for example, for a multiplier of size M, instead of M rows of partial products, M/2 rows are formed by selecting from multiples [-2, -1, 0, 1, 2] of the multiplicand. These multiples are computed from an 3 bit slices, overlapped by 1 bit, of the multiplier. Higher radices use wider slices of the multiplier to encode fewer multiples and therefore fewer rows. @@ -118,15 +118,15 @@ Instead of using the 1's in the multiplier to select shifted versions of the mul : Radix-4 Table -Our [`RadixEncoder`]() module is general, creating selection tables for arbitrary Booth radices of powers of 2. Currently we are limited to radix-16 because of challenges in creating the odd multiples efficiently, and there are more advanced techniques for efficiently generating higher radices than 16 than our current encoding/selection/partial-product generation scheme. +Our [`RadixEncoder`] module is general, creating selection tables for arbitrary Booth radices of powers of 2. Currently we are limited to radix-16 because of challenges in creating the odd multiples efficiently, and there are more advanced techniques for efficiently generating higher radices than 16 than our current encoding/selection/partial-product generation scheme. ### Sign Extension Option -The [`PartialProductGenerator`]() class also provides for sign extension with multiple options including `SignExtension.none` which is no sign extension for help in debugging, as well as `SignExtension.compactRect` which is a compact form which works for rectangular products where the multiplicand and multiplier can be of different widths. +The [`PartialProductGenerator`] class also provides for sign extension with multiple options including `SignExtension.none` which is no sign extension for help in debugging, as well as `SignExtension.compactRect` which is a compact form which works for rectangular products where the multiplicand and multiplier can be of different widths. ### Partial Proeduct Visualization -Creating new arithmetic building blocks from these components is tricky and visualizing intermediate results really helps. To that end, our [`PartialProductGenerator`]() class has visualization extension [`EvaluatePartialProduct`]() which help evaluate the current `Logic` values in array form during simulation to help with debug. The evaluation routine with the extension also adds the addends for you to help sanity check the partial product generation. The routine is `EvaluateLivePartialProduct.representation`. +Creating new arithmetic building blocks from these components is tricky and visualizing intermediate results really helps. To that end, our [`PartialProductGenerator`] class has visualization extension [`EvaluatePartialProduct`] which help evaluate the current `Logic` values in array form during simulation to help with debug. The evaluation routine with the extension also adds the addends for you to help sanity check the partial product generation. The routine is `EvaluateLivePartialProduct.representation`. ```text 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 @@ -142,7 +142,7 @@ Creating new arithmetic building blocks from these components is tricky and visu Once you have a partial product matrix, you would like to add up the addends. Traditionally this is done using compression trees which instantate 2:1 and 3:2 column compressors (or carry-save adders) to reduce the matrix to two addends. The final two addends are often added with an efficient final adder. -Our [`ColumnCompressor`]() class uses a delay-driven approach to efficiently compress the rows of the partial product matrix. Its only arguement is a [`PartialProductGenerator`]() , and it creates a list of ColumnQueues containing the final two addends stored by column after compression. An extractRow() routine can be used to extract the columns. [`ColumnCompressor`]() currently has an extension [`EvaluateColumnCompressor`]() which can be used to print out the compression progress. Here is the legend for these printouts. +Our [`ColumnCompressor`] class uses a delay-driven approach to efficiently compress the rows of the partial product matrix. Its only arguement is a [`PartialProductGenerator`] , and it creates a list of ColumnQueues containing the final two addends stored by column after compression. An extractRow() routine can be used to extract the columns. [`ColumnCompressor`] currently has an extension [`EvaluateColumnCompressor`] which can be used to print out the compression progress. Here is the legend for these printouts. - ppR,C = partial product entry at row R, column C - sR,C = sum term coming last from row R, column C @@ -184,11 +184,11 @@ Any adder can be used as the final adder of the final two addends produced from Here is a code snippet that shows how these components can be used to create a multiplier. -First the partial product generator is used, which we pass in the [`RadixEncoder`](), whether the operands are signed, and the kind of sign extension to use on the partial products. +First the partial product generator is used, which we pass in the [`RadixEncoder`], whether the operands are signed, and the kind of sign extension to use on the partial products. -Next, we use the [`ColumnCompressor`]() to compress the partial products into two final addends. +Next, we use the [`ColumnCompressor`] to compress the partial products into two final addends. -We then choose a[`ParallelPrefixAdder`]() using the [`BrentKung`]() tree style to do the addition. We pass in the two extracted rows of the compressor. +We then choose a[`ParallelPrefixAdder`] using the [`BrentKung`] tree style to do the addition. We pass in the two extracted rows of the compressor. Finally, we produce the product. ```dart From a4d73b6a4ffd9da816a0583743da38a0bc5cc5dd Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Sat, 31 Aug 2024 18:08:02 -0700 Subject: [PATCH 30/38] major restructuring of partial product compressor classes, documentation fixes, new ones complement adder --- doc/README.md | 9 +- doc/components/adder.md | 39 +- doc/components/multiplier_components.md | 32 +- lib/rohd_hcl.dart | 7 +- lib/src/arithmetic/addend_compressor.dart | 75 ---- lib/src/arithmetic/arithmetic.dart | 9 + lib/src/arithmetic/evaluate_compressor.dart | 78 ++++ .../arithmetic/evaluate_partial_product.dart | 107 +++++ lib/src/arithmetic/multiplier.dart | 26 +- .../parallel_prefix_operations.dart | 22 +- .../arithmetic/partial_product_generator.dart | 365 ++---------------- .../partial_product_test_sign_extend.dart | 274 +++++++++++++ lib/src/arithmetic/sign_magnitude_adder.dart | 107 +++-- .../config_compression_tree_multiplier.dart | 2 +- .../config_parallel_prefix_adder.dart | 2 +- test/arithmetic/addend_compressor_test.dart | 21 +- test/arithmetic/adder_test.dart | 99 ++++- test/arithmetic/multiplier_encoder_test.dart | 47 ++- test/arithmetic/multiplier_test.dart | 18 +- .../parallel_prefix_operations_test.dart | 10 +- 20 files changed, 818 insertions(+), 531 deletions(-) create mode 100644 lib/src/arithmetic/arithmetic.dart create mode 100644 lib/src/arithmetic/evaluate_compressor.dart create mode 100644 lib/src/arithmetic/evaluate_partial_product.dart create mode 100644 lib/src/arithmetic/partial_product_test_sign_extend.dart diff --git a/doc/README.md b/doc/README.md index 7533a346..d3127cab 100644 --- a/doc/README.md +++ b/doc/README.md @@ -25,8 +25,8 @@ Some in-development items will have opened issues, as well. Feel free to create - Find maximum - Find N'th pattern from the start/end - Count - - [Count bit occurence](./components/count.md) - - Count pattern occurence + - [Count bit occurrence](./components/count.md) + - Count pattern occurrence - Detection - [Edge detection](./components/edge_detector.md) - Sort @@ -34,9 +34,14 @@ Some in-development items will have opened issues, as well. Feel free to create - Arithmetic - [Prefix Trees](./components/parallel_prefix_operations.md) - [Adders](./components/adder.md) + - [Sign Magnitude Adder](./components/adder.md#ripple-carry-adder) - Subtractors + - [One's Complement Adder Subtractor](./components/adder.md#ones-complement-adder-subtractor) - Multipliers - [Pipelined Integer Multiplier](./components/multiplier.md#carry-save-multiplier) + - [Compression Tree Multiplier](./components/multiplier.md#compression-tree-multiplier) + - [Compression Tree Multiply-Accumulate](./components/multiplier.md#compression-tree-multiply-accumulate) + - [Booth Encoding and Compression Components](./components/multiplier_components.md) - Dividers - Log - Square root diff --git a/doc/components/adder.md b/doc/components/adder.md index 3dacb0f7..51b9c94c 100644 --- a/doc/components/adder.md +++ b/doc/components/adder.md @@ -1,16 +1,17 @@ # Adder -ROHD HCL provides a set of adder modules to get the sum from a list of logic. As of now, we have +ROHD-HCL provides a set of adder modules to get the sum from a pair of Logic. As of now, we have - [Ripple Carry Adder](#ripple-carry-adder) - [Parallel Prefix Adder](#parallel-prefix-adder) +- [One's Complement Adder Subtractor](#ones-complement-adder-subtractor) - [Sign Magnitude Adder](#sign-magnitude-adder) ## Ripple Carry Adder A ripple carry adder is a digital circuit used for binary addition. It consists of a series of full adders connected in a chain, with the carry output of each adder linked to the carry input of the next one. Starting from the least significant bit (LSB) to most significant bit (MSB), the adder sequentially adds corresponding bits of two binary numbers. -The [`RippleCarryAdder`](https://intel.github.io/rohd-hcl/rohd_hcl/RippleCarryAdder-class.html) module in ROHD-HCL accept input `Logic`s a and b as the input pin and the name of the module `name`. Note that the width of the inputs must be the same or a `RohdHclException` will be thrown. +The [`RippleCarryAdder`](https://intel.github.io/rohd-hcl/rohd_hcl/RippleCarryAdder-class.html) module in ROHD-HCL accept input `Logic`s a and b as the input pin and the name of the module `name`. Note that the width of the inputs must be the same or a `RohdHclException` will be thrown. An example is shown below to add two inputs of signals that have 8-bits of width. @@ -27,7 +28,7 @@ final sum = rippleCarryAdder.sum; ## Parallel Prefix Adder -A parallel prefix adder is an adder that uses n instance of a parallel prefix tree (see `Parallel Prefix Operations`) to efficiently connect a set of `Full Adder` circuits to form a complete adder. +A parallel prefix adder is an adder that uses different varieties of a parallel prefix tree (see `Parallel Prefix Operations`) to efficiently connect a set of `Full Adder` circuits to form a complete adder. Here is an example of instantiating a `ParallelPrefixAdder`: @@ -46,11 +47,39 @@ Here is an example of instantiating a `ParallelPrefixAdder`: print('${sum.value.toBigInt()}'); ``` +## One's Complement Adder Subtractor + +A ones-complement adder (and subtractor) is useful in efficient arithmetic operations as the +end-around carry can be bypassed and used later. + +The `OnesComplementAdder` can take a subtraction command as either a Logic `subtractIn` or a boolean `subtract` (the Logic overrides the boolean). If Logic `carry` is provided, the end-around carry is output on `carry` and the value will be one less than expected when `carry` is high. An `adderGen` adder function can be provided that generates your favorite internal adder (such as a parallel prefix adder). + +The output of `OnesComplementAdder` is a `sum` which is the magnitude and a `sign`. + +Here is an example of instantiating a `OnesComplementAdder` as a subtractor, but saving the `carry`: + +```dart + const width = 4; + final a = Logic(width: width); + final b = Logic(width: width); + + a.put(av); + b.put(bv); + final carry = Logic(); + final adder = OnesComplementAdder( + a, b, null, carry, RippleCarryAdder.new, + subtract: true); + final mag = adder.sum.value.toInt() + (carry.value.isZero ? 0 : 1)); + final out = (adder.sign.value.toInt() == 1 ? -mag : mag); +``` + ## Sign Magnitude Adder -A sign magnitude adder is useful in situations where the sign of the addends is seperated from their magnitude (e.g., not 2s complement), such as in floating point multipliers. The `SignMagnitudeAdder` inherits from `Adder` but adds the `Logic` inputs for the two operands. +A sign magnitude adder is useful in situations where the sign of the addends is separated from their magnitude (e.g., not 2s complement), such as in floating point multipliers. The `SignMagnitudeAdder` inherits from `Adder` but adds the `Logic` inputs for the two operands. + +If you can supply the largest magnitude number first, then you can disable a comparator generation inside by declaring the `largestMagnitudeFirst` option as true. -If you can supply the largest magnitude number first, then you can disable a comparator generation inside by declaring the `largestMagnitudeFirst` option as true. +The `SignMagnitudeAdder` uses a `OnesComplementAdder` internally. Here is an example of instantiating a `SignMagnitudeAdder`: diff --git a/doc/components/multiplier_components.md b/doc/components/multiplier_components.md index d502d401..2c619da6 100644 --- a/doc/components/multiplier_components.md +++ b/doc/components/multiplier_components.md @@ -1,6 +1,6 @@ # Booth Encoding Multiplier Building Blocks -The Compression Tree multipliers [`CompressionTreeMultiplier`] and [`CompressionTreeMultiplyAccumulate`] use a set of building blocks that can also be used for building up other multipliers and arithmetic circuits. These are from the family of Booth-encoding multipliers which are comprised of three major stages: +The Compression Tree multipliers `CompressionTreeMultiplier` and `CompressionTreeMultiplyAccumulate` use a set of building blocks that can also be used for building up other multipliers and arithmetic circuits. These are from the family of Booth-encoding multipliers which are comprised of three major stages: 1) Booth radix encoding (typically radix-4) generating partial products 2) Partial product array column compression to two addends @@ -28,7 +28,7 @@ Think of the hand-multiplication process where you write down the multiplicand a 0 0 1 0 0 1 0 (18) ``` -With Booth encoding, we take multiple adjacent bits of the multiplier (6) to form these rows. In the case that most closely matches hand-multiplication, radix-2 Booth encoding, we take two adjacent bit slices to create multiples [-1,-0, +0, +1] where a leading bit in the slice would indicate negation. These then select the appropriate multiple to shift into the row. So (6) = [0 1 1 0] gets sliced left-to-right (leading with a 0) to the create multiple selectors: [0 0], [1 0], [1 1], [0 1]. These slices are radix encoded into multiple (+-0, +-1) selectors as follows according to radix-2: +With Booth encoding, we take multiple adjacent bits of the multiplier (6) to form these rows. In the case that most closely matches hand-multiplication, radix-2 Booth encoding, we take two adjacent bit slices to create multiples [-1,-0, +0, +1] where a leading bit in the slice would indicate negation. These then select the appropriate multiple to shift into the row. So (6) = [0 1 1 0] gets sliced left-to-right (leading with a 0) to create multiple selectors: [0 0], [1 0], [1 1], [0 1]. These slices are radix encoded into multiple (±0, ±1) selectors as follows according to radix-2: | bit_i | bit_i-1 | multiple| |:-----:|:-------:|:-------:| @@ -49,9 +49,9 @@ row slice mult 03 [0 1] = +1 0 0 1 1 ``` -A few things to note: first, that we are negating by 1s complement (so we need a -0) and second, these rows do not add up to (18: 10010). For Booth encoded rows to add up properly, they need to be in 2s complement form and they need to be sign-extended. +A few things to note: first, that we are negating by 1s complement (so we need a -0) and second, these rows do not add up to (18: 10010). For Booth encoded rows to add up properly, they need to be in 2s complement form, and they need to be sign-extended. - Here is the matrix with crude sign extension (this formatting is available from our [`PartialProductGenerator`] component). With 2s complementation, and sign bits folded in (note the LSB of each row has a sign term from the previous row), these addends are correclty formed and add to (18: 10010). + Here is the matrix with crude sign extension (this formatting is available from our `PartialProductGenerator` component). With 2s complementation, and sign bits folded in (note the LSB of each row has a sign term from the previous row), these addends are correctly formed and add to (18: 10010). ```text 7 6 5 4 3 2 1 0 @@ -90,7 +90,7 @@ Note that radix-4 shifts by 2 positions each row, but with only two rows and wit ## Partial Product Generator -This building block creates a set of rows of partial products from a multiplicand and a multiplier. It maintains the partial products as a list of rows, which are themselves lists of Logic as well as a row shift value for each row to represent the starting column of the row's least-significant bit. Its primary inputs are the multiplicand, multplier, [`RadixEncoder`], whether the operands are signed, and the type of [`SignExtension`] to use in generating the partial product rows. +This building block creates a set of rows of partial products from a multiplicand and a multiplier. It maintains the partial products as a list of rows, which are themselves lists of Logic as well as a row shift value for each row to represent the starting column of the row's least-significant bit. Its primary inputs are the multiplicand, multiplier, `RadixEncoder`, whether the operands are signed, and the type of `SignExtension` to use in generating the partial product rows. The partial product generator produces a set of addends in shifted position to be added. The main output of the component is @@ -101,9 +101,9 @@ The partial product generator produces a set of addends in shifted position to b ### Radix Encoding -An argument to the [`PartialProductGenerator`] is the [`RadixEncoder`] to be used. The [`RadixEncoder`] takes a single argument which is the radix (power of 2) to be used. +An argument to the `PartialProductGenerator` is the `RadixEncoder` to be used. The [`RadixEncoder`] takes a single argument which is the radix (power of 2) to be used. -Instead of using the 1's in the multiplier to select shifted versions of the multiplicand to add in a partial product matrix, radix-encoding will encoding multiples of the multiplicand by examining adjacent bits of the multiplier. For radix-4, for example, for a multiplier of size M, instead of M rows of partial products, M/2 rows are formed by selecting from multiples [-2, -1, 0, 1, 2] of the multiplicand. These multiples are computed from an 3 bit slices, overlapped by 1 bit, of the multiplier. Higher radices use wider slices of the multiplier to encode fewer multiples and therefore fewer rows. +Instead of using the 1's in the multiplier to select shifted versions of the multiplicand to add in a partial product matrix, radix-encoding will encode multiples of the multiplicand by examining adjacent bits of the multiplier. For radix-4, for example, for a multiplier of size M, instead of M rows of partial products, M/2 rows are formed by selecting from multiples [-2, -1, 0, 1, 2] of the multiplicand. These multiples are computed from an 3 bit slices, overlapped by 1 bit, of the multiplier. Higher radices use wider slices of the multiplier to encode fewer multiples and therefore fewer rows. | bit_i | bit_i-1 | bit_i-2 | multiple| |:-----:|:-------:|:-------:|:-------:| @@ -118,15 +118,15 @@ Instead of using the 1's in the multiplier to select shifted versions of the mul : Radix-4 Table -Our [`RadixEncoder`] module is general, creating selection tables for arbitrary Booth radices of powers of 2. Currently we are limited to radix-16 because of challenges in creating the odd multiples efficiently, and there are more advanced techniques for efficiently generating higher radices than 16 than our current encoding/selection/partial-product generation scheme. +Our `RadixEncoder` module is general, creating selection tables for arbitrary Booth radices of powers of 2. Currently, we are limited to radix-16 because of challenges in creating the odd multiples efficiently, and there are more advanced techniques for efficiently generating higher radices than 16 than our current encoding/selection/partial-product generation scheme. ### Sign Extension Option -The [`PartialProductGenerator`] class also provides for sign extension with multiple options including `SignExtension.none` which is no sign extension for help in debugging, as well as `SignExtension.compactRect` which is a compact form which works for rectangular products where the multiplicand and multiplier can be of different widths. +The `PartialProductGenerator` class also provides for sign extension with multiple options including `SignExtension.none` which is no sign extension for help in debugging, as well as `SignExtension.compactRect` which is a compact form which works for rectangular products where the multiplicand and multiplier can be of different widths. -### Partial Proeduct Visualization +### Partial Product Visualization -Creating new arithmetic building blocks from these components is tricky and visualizing intermediate results really helps. To that end, our [`PartialProductGenerator`] class has visualization extension [`EvaluatePartialProduct`] which help evaluate the current `Logic` values in array form during simulation to help with debug. The evaluation routine with the extension also adds the addends for you to help sanity check the partial product generation. The routine is `EvaluateLivePartialProduct.representation`. +Creating new arithmetic building blocks from these components is tricky and visualizing intermediate results really helps. To that end, our `PartialProductGenerator` class has visualization extension `EvaluatePartialProduct` which help evaluate the current `Logic` values in array form during simulation to help with debug. The evaluation routine with the extension also adds the addends for you to help sanity check the partial product generation. The routine is `EvaluateLivePartialProduct.representation`. ```text 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 @@ -140,9 +140,9 @@ Creating new arithmetic building blocks from these components is tricky and visu ## Compression Tree -Once you have a partial product matrix, you would like to add up the addends. Traditionally this is done using compression trees which instantate 2:1 and 3:2 column compressors (or carry-save adders) to reduce the matrix to two addends. The final two addends are often added with an efficient final adder. +Once you have a partial product matrix, you would like to add up the addends. Traditionally this is done using compression trees which instantiate 2:1 and 3:2 column compressors (or carry-save adders) to reduce the matrix to two addends. The final two addends are often added with an efficient final adder. -Our [`ColumnCompressor`] class uses a delay-driven approach to efficiently compress the rows of the partial product matrix. Its only arguement is a [`PartialProductGenerator`] , and it creates a list of ColumnQueues containing the final two addends stored by column after compression. An extractRow() routine can be used to extract the columns. [`ColumnCompressor`] currently has an extension [`EvaluateColumnCompressor`] which can be used to print out the compression progress. Here is the legend for these printouts. +Our `ColumnCompressor` class uses a delay-driven approach to efficiently compress the rows of the partial product matrix. Its only argument is a `PartialProductGenerator`, and it creates a list of `ColumnQueue`s containing the final two addends stored by column after compression. An `extractRow`routine can be used to extract the columns. `ColumnCompressor` currently has an extension `EvaluateColumnCompressor` which can be used to print out the compression progress. Here is the legend for these printouts. - ppR,C = partial product entry at row R, column C - sR,C = sum term coming last from row R, column C @@ -184,11 +184,11 @@ Any adder can be used as the final adder of the final two addends produced from Here is a code snippet that shows how these components can be used to create a multiplier. -First the partial product generator is used, which we pass in the [`RadixEncoder`], whether the operands are signed, and the kind of sign extension to use on the partial products. +First the partial product generator is used, which we pass in the `RadixEncoder`, whether the operands are signed, and the kind of sign extension to use on the partial products. -Next, we use the [`ColumnCompressor`] to compress the partial products into two final addends. +Next, we use the `ColumnCompressor` to compress the partial products into two final addends. -We then choose a[`ParallelPrefixAdder`] using the [`BrentKung`] tree style to do the addition. We pass in the two extracted rows of the compressor. +We then choose a `ParallelPrefixAdder` using the `BrentKung` tree style to do the addition. We pass in the two extracted rows of the compressor. Finally, we produce the product. ```dart diff --git a/lib/rohd_hcl.dart b/lib/rohd_hcl.dart index c6956cc4..4b642d8d 100644 --- a/lib/rohd_hcl.dart +++ b/lib/rohd_hcl.dart @@ -3,12 +3,7 @@ export 'src/arbiters/arbiters.dart'; export 'src/arithmetic/adder.dart'; -export 'src/arithmetic/carry_save_mutiplier.dart'; -export 'src/arithmetic/multiplier.dart'; -export 'src/arithmetic/multiplier_lib.dart'; -export 'src/arithmetic/parallel_prefix_operations.dart'; -export 'src/arithmetic/ripple_carry_adder.dart'; -export 'src/arithmetic/sign_magnitude_adder.dart'; +export 'src/arithmetic/arithmetic.dart'; export 'src/binary_gray.dart'; export 'src/component_config/component_config.dart'; export 'src/count.dart'; diff --git a/lib/src/arithmetic/addend_compressor.dart b/lib/src/arithmetic/addend_compressor.dart index ac101db9..9fa1f451 100644 --- a/lib/src/arithmetic/addend_compressor.dart +++ b/lib/src/arithmetic/addend_compressor.dart @@ -7,12 +7,10 @@ // 2024 June 04 // Author: Desmond Kirkpatrick -import 'dart:io'; import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; -import 'package:rohd_hcl/src/utils.dart'; /// Base class for column compressor function abstract class AddendCompressor extends Module { @@ -140,14 +138,6 @@ class _CompressTerm implements Comparable<_CompressTerm> { : 0; final majority = (count > termValues.length ~/ 2 ? LogicValue.one : LogicValue.zero); - // Alternative method: - // final x = Logic(width: termValues.length); - // x.put(termValues.swizzle()); - // final newCount = Count(x).index.value.toInt(); - // stdout.write('count=$count newCount=$newCount\n'); - // if (newCount != count) { - // throw RohdHclException('count=$count newCount=$newCount'); - // } value = majority; } return value; @@ -261,68 +251,3 @@ class ColumnCompressor { } } } - -/// Debug routines for printing out ColumnCompressor during -/// simulation with live logic values -extension EvaluateLiveColumnCompressor on ColumnCompressor { - /// Evaluate the (un)compressed partial product array - /// logic=true will read the logic gate outputs at each level - /// printOut=true will print out the array in the StringBuffer - (BigInt, StringBuffer) evaluate({bool printOut = false, bool logic = false}) { - final ts = StringBuffer(); - final rows = longestColumn(); - final width = pp.maxWidth(); - - var accum = BigInt.zero; - for (var row = 0; row < rows; row++) { - final rowBits = []; - for (var col = columns.length - 1; col >= 0; col--) { - final colList = columns[col].toList(); - if (row < colList.length) { - final value = - logic ? colList[row].logic.value : (colList[row].evaluate()); - rowBits.add(value); - if (printOut) { - ts.write('\t${value.bitString}'); - } - } else if (printOut) { - ts.write('\t'); - } - } - rowBits.addAll(List.filled(pp.rowShift[row], LogicValue.zero)); - final val = rowBits.swizzle().zeroExtend(width).toBigInt(); - accum += val; - if (printOut) { - ts.write('\t${rowBits.swizzle().zeroExtend(width).bitString} ($val)'); - if (row == rows - 1) { - ts.write(' Total=${accum.toSigned(width)}\n'); - stdout.write(ts); - } else { - ts.write('\n'); - } - } - } - if (printOut) { - // We need this to be able to debug, but git lint flunks print - // print(ts); - } - return (accum.toSigned(width), ts); - } - - /// Return a string representing the compression tree in its current state - String representation() { - final ts = StringBuffer(); - for (var row = 0; row < longestColumn(); row++) { - for (var col = columns.length - 1; col >= 0; col--) { - final colList = columns[col].toList(); - if (row < colList.length) { - ts.write('\t${colList[row]}'); - } else { - ts.write('\t'); - } - } - ts.write('\n'); - } - return ts.toString(); - } -} diff --git a/lib/src/arithmetic/arithmetic.dart b/lib/src/arithmetic/arithmetic.dart new file mode 100644 index 00000000..38ed18fe --- /dev/null +++ b/lib/src/arithmetic/arithmetic.dart @@ -0,0 +1,9 @@ +// Copyright (C) 2023-2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause + +export 'carry_save_mutiplier.dart'; +export 'multiplier.dart'; +export 'multiplier_lib.dart'; +export 'parallel_prefix_operations.dart'; +export 'ripple_carry_adder.dart'; +export 'sign_magnitude_adder.dart'; diff --git a/lib/src/arithmetic/evaluate_compressor.dart b/lib/src/arithmetic/evaluate_compressor.dart new file mode 100644 index 00000000..46574347 --- /dev/null +++ b/lib/src/arithmetic/evaluate_compressor.dart @@ -0,0 +1,78 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// addend_compressor.dart +// Column compression of partial prodcuts +// +// 2024 June 04 +// Author: Desmond Kirkpatrick + +import 'dart:io'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; +import 'package:rohd_hcl/src/utils.dart'; + +/// Debug routines for printing out ColumnCompressor during +/// simulation with live logic values +extension EvaluateLiveColumnCompressor on ColumnCompressor { + /// Evaluate the (un)compressed partial product array + /// logic=true will read the logic gate outputs at each level + /// printOut=true will print out the array in the StringBuffer + (BigInt, StringBuffer) evaluate({bool printOut = false, bool logic = false}) { + final ts = StringBuffer(); + final rows = longestColumn(); + final width = pp.maxWidth(); + + var accum = BigInt.zero; + for (var row = 0; row < rows; row++) { + final rowBits = []; + for (var col = columns.length - 1; col >= 0; col--) { + final colList = columns[col].toList(); + if (row < colList.length) { + final value = + logic ? colList[row].logic.value : (colList[row].evaluate()); + rowBits.add(value); + if (printOut) { + ts.write('\t${value.bitString}'); + } + } else if (printOut) { + ts.write('\t'); + } + } + rowBits.addAll(List.filled(pp.rowShift[row], LogicValue.zero)); + final val = rowBits.swizzle().zeroExtend(width).toBigInt(); + accum += val; + if (printOut) { + ts.write('\t${rowBits.swizzle().zeroExtend(width).bitString} ($val)'); + if (row == rows - 1) { + ts.write(' Total=${accum.toSigned(width)}\n'); + stdout.write(ts); + } else { + ts.write('\n'); + } + } + } + if (printOut) { + // We need this to be able to debug, but git lint flunks print + // print(ts); + } + return (accum.toSigned(width), ts); + } + + /// Return a string representing the compression tree in its current state + String representation() { + final ts = StringBuffer(); + for (var row = 0; row < longestColumn(); row++) { + for (var col = columns.length - 1; col >= 0; col--) { + final colList = columns[col].toList(); + if (row < colList.length) { + ts.write('\t${colList[row]}'); + } else { + ts.write('\t'); + } + } + ts.write('\n'); + } + return ts.toString(); + } +} diff --git a/lib/src/arithmetic/evaluate_partial_product.dart b/lib/src/arithmetic/evaluate_partial_product.dart new file mode 100644 index 00000000..e421d13d --- /dev/null +++ b/lib/src/arithmetic/evaluate_partial_product.dart @@ -0,0 +1,107 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// partial_product_generator.dart +// Partial Product matrix generation from Booth recoded multiplicand +// +// 2024 May 15 +// Author: Desmond Kirkpatrick + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// Debug routines for printing out partial product matrix during +/// simulation with live logic values +extension EvaluateLivePartialProduct on PartialProductGenerator { + /// Accumulate the partial products and return as BigInt + BigInt evaluate() { + final maxW = maxWidth(); + var accum = BigInt.from(0); + for (var row = 0; row < rows; row++) { + final pp = partialProducts[row].rswizzle().value; + final value = pp.zeroExtend(maxW) << rowShift[row]; + if (pp.isValid) { + accum += value.toBigInt(); + } + } + final sum = LogicValue.ofBigInt(accum, maxW).toBigInt(); + return signed ? sum.toSigned(maxW) : sum; + } + + /// Print out the partial product matrix + String representation() { + final str = StringBuffer(); + + final maxW = maxWidth(); + final nonSignExtendedPad = isSignExtended + ? 0 + : shift > 2 + ? shift - 1 + : 1; + // We will print encoding(1-hot multiples and sign) before each row + final shortPrefix = '99 ${'M='}99 S= : '.length + 3 * nonSignExtendedPad; + + // print bit position header + str.write(' ' * shortPrefix); + for (var i = maxW - 1; i >= 0; i--) { + final bits = i > 9 ? 2 : 1; + str + ..write('$i') + ..write(' ' * (3 - bits)); + } + str.write('\n'); + // Partial product matrix: rows of multiplicand multiples shift by + // rowshift[row] + for (var row = 0; row < rows; row++) { + final rowStr = (row < 10) ? '0$row' : '$row'; + if (row < encoder.rows) { + final encoding = encoder.getEncoding(row); + if (encoding.multiples.value.isValid) { + final first = encoding.multiples.value.firstOne() ?? -1; + final multiple = first + 1; + str.write('$rowStr M=' + '${multiple.toString().padLeft(2)} ' + 'S=${encoding.sign.value.toInt()}: '); + } else { + str.write(' ' * shortPrefix); + } + } else { + str.write('$rowStr ${'M='} S= : '); + } + final entry = partialProducts[row].reversed.toList(); + final prefixCnt = + maxW - (entry.length + rowShift[row]) + nonSignExtendedPad; + str.write(' ' * prefixCnt); + for (var col = 0; col < entry.length; col++) { + str.write('${entry[col].value.bitString} '); + } + final suffixCnt = rowShift[row]; + final value = entry.swizzle().value.zeroExtend(maxW) << suffixCnt; + final intValue = value.isValid ? value.toBigInt() : BigInt.from(-1); + str + ..write(' ' * suffixCnt) + ..write(': ${value.bitString}') + ..write(' = ${value.isValid ? intValue : ""}' + ' (${value.isValid ? intValue.toSigned(maxW) : ""})\n'); + } + // Compute and print binary representation from accumulated value + // Later: we will compare with a compression tree result + str + ..write('=' * (shortPrefix + 3 * maxW)) + ..write('\n') + ..write(' ' * shortPrefix); + + final sum = LogicValue.ofBigInt(evaluate(), maxW); + // print out the sum as a MSB-first bitvector + for (final elem in [for (var i = 0; i < maxW; i++) sum[i]].reversed) { + str.write('${elem.toInt()} '); + } + final val = evaluate(); + str.write(': ${sum.bitString} = ' + '${val.toUnsigned(maxW)}'); + if (isSignExtended) { + str.write(' ($val)\n\n'); + } + return str.toString(); + } +} diff --git a/lib/src/arithmetic/multiplier.dart b/lib/src/arithmetic/multiplier.dart index e69184ad..3c21bc0c 100644 --- a/lib/src/arithmetic/multiplier.dart +++ b/lib/src/arithmetic/multiplier.dart @@ -75,19 +75,22 @@ class CompressionTreeMultiplier extends Multiplier { /// Construct a compression tree integer multipler with /// a given radix and final adder functor CompressionTreeMultiplier(super.a, super.b, int radix, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppTree, - {super.signed = false}) + {ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppTree = KoggeStone.new, + super.signed = false}) : super( name: 'Compression Tree Multiplier: ' 'R${radix}_${ppTree.call([ Logic() ], (a, b) => Logic()).runtimeType}') { final product = addOutput('product', width: a.width + b.width); - final pp = - PartialProductGenerator(a, b, RadixEncoder(radix), signed: signed); + final pp = CompactRectSignExtendPartialProductGenerator( + a, b, RadixEncoder(radix), + signed: signed); final compressor = ColumnCompressor(pp)..compress(); final adder = ParallelPrefixAdder( - compressor.extractRow(0), compressor.extractRow(1), ppTree); + compressor.extractRow(0), compressor.extractRow(1), + ppGen: ppTree); product <= adder.sum.slice(a.width + b.width - 1, 0); } } @@ -101,14 +104,16 @@ class CompressionTreeMultiplyAccumulate extends MultiplyAccumulate { /// Construct a compression tree integer multipler with /// a given radix and final adder functor CompressionTreeMultiplyAccumulate(super.a, super.b, super.c, int radix, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppTree, - {required super.signed}) + {required super.signed, + ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppTree = KoggeStone.new}) : super( name: 'Compression Tree Multiply Accumulate: ' 'R${radix}_${ppTree.call([Logic()], (a, b) => Logic()).name}') { final accumulate = addOutput('accumulate', width: a.width + b.width + 1); - final pp = - PartialProductGenerator(a, b, RadixEncoder(radix), signed: signed); + final pp = CompactRectSignExtendPartialProductGenerator( + a, b, RadixEncoder(radix), + signed: signed); // TODO(desmonddak): This sign extension method for the additional // addend may only work with signExtendCompact. @@ -139,7 +144,8 @@ class CompressionTreeMultiplyAccumulate extends MultiplyAccumulate { final compressor = ColumnCompressor(pp)..compress(); final adder = ParallelPrefixAdder( - compressor.extractRow(0), compressor.extractRow(1), ppTree); + compressor.extractRow(0), compressor.extractRow(1), + ppGen: ppTree); accumulate <= adder.sum.slice(a.width + b.width, 0); } } diff --git a/lib/src/arithmetic/parallel_prefix_operations.dart b/lib/src/arithmetic/parallel_prefix_operations.dart index 0cc2b695..7a166050 100644 --- a/lib/src/arithmetic/parallel_prefix_operations.dart +++ b/lib/src/arithmetic/parallel_prefix_operations.dart @@ -162,7 +162,8 @@ class ParallelPrefixOrScan extends Module { /// OrScan constructor ParallelPrefixOrScan(Logic inp, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen) + {ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppGen = KoggeStone.new}) : super( name: 'ParallelPrefixOrScan: ${ppGen.call([ Logic() @@ -181,13 +182,14 @@ class ParallelPrefixPriorityFinder extends Module { /// Priority Finder constructor ParallelPrefixPriorityFinder(Logic inp, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen) + {ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppGen = KoggeStone.new}) : super( name: 'ParallelPrefixFinder: ${ppGen.call([ Logic() ], (a, b) => Logic()).name}') { inp = addInput('inp', inp, width: inp.width); - final u = ParallelPrefixOrScan(inp, ppGen); + final u = ParallelPrefixOrScan(inp, ppGen: ppGen); addOutput('out', width: inp.width) <= (u.out & ~(u.out << Const(1))); } } @@ -200,14 +202,15 @@ class ParallelPrefixPriorityEncoder extends Module { /// PriorityEncoder constructor ParallelPrefixPriorityEncoder(Logic inp, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen) + {ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppGen = KoggeStone.new}) : super( name: 'ParallelPrefixEncoder: ${ppGen.call([ Logic() ], (a, b) => Logic()).name}') { inp = addInput('inp', inp, width: inp.width); addOutput('out', width: log2Ceil(inp.width)); - final u = ParallelPrefixPriorityFinder(inp, ppGen); + final u = ParallelPrefixPriorityFinder(inp, ppGen: ppGen); out <= OneHotToBinary(u.out).binary; } } @@ -216,7 +219,8 @@ class ParallelPrefixPriorityEncoder extends Module { class ParallelPrefixAdder extends Adder { /// Adder constructor ParallelPrefixAdder(super.a, super.b, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen) + {ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppGen = KoggeStone.new}) // : _out = Logic(width: a.width), : super( name: 'ParallelPrefixAdder: ${ppGen.call([ @@ -244,7 +248,8 @@ class ParallelPrefixIncr extends Module { /// Increment constructor ParallelPrefixIncr(Logic inp, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen) + {ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppGen = KoggeStone.new}) : super( name: 'ParallelPrefixIncr: ${ppGen.call([ Logic() @@ -265,7 +270,8 @@ class ParallelPrefixDecr extends Module { /// Decrement constructor ParallelPrefixDecr(Logic inp, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen) + {ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppGen = KoggeStone.new}) : super( name: 'ParallelPrefixDecr: ${ppGen.call([ Logic() diff --git a/lib/src/arithmetic/partial_product_generator.dart b/lib/src/arithmetic/partial_product_generator.dart index d30d06ef..3b1e5df8 100644 --- a/lib/src/arithmetic/partial_product_generator.dart +++ b/lib/src/arithmetic/partial_product_generator.dart @@ -9,27 +9,10 @@ import 'dart:math'; +import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -/// Methods for sign extending the [PartialProductGenerator] -enum SignExtension { - /// No sign extension - none, - - /// Brute force sign extend each row to the full width of the product - brute, - - /// Extend using stop bits in each row (and an extra row for final sign) - stop, - - /// Fold in last row sign bit (Mohanty, B.K., Choubey, A.) - compact, - - /// Sign folding that works for rectangular partial products - compactRect -} - /// A [PartialProductGenerator] class that generates a set of partial products. /// Essentially a set of /// shifted rows of [Logic] addends generated by Booth recoding and @@ -66,14 +49,13 @@ class PartialProductGenerator { /// Operands are signed final bool signed; - // Used to avoid sign extending more than once - var _signExtended = false; + /// Used to avoid sign extending more than once + bool isSignExtended = false; /// Construct a [PartialProductGenerator] -- the partial product matrix PartialProductGenerator( Logic multiplicand, Logic multiplier, RadixEncoder radixEncoder, - {required this.signed, - SignExtension signExtension = SignExtension.compactRect}) { + {required this.signed}) { encoder = MultiplierEncoder(multiplier, radixEncoder, signed: signed); selector = MultiplicandSelector(radixEncoder.radix, multiplicand, signed: signed); @@ -87,20 +69,13 @@ class PartialProductGenerator { '${selector.shift + (signed ? 1 : 0)}'); } _build(); - switch (signExtension) { - case SignExtension.none: - ; - case SignExtension.brute: - _bruteForceSignExtend(); - case SignExtension.stop: - _signExtendWithStopBitsRect(); - case SignExtension.compact: - _signExtendCompact(); - case SignExtension.compactRect: - _signExtendCompactRect(); - } + signExtend(); } + /// Perform sign extension (defined in child classes) + @protected + void signExtend() {} + /// Setup the partial products array (partialProducts and rowShift) void _build() { partialProducts = >[]; @@ -113,94 +88,26 @@ class PartialProductGenerator { } } - /// Fully sign extend the PP array: useful for reference only - void _bruteForceSignExtend() { - if (_signExtended) { - throw RohdHclException('Partial Product array already sign-extended'); - } - _signExtended = true; - final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; + /// Return the actual largest width of all rows + int maxWidth() { + var maxW = 0; for (var row = 0; row < rows; row++) { - final addend = partialProducts[row]; - final sign = signed ? addend.last : signs[row]; - addend.addAll(List.filled((rows - row) * shift, sign)); - if (row > 0) { - addend - ..insertAll(0, List.filled(shift - 1, Const(0))) - ..insert(0, signs[row - 1]); - rowShift[row] -= shift; + final entry = partialProducts[row]; + if (entry.length + rowShift[row] > maxW) { + maxW = entry.length + rowShift[row]; } } - // Insert carry bit in extra row - partialProducts.add(List.generate(selector.width, (i) => Const(0))); - partialProducts.last.insert(0, signs[rows - 2]); - rowShift.add((rows - 2) * shift); + return maxW; } +} - /// Sign extend the PP array using stop bits - /// If possible, fold the final carry into another row (only when rectangular - /// enough that carry bit lands outside another row). - /// This technique can then be combined with a first-row extension technique - /// for folding in the final carry. - void _signExtendWithStopBitsRect() { - if (_signExtended) { - throw RohdHclException('Partial Product array already sign-extended'); - } - _signExtended = true; - - final finalCarryPos = shift * (rows - 1); - final finalCarryRelPos = finalCarryPos - selector.width - shift; - final finalCarryRow = - ((encoder.multiplier.width > selector.multiplicand.width) && - (finalCarryRelPos > 0)) - ? (finalCarryRelPos / shift).floor() - : 0; - - final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; - for (var row = 0; row < rows; row++) { - final addend = partialProducts[row]; - final sign = signed ? addend.last : signs[row]; - if (row == 0) { - if (signed) { - addend.addAll(List.filled(shift - 1, sign)); // signed only? - } else { - addend.addAll(List.filled(shift, sign)); - } - addend.add(~sign); - } else { - if (signed) { - addend.last = ~sign; - } else { - addend.add(~sign); - } - addend - ..addAll(List.filled(shift - 1, Const(1))) - ..insertAll(0, List.filled(shift - 1, Const(0))) - ..insert(0, signs[row - 1]); - rowShift[row] -= shift; - } - } - - if (finalCarryRow > 0) { - final extensionRow = partialProducts[finalCarryRow]; - extensionRow - ..addAll(List.filled( - finalCarryPos - (extensionRow.length + rowShift[finalCarryRow]), - Const(0))) - ..add(signs[rows - 1]); - } else if (signed) { - // Create an extra row to hold the final carry bit - partialProducts - .add(List.filled(selector.width, Const(0), growable: true)); - partialProducts.last.insert(0, signs[rows - 2]); - rowShift.add((rows - 2) * shift); - - // Hack for radix-2 - if (shift == 1) { - partialProducts.last.last = ~partialProducts.last.last; - } - } - } +/// A Partial Product Generator using Brute Sign Extension +class CompactRectSignExtendPartialProductGenerator + extends PartialProductGenerator { + /// Construct a compact rect sign extending Partial Product Generator + CompactRectSignExtendPartialProductGenerator( + super.multiplicand, super.multiplier, super.radixEncoder, + {required super.signed}); void _addStopSignFlip(List addend, Logic sign) { if (signed) { @@ -218,116 +125,16 @@ class PartialProductGenerator { } } - /// Sign extend the PP array using stop bits without adding a row. - void _signExtendCompact() { - // An implementation of - // 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 (_signExtended) { - throw RohdHclException('Partial Product array already sign-extended'); - } - _signExtended = true; - - final lastRow = rows - 1; - final firstAddend = partialProducts[0]; - final lastAddend = partialProducts[lastRow]; - var alignRow0Sign = selector.width - - shift * lastRow - - ((shift > 1) - ? 1 - : signed - ? 1 - : 0); - - if (alignRow0Sign < 0) { - alignRow0Sign = 0; - } - - final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; - - final propagate = - List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); - for (var row = 0; row < rows; row++) { - propagate[row].add(signs[row]); - for (var col = 0; col < 2 * (shift - 1); col++) { - propagate[row].add(partialProducts[row][col]); - } - for (var col = 1; col < propagate[row].length; col++) { - propagate[row][col] = propagate[row][col] & propagate[row][col - 1]; - } - } - final m = - List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); - for (var row = 0; row < rows; row++) { - for (var c = 0; c < shift - 1; c++) { - m[row].add(partialProducts[row][c] ^ propagate[row][c]); - } - m[row].addAll(List.filled(shift - 1, Logic())); - } - - for (var i = shift - 1; i < m[lastRow].length; i++) { - m[lastRow][i] = lastAddend[i] ^ - (i < alignRow0Sign ? propagate[lastRow][i] : Const(0)); - } - - final remainders = List.filled(rows, Logic()); - for (var row = 0; row < lastRow; row++) { - remainders[row] = propagate[row][shift - 1]; - } - remainders[lastRow] <= propagate[lastRow][alignRow0Sign]; - - // Compute Sign extension for row==0 - final firstSign = signed ? firstAddend.last : signs[0]; - final q = [ - firstSign ^ remainders[lastRow], - ~(firstSign & ~remainders[lastRow]), - ]; - q.insertAll(1, List.filled(shift - 1, ~q[1])); - - for (var row = 0; row < rows; row++) { - final addend = partialProducts[row]; - if (row > 0) { - final mLimit = (row == lastRow) ? 2 * (shift - 1) : shift - 1; - for (var i = 0; i < mLimit; i++) { - addend[i] = m[row][i]; - } - // Stop bits - if (signed) { - addend.last = ~addend.last; - } else { - addend.add(~signs[row]); - } - addend - ..insert(0, remainders[row - 1]) - ..addAll(List.filled(shift - 1, Const(1))); - rowShift[row] -= 1; - } else { - for (var i = 0; i < shift - 1; i++) { - firstAddend[i] = m[0][i]; - } - if (signed) { - firstAddend.last = q[0]; - } else { - firstAddend.add(q[0]); - } - firstAddend.addAll(q.getRange(1, q.length)); - } - } - if (shift == 1) { - lastAddend.add(Const(1)); - } - } - - /// Sign extend the PP array using stop bits without adding a row - /// This routine works with different widths of multiplicand/multiplier, - /// an extension of Mohanty, B.K., Choubey designed by - /// Desmond A. Kirkpatrick - void _signExtendCompactRect() { - if (_signExtended) { + // /// Sign extend the PP array using stop bits without adding a row + // /// This routine works with different widths of multiplicand/multiplier, + // /// an extension of Mohanty, B.K., Choubey designed by + // /// Desmond A. Kirkpatrick + @override + void signExtend() { + if (isSignExtended) { throw RohdHclException('Partial Product array already sign-extended'); } - _signExtended = true; + isSignExtended = true; final lastRow = rows - 1; final firstAddend = partialProducts[0]; @@ -450,112 +257,4 @@ class PartialProductGenerator { lastAddend.add(Const(1)); } } - - /// Return the actual largest width of all rows - int maxWidth() { - var maxW = 0; - for (var row = 0; row < rows; row++) { - final entry = partialProducts[row]; - if (entry.length + rowShift[row] > maxW) { - maxW = entry.length + rowShift[row]; - } - } - return maxW; - } -} - -/// Debug routines for printing out partial product matrix during -/// simulation with live logic values -extension EvaluateLivePartialProduct on PartialProductGenerator { - /// Accumulate the partial products and return as BigInt - BigInt evaluate() { - final maxW = maxWidth(); - var accum = BigInt.from(0); - for (var row = 0; row < rows; row++) { - final pp = partialProducts[row].rswizzle().value; - final value = pp.zeroExtend(maxW) << rowShift[row]; - if (pp.isValid) { - accum += value.toBigInt(); - } - } - final sum = LogicValue.ofBigInt(accum, maxW).toBigInt(); - return signed ? sum.toSigned(maxW) : sum; - } - - /// Print out the partial product matrix - String representation() { - final str = StringBuffer(); - - final maxW = maxWidth(); - final nonSignExtendedPad = _signExtended - ? 0 - : shift > 2 - ? shift - 1 - : 1; - // We will print encoding(1-hot multiples and sign) before each row - final shortPrefix = '99 ${'M='}99 S= : '.length + 3 * nonSignExtendedPad; - - // print bit position header - str.write(' ' * shortPrefix); - for (var i = maxW - 1; i >= 0; i--) { - final bits = i > 9 ? 2 : 1; - str - ..write('$i') - ..write(' ' * (3 - bits)); - } - str.write('\n'); - // Partial product matrix: rows of multiplicand multiples shift by - // rowshift[row] - for (var row = 0; row < rows; row++) { - final rowStr = (row < 10) ? '0$row' : '$row'; - if (row < encoder.rows) { - final encoding = encoder.getEncoding(row); - if (encoding.multiples.value.isValid) { - final first = encoding.multiples.value.firstOne() ?? -1; - final multiple = first + 1; - str.write('$rowStr M=' - '${multiple.toString().padLeft(2)} ' - 'S=${encoding.sign.value.toInt()}: '); - } else { - str.write(' ' * shortPrefix); - } - } else { - str.write('$rowStr ${'M='} S= : '); - } - final entry = partialProducts[row].reversed.toList(); - final prefixCnt = - maxW - (entry.length + rowShift[row]) + nonSignExtendedPad; - str.write(' ' * prefixCnt); - for (var col = 0; col < entry.length; col++) { - str.write('${entry[col].value.bitString} '); - } - final suffixCnt = rowShift[row]; - final value = entry.swizzle().value.zeroExtend(maxW) << suffixCnt; - final intValue = value.isValid ? value.toBigInt() : BigInt.from(-1); - str - ..write(' ' * suffixCnt) - ..write(': ${value.bitString}') - ..write(' = ${value.isValid ? intValue : ""}' - ' (${value.isValid ? intValue.toSigned(maxW) : ""})\n'); - } - // Compute and print binary representation from accumulated value - // Later: we will compare with a compression tree result - str - ..write('=' * (shortPrefix + 3 * maxW)) - ..write('\n') - ..write(' ' * shortPrefix); - - final sum = LogicValue.ofBigInt(evaluate(), maxW); - // print out the sum as a MSB-first bitvector - for (final elem in [for (var i = 0; i < maxW; i++) sum[i]].reversed) { - str.write('${elem.toInt()} '); - } - final val = evaluate(); - str.write(': ${sum.bitString} = ' - '${val.toUnsigned(maxW)}'); - if (_signExtended) { - str.write(' ($val)\n\n'); - } - return str.toString(); - } } diff --git a/lib/src/arithmetic/partial_product_test_sign_extend.dart b/lib/src/arithmetic/partial_product_test_sign_extend.dart new file mode 100644 index 00000000..b056640b --- /dev/null +++ b/lib/src/arithmetic/partial_product_test_sign_extend.dart @@ -0,0 +1,274 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// partial_product_generator.dart +// Partial Product matrix generation from Booth recoded multiplicand +// +// 2024 May 15 +// Author: Desmond Kirkpatrick + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// Methods for sign extending the [PartialProductGenerator] +enum SignExtension { + /// No sign extension + none, + + /// Brute force sign extend each row to the full width of the product + brute, + + /// Extend using stop bits in each row (and an extra row for final sign) + stop, + + /// Fold in last row sign bit (Mohanty, B.K., Choubey, A.) + compact, + + /// Sign folding that works for rectangular partial products + compactRect +} + +/// Used to test different sign extension methods +typedef PPGFunction = PartialProductGenerator + Function(Logic a, Logic b, RadixEncoder radixEncoder, {bool signed}); + +/// Used to test different sign extension methods +PPGFunction curryPartialProductGenerator(SignExtension signExtension) => (a, b, + encoder, + {signed = false}) => + switch (signExtension) { + SignExtension.none => + PartialProductGenerator(a, b, encoder, signed: signed), + SignExtension.brute => + BruteSignExtendPartialProductGenerator(a, b, encoder, signed: signed), + SignExtension.stop => StopBitsSignExtendPartialProductGenerator( + a, b, encoder, + signed: signed), + SignExtension.compact => + CompactSignExtendPartialProductGenerator(a, b, encoder, signed: signed), + SignExtension.compactRect => CompactRectSignExtendPartialProductGenerator( + a, b, encoder, + signed: signed), + }; + +//// These other sign extensions are for asssisting with testing and debugging +/// More robust and simpler sign extensions in case +/// complex sign extension routines obscure other bugs. + +/// A Partial Product Generator using Brute Sign Extension +class BruteSignExtendPartialProductGenerator extends PartialProductGenerator { + /// Construct a brute-force sign extending Partial Product Generator + BruteSignExtendPartialProductGenerator( + super.multiplicand, super.multiplier, super.radixEncoder, + {required super.signed}); + + /// Fully sign extend the PP array: useful for reference only + @override + void signExtend() { + if (isSignExtended) { + throw RohdHclException('Partial Product array already sign-extended'); + } + isSignExtended = true; + final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; + for (var row = 0; row < rows; row++) { + final addend = partialProducts[row]; + final sign = signed ? addend.last : signs[row]; + addend.addAll(List.filled((rows - row) * shift, sign)); + if (row > 0) { + addend + ..insertAll(0, List.filled(shift - 1, Const(0))) + ..insert(0, signs[row - 1]); + rowShift[row] -= shift; + } + } + // Insert carry bit in extra row + partialProducts.add(List.generate(selector.width, (i) => Const(0))); + partialProducts.last.insert(0, signs[rows - 2]); + rowShift.add((rows - 2) * shift); + } +} + +/// A Partial Product Generator using Brute Sign Extension +class CompactSignExtendPartialProductGenerator extends PartialProductGenerator { + /// Construct a compact sign extending Partial Product Generator + CompactSignExtendPartialProductGenerator( + super.multiplicand, super.multiplier, super.radixEncoder, + {required super.signed}); + + /// Sign extend the PP array using stop bits without adding a row. + @override + void signExtend() { + // An implementation of + // 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 (isSignExtended) { + throw RohdHclException('Partial Product array already sign-extended'); + } + isSignExtended = true; + + final lastRow = rows - 1; + final firstAddend = partialProducts[0]; + final lastAddend = partialProducts[lastRow]; + var alignRow0Sign = selector.width - + shift * lastRow - + ((shift > 1) + ? 1 + : signed + ? 1 + : 0); + + if (alignRow0Sign < 0) { + alignRow0Sign = 0; + } + + final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; + + final propagate = + List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); + for (var row = 0; row < rows; row++) { + propagate[row].add(signs[row]); + for (var col = 0; col < 2 * (shift - 1); col++) { + propagate[row].add(partialProducts[row][col]); + } + for (var col = 1; col < propagate[row].length; col++) { + propagate[row][col] = propagate[row][col] & propagate[row][col - 1]; + } + } + final m = + List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); + for (var row = 0; row < rows; row++) { + for (var c = 0; c < shift - 1; c++) { + m[row].add(partialProducts[row][c] ^ propagate[row][c]); + } + m[row].addAll(List.filled(shift - 1, Logic())); + } + + for (var i = shift - 1; i < m[lastRow].length; i++) { + m[lastRow][i] = lastAddend[i] ^ + (i < alignRow0Sign ? propagate[lastRow][i] : Const(0)); + } + + final remainders = List.filled(rows, Logic()); + for (var row = 0; row < lastRow; row++) { + remainders[row] = propagate[row][shift - 1]; + } + remainders[lastRow] <= propagate[lastRow][alignRow0Sign]; + + // Compute Sign extension for row==0 + final firstSign = signed ? firstAddend.last : signs[0]; + final q = [ + firstSign ^ remainders[lastRow], + ~(firstSign & ~remainders[lastRow]), + ]; + q.insertAll(1, List.filled(shift - 1, ~q[1])); + + for (var row = 0; row < rows; row++) { + final addend = partialProducts[row]; + if (row > 0) { + final mLimit = (row == lastRow) ? 2 * (shift - 1) : shift - 1; + for (var i = 0; i < mLimit; i++) { + addend[i] = m[row][i]; + } + // Stop bits + if (signed) { + addend.last = ~addend.last; + } else { + addend.add(~signs[row]); + } + addend + ..insert(0, remainders[row - 1]) + ..addAll(List.filled(shift - 1, Const(1))); + rowShift[row] -= 1; + } else { + for (var i = 0; i < shift - 1; i++) { + firstAddend[i] = m[0][i]; + } + if (signed) { + firstAddend.last = q[0]; + } else { + firstAddend.add(q[0]); + } + firstAddend.addAll(q.getRange(1, q.length)); + } + } + if (shift == 1) { + lastAddend.add(Const(1)); + } + } +} + +/// A Partial Product Generator using Brute Sign Extension +class StopBitsSignExtendPartialProductGenerator + extends PartialProductGenerator { + /// Construct a stop bits sign extending Partial Product Generator + StopBitsSignExtendPartialProductGenerator( + super.multiplicand, super.multiplier, super.radixEncoder, + {required super.signed}); + + /// Sign extend the PP array using stop bits + /// If possible, fold the final carry into another row (only when rectangular + /// enough that carry bit lands outside another row). + /// This technique can then be combined with a first-row extension technique + /// for folding in the final carry. + @override + void signExtend() { + if (isSignExtended) { + throw RohdHclException('Partial Product array already sign-extended'); + } + isSignExtended = true; + + final finalCarryPos = shift * (rows - 1); + final finalCarryRelPos = finalCarryPos - selector.width - shift; + final finalCarryRow = + ((encoder.multiplier.width > selector.multiplicand.width) && + (finalCarryRelPos > 0)) + ? (finalCarryRelPos / shift).floor() + : 0; + + final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; + for (var row = 0; row < rows; row++) { + final addend = partialProducts[row]; + final sign = signed ? addend.last : signs[row]; + if (row == 0) { + if (signed) { + addend.addAll(List.filled(shift - 1, sign)); // signed only? + } else { + addend.addAll(List.filled(shift, sign)); + } + addend.add(~sign); + } else { + if (signed) { + addend.last = ~sign; + } else { + addend.add(~sign); + } + addend + ..addAll(List.filled(shift - 1, Const(1))) + ..insertAll(0, List.filled(shift - 1, Const(0))) + ..insert(0, signs[row - 1]); + rowShift[row] -= shift; + } + } + + if (finalCarryRow > 0) { + final extensionRow = partialProducts[finalCarryRow]; + extensionRow + ..addAll(List.filled( + finalCarryPos - (extensionRow.length + rowShift[finalCarryRow]), + Const(0))) + ..add(signs[rows - 1]); + } else if (signed) { + // Create an extra row to hold the final carry bit + partialProducts + .add(List.filled(selector.width, Const(0), growable: true)); + partialProducts.last.insert(0, signs[rows - 2]); + rowShift.add((rows - 2) * shift); + + // Hack for radix-2 + if (shift == 1) { + partialProducts.last.last = ~partialProducts.last.last; + } + } + } +} diff --git a/lib/src/arithmetic/sign_magnitude_adder.dart b/lib/src/arithmetic/sign_magnitude_adder.dart index df0b9fdb..f0efa103 100644 --- a/lib/src/arithmetic/sign_magnitude_adder.dart +++ b/lib/src/arithmetic/sign_magnitude_adder.dart @@ -11,52 +11,109 @@ import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -/// An Adder which performs one's complement arithmetic using an -/// adder that is passed in using a functor. If the caller can guarantee -/// that the larger magnitude or negative value is provided first in 'a', then -/// they can set 'largestMagnitudeFirst' to 'true' to avoid a comparator. +/// A [SignMagnitudeAdder] performsa addition on values in sign/magnitude format. class SignMagnitudeAdder extends Adder { /// The sign of the first input - @protected - late final Logic aSign; + Logic aSign; /// The sign of the second input - @protected - late final Logic bSign; + Logic bSign; /// The sign of the result Logic get sign => output('sign'); + @protected + Logic _sign = Logic(); + /// Largest magnitude argument is provided in [a] or if equal /// the argument with a negative sign. bool largestMagnitudeFirst; - /// [SignMagnitudeAdder] constructor with an unsigned adder functor - SignMagnitudeAdder(Logic as, super.a, Logic bs, super.b, + /// [SignMagnitudeAdder] constructor with an adder functor [adderGen] + ///Inputs are (sign, magnitude) pairs: ([aSign], [a]) and ([bSign], [b]). + /// If the caller can guarantee that the larger magnitude value + /// is provided first in [a], then they can set [largestMagnitudeFirst] + /// too 'true' to avoid a comparator. + // TODO(desmonddak): this adder may need a carry-in for rounding + SignMagnitudeAdder(this.aSign, super.a, this.bSign, super.b, Adder Function(Logic, Logic) adderGen, {this.largestMagnitudeFirst = false}) : super( - name: 'Ones Complement Adder: ' + name: 'Sign Magnitude Adder: ' '${adderGen.call(Logic(), Logic()).name}') { - aSign = addInput('aSign', as); - bSign = addInput('bSign', bs); - final sign = addOutput('sign'); + aSign = addInput('aSign', aSign); + bSign = addInput('bSign', bSign); + _sign = addOutput('sign'); final bLarger = a.lt(b) | (a.eq(b) & bSign.gt(aSign)); - final computeSign = mux(largestMagnitudeFirst ? Const(1) : Const(0), aSign, - mux(bLarger, bSign, aSign)); + _sign <= (largestMagnitudeFirst ? aSign : mux(bLarger, bSign, aSign)); + final adder = OnesComplementAdder(a, b, aSign ^ bSign, null, adderGen); + sum <= adder.sum; + } +} + +/// An adder (and subtractor) [OnesComplementAdder] that operates on +/// ones-complement values. +class OnesComplementAdder extends Adder { + /// The sign of the result + Logic get sign => output('sign'); + + /// The end-around carry which should be added to the resulting [sum] + /// If the input [carry] is not null, this value is stored there. Otherwise, + /// the end-around carry is internally added to [sum] + Logic? get carry => tryOutput('carry'); + + @protected + Logic _sign = Logic(); + + /// [OnesComplementAdder] constructor with an adder functor [adderGen] + /// Either a Logic [subtractIn] or a boolean [subtract] can enable + /// subtraction, with [subtractIn] overriding [subtract]. If Logic [carry] + /// is provided as not null, then the end-around carry is not performed and is + /// left to the caller via the output [carry]. + OnesComplementAdder(super.a, super.b, Logic? subtractIn, Logic? carry, + Adder Function(Logic, Logic) adderGen, + {bool subtract = false}) + : super( + name: 'Ones Complement Adder: ' + '${adderGen.call(Logic(), Logic()).name}') { + if (subtractIn != null) { + subtractIn = addInput('subtractIn', subtractIn); + } + _sign = addOutput('sign'); + if (carry != null) { + addOutput('carry'); + carry <= this.carry!; + } + if ((subtractIn != null) & subtract) { + throw RohdHclException( + 'Subtraction is controlled by a non-null subtractIn: ' + 'subtract boolean is ignored'); + } + final doSubtract = subtractIn ?? (subtract ? Const(1) : Const(0)); + + final ax = a.zeroExtend(a.width); + final bx = b.zeroExtend(b.width); - final ax = a.zeroExtend(a.width + 1); - final bx = b.zeroExtend(b.width + 1); + final adder = adderGen(ax, mux(doSubtract, ~bx, bx)); - final aOnesComplement = mux(aSign, ~ax, ax); - final bOnesComplement = mux(bSign, ~bx, bx); + if (this.carry != null) { + this.carry! <= adder.sum[-1]; + } + final endAround = mux(doSubtract, adder.sum[-1], Const(0)); + final magnitude = adder.sum.slice(a.width - 1, 0); - final adder = adderGen(aOnesComplement, bOnesComplement); - final endAround = adder.sum[-1] & (aSign | bSign); - final localOut = mux(endAround, adder.sum + 1, adder.sum); - sign <= computeSign; - sum <= mux(sign, ~localOut, localOut).slice(ax.width - 1, 0); + sum <= + mux( + doSubtract, + mux( + endAround, + [if (this.carry != null) magnitude else magnitude + 1] + .first, + ~magnitude) + .zeroExtend(sum.width), + adder.sum); + _sign <= mux(doSubtract, ~endAround, Const(0)); } } diff --git a/lib/src/component_config/components/config_compression_tree_multiplier.dart b/lib/src/component_config/components/config_compression_tree_multiplier.dart index a30f4187..2f6fbbeb 100644 --- a/lib/src/component_config/components/config_compression_tree_multiplier.dart +++ b/lib/src/component_config/components/config_compression_tree_multiplier.dart @@ -45,7 +45,7 @@ class CompressionTreeMultiplierConfigurator extends Configurator { Logic(name: 'a', width: multiplicandWidthKnob.value), Logic(name: 'b', width: multiplierWidthKnob.value), radixKnob.value, - generatorMap[prefixTreeKnob.value]!); + ppTree: generatorMap[prefixTreeKnob.value]!); @override late final Map> knobs = UnmodifiableMapView({ diff --git a/lib/src/component_config/components/config_parallel_prefix_adder.dart b/lib/src/component_config/components/config_parallel_prefix_adder.dart index b759e143..0cda4da1 100644 --- a/lib/src/component_config/components/config_parallel_prefix_adder.dart +++ b/lib/src/component_config/components/config_parallel_prefix_adder.dart @@ -35,7 +35,7 @@ class ParallelPrefixAdderConfigurator extends Configurator { Module createModule() => ParallelPrefixAdder( Logic(name: 'a', width: dataWidthKnob.value), Logic(name: 'b', width: dataWidthKnob.value), - generatorMap[prefixTreeKnob.value]!); + ppGen: generatorMap[prefixTreeKnob.value]!); @override late final Map> knobs = UnmodifiableMapView({ diff --git a/test/arithmetic/addend_compressor_test.dart b/test/arithmetic/addend_compressor_test.dart index be2ebeee..b737e2b2 100644 --- a/test/arithmetic/addend_compressor_test.dart +++ b/test/arithmetic/addend_compressor_test.dart @@ -11,6 +11,9 @@ import 'dart:io'; import 'dart:math'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_hcl/src/arithmetic/evaluate_compressor.dart'; +import 'package:rohd_hcl/src/arithmetic/evaluate_partial_product.dart'; +import 'package:rohd_hcl/src/arithmetic/partial_product_test_sign_extend.dart'; import 'package:test/test.dart'; void testCompressionExhaustive(PartialProductGenerator pp) { @@ -60,7 +63,7 @@ void testCompressionExhaustive(PartialProductGenerator pp) { final a = compressor.extractRow(0); final b = compressor.extractRow(1); - final adder = ParallelPrefixAdder(a, b, KoggeStone.new); + final adder = ParallelPrefixAdder(a, b); final adderValue = adder.sum.value.toBigInt().toSigned(compressor.columns.length); expect(adderValue, equals(product), @@ -77,7 +80,7 @@ void main() { stdout.write('\n'); for (final signed in [false, true]) { - for (var radix = 2; radix < 4; radix *= 2) { + for (var radix = 2; radix < 8; radix *= 2) { final encoder = RadixEncoder(radix); // stdout.write('encoding with radix=$radix\n'); final shift = log2Ceil(encoder.radix); @@ -86,9 +89,10 @@ void main() { if (signExtension == SignExtension.none) { continue; } - final pp = PartialProductGenerator(Logic(name: 'X', width: width), + final ppg = curryPartialProductGenerator(signExtension); + final pp = ppg(Logic(name: 'X', width: width), Logic(name: 'Y', width: width), encoder, - signed: signed, signExtension: signExtension); + signed: signed); testCompressionExhaustive(pp); } @@ -117,7 +121,8 @@ void main() { b.put(bB); const radix = 2; final encoder = RadixEncoder(radix); - final pp = PartialProductGenerator(a, b, encoder, signed: signed); + final pp = CompactRectSignExtendPartialProductGenerator(a, b, encoder, + signed: signed); expect(pp.evaluate(), equals(BigInt.from(av * bv))); final compressor = ColumnCompressor(pp); expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); @@ -148,7 +153,8 @@ void main() { const radix = 2; final encoder = RadixEncoder(radix); - final pp = PartialProductGenerator(a, b, encoder, signed: signed); + final pp = CompactRectSignExtendPartialProductGenerator(a, b, encoder, + signed: signed); expect(pp.evaluate(), equals(BigInt.from(av * bv))); final compressor = ColumnCompressor(pp); expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); @@ -179,7 +185,8 @@ void main() { const radix = 8; final encoder = RadixEncoder(radix); - final pp = PartialProductGenerator(a, b, encoder, signed: signed); + final pp = CompactRectSignExtendPartialProductGenerator(a, b, encoder, + signed: signed); expect(pp.evaluate(), equals(BigInt.from(av * bv))); final compressor = ColumnCompressor(pp); expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); diff --git a/test/arithmetic/adder_test.dart b/test/arithmetic/adder_test.dart index 0288a8ab..0b1b9bbe 100644 --- a/test/arithmetic/adder_test.dart +++ b/test/arithmetic/adder_test.dart @@ -193,14 +193,15 @@ void main() { for (final n in [64, 64, 65]) { testAdderRandom(n, 30, RippleCarryAdder.new); for (final ppGen in generators) { - testAdderRandom(n, 30, (a, b) => ParallelPrefixAdder(a, b, ppGen)); + testAdderRandom( + n, 30, (a, b) => ParallelPrefixAdder(a, b, ppGen: ppGen)); } } }); group('exhaustive', () { testExhaustive(4, RippleCarryAdder.new); for (final ppGen in generators) { - testExhaustive(4, (a, b) => ParallelPrefixAdder(a, b, ppGen)); + testExhaustive(4, (a, b) => ParallelPrefixAdder(a, b, ppGen: ppGen)); } }); group('SignMagnitude random', () { @@ -208,8 +209,9 @@ void main() { testRandomSignMagnitude(4, 30, RippleCarryAdder.new); testRandomSignMagnitude(4, 30, RippleCarryAdder.new, sortOperands: false); testRandomSignMagnitude( - 4, 30, (a, b) => ParallelPrefixAdder(a, b, ppGen)); - testRandomSignMagnitude(4, 30, (a, b) => ParallelPrefixAdder(a, b, ppGen), + 4, 30, (a, b) => ParallelPrefixAdder(a, b, ppGen: ppGen)); + testRandomSignMagnitude( + 4, 30, (a, b) => ParallelPrefixAdder(a, b, ppGen: ppGen), sortOperands: false); } }); @@ -218,8 +220,9 @@ void main() { testExhaustiveSignMagnitude(4, RippleCarryAdder.new); testExhaustiveSignMagnitude(4, RippleCarryAdder.new, sortOperands: false); testExhaustiveSignMagnitude( - 4, (a, b) => ParallelPrefixAdder(a, b, ppGen)); - testExhaustiveSignMagnitude(4, (a, b) => ParallelPrefixAdder(a, b, ppGen), + 4, (a, b) => ParallelPrefixAdder(a, b, ppGen: ppGen)); + testExhaustiveSignMagnitude( + 4, (a, b) => ParallelPrefixAdder(a, b, ppGen: ppGen), sortOperands: false); } }); @@ -231,7 +234,7 @@ void main() { a.put(18); b.put(24); - final adder = ParallelPrefixAdder(a, b, BrentKung.new); + final adder = ParallelPrefixAdder(a, b, ppGen: BrentKung.new); final sum = adder.sum; expect(sum.value.toBigInt(), equals(BigInt.from(18 + 24))); @@ -243,15 +246,93 @@ void main() { final bSign = Logic(name: 'bSign'); final b = Logic(name: 'b', width: width); - aSign.put(1); + aSign.put(0); a.put(24); + bSign.put(1); b.put(18); - bSign.put(0); final adder = SignMagnitudeAdder(aSign, a, bSign, b, RippleCarryAdder.new, largestMagnitudeFirst: true); final sum = adder.sum; expect(sum.value.toBigInt(), equals(BigInt.from(24 - 18))); + aSign.put(1); + a.put(24); + bSign.put(0); + b.put(18); + + expect(-sum.value.toBigInt(), equals(BigInt.from(18 - 24))); + }); + + test('ones complement with boolean subtract', () { + const width = 2; + final a = Logic(width: width); + final b = Logic(width: width); + + for (final subtract in [false, true]) { + for (var av = 0; av < pow(2, width); av++) { + for (var bv = 0; bv < pow(2, width); bv++) { + a.put(av); + b.put(bv); + final carry = Logic(); + final adder = OnesComplementAdder( + a, b, null, carry, ParallelPrefixAdder.new, + subtract: subtract); + final mag = adder.sum.value.toInt() + + (subtract ? (carry.value.isZero ? 0 : 1) : 0); + final out = (adder.sign.value.toInt() == 1 ? -mag : mag); + + final expected = [if (subtract) av - bv else av + bv].first; + expect(out, equals(expected)); + } + } + } + }); + + test('ones complement with Logic subtract', () { + const width = 2; + final a = Logic(width: width); + final b = Logic(width: width); + + for (final subtractIn in [Const(0), Const(1)]) { + for (var av = 0; av < pow(2, width); av++) { + for (var bv = 0; bv < pow(2, width); bv++) { + a.put(av); + b.put(bv); + final carry = Logic(); + final adder = OnesComplementAdder( + a, b, subtractIn, carry, RippleCarryAdder.new); + final mag = adder.sum.value.toInt() + + (subtractIn.value == LogicValue.one + ? (carry.value.isZero ? 0 : 1) + : 0); + final out = (adder.sign.value.toInt() == 1 ? -mag : mag); + + final expected = [ + if (subtractIn.value == LogicValue.one) av - bv else av + bv + ].first; + expect(out, equals(expected)); + } + } + } + }); + test('trivial sign magnitude with onescomplement adder test', () async { + const width = 8; + final aSign = Logic(name: 'aSign'); + final a = Logic(name: 'a', width: width); + final bSign = Logic(name: 'bSign'); + final b = Logic(name: 'b', width: width); + + aSign.put(1); + a.put(24); + b.put(6); + bSign.put(0); + + final adder = OnesComplementAdder(a, b, null, null, RippleCarryAdder.new, + subtract: true); + + final sum = adder.sum; + // print('${adder.sign.value.toInt()} ${sum.value.toInt()}'); + expect(-sum.value.toBigInt(), equals(BigInt.from(6 - 24))); }); } diff --git a/test/arithmetic/multiplier_encoder_test.dart b/test/arithmetic/multiplier_encoder_test.dart index f2380772..9b98c1f1 100644 --- a/test/arithmetic/multiplier_encoder_test.dart +++ b/test/arithmetic/multiplier_encoder_test.dart @@ -12,6 +12,8 @@ import 'dart:math'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_hcl/src/arithmetic/evaluate_partial_product.dart'; +import 'package:rohd_hcl/src/arithmetic/partial_product_test_sign_extend.dart'; import 'package:test/test.dart'; void checkEvaluateExhaustive(PartialProductGenerator pp) { @@ -71,7 +73,9 @@ void main() { logicX.put(X); logicY.put(Y); logicZ.put(Z); - final pp = PartialProductGenerator(logicX, logicY, encoder, signed: true); + final pp = CompactRectSignExtendPartialProductGenerator( + logicX, logicY, encoder, + signed: true); final lastLength = pp.partialProducts[pp.rows - 1].length + pp.rowShift[pp.rows - 1]; @@ -120,9 +124,10 @@ void main() { if (signExtension == SignExtension.none) { continue; } - final pp = PartialProductGenerator(Logic(name: 'X', width: width), + final ppg = curryPartialProductGenerator(signExtension); + final pp = ppg(Logic(name: 'X', width: width), Logic(name: 'Y', width: width), encoder, - signed: signed, signExtension: signExtension); + signed: signed); checkEvaluateExhaustive(pp); } @@ -145,9 +150,10 @@ void main() { SignExtension.stop, SignExtension.compactRect ]) { - final pp = PartialProductGenerator(Logic(name: 'X', width: width), + final ppg = curryPartialProductGenerator(signExtension); + final pp = ppg(Logic(name: 'X', width: width), Logic(name: 'Y', width: width + skew), encoder, - signed: signed, signExtension: signExtension); + signed: signed); checkEvaluateRandom(pp, 20); } } @@ -159,14 +165,15 @@ void main() { test('Rectangle Q collision tests,', () async { // These collide with the normal q extension bits // These are unsigned tests - final alignTest = >[] - ..insert(0, [(2, 5, 0), (4, 7, 1), (8, 7, 2), (16, 9, 3)]) - ..insert(1, [(2, 5, 1), (4, 6, 2), (8, 9, 3), (16, 8, 4)]) - ..insert(2, [(2, 5, 2), (4, 7, 3), (8, 8, 4), (16, 11, 5)]) - ..insert(3, [(4, 6, 4), (8, 7, 5), (16, 10, 6)]) - ..insert(4, [(8, 9, 6), (16, 9, 7)]) - ..insert(5, [(16, 8, 8)]); + final alignTest = [ + [(2, 5, 0), (4, 7, 1), (8, 7, 2), (16, 9, 3)], + [(2, 5, 1), (4, 6, 2), (8, 9, 3), (16, 8, 4)], + [(2, 5, 2), (4, 7, 3), (8, 8, 4), (16, 11, 5)], + [(4, 6, 4), (8, 7, 5), (16, 10, 6)], + [(8, 9, 6), (16, 9, 7)], + [(16, 8, 8)], + ]; for (final alignList in alignTest) { for (final align in alignList) { final radix = align.$1; @@ -178,8 +185,10 @@ void main() { final Y = BigInt.from(2060).toUnsigned(width + skew); final product = X * Y; - final pp = PartialProductGenerator(Logic(name: 'X', width: width), - Logic(name: 'Y', width: width + skew), encoder, + final pp = CompactRectSignExtendPartialProductGenerator( + Logic(name: 'X', width: width), + Logic(name: 'Y', width: width + skew), + encoder, signed: false); pp.multiplicand.put(X); @@ -203,9 +212,10 @@ void main() { if (signExtension == SignExtension.none) { continue; } - final pp = PartialProductGenerator(Logic(name: 'X', width: width), + final ppg = curryPartialProductGenerator(signExtension); + final pp = ppg(Logic(name: 'X', width: width), Logic(name: 'Y', width: width + skew), encoder, - signed: signed, signExtension: signExtension); + signed: signed); checkEvaluateRandom(pp, 100); } } @@ -226,9 +236,10 @@ void main() { SignExtension.compactRect ]) { { - final pp = PartialProductGenerator(Logic(name: 'X', width: width), + final ppg = curryPartialProductGenerator(signExtension); + final pp = ppg(Logic(name: 'X', width: width), Logic(name: 'Y', width: width + skew), encoder, - signed: signed, signExtension: signExtension); + signed: signed); checkEvaluateExhaustive(pp); } } diff --git a/test/arithmetic/multiplier_test.dart b/test/arithmetic/multiplier_test.dart index bb8e7a38..3eef9a35 100644 --- a/test/arithmetic/multiplier_test.dart +++ b/test/arithmetic/multiplier_test.dart @@ -108,7 +108,8 @@ void main() { ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppTree, {required bool signed}) => - (a, b) => CompressionTreeMultiplier(a, b, radix, ppTree, signed: signed); + (a, b) => CompressionTreeMultiplier(a, b, radix, + ppTree: ppTree, signed: signed); MultiplyAccumulateCallback curryMultiplierAsMultiplyAccumulate( int radix, @@ -123,8 +124,8 @@ void main() { ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppTree, {required bool signed}) => - (a, b, c) => CompressionTreeMultiplyAccumulate(a, b, c, radix, ppTree, - signed: signed); + (a, b, c) => CompressionTreeMultiplyAccumulate(a, b, c, radix, + ppTree: ppTree, signed: signed); group('Curried Test of Compression Tree Multiplier', () { for (final signed in [false, true]) { @@ -180,8 +181,7 @@ void main() { b.put(bB); c.put(bC); - final mod = CompressionTreeMultiplyAccumulate(a, b, c, 4, KoggeStone.new, - signed: signed); + final mod = CompressionTreeMultiplyAccumulate(a, b, c, 4, signed: signed); checkMultiplyAccumulate(mod, bA, bB, bC); } }); @@ -212,8 +212,7 @@ void main() { b.put(bB); c.put(bC); - final mod = CompressionTreeMultiplyAccumulate(a, b, c, 4, KoggeStone.new, - signed: signed); + final mod = CompressionTreeMultiplyAccumulate(a, b, c, 4, signed: signed); checkMultiplyAccumulate(mod, bA, bB, bC); } }); @@ -229,9 +228,8 @@ void main() { b.put(3); c.put(5); - final multiplier = CompressionTreeMultiplyAccumulate( - a, b, c, radix, KoggeStone.new, - signed: true); + final multiplier = + CompressionTreeMultiplyAccumulate(a, b, c, radix, signed: true); final accumulate = multiplier.accumulate; expect(accumulate.value.toBigInt(), equals(BigInt.from(15 * 3 + 5))); }); diff --git a/test/arithmetic/parallel_prefix_operations_test.dart b/test/arithmetic/parallel_prefix_operations_test.dart index 2d82416d..68857423 100644 --- a/test/arithmetic/parallel_prefix_operations_test.dart +++ b/test/arithmetic/parallel_prefix_operations_test.dart @@ -157,7 +157,7 @@ void main() { group('or_scan', () { for (final n in [7, 8, 9]) { for (final ppGen in generators) { - testOrScan(n, (inp) => ParallelPrefixOrScan(inp, ppGen)); + testOrScan(n, (inp) => ParallelPrefixOrScan(inp, ppGen: ppGen)); } } }); @@ -166,7 +166,7 @@ void main() { for (final n in [7, 8, 9]) { for (final ppGen in generators) { testPriorityFinder( - n, (inp) => ParallelPrefixPriorityFinder(inp, ppGen)); + n, (inp) => ParallelPrefixPriorityFinder(inp, ppGen: ppGen)); } } }); @@ -175,7 +175,7 @@ void main() { for (final n in [7, 8, 9]) { for (final ppGen in generators) { testPriorityEncoder( - n, (inp) => ParallelPrefixPriorityEncoder(inp, ppGen)); + n, (inp) => ParallelPrefixPriorityEncoder(inp, ppGen: ppGen)); } } }); @@ -183,7 +183,7 @@ void main() { group('incr', () { for (final n in [7, 8, 9]) { for (final ppGen in generators) { - testIncr(n, (inp) => ParallelPrefixIncr(inp, ppGen)); + testIncr(n, (inp) => ParallelPrefixIncr(inp, ppGen: ppGen)); } } }); @@ -191,7 +191,7 @@ void main() { group('decr', () { for (final n in [7, 8, 9]) { for (final ppGen in generators) { - testDecr(n, (inp) => ParallelPrefixDecr(inp, ppGen)); + testDecr(n, (inp) => ParallelPrefixDecr(inp, ppGen: ppGen)); } } }); From 029c48024a1948a54bae3ca76fb248283e1d948c Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Sat, 31 Aug 2024 20:06:28 -0700 Subject: [PATCH 31/38] simplify doc for multiplier --- doc/components/multiplier.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/doc/components/multiplier.md b/doc/components/multiplier.md index c7716962..bc436e3c 100644 --- a/doc/components/multiplier.md +++ b/doc/components/multiplier.md @@ -1,6 +1,6 @@ # Multiplier -ROHD HCL provides an abstract `Multiplier` module which multiplies two +ROHD-HCL provides an abstract `Multiplier` module which multiplies two numbers represented as two `Logic`s, potentially of different widths, treating them as either signed (2s complement) or unsigned. It produces the product as a `Logic` with width equal to the sum of the @@ -82,9 +82,9 @@ digital signal processing. The parameters of the `CompressionTreeMultiplier` are: -- Two input terms a and b +- Two input terms `a` and `b` - The radix used for Booth encoding (2, 4, 8, and 16 are currently supported) -- The type of `ParallelPrefix` tree used in the final `ParallelPrefixAdder` +- The type of `ParallelPrefix` tree used in the final `ParallelPrefixAdder` (optional) - Whether the operands should be treated as signed (2s complement) or unsigned Here is an example of use of the `CompressionTreeMultiplier`: @@ -100,7 +100,7 @@ Here is an example of use of the `CompressionTreeMultiplier`: b.put(3); final multiplier = - CompressionTreeMultiplier(a, b, radix, KoggeStone.new, signed: true); + CompressionTreeMultiplier(a, b, radix, signed: true); final product = multiplier.product; @@ -119,7 +119,7 @@ The parameters of the - Two input terms a and b - The accumulate input term c - The radix used for Booth encoding (2, 4, 8, and 16 are currently supported) -- The type of `ParallelPrefix` tree used in the final `ParallelPrefixAdder` +- The type of `ParallelPrefix` tree used in the final `ParallelPrefixAdder` (optional) - Whether the operands should be treated as signed (2s complement) or unsigned Here is an example of using the `CompressionTreeMultiplyAccumulate`: @@ -137,8 +137,7 @@ Here is an example of using the `CompressionTreeMultiplyAccumulate`: c.put(5); final multiplier = CompressionTreeMultiplyAccumulate( - a, b, c, radix, KoggeStone.new, - signed: true); + a, b, c, radix, signed: true); final accumulate = multiplier.accumulate; From 79118f01657d2ebba0b0952e9ee83f69993d4bac Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Sun, 1 Sep 2024 09:53:55 -0700 Subject: [PATCH 32/38] cleaned up interfaces using optional named args adderGen and ppGen, doc cleanup, split ones_complement_adder to its own file --- doc/README.md | 2 +- doc/components/adder.md | 6 +- doc/components/arbiter.md | 2 +- doc/components/binary_gray.md | 2 +- doc/components/count.md | 4 +- doc/components/edge_detector.md | 2 +- doc/components/fifo.md | 4 +- doc/components/find.md | 4 +- doc/components/memory.md | 2 +- doc/components/multiplier.md | 2 +- doc/components/onehot.md | 6 +- doc/components/parallel_prefix_operations.md | 10 +-- doc/components/parity.md | 2 +- doc/components/ready_valid_bfm.md | 2 +- doc/components/rotate.md | 4 +- doc/components/shift_register.md | 2 +- doc/components/sort.md | 2 +- doc/components/standard_interfaces.md | 2 +- lib/src/arithmetic/arithmetic.dart | 1 + lib/src/arithmetic/ones_complement_adder.dart | 79 +++++++++++++++++++ lib/src/arithmetic/sign_magnitude_adder.dart | 68 +--------------- test/arithmetic/adder_test.dart | 15 ++-- 22 files changed, 120 insertions(+), 103 deletions(-) create mode 100644 lib/src/arithmetic/ones_complement_adder.dart diff --git a/doc/README.md b/doc/README.md index d3127cab..1ea46548 100644 --- a/doc/README.md +++ b/doc/README.md @@ -3,7 +3,7 @@ Below is a list of components grouped by category. Ones with links are documented and completed, while others are still in planning or development stages. -Some in-development items will have opened issues, as well. Feel free to create a pull request or file issues to add more ideas to this list. If you plan to develop and contribute a component, please be sure to open an issue so that there's not multiple people working on the same thing. Make sure to check if someone else has an open issue for a certain component before starting. +Some in-development items will have opened issues, as well. Feel free to create a pull request or file issues to add more ideas to this list. If you plan to develop and contribute a component, please be sure to open an issue so that there are not multiple people working on the same thing. Make sure to check if someone else has an open issue for a certain component before starting. - Encoders & Decoders - [1-hot to Binary](./components/onehot.md) diff --git a/doc/components/adder.md b/doc/components/adder.md index 51b9c94c..49af3cf2 100644 --- a/doc/components/adder.md +++ b/doc/components/adder.md @@ -40,7 +40,7 @@ Here is an example of instantiating a `ParallelPrefixAdder`: a.put(18); b.put(24); - final adder = ParallelPrefixAdder(a, b, BrentKung.new); + final adder = ParallelPrefixAdder(a, b, ppGen: BrentKung.new); final sum = adder.sum; @@ -67,7 +67,7 @@ Here is an example of instantiating a `OnesComplementAdder` as a subtractor, but b.put(bv); final carry = Logic(); final adder = OnesComplementAdder( - a, b, null, carry, RippleCarryAdder.new, + a, b, carryOut: carry, adderGen: RippleCarryAdder.new, subtract: true); final mag = adder.sum.value.toInt() + (carry.value.isZero ? 0 : 1)); final out = (adder.sign.value.toInt() == 1 ? -mag : mag); @@ -95,7 +95,7 @@ Here is an example of instantiating a `SignMagnitudeAdder`: b.put(18); bSign.put(0); - final adder = SignMagnitudeAdder(aSign, a, bSign, b, RippleCarryAdder.new, + final adder = SignMagnitudeAdder(aSign, a, bSign, b, adderGen: RippleCarryAdder.new, largestMagnitudeFirst: true); final sum = adder.sum; diff --git a/doc/components/arbiter.md b/doc/components/arbiter.md index 2bded9d9..f8ffb630 100644 --- a/doc/components/arbiter.md +++ b/doc/components/arbiter.md @@ -1,6 +1,6 @@ # Arbiters -ROHD HCL implements a generic `abstract` [`Arbiter`](https://intel.github.io/rohd-hcl/rohd_hcl/Arbiter-class.html) class that other arbiters can extend. It accepts a `List` of `requests`, where each request is a `1-bit` signal indicating that there is a request for a resource. The output `grants` is a `List` where each element corresponds to the request with the same index. The arbiter implementation decides how to select which request receives a grant. +ROHD-HCL implements a generic `abstract` [`Arbiter`](https://intel.github.io/rohd-hcl/rohd_hcl/Arbiter-class.html) class that other arbiters can extend. It accepts a `List` of `requests`, where each request is a `1-bit` signal indicating that there is a request for a resource. The output `grants` is a `List` where each element corresponds to the request with the same index. The arbiter implementation decides how to select which request receives a grant. ## Stateful Arbiter diff --git a/doc/components/binary_gray.md b/doc/components/binary_gray.md index 493921fc..5d334f3e 100644 --- a/doc/components/binary_gray.md +++ b/doc/components/binary_gray.md @@ -1,6 +1,6 @@ # Binary Gray -ROHD HCL provides a module to perform conversion on binary to gray and gray to binary. +ROHD-HCL provides a module to perform conversion on binary to gray and gray to binary. ## Binary-to-Gray diff --git a/doc/components/count.md b/doc/components/count.md index 1b56d85e..e77d43a0 100644 --- a/doc/components/count.md +++ b/doc/components/count.md @@ -1,6 +1,6 @@ # Count -ROHD HCL comes with a `Count` component. The detailed API docs are available [here](https://intel.github.io/rohd-hcl/rohd_hcl/rohd_hcl-library.html). +ROHD-HCL comes with a `Count` component. The detailed API docs are available [here](https://intel.github.io/rohd-hcl/rohd_hcl/rohd_hcl-library.html). A `Count` will count all one(`1`)/zero(`0`) within a given Logic `bus`. @@ -12,7 +12,7 @@ This will return a Logic value labeled as `countOne` for `1` and `countZero` for ## Count One -To count all ones just pass in the `bus` with `countOne` as `true`. By default countOne is `true`. +To count all ones just pass in the `bus` with `countOne` as `true`. By default, `countOne` is `true`. ## Count Zero diff --git a/doc/components/edge_detector.md b/doc/components/edge_detector.md index 7984bd98..f89fb913 100644 --- a/doc/components/edge_detector.md +++ b/doc/components/edge_detector.md @@ -1,3 +1,3 @@ # Edge Detection -The `EdgeDetector` is a simple utility to determine whether the current value of a 1-bit signal is different from the value in the previous cycle. It is a fully synchronous design, so it does not asynchronously detect edges. It optionally supports a reset, with an optional reset value. It can be configured to detect positive, negative, or "any" edges. +The `EdgeDetector` is a simple utility to determine whether the current value of a 1-bit signal is different from the value in the previous cycle. It is a fully synchronous design, so it does not asynchronously detect edges. It optionally supports a reset, with an optional reset value. Furthermore, it can be configured to detect positive, negative, or "any" edges. diff --git a/doc/components/fifo.md b/doc/components/fifo.md index ebd70049..dcade2d2 100644 --- a/doc/components/fifo.md +++ b/doc/components/fifo.md @@ -1,6 +1,6 @@ # FIFO -ROHD HCL comes with a simple FIFO (First In, First Out). The detailed API docs are available [here](https://intel.github.io/rohd-hcl/rohd_hcl/Fifo-class.html). +ROHD-HCL comes with a simple FIFO (First In, First Out). The detailed API docs are available [here](https://intel.github.io/rohd-hcl/rohd_hcl/Fifo-class.html). The underlying implementation uses a flop-based memory (see [`RegisterFile`](https://intel.github.io/rohd-hcl/rohd_hcl/RegisterFile-class.html)) to store data until it is ready to be popped, with independent read and write pointers. @@ -40,7 +40,7 @@ The FIFO comes with both a checker and a tracker that you can leverage in your t ### Checker -The `FifoChecker` is a ROHD-VF component which will watch for proper usge of a FIFO in your simulation. It is intended to check usage, not the internal workings of the FIFO, which are already pre-validated in the unit tests. This means it covers things like underflow, overflow, and that the FIFO is empty at the end of the test. +The `FifoChecker` is a ROHD-VF component which will watch for proper usage of a FIFO in your simulation. It is intended to check usage, not the internal workings of the FIFO, which are already pre-validated in the unit tests. This means it covers things like underflow, overflow, and that the FIFO is empty at the end of the test. ### Tracker diff --git a/doc/components/find.md b/doc/components/find.md index b71048ab..4c650ac6 100644 --- a/doc/components/find.md +++ b/doc/components/find.md @@ -1,6 +1,6 @@ # Find -ROHD HCL comes with a Find. The detailed API docs are available [here](https://intel.github.io/rohd-hcl/rohd_hcl/rohd_hcl-library.html). +ROHD-HCL comes with a Find. The detailed API docs are available [here](https://intel.github.io/rohd-hcl/rohd_hcl/rohd_hcl-library.html). A Find will search for first/nth occurrence of one(`1`)/zero(`0`) within a given Logic `bus`. The underlying implementation uses a `Count` to count 1's or 0's whenever a Logic `n` value @@ -10,7 +10,7 @@ It takes a Binary Logic `bus` and finds the position of any one or zero within t That is to say, By default a Find will go for finding the first occurrence when no `n` is passed. In addition, with `countOne` which is set as `true` by default to search only one (`1`). Both boolean `countOne` and Logic `n` are optional. Only Logic `bus` is mandatory argument. -This has an output pin named as `find`, for the index position on the occurrence searched (`1`s or `0`s) taken from the LSB (Least significant bit). +This has an output pin named as `find`, for the index position on the occurrence searched (`1`s or `0`s) taken from the LSB (Least Significant Bit). ## Find First diff --git a/doc/components/memory.md b/doc/components/memory.md index 45c19462..e1eae3ac 100644 --- a/doc/components/memory.md +++ b/doc/components/memory.md @@ -1,6 +1,6 @@ # Memory -ROHD HCL provides a generic `abstract` [`Memory`](https://intel.github.io/rohd-hcl/rohd_hcl/Memory-class.html) class which accepts a dynamic number of `writePorts` and `readPorts`, where each port is of type [`DataPortInterface`](https://intel.github.io/rohd-hcl/rohd_hcl/DataPortInterface-class.html). A `DataPortInterface` is a simple interface with `en` and `addr` as `control` signals and `data` signal(s). In a write interface, all signals are in the same direction. In a read interface, the `control` signals are in the opposite direction of the `data` signal(s). +ROHD-HCL provides a generic `abstract` [`Memory`](https://intel.github.io/rohd-hcl/rohd_hcl/Memory-class.html) class which accepts a dynamic number of `writePorts` and `readPorts`, where each port is of type [`DataPortInterface`](https://intel.github.io/rohd-hcl/rohd_hcl/DataPortInterface-class.html). A `DataPortInterface` is a simple interface with `en` and `addr` as `control` signals and `data` signal(s). In a write interface, all signals are in the same direction. In a read interface, the `control` signals are in the opposite direction of the `data` signal(s). ## Masks diff --git a/doc/components/multiplier.md b/doc/components/multiplier.md index bc436e3c..56cf1ac9 100644 --- a/doc/components/multiplier.md +++ b/doc/components/multiplier.md @@ -16,7 +16,7 @@ as two `Logic`s and adds the result to a third `Logic` with width equal to the sum of the widths of the main inputs. We have a high-performance implementation: -- [Compression Tree Multipy Accumulate](#compression-tree-multiply-accumulate) +- [Compression Tree Multiply Accumulate](#compression-tree-multiply-accumulate) The compression tree based arithmetic units are built from a set of components for Booth-encoding, column compression, and parallel prefix adders described in the [`Booth Encoding Multiplier Building Blocks`](./multiplier_components.md#booth-encoding-multiplier-building-blocks) section. diff --git a/doc/components/onehot.md b/doc/components/onehot.md index fa42abf4..bcd549cb 100644 --- a/doc/components/onehot.md +++ b/doc/components/onehot.md @@ -1,11 +1,11 @@ # One Hot Codecs -ROHD HCL implements a set of one hot encoder and decoders. +ROHD-HCL implements a set of one hot encoder and decoders. For example, we have an encoder [`BinaryToOneHot`](https://intel.github.io/rohd-hcl/rohd_hcl/BinaryToOneHot-class.html) class, and a couple of implementations of decoder classes like `CaseOneHotToBinary` and a more performant `TreeBinaryToOneHot`. The `OneHotToBinary` default constructor will select an implementation based on the width of the input. -The encoders take a Logic bitvector, with the constraint that only a single bit is set to '1' and outputs the bit position in binary. +The encoders take a Logic bit-vector, with the constraint that only a single bit is set to '1' and outputs the bit position in binary. -The decoders take a Logic input representing the bit position to be set to '1', and returns a Logic bitvector with that bit position set to '1', and all others set to '0' +The decoders take a Logic input representing the bit position to be set to '1', and returns a Logic bit-vector with that bit position set to '1', and all others set to '0' [OneHotToBinary Schematic](https://intel.github.io/rohd-hcl/CaseOneHotToBinary.html) diff --git a/doc/components/parallel_prefix_operations.md b/doc/components/parallel_prefix_operations.md index d80492dc..c69c2061 100644 --- a/doc/components/parallel_prefix_operations.md +++ b/doc/components/parallel_prefix_operations.md @@ -5,14 +5,14 @@ implementation of computations which involve associative operators. They are used in computations like encoding, or-reduction, and addition. By leveraging advanced programming idioms, like functors, allowing for passing of a function that generates prefix trees -into an scan-based generator for that computation, we can have a wide +into a scan-based generator for that computation, we can have a wide variety of that computation supported by our component library. For -example, we have tree patterns defined by ripple, Sklanksy, +example, we have tree patterns defined by ripple, Sklansky, Kogge-Stone, and Brent-Kung which gives us those four varieties of prefix reduction trees we can use across addition, or-reduction, and priority encoding. -ROHD HCL implements a set of parallel prefix compute operations using +ROHD-HCL implements a set of parallel prefix compute operations using different parallel prefix computation trees based on the ['ParallelPrefix'](https://intel.github.io/rohd-hcl/rohd_hcl/ParallelPrefix-class.html) node class. @@ -38,8 +38,8 @@ types: - ['Ripple'](https://intel.github.io/rohd-hcl/rohd_hcl/Ripple-class.html) - ['Sklansky](https://intel.github.io/rohd-hcl/rohd_hcl/Sklansky-class.html) -- ['KoggeStone'](https://intel.github.io/rohd-hcl/rohd_hcl/KoggeStone-class.html) -- ['BrentKung](https://intel.github.io/rohd-hcl/rohd_hcl/BrentKung-class.html) +- ['Kogge-Stone'](https://intel.github.io/rohd-hcl/rohd_hcl/KoggeStone-class.html) +- ['Brent-Kung](https://intel.github.io/rohd-hcl/rohd_hcl/BrentKung-class.html) Here is an example adder schematic: [ParallelPrefixAdder Schematic](https://intel.github.io/rohd-hcl/ParallelPrefixAdder.html) diff --git a/doc/components/parity.md b/doc/components/parity.md index 27fe9e28..08d7b4b1 100644 --- a/doc/components/parity.md +++ b/doc/components/parity.md @@ -1,6 +1,6 @@ # Parity -ROHD HCL implements a `Parity` component for error checking. For satisfying the functionality of Parity error checking in `Logic` data, ROHD HCL provides 2 `Module`, namely `ParityTransmitter` and `ParityReceiver`. +ROHD-HCL implements a `Parity` component for error checking. For satisfying the functionality of Parity error checking in `Logic` data, ROHD-HCL provides 2 `Module`, namely `ParityTransmitter` and `ParityReceiver`. ## Parity Transmitter diff --git a/doc/components/ready_valid_bfm.md b/doc/components/ready_valid_bfm.md index 74e3ebc9..26a3db76 100644 --- a/doc/components/ready_valid_bfm.md +++ b/doc/components/ready_valid_bfm.md @@ -6,7 +6,7 @@ The Ready/Valid BFM is a collection of [ROHD-VF](https://github.com/intel/rohd-v - When a receiver is able to accept something, it raises `ready`. - When both `valid` and `ready` are high, the transaction is accepted by both sides. -The main two components are the `ReadyValidTransmitterAgent` and `ReadyValidReceiverAgent`, which transmit and receive `data`, respectively. Any bundle of information can be mapped onto the `data` bus. Both agents provide a `blockRate` argument which controls a random weighted chance of preventing a transaction from occuring (either delaying a `valid` or dropping a `ready`). +The main two components are the `ReadyValidTransmitterAgent` and `ReadyValidReceiverAgent`, which transmit and receive `data`, respectively. Any bundle of information can be mapped onto the `data` bus. Both agents provide a `blockRate` argument which controls a random weighted chance of preventing a transaction from occurring (either delaying a `valid` or dropping a `ready`). Additionally, the `ReadyValidMonitor` can be placed on any ready/valid protocol to observe transactions that are accepted. The resulting `ReadyValidPacket`s can also be logged via the `ReadyValidTracker`. diff --git a/doc/components/rotate.md b/doc/components/rotate.md index 28f93167..84d10008 100644 --- a/doc/components/rotate.md +++ b/doc/components/rotate.md @@ -1,6 +1,6 @@ # Rotate -ROHD HCL comes with a variety of utilities for performing rotations across +ROHD-HCL comes with a variety of utilities for performing rotations across - Direction (Left and Right) - Amount (Dynamic and Fixed), and @@ -38,7 +38,7 @@ A "non-fixed" version is implemented as a case statement with a static swizzle p ### Module Types -Rotations is also accessible via `Module` construction instead of `extension`s, if preferred. +Rotations are also accessible via `Module` construction instead of `extension`s, if preferred. If you want to rotate a signal by a fixed amount, you can use the "fixed" rotation modules: [`RotateLeftFixed`](https://intel.github.io/rohd-hcl/rohd_hcl/RotateLeftFixed-class.html) and [`RotateRightFixed`](https://intel.github.io/rohd-hcl/rohd_hcl/RotateRightFixed-class.html). diff --git a/doc/components/shift_register.md b/doc/components/shift_register.md index f4df3de0..634d8ff9 100644 --- a/doc/components/shift_register.md +++ b/doc/components/shift_register.md @@ -1,6 +1,6 @@ # Shift Register -The `ShiftRegister` in ROHD HCL is a configurable shift register including: +The `ShiftRegister` in ROHD-HCL is a configurable shift register including: - support for any width data - a configurable `depth` (which corresponds to the latency) diff --git a/doc/components/sort.md b/doc/components/sort.md index 9abec28e..e1536415 100644 --- a/doc/components/sort.md +++ b/doc/components/sort.md @@ -1,6 +1,6 @@ # Sort -ROHD HCL provides a component to perform sort of a list of Logic. As of now, we have +ROHD-HCL provides a component to perform sort of a list of Logic. As of now, we have - [Bitonic Sort](#bitonic-sort) diff --git a/doc/components/standard_interfaces.md b/doc/components/standard_interfaces.md index acf807cb..8bb3ddb4 100644 --- a/doc/components/standard_interfaces.md +++ b/doc/components/standard_interfaces.md @@ -1,6 +1,6 @@ # Standard Interfaces -ROHD HCL provides a set of standard interfaces using ROHD `Interface`s. This makes it easy to instantiate and connect common interfaces in a configurable way. +ROHD-HCL provides a set of standard interfaces using ROHD `Interface`s. This makes it easy to instantiate and connect common interfaces in a configurable way. ## APB diff --git a/lib/src/arithmetic/arithmetic.dart b/lib/src/arithmetic/arithmetic.dart index 38ed18fe..ce80df0c 100644 --- a/lib/src/arithmetic/arithmetic.dart +++ b/lib/src/arithmetic/arithmetic.dart @@ -4,6 +4,7 @@ export 'carry_save_mutiplier.dart'; export 'multiplier.dart'; export 'multiplier_lib.dart'; +export 'ones_complement_adder.dart'; export 'parallel_prefix_operations.dart'; export 'ripple_carry_adder.dart'; export 'sign_magnitude_adder.dart'; diff --git a/lib/src/arithmetic/ones_complement_adder.dart b/lib/src/arithmetic/ones_complement_adder.dart new file mode 100644 index 00000000..a9ea4905 --- /dev/null +++ b/lib/src/arithmetic/ones_complement_adder.dart @@ -0,0 +1,79 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// sign_magnitude_adder.dart +// Implementation of a One's Complement Adder +// +// 2024 August 31 +// Author: Desmond Kirkpatrick + +import 'package:meta/meta.dart'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// An adder (and subtractor) [OnesComplementAdder] that operates on +/// ones-complement values. +class OnesComplementAdder extends Adder { + /// The sign of the result + Logic get sign => output('sign'); + + /// The end-around carry which should be added to the resulting [sum] + /// If the input [carryOut] is not null, this value is stored there. + /// Otherwise, the end-around carry is internally added to [sum] + Logic? get carryOut => tryOutput('carryOut'); + + @protected + Logic _sign = Logic(); + + /// [OnesComplementAdder] constructor with an adder functor [adderGen] + /// Either a Logic [subtractIn] or a boolean [subtract] can enable + /// subtraction, with [subtractIn] overriding [subtract]. If Logic [carryOut] + /// is provided as not null, then the end-around carry is not performed and is + /// left to the caller via the output [carryOut]. + OnesComplementAdder(super.a, super.b, + {Adder Function(Logic, Logic) adderGen = ParallelPrefixAdder.new, + Logic? subtractIn, + Logic? carryOut, + bool subtract = false}) + : super( + name: 'Ones Complement Adder: ' + '${adderGen.call(Logic(), Logic()).name}') { + if (subtractIn != null) { + subtractIn = addInput('subtractIn', subtractIn); + } + _sign = addOutput('sign'); + if (carryOut != null) { + addOutput('carryOut'); + carryOut <= this.carryOut!; + } + if ((subtractIn != null) & subtract) { + throw RohdHclException( + 'Subtraction is controlled by a non-null subtractIn: ' + 'subtract boolean is ignored'); + } + final doSubtract = subtractIn ?? (subtract ? Const(1) : Const(0)); + + final ax = a.zeroExtend(a.width); + final bx = b.zeroExtend(b.width); + + final adder = adderGen(ax, mux(doSubtract, ~bx, bx)); + + if (this.carryOut != null) { + this.carryOut! <= adder.sum[-1]; + } + final endAround = mux(doSubtract, adder.sum[-1], Const(0)); + final magnitude = adder.sum.slice(a.width - 1, 0); + + sum <= + mux( + doSubtract, + mux( + endAround, + [if (this.carryOut != null) magnitude else magnitude + 1] + .first, + ~magnitude) + .zeroExtend(sum.width), + adder.sum); + _sign <= mux(doSubtract, ~endAround, Const(0)); + } +} diff --git a/lib/src/arithmetic/sign_magnitude_adder.dart b/lib/src/arithmetic/sign_magnitude_adder.dart index f0efa103..008dcd98 100644 --- a/lib/src/arithmetic/sign_magnitude_adder.dart +++ b/lib/src/arithmetic/sign_magnitude_adder.dart @@ -48,72 +48,8 @@ class SignMagnitudeAdder extends Adder { final bLarger = a.lt(b) | (a.eq(b) & bSign.gt(aSign)); _sign <= (largestMagnitudeFirst ? aSign : mux(bLarger, bSign, aSign)); - final adder = OnesComplementAdder(a, b, aSign ^ bSign, null, adderGen); + final adder = OnesComplementAdder(a, b, + subtractIn: aSign ^ bSign, adderGen: adderGen); sum <= adder.sum; } } - -/// An adder (and subtractor) [OnesComplementAdder] that operates on -/// ones-complement values. -class OnesComplementAdder extends Adder { - /// The sign of the result - Logic get sign => output('sign'); - - /// The end-around carry which should be added to the resulting [sum] - /// If the input [carry] is not null, this value is stored there. Otherwise, - /// the end-around carry is internally added to [sum] - Logic? get carry => tryOutput('carry'); - - @protected - Logic _sign = Logic(); - - /// [OnesComplementAdder] constructor with an adder functor [adderGen] - /// Either a Logic [subtractIn] or a boolean [subtract] can enable - /// subtraction, with [subtractIn] overriding [subtract]. If Logic [carry] - /// is provided as not null, then the end-around carry is not performed and is - /// left to the caller via the output [carry]. - OnesComplementAdder(super.a, super.b, Logic? subtractIn, Logic? carry, - Adder Function(Logic, Logic) adderGen, - {bool subtract = false}) - : super( - name: 'Ones Complement Adder: ' - '${adderGen.call(Logic(), Logic()).name}') { - if (subtractIn != null) { - subtractIn = addInput('subtractIn', subtractIn); - } - _sign = addOutput('sign'); - if (carry != null) { - addOutput('carry'); - carry <= this.carry!; - } - if ((subtractIn != null) & subtract) { - throw RohdHclException( - 'Subtraction is controlled by a non-null subtractIn: ' - 'subtract boolean is ignored'); - } - final doSubtract = subtractIn ?? (subtract ? Const(1) : Const(0)); - - final ax = a.zeroExtend(a.width); - final bx = b.zeroExtend(b.width); - - final adder = adderGen(ax, mux(doSubtract, ~bx, bx)); - - if (this.carry != null) { - this.carry! <= adder.sum[-1]; - } - final endAround = mux(doSubtract, adder.sum[-1], Const(0)); - final magnitude = adder.sum.slice(a.width - 1, 0); - - sum <= - mux( - doSubtract, - mux( - endAround, - [if (this.carry != null) magnitude else magnitude + 1] - .first, - ~magnitude) - .zeroExtend(sum.width), - adder.sum); - _sign <= mux(doSubtract, ~endAround, Const(0)); - } -} diff --git a/test/arithmetic/adder_test.dart b/test/arithmetic/adder_test.dart index 0b1b9bbe..5450ce87 100644 --- a/test/arithmetic/adder_test.dart +++ b/test/arithmetic/adder_test.dart @@ -275,9 +275,8 @@ void main() { a.put(av); b.put(bv); final carry = Logic(); - final adder = OnesComplementAdder( - a, b, null, carry, ParallelPrefixAdder.new, - subtract: subtract); + final adder = + OnesComplementAdder(a, b, carryOut: carry, subtract: subtract); final mag = adder.sum.value.toInt() + (subtract ? (carry.value.isZero ? 0 : 1) : 0); final out = (adder.sign.value.toInt() == 1 ? -mag : mag); @@ -300,8 +299,10 @@ void main() { a.put(av); b.put(bv); final carry = Logic(); - final adder = OnesComplementAdder( - a, b, subtractIn, carry, RippleCarryAdder.new); + final adder = OnesComplementAdder(a, b, + subtractIn: subtractIn, + carryOut: carry, + adderGen: RippleCarryAdder.new); final mag = adder.sum.value.toInt() + (subtractIn.value == LogicValue.one ? (carry.value.isZero ? 0 : 1) @@ -328,8 +329,8 @@ void main() { b.put(6); bSign.put(0); - final adder = OnesComplementAdder(a, b, null, null, RippleCarryAdder.new, - subtract: true); + final adder = OnesComplementAdder(a, b, + adderGen: RippleCarryAdder.new, subtract: true); final sum = adder.sum; // print('${adder.sign.value.toInt()} ${sum.value.toInt()}'); From b88fda8361586b2150ea41ea2bfa75cb0acc77d5 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Wed, 4 Sep 2024 15:04:22 -0700 Subject: [PATCH 33/38] Hid ColumnCompression code, exposed AddendCompressor. General cleanup of comments, file headers. --- doc/components/multiplier_components.md | 11 +- lib/rohd_hcl.dart | 1 - lib/src/arithmetic/addend_compressor.dart | 119 ++++++++++++++++-- lib/src/arithmetic/arithmetic.dart | 1 + lib/src/arithmetic/evaluate_compressor.dart | 78 ------------ lib/src/arithmetic/multiplier.dart | 28 ++--- lib/src/arithmetic/ones_complement_adder.dart | 2 +- .../parallel_prefix_operations.dart | 2 - .../arithmetic/partial_product_generator.dart | 27 ++-- .../partial_product_test_sign_extend.dart | 55 ++++---- test/arithmetic/addend_compressor_test.dart | 38 +++--- test/arithmetic/adder_test.dart | 22 ++-- test/arithmetic/multiplier_encoder_test.dart | 4 +- .../parallel_prefix_operations_test.dart | 2 +- 14 files changed, 201 insertions(+), 189 deletions(-) delete mode 100644 lib/src/arithmetic/evaluate_compressor.dart diff --git a/doc/components/multiplier_components.md b/doc/components/multiplier_components.md index 2c619da6..439f4eb2 100644 --- a/doc/components/multiplier_components.md +++ b/doc/components/multiplier_components.md @@ -184,19 +184,18 @@ Any adder can be used as the final adder of the final two addends produced from Here is a code snippet that shows how these components can be used to create a multiplier. -First the partial product generator is used, which we pass in the `RadixEncoder`, whether the operands are signed, and the kind of sign extension to use on the partial products. +First the partial product generator is used, which has compact sign extension for rectangular products (`PartialProductGeneratorCompactRectSignExtension`) which we pass in the `RadixEncoder`, whether the operands are signed, and the kind of sign extension to use on the partial products. Note that sign extension is needed regardless of whether operands are signed or not due to Booth encoding. -Next, we use the `ColumnCompressor` to compress the partial products into two final addends. +Next, we use the `AddendCompressor` to compress the partial products into two final addends. We then choose a `ParallelPrefixAdder` using the `BrentKung` tree style to do the addition. We pass in the two extracted rows of the compressor. Finally, we produce the product. ```dart final pp = - PartialProductGenerator(a, b, RadixEncoder(radix), signed: true, - signExtension: SignExtension.compactRect); - final compressor = ColumnCompressor(pp)..compress(); + PartialProductGeneratorCompactRectSignExtension(a, b, RadixEncoder(radix), signed: true); + final compressor = AddendCompressor(pp); final adder = ParallelPrefixAdder( - compressor.extractRow(0), compressor.extractRow(1), BrentKung.new); + compressor.sum.elements.first, compressor.sum.elements.last, BrentKung.new); product <= adder.sum.slice(a.width + b.width - 1, 0); ``` diff --git a/lib/rohd_hcl.dart b/lib/rohd_hcl.dart index 4b642d8d..9b147023 100644 --- a/lib/rohd_hcl.dart +++ b/lib/rohd_hcl.dart @@ -2,7 +2,6 @@ // SPDX-License-Identifier: BSD-3-Clause export 'src/arbiters/arbiters.dart'; -export 'src/arithmetic/adder.dart'; export 'src/arithmetic/arithmetic.dart'; export 'src/binary_gray.dart'; export 'src/component_config/component_config.dart'; diff --git a/lib/src/arithmetic/addend_compressor.dart b/lib/src/arithmetic/addend_compressor.dart index 9fa1f451..6a17b728 100644 --- a/lib/src/arithmetic/addend_compressor.dart +++ b/lib/src/arithmetic/addend_compressor.dart @@ -7,13 +7,51 @@ // 2024 June 04 // Author: Desmond Kirkpatrick +import 'dart:io'; + import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; +import 'package:rohd_hcl/src/utils.dart'; + +/// A simple full-adder with inputs `a` and `b` to be added with a `carryIn`. +class AddendCompressor extends Module { + /// The addition's result [sum]. + LogicArray get sum => output('sums') as LogicArray; + + /// Return the maximum [width] of the addends + int get width => _pp.maxWidth(); + + late final PartialProductGenerator _pp; + late final _ColumnCompressor _cc; -/// Base class for column compressor function -abstract class AddendCompressor extends Module { + /// Construct an addend compressor module from a PartialProductGenerator + AddendCompressor(this._pp, {bool doCompress = true}) { + _cc = _ColumnCompressor(_pp); + + addOutputArray('sums', dimensions: [2], elementWidth: width); + if (doCompress) { + _cc.compress(); + } + + sum <= [_cc.extractRow(0), _cc.extractRow(1)].swizzle(); + } + + /// Compress the columns down to two rows + void compress() => _cc.compress(); + + /// evaluate the value of the compressor, used for live debug + (BigInt, StringBuffer) evaluate( + {bool printOut = false, bool logic = false}) => + _cc.evaluate(printOut: printOut, logic: logic); + + /// Return the row shift of the PartialProductGenerator at a given [row] + int rowShift(int row) => _pp.rowShift[row]; +} + +/// Base class for bit-level column compressor function +abstract class BitCompressor extends Module { /// Input bits to compress @protected late final Logic compressBits; @@ -25,7 +63,7 @@ abstract class AddendCompressor extends Module { Logic get carry => output('carry'); /// Construct a column compressor - AddendCompressor(Logic compressBits) { + BitCompressor(Logic compressBits) { this.compressBits = addInput( 'compressBits', compressBits, @@ -37,7 +75,7 @@ abstract class AddendCompressor extends Module { } /// 2-input column compressor (half-adder) -class Compressor2 extends AddendCompressor { +class Compressor2 extends BitCompressor { /// Construct a 2-input compressor (half-adder) Compressor2(super.compressBits) { sum <= compressBits.xor(); @@ -46,7 +84,7 @@ class Compressor2 extends AddendCompressor { } /// 3-input column compressor (full-adder) -class Compressor3 extends AddendCompressor { +class Compressor3 extends BitCompressor { /// Construct a 3-input column compressor (full-adder) Compressor3(super.compressBits) { sum <= compressBits.xor(); @@ -159,19 +197,20 @@ class _CompressTerm implements Comparable<_CompressTerm> { } /// A column of partial product terms -typedef ColumnQueue = PriorityQueue<_CompressTerm>; +typedef _ColumnQueue = PriorityQueue<_CompressTerm>; /// A column compressor -class ColumnCompressor { +class _ColumnCompressor { /// Columns of partial product CompressTerms - late final List columns; + + late final List<_ColumnQueue> columns; /// The partial product generator to be compressed final PartialProductGenerator pp; /// Initialize a ColumnCompressor for a set of partial products - ColumnCompressor(this.pp) { - columns = List.generate(pp.maxWidth(), (i) => ColumnQueue()); + _ColumnCompressor(this.pp) { + columns = List.generate(pp.maxWidth(), (i) => _ColumnQueue()); for (var row = 0; row < pp.rows; row++) { for (var col = 0; col < pp.partialProducts[row].length; col++) { @@ -214,7 +253,7 @@ class ColumnCompressor { final first = queue.removeFirst(); final second = queue.removeFirst(); final inputs = <_CompressTerm>[first, second]; - AddendCompressor compressor; + BitCompressor compressor; if (depth > 3) { inputs.add(queue.removeFirst()); compressor = @@ -250,4 +289,62 @@ class ColumnCompressor { } } } + + (BigInt, StringBuffer) evaluate({bool printOut = false, bool logic = false}) { + final ts = StringBuffer(); + final rows = longestColumn(); + final width = pp.maxWidth(); + + var accum = BigInt.zero; + for (var row = 0; row < rows; row++) { + final rowBits = []; + for (var col = columns.length - 1; col >= 0; col--) { + final colList = columns[col].toList(); + if (row < colList.length) { + final value = + logic ? colList[row].logic.value : (colList[row].evaluate()); + rowBits.add(value); + if (printOut) { + ts.write('\t${value.bitString}'); + } + } else if (printOut) { + ts.write('\t'); + } + } + rowBits.addAll(List.filled(pp.rowShift[row], LogicValue.zero)); + final val = rowBits.swizzle().zeroExtend(width).toBigInt(); + accum += val; + if (printOut) { + ts.write('\t${rowBits.swizzle().zeroExtend(width).bitString} ($val)'); + if (row == rows - 1) { + ts.write(' Total=${accum.toSigned(width)}\n'); + stdout.write(ts); + } else { + ts.write('\n'); + } + } + } + if (printOut) { + // We need this to be able to debug, but git lint flunks print + // print(ts); + } + return (accum.toSigned(width), ts); + } + + /// Return a string representing the compression tree in its current state + String representation() { + final ts = StringBuffer(); + for (var row = 0; row < longestColumn(); row++) { + for (var col = columns.length - 1; col >= 0; col--) { + final colList = columns[col].toList(); + if (row < colList.length) { + ts.write('\t${colList[row]}'); + } else { + ts.write('\t'); + } + } + ts.write('\n'); + } + return ts.toString(); + } } diff --git a/lib/src/arithmetic/arithmetic.dart b/lib/src/arithmetic/arithmetic.dart index ce80df0c..c454729d 100644 --- a/lib/src/arithmetic/arithmetic.dart +++ b/lib/src/arithmetic/arithmetic.dart @@ -1,6 +1,7 @@ // Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause +export 'adder.dart'; export 'carry_save_mutiplier.dart'; export 'multiplier.dart'; export 'multiplier_lib.dart'; diff --git a/lib/src/arithmetic/evaluate_compressor.dart b/lib/src/arithmetic/evaluate_compressor.dart deleted file mode 100644 index 46574347..00000000 --- a/lib/src/arithmetic/evaluate_compressor.dart +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (C) 2024 Intel Corporation -// SPDX-License-Identifier: BSD-3-Clause -// -// addend_compressor.dart -// Column compression of partial prodcuts -// -// 2024 June 04 -// Author: Desmond Kirkpatrick - -import 'dart:io'; -import 'package:rohd/rohd.dart'; -import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; -import 'package:rohd_hcl/src/utils.dart'; - -/// Debug routines for printing out ColumnCompressor during -/// simulation with live logic values -extension EvaluateLiveColumnCompressor on ColumnCompressor { - /// Evaluate the (un)compressed partial product array - /// logic=true will read the logic gate outputs at each level - /// printOut=true will print out the array in the StringBuffer - (BigInt, StringBuffer) evaluate({bool printOut = false, bool logic = false}) { - final ts = StringBuffer(); - final rows = longestColumn(); - final width = pp.maxWidth(); - - var accum = BigInt.zero; - for (var row = 0; row < rows; row++) { - final rowBits = []; - for (var col = columns.length - 1; col >= 0; col--) { - final colList = columns[col].toList(); - if (row < colList.length) { - final value = - logic ? colList[row].logic.value : (colList[row].evaluate()); - rowBits.add(value); - if (printOut) { - ts.write('\t${value.bitString}'); - } - } else if (printOut) { - ts.write('\t'); - } - } - rowBits.addAll(List.filled(pp.rowShift[row], LogicValue.zero)); - final val = rowBits.swizzle().zeroExtend(width).toBigInt(); - accum += val; - if (printOut) { - ts.write('\t${rowBits.swizzle().zeroExtend(width).bitString} ($val)'); - if (row == rows - 1) { - ts.write(' Total=${accum.toSigned(width)}\n'); - stdout.write(ts); - } else { - ts.write('\n'); - } - } - } - if (printOut) { - // We need this to be able to debug, but git lint flunks print - // print(ts); - } - return (accum.toSigned(width), ts); - } - - /// Return a string representing the compression tree in its current state - String representation() { - final ts = StringBuffer(); - for (var row = 0; row < longestColumn(); row++) { - for (var col = columns.length - 1; col >= 0; col--) { - final colList = columns[col].toList(); - if (row < colList.length) { - ts.write('\t${colList[row]}'); - } else { - ts.write('\t'); - } - } - ts.write('\n'); - } - return ts.toString(); - } -} diff --git a/lib/src/arithmetic/multiplier.dart b/lib/src/arithmetic/multiplier.dart index 3c21bc0c..1dd1e73d 100644 --- a/lib/src/arithmetic/multiplier.dart +++ b/lib/src/arithmetic/multiplier.dart @@ -84,12 +84,13 @@ class CompressionTreeMultiplier extends Multiplier { Logic() ], (a, b) => Logic()).runtimeType}') { final product = addOutput('product', width: a.width + b.width); - final pp = CompactRectSignExtendPartialProductGenerator( + final pp = PartialProductGeneratorCompactRectSignExtension( a, b, RadixEncoder(radix), signed: signed); - final compressor = ColumnCompressor(pp)..compress(); + + final compressor = AddendCompressor(pp); final adder = ParallelPrefixAdder( - compressor.extractRow(0), compressor.extractRow(1), + compressor.sum.elements.first, compressor.sum.elements.last, ppGen: ppTree); product <= adder.sum.slice(a.width + b.width - 1, 0); } @@ -111,12 +112,12 @@ class CompressionTreeMultiplyAccumulate extends MultiplyAccumulate { name: 'Compression Tree Multiply Accumulate: ' 'R${radix}_${ppTree.call([Logic()], (a, b) => Logic()).name}') { final accumulate = addOutput('accumulate', width: a.width + b.width + 1); - final pp = CompactRectSignExtendPartialProductGenerator( + final pp = PartialProductGeneratorCompactRectSignExtension( a, b, RadixEncoder(radix), signed: signed); // TODO(desmonddak): This sign extension method for the additional - // addend may only work with signExtendCompact. + // addend may only work with CompactRectSignExtension final lastLength = pp.partialProducts[pp.rows - 1].length + pp.rowShift[pp.rows - 1]; @@ -130,23 +131,16 @@ class CompressionTreeMultiplyAccumulate extends MultiplyAccumulate { ..add(~sign) ..add(Const(1)); - // Evaluate works only because the compressed rows have the same shape - // So the rowshift is valid. - // But this requires that we prefix the PP with the addend (not add) to - // keep the evaluate routine working. + // For online evaluate in _ColumnCompressor to work, we need to + // insert the row rather than append it. pp.partialProducts.insert(0, l); pp.rowShift.insert(0, 0); - // TODO(desmonddak): probably cleaner to add row at end, but - // compressor fails to properly handle, so we need to debug - // pp.partialProducts.add(l); - // pp.rowShift.add(0); - final compressor = ColumnCompressor(pp)..compress(); - + final compressor = AddendCompressor(pp); final adder = ParallelPrefixAdder( - compressor.extractRow(0), compressor.extractRow(1), + compressor.sum.elements.first, compressor.sum.elements.last, ppGen: ppTree); - accumulate <= adder.sum.slice(a.width + b.width, 0); + accumulate <= adder.sum.slice(a.width + b.width - 1, 0); } } diff --git a/lib/src/arithmetic/ones_complement_adder.dart b/lib/src/arithmetic/ones_complement_adder.dart index a9ea4905..eead4be0 100644 --- a/lib/src/arithmetic/ones_complement_adder.dart +++ b/lib/src/arithmetic/ones_complement_adder.dart @@ -1,7 +1,7 @@ // Copyright (C) 2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // -// sign_magnitude_adder.dart +// ones_complement_adder.dart // Implementation of a One's Complement Adder // // 2024 August 31 diff --git a/lib/src/arithmetic/parallel_prefix_operations.dart b/lib/src/arithmetic/parallel_prefix_operations.dart index 7a166050..e1af3954 100644 --- a/lib/src/arithmetic/parallel_prefix_operations.dart +++ b/lib/src/arithmetic/parallel_prefix_operations.dart @@ -221,7 +221,6 @@ class ParallelPrefixAdder extends Adder { ParallelPrefixAdder(super.a, super.b, {ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen = KoggeStone.new}) - // : _out = Logic(width: a.width), : super( name: 'ParallelPrefixAdder: ${ppGen.call([ Logic() @@ -230,7 +229,6 @@ class ParallelPrefixAdder extends Adder { List.generate( a.width, (i) => [a[i] & b[i], a[i] | b[i]].swizzle()), (lhs, rhs) => [rhs[1] | rhs[0] & lhs[1], rhs[0] & lhs[0]].swizzle()); - // carryOut <= u.val[a.width - 1][1]; sum <= [ u.val[a.width - 1][1], diff --git a/lib/src/arithmetic/partial_product_generator.dart b/lib/src/arithmetic/partial_product_generator.dart index 3b1e5df8..2f4efeed 100644 --- a/lib/src/arithmetic/partial_product_generator.dart +++ b/lib/src/arithmetic/partial_product_generator.dart @@ -17,7 +17,7 @@ import 'package:rohd_hcl/rohd_hcl.dart'; /// Essentially a set of /// shifted rows of [Logic] addends generated by Booth recoding and /// manipulated by sign extension, before being compressed -class PartialProductGenerator { +abstract class PartialProductGenerator { /// Get the shift increment between neighboring product rows int get shift => selector.shift; @@ -74,7 +74,7 @@ class PartialProductGenerator { /// Perform sign extension (defined in child classes) @protected - void signExtend() {} + void signExtend(); /// Setup the partial products array (partialProducts and rowShift) void _build() { @@ -101,11 +101,22 @@ class PartialProductGenerator { } } +/// A Partial Product Generator with no sign extension +class PartialProductGeneratorNoSignExtension extends PartialProductGenerator { + /// Construct a basic Partial Product Generator + PartialProductGeneratorNoSignExtension( + super.multiplicand, super.multiplier, super.radixEncoder, + {required super.signed}); + + @override + void signExtend() {} +} + /// A Partial Product Generator using Brute Sign Extension -class CompactRectSignExtendPartialProductGenerator +class PartialProductGeneratorCompactRectSignExtension extends PartialProductGenerator { /// Construct a compact rect sign extending Partial Product Generator - CompactRectSignExtendPartialProductGenerator( + PartialProductGeneratorCompactRectSignExtension( super.multiplicand, super.multiplier, super.radixEncoder, {required super.signed}); @@ -125,10 +136,10 @@ class CompactRectSignExtendPartialProductGenerator } } - // /// Sign extend the PP array using stop bits without adding a row - // /// This routine works with different widths of multiplicand/multiplier, - // /// an extension of Mohanty, B.K., Choubey designed by - // /// Desmond A. Kirkpatrick + /// Sign extend the PP array using stop bits without adding a row + /// This routine works with different widths of multiplicand/multiplier, + /// an extension of Mohanty, B.K., Choubey designed by + /// Desmond A. Kirkpatrick @override void signExtend() { if (isSignExtended) { diff --git a/lib/src/arithmetic/partial_product_test_sign_extend.dart b/lib/src/arithmetic/partial_product_test_sign_extend.dart index b056640b..6a1ffe36 100644 --- a/lib/src/arithmetic/partial_product_test_sign_extend.dart +++ b/lib/src/arithmetic/partial_product_test_sign_extend.dart @@ -1,8 +1,8 @@ // Copyright (C) 2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // -// partial_product_generator.dart -// Partial Product matrix generation from Booth recoded multiplicand +// partial_product_test_sign_extend.dart +// Partial Product Genereator sign extension methods. // // 2024 May 15 // Author: Desmond Kirkpatrick @@ -33,32 +33,34 @@ typedef PPGFunction = PartialProductGenerator Function(Logic a, Logic b, RadixEncoder radixEncoder, {bool signed}); /// Used to test different sign extension methods -PPGFunction curryPartialProductGenerator(SignExtension signExtension) => (a, b, - encoder, - {signed = false}) => - switch (signExtension) { - SignExtension.none => - PartialProductGenerator(a, b, encoder, signed: signed), - SignExtension.brute => - BruteSignExtendPartialProductGenerator(a, b, encoder, signed: signed), - SignExtension.stop => StopBitsSignExtendPartialProductGenerator( - a, b, encoder, - signed: signed), - SignExtension.compact => - CompactSignExtendPartialProductGenerator(a, b, encoder, signed: signed), - SignExtension.compactRect => CompactRectSignExtendPartialProductGenerator( - a, b, encoder, - signed: signed), - }; +PPGFunction curryPartialProductGenerator(SignExtension signExtension) => + (a, b, encoder, {signed = false}) => switch (signExtension) { + SignExtension.none => PartialProductGeneratorNoSignExtension( + a, b, encoder, + signed: signed), + SignExtension.brute => PartialProductGeneratorBruteSignExtension( + a, b, encoder, + signed: signed), + SignExtension.stop => PartialProductGeneratorStopBitsSignExtension( + a, b, encoder, + signed: signed), + SignExtension.compact => PartialProductGeneratorCompactSignExtension( + a, b, encoder, + signed: signed), + SignExtension.compactRect => + PartialProductGeneratorCompactRectSignExtension(a, b, encoder, + signed: signed), + }; -//// These other sign extensions are for asssisting with testing and debugging +/// These other sign extensions are for asssisting with testing and debugging. /// More robust and simpler sign extensions in case /// complex sign extension routines obscure other bugs. /// A Partial Product Generator using Brute Sign Extension -class BruteSignExtendPartialProductGenerator extends PartialProductGenerator { +class PartialProductGeneratorBruteSignExtension + extends PartialProductGenerator { /// Construct a brute-force sign extending Partial Product Generator - BruteSignExtendPartialProductGenerator( + PartialProductGeneratorBruteSignExtension( super.multiplicand, super.multiplier, super.radixEncoder, {required super.signed}); @@ -89,9 +91,10 @@ class BruteSignExtendPartialProductGenerator extends PartialProductGenerator { } /// A Partial Product Generator using Brute Sign Extension -class CompactSignExtendPartialProductGenerator extends PartialProductGenerator { +class PartialProductGeneratorCompactSignExtension + extends PartialProductGenerator { /// Construct a compact sign extending Partial Product Generator - CompactSignExtendPartialProductGenerator( + PartialProductGeneratorCompactSignExtension( super.multiplicand, super.multiplier, super.radixEncoder, {required super.signed}); @@ -199,10 +202,10 @@ class CompactSignExtendPartialProductGenerator extends PartialProductGenerator { } /// A Partial Product Generator using Brute Sign Extension -class StopBitsSignExtendPartialProductGenerator +class PartialProductGeneratorStopBitsSignExtension extends PartialProductGenerator { /// Construct a stop bits sign extending Partial Product Generator - StopBitsSignExtendPartialProductGenerator( + PartialProductGeneratorStopBitsSignExtension( super.multiplicand, super.multiplier, super.radixEncoder, {required super.signed}); diff --git a/test/arithmetic/addend_compressor_test.dart b/test/arithmetic/addend_compressor_test.dart index b737e2b2..156c8666 100644 --- a/test/arithmetic/addend_compressor_test.dart +++ b/test/arithmetic/addend_compressor_test.dart @@ -1,7 +1,7 @@ // Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // -// compressor_test.dart +// addend_compressor_test.dart // Tests for the select interface of Booth encoding // // 2024 June 04 @@ -11,7 +11,6 @@ import 'dart:io'; import 'dart:math'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -import 'package:rohd_hcl/src/arithmetic/evaluate_compressor.dart'; import 'package:rohd_hcl/src/arithmetic/evaluate_partial_product.dart'; import 'package:rohd_hcl/src/arithmetic/partial_product_test_sign_extend.dart'; import 'package:test/test.dart'; @@ -20,7 +19,7 @@ void testCompressionExhaustive(PartialProductGenerator pp) { final widthX = pp.selector.multiplicand.width; final widthY = pp.encoder.multiplier.width; - final compressor = ColumnCompressor(pp); + final compressor = AddendCompressor(pp); final limitX = pow(2, widthX); final limitY = pow(2, widthY); @@ -48,7 +47,6 @@ void testCompressionExhaustive(PartialProductGenerator pp) { 'vs expected $product\n') ..write(pp); } - compressor.compress(); final compressedValue = compressor.evaluate().$1; expect(compressedValue, equals(product), reason: 'Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $compressedValue ' @@ -61,11 +59,11 @@ void testCompressionExhaustive(PartialProductGenerator pp) { 'vs expected $product' '\n$pp'); - final a = compressor.extractRow(0); - final b = compressor.extractRow(1); + final a = compressor.sum.elements.first; + final b = compressor.sum.elements.last; + final adder = ParallelPrefixAdder(a, b); - final adderValue = - adder.sum.value.toBigInt().toSigned(compressor.columns.length); + final adderValue = adder.sum.value.toBigInt().toSigned(compressor.width); expect(adderValue, equals(product), reason: 'Fail: $i($X)[$widthX] * $j($Y)[$widthY]: ' '$adderValue vs expected $product' @@ -80,13 +78,13 @@ void main() { stdout.write('\n'); for (final signed in [false, true]) { - for (var radix = 2; radix < 8; radix *= 2) { + for (var radix = 4; radix < 8; radix *= 2) { final encoder = RadixEncoder(radix); // stdout.write('encoding with radix=$radix\n'); final shift = log2Ceil(encoder.radix); for (var width = shift + 1; width < 2 * shift + 1; width++) { for (final signExtension in SignExtension.values) { - if (signExtension == SignExtension.none) { + if (signExtension != SignExtension.compactRect) { continue; } final ppg = curryPartialProductGenerator(signExtension); @@ -121,13 +119,11 @@ void main() { b.put(bB); const radix = 2; final encoder = RadixEncoder(radix); - final pp = CompactRectSignExtendPartialProductGenerator(a, b, encoder, + final pp = PartialProductGeneratorCompactRectSignExtension(a, b, encoder, signed: signed); expect(pp.evaluate(), equals(BigInt.from(av * bv))); - final compressor = ColumnCompressor(pp); - expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); - - compressor.compress(); + final compressor = AddendCompressor(pp); + // expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); } }); @@ -153,12 +149,10 @@ void main() { const radix = 2; final encoder = RadixEncoder(radix); - final pp = CompactRectSignExtendPartialProductGenerator(a, b, encoder, + final pp = PartialProductGeneratorCompactRectSignExtension(a, b, encoder, signed: signed); expect(pp.evaluate(), equals(BigInt.from(av * bv))); - final compressor = ColumnCompressor(pp); - expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); - compressor.compress(); + final compressor = AddendCompressor(pp); expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); } }); @@ -185,12 +179,10 @@ void main() { const radix = 8; final encoder = RadixEncoder(radix); - final pp = CompactRectSignExtendPartialProductGenerator(a, b, encoder, + final pp = PartialProductGeneratorCompactRectSignExtension(a, b, encoder, signed: signed); expect(pp.evaluate(), equals(BigInt.from(av * bv))); - final compressor = ColumnCompressor(pp); - expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); - compressor.compress(); + final compressor = AddendCompressor(pp); expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); } }); diff --git a/test/arithmetic/adder_test.dart b/test/arithmetic/adder_test.dart index 5450ce87..1e0af79e 100644 --- a/test/arithmetic/adder_test.dart +++ b/test/arithmetic/adder_test.dart @@ -12,12 +12,6 @@ import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:test/test.dart'; -Logic msb(Logic a) => a[a.width - 1]; -Logic lsb(Logic a) => a[0]; - -LogicValue msbV(LogicValue a) => a[a.width - 1]; -LogicValue lsbV(LogicValue a) => a[0]; - void checkAdder(Adder adder, LogicValue av, LogicValue bv) { final aB = av.toBigInt(); final bB = bv.toBigInt(); @@ -100,16 +94,17 @@ void checkSignMagnitudeAdder(SignMagnitudeAdder adder, LogicValue aSign, } void testExhaustiveSignMagnitude(int n, Adder Function(Logic a, Logic b) fn, - {bool sortOperands = true}) { + {bool operandsArePresorted = true}) { final aSign = Logic(name: 'aSign'); final a = Logic(name: 'a', width: n); final bSign = Logic(name: 'bSign'); final b = Logic(name: 'b', width: n); final adder = SignMagnitudeAdder(aSign, a, bSign, b, fn, - largestMagnitudeFirst: sortOperands); - test('exhaustive Sign Magnitude: ${adder.name}_W${a.width}_N$sortOperands', - () { + largestMagnitudeFirst: operandsArePresorted); + test( + 'exhaustive Sign Magnitude: ' + '${adder.name}_W${a.width}_N$operandsArePresorted', () { for (var i = 0; i < pow(2, n); i += 1) { for (var j = 0; j < pow(2, n); j += 1) { final bI = BigInt.from(i).toSigned(n); @@ -118,7 +113,7 @@ void testExhaustiveSignMagnitude(int n, Adder Function(Logic a, Logic b) fn, final bigger = bI; final smaller = bJ; // When equal, we want the negative one first to produce 1 1...1 - if (sortOperands & + if (operandsArePresorted & ((bI.abs() < bJ.abs()) || (bI.abs() == bJ.abs() && (bI > bJ)))) { continue; } else { @@ -218,12 +213,13 @@ void main() { group('SignMagnitude exhaustive', () { for (final ppGen in generators) { testExhaustiveSignMagnitude(4, RippleCarryAdder.new); - testExhaustiveSignMagnitude(4, RippleCarryAdder.new, sortOperands: false); + testExhaustiveSignMagnitude(4, RippleCarryAdder.new, + operandsArePresorted: false); testExhaustiveSignMagnitude( 4, (a, b) => ParallelPrefixAdder(a, b, ppGen: ppGen)); testExhaustiveSignMagnitude( 4, (a, b) => ParallelPrefixAdder(a, b, ppGen: ppGen), - sortOperands: false); + operandsArePresorted: false); } }); test('trivial parallel prefix adder test', () async { diff --git a/test/arithmetic/multiplier_encoder_test.dart b/test/arithmetic/multiplier_encoder_test.dart index 9b98c1f1..c575a3f3 100644 --- a/test/arithmetic/multiplier_encoder_test.dart +++ b/test/arithmetic/multiplier_encoder_test.dart @@ -73,7 +73,7 @@ void main() { logicX.put(X); logicY.put(Y); logicZ.put(Z); - final pp = CompactRectSignExtendPartialProductGenerator( + final pp = PartialProductGeneratorCompactRectSignExtension( logicX, logicY, encoder, signed: true); @@ -185,7 +185,7 @@ void main() { final Y = BigInt.from(2060).toUnsigned(width + skew); final product = X * Y; - final pp = CompactRectSignExtendPartialProductGenerator( + final pp = PartialProductGeneratorCompactRectSignExtension( Logic(name: 'X', width: width), Logic(name: 'Y', width: width + skew), encoder, diff --git a/test/arithmetic/parallel_prefix_operations_test.dart b/test/arithmetic/parallel_prefix_operations_test.dart index 68857423..e965960c 100644 --- a/test/arithmetic/parallel_prefix_operations_test.dart +++ b/test/arithmetic/parallel_prefix_operations_test.dart @@ -1,7 +1,7 @@ // Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // -// parallel-prefix_operations.dart +// parallel_prefix_operations.dart // Implementation of operations using various parallel-prefix trees. // // 2023 Sep 29 From 4180ad911b7b5d3ff9e492389d74d27150765389 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Wed, 4 Sep 2024 17:09:45 -0700 Subject: [PATCH 34/38] back out AddendCompressor and leave as a ColumnCompressor object generator (not module) --- doc/components/multiplier_components.md | 6 +- lib/src/arithmetic/addend_compressor.dart | 151 ++++---------------- lib/src/arithmetic/evaluate_compressor.dart | 79 ++++++++++ lib/src/arithmetic/multiplier.dart | 10 +- test/arithmetic/addend_compressor_test.dart | 24 ++-- 5 files changed, 131 insertions(+), 139 deletions(-) create mode 100644 lib/src/arithmetic/evaluate_compressor.dart diff --git a/doc/components/multiplier_components.md b/doc/components/multiplier_components.md index 439f4eb2..2d084b6f 100644 --- a/doc/components/multiplier_components.md +++ b/doc/components/multiplier_components.md @@ -186,7 +186,7 @@ Here is a code snippet that shows how these components can be used to create a m First the partial product generator is used, which has compact sign extension for rectangular products (`PartialProductGeneratorCompactRectSignExtension`) which we pass in the `RadixEncoder`, whether the operands are signed, and the kind of sign extension to use on the partial products. Note that sign extension is needed regardless of whether operands are signed or not due to Booth encoding. -Next, we use the `AddendCompressor` to compress the partial products into two final addends. +Next, we use the `ColumnCompressor` to compress the partial products into two final addends. We then choose a `ParallelPrefixAdder` using the `BrentKung` tree style to do the addition. We pass in the two extracted rows of the compressor. Finally, we produce the product. @@ -194,8 +194,8 @@ Finally, we produce the product. ```dart final pp = PartialProductGeneratorCompactRectSignExtension(a, b, RadixEncoder(radix), signed: true); - final compressor = AddendCompressor(pp); + final compressor = ColumnCompressor(pp)..compress(); final adder = ParallelPrefixAdder( - compressor.sum.elements.first, compressor.sum.elements.last, BrentKung.new); + compressor.exractRow(0), compressor.extractRow(1), BrentKung.new); product <= adder.sum.slice(a.width + b.width - 1, 0); ``` diff --git a/lib/src/arithmetic/addend_compressor.dart b/lib/src/arithmetic/addend_compressor.dart index 6a17b728..ae8780c6 100644 --- a/lib/src/arithmetic/addend_compressor.dart +++ b/lib/src/arithmetic/addend_compressor.dart @@ -15,41 +15,6 @@ import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; import 'package:rohd_hcl/src/utils.dart'; -/// A simple full-adder with inputs `a` and `b` to be added with a `carryIn`. -class AddendCompressor extends Module { - /// The addition's result [sum]. - LogicArray get sum => output('sums') as LogicArray; - - /// Return the maximum [width] of the addends - int get width => _pp.maxWidth(); - - late final PartialProductGenerator _pp; - late final _ColumnCompressor _cc; - - /// Construct an addend compressor module from a PartialProductGenerator - AddendCompressor(this._pp, {bool doCompress = true}) { - _cc = _ColumnCompressor(_pp); - - addOutputArray('sums', dimensions: [2], elementWidth: width); - if (doCompress) { - _cc.compress(); - } - - sum <= [_cc.extractRow(0), _cc.extractRow(1)].swizzle(); - } - - /// Compress the columns down to two rows - void compress() => _cc.compress(); - - /// evaluate the value of the compressor, used for live debug - (BigInt, StringBuffer) evaluate( - {bool printOut = false, bool logic = false}) => - _cc.evaluate(printOut: printOut, logic: logic); - - /// Return the row shift of the PartialProductGenerator at a given [row] - int rowShift(int row) => _pp.rowShift[row]; -} - /// Base class for bit-level column compressor function abstract class BitCompressor extends Module { /// Input bits to compress @@ -95,7 +60,7 @@ class Compressor3 extends BitCompressor { } /// Compress terms -enum _CompressTermType { +enum CompressTermType { /// A carry term carry, @@ -107,12 +72,12 @@ enum _CompressTermType { } /// A compression term -class _CompressTerm implements Comparable<_CompressTerm> { +class CompressTerm implements Comparable { /// The type of term we have - late final _CompressTermType type; + late final CompressTermType type; /// The inputs that drove this Term - late final List<_CompressTerm> inputs; + late final List inputs; /// The row of the terminal final int row; @@ -133,12 +98,12 @@ class _CompressTerm implements Comparable<_CompressTerm> { static const carryDelay = 0.75; /// CompressTerm constructor - _CompressTerm(this.type, this.logic, this.inputs, this.row, this.col) { + CompressTerm(this.type, this.logic, this.inputs, this.row, this.col) { delay = 0.0; final deltaDelay = switch (type) { - _CompressTermType.carry => carryDelay, - _CompressTermType.sum => sumDelay, - _CompressTermType.pp => 0.0 + CompressTermType.carry => carryDelay, + CompressTermType.sum => sumDelay, + CompressTermType.pp => 0.0 }; for (final i in inputs) { if (i.delay + deltaDelay > delay) { @@ -148,7 +113,7 @@ class _CompressTerm implements Comparable<_CompressTerm> { } @override int compareTo(Object other) { - if (other is! _CompressTerm) { + if (other is! CompressTerm) { throw Exception('Input must be of type CompressTerm '); } return delay > other.delay ? 1 : (delay < other.delay ? -1 : 0); @@ -158,14 +123,14 @@ class _CompressTerm implements Comparable<_CompressTerm> { LogicValue evaluate() { late LogicValue value; switch (type) { - case _CompressTermType.pp: + case CompressTermType.pp: value = logic.value; - case _CompressTermType.sum: + case CompressTermType.sum: // xor the eval of the terms final termValues = [for (final term in inputs) term.evaluate()]; final sum = termValues.swizzle().xor(); value = sum; - case _CompressTermType.carry: + case CompressTermType.carry: final termValues = [for (final term in inputs) term.evaluate()]; final termValuesInt = [ for (var i = 0; i < termValues.length; i++) termValues[i].toInt() @@ -185,9 +150,9 @@ class _CompressTerm implements Comparable<_CompressTerm> { String toString() { final str = StringBuffer(); final ts = switch (type) { - _CompressTermType.pp => 'pp', - _CompressTermType.carry => 'c', - _CompressTermType.sum => 's' + CompressTermType.pp => 'pp', + CompressTermType.carry => 'c', + CompressTermType.sum => 's' }; str ..write(ts) @@ -197,25 +162,25 @@ class _CompressTerm implements Comparable<_CompressTerm> { } /// A column of partial product terms -typedef _ColumnQueue = PriorityQueue<_CompressTerm>; +typedef ColumnQueue = PriorityQueue; /// A column compressor -class _ColumnCompressor { +class ColumnCompressor { /// Columns of partial product CompressTerms - late final List<_ColumnQueue> columns; + late final List columns; /// The partial product generator to be compressed final PartialProductGenerator pp; /// Initialize a ColumnCompressor for a set of partial products - _ColumnCompressor(this.pp) { - columns = List.generate(pp.maxWidth(), (i) => _ColumnQueue()); + ColumnCompressor(this.pp) { + columns = List.generate(pp.maxWidth(), (i) => ColumnQueue()); for (var row = 0; row < pp.rows; row++) { for (var col = 0; col < pp.partialProducts[row].length; col++) { final trueColumn = pp.rowShift[row] + col; - final term = _CompressTerm(_CompressTermType.pp, + final term = CompressTerm(CompressTermType.pp, pp.partialProducts[row][col], [], row, trueColumn); columns[trueColumn].add(term); } @@ -243,8 +208,8 @@ class _ColumnCompressor { } /// Core iterator for column compressor routine - List<_CompressTerm> _compressIter(int iteration) { - final terms = <_CompressTerm>[]; + List _compressIter(int iteration) { + final terms = []; for (var col = 0; col < columns.length; col++) { final queue = columns[col]; final depth = queue.length; @@ -252,7 +217,7 @@ class _ColumnCompressor { if (depth > 2) { final first = queue.removeFirst(); final second = queue.removeFirst(); - final inputs = <_CompressTerm>[first, second]; + final inputs = [first, second]; BitCompressor compressor; if (depth > 3) { inputs.add(queue.removeFirst()); @@ -262,13 +227,13 @@ class _ColumnCompressor { compressor = Compressor2([for (final i in inputs) i.logic].swizzle()); } - final t = _CompressTerm( - _CompressTermType.sum, compressor.sum, inputs, 0, col); + final t = CompressTerm( + CompressTermType.sum, compressor.sum, inputs, 0, col); terms.add(t); columns[col].add(t); if (col < columns.length - 1) { - final t = _CompressTerm( - _CompressTermType.carry, compressor.carry, inputs, 0, col); + final t = CompressTerm( + CompressTermType.carry, compressor.carry, inputs, 0, col); columns[col + 1].add(t); terms.add(t); } @@ -280,7 +245,7 @@ class _ColumnCompressor { /// Compress the partial products array to two addends void compress() { - final terms = <_CompressTerm>[]; + final terms = []; var iterations = longestColumn(); while (iterations > 0) { terms.addAll(_compressIter(iterations--)); @@ -289,62 +254,4 @@ class _ColumnCompressor { } } } - - (BigInt, StringBuffer) evaluate({bool printOut = false, bool logic = false}) { - final ts = StringBuffer(); - final rows = longestColumn(); - final width = pp.maxWidth(); - - var accum = BigInt.zero; - for (var row = 0; row < rows; row++) { - final rowBits = []; - for (var col = columns.length - 1; col >= 0; col--) { - final colList = columns[col].toList(); - if (row < colList.length) { - final value = - logic ? colList[row].logic.value : (colList[row].evaluate()); - rowBits.add(value); - if (printOut) { - ts.write('\t${value.bitString}'); - } - } else if (printOut) { - ts.write('\t'); - } - } - rowBits.addAll(List.filled(pp.rowShift[row], LogicValue.zero)); - final val = rowBits.swizzle().zeroExtend(width).toBigInt(); - accum += val; - if (printOut) { - ts.write('\t${rowBits.swizzle().zeroExtend(width).bitString} ($val)'); - if (row == rows - 1) { - ts.write(' Total=${accum.toSigned(width)}\n'); - stdout.write(ts); - } else { - ts.write('\n'); - } - } - } - if (printOut) { - // We need this to be able to debug, but git lint flunks print - // print(ts); - } - return (accum.toSigned(width), ts); - } - - /// Return a string representing the compression tree in its current state - String representation() { - final ts = StringBuffer(); - for (var row = 0; row < longestColumn(); row++) { - for (var col = columns.length - 1; col >= 0; col--) { - final colList = columns[col].toList(); - if (row < colList.length) { - ts.write('\t${colList[row]}'); - } else { - ts.write('\t'); - } - } - ts.write('\n'); - } - return ts.toString(); - } } diff --git a/lib/src/arithmetic/evaluate_compressor.dart b/lib/src/arithmetic/evaluate_compressor.dart new file mode 100644 index 00000000..a459e6e8 --- /dev/null +++ b/lib/src/arithmetic/evaluate_compressor.dart @@ -0,0 +1,79 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// addend_compressor.dart +// Column compression of partial prodcuts +// +// 2024 June 04 +// Author: Desmond Kirkpatrick + +import 'dart:io'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; +import 'package:rohd_hcl/src/utils.dart'; + +/// Debug routines for printing out ColumnCompressor during +/// simulation with live logic values +extension EvaluateLiveColumnCompressor on ColumnCompressor { + /// Evaluate the (un)compressed partial product array + /// logic=true will read the logic gate outputs at each level + /// printOut=true will print out the array in the StringBuffer + (BigInt, StringBuffer) evaluate({bool printOut = false, bool logic = false}) { + final ts = StringBuffer(); + final rows = longestColumn(); + final width = pp.maxWidth(); + + var accum = BigInt.zero; + for (var row = 0; row < rows; row++) { + final rowBits = []; + for (var col = columns.length - 1; col >= 0; col--) { + final colList = columns[col].toList(); + if (row < colList.length) { + final value = + logic ? colList[row].logic.value : (colList[row].evaluate()); + rowBits.add(value); + if (printOut) { + ts.write('\t${value.bitString}'); + } + } else if (printOut) { + ts.write('\t'); + } + } + rowBits.addAll(List.filled(pp.rowShift[row], LogicValue.zero)); + final val = rowBits.swizzle().zeroExtend(width).toBigInt(); + accum += val; + if (printOut) { + ts.write('\t${rowBits.swizzle().zeroExtend(width).bitString} ($val)'); + if (row == rows - 1) { + ts.write(' Total=${accum.toSigned(width)}\n'); + stdout.write(ts); + } else { + ts.write('\n'); + } + } + } + if (printOut) { + // We need this to be able to debug, but git lint flunks print + // print(ts); + } + return (accum.toSigned(width), ts); + } + + + /// Return a string representing the compression tree in its current state + String representation() { + final ts = StringBuffer(); + for (var row = 0; row < longestColumn(); row++) { + for (var col = columns.length - 1; col >= 0; col--) { + final colList = columns[col].toList(); + if (row < colList.length) { + ts.write('\t${colList[row]}'); + } else { + ts.write('\t'); + } + } + ts.write('\n'); + } + return ts.toString(); + } +} diff --git a/lib/src/arithmetic/multiplier.dart b/lib/src/arithmetic/multiplier.dart index 1dd1e73d..1fde5742 100644 --- a/lib/src/arithmetic/multiplier.dart +++ b/lib/src/arithmetic/multiplier.dart @@ -88,9 +88,9 @@ class CompressionTreeMultiplier extends Multiplier { a, b, RadixEncoder(radix), signed: signed); - final compressor = AddendCompressor(pp); + final compressor = ColumnCompressor(pp)..compress(); final adder = ParallelPrefixAdder( - compressor.sum.elements.first, compressor.sum.elements.last, + compressor.extractRow(0), compressor.extractRow(1), ppGen: ppTree); product <= adder.sum.slice(a.width + b.width - 1, 0); } @@ -136,11 +136,11 @@ class CompressionTreeMultiplyAccumulate extends MultiplyAccumulate { pp.partialProducts.insert(0, l); pp.rowShift.insert(0, 0); - final compressor = AddendCompressor(pp); + final compressor = ColumnCompressor(pp)..compress(); final adder = ParallelPrefixAdder( - compressor.sum.elements.first, compressor.sum.elements.last, + compressor.extractRow(0), compressor.extractRow(1), ppGen: ppTree); - accumulate <= adder.sum.slice(a.width + b.width - 1, 0); + accumulate <= adder.sum.slice(a.width + b.width - 1 + 1, 0); } } diff --git a/test/arithmetic/addend_compressor_test.dart b/test/arithmetic/addend_compressor_test.dart index 156c8666..c147026c 100644 --- a/test/arithmetic/addend_compressor_test.dart +++ b/test/arithmetic/addend_compressor_test.dart @@ -11,6 +11,7 @@ import 'dart:io'; import 'dart:math'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_hcl/src/arithmetic/evaluate_compressor.dart'; import 'package:rohd_hcl/src/arithmetic/evaluate_partial_product.dart'; import 'package:rohd_hcl/src/arithmetic/partial_product_test_sign_extend.dart'; import 'package:test/test.dart'; @@ -19,7 +20,7 @@ void testCompressionExhaustive(PartialProductGenerator pp) { final widthX = pp.selector.multiplicand.width; final widthY = pp.encoder.multiplier.width; - final compressor = AddendCompressor(pp); + final compressor = ColumnCompressor(pp); final limitX = pow(2, widthX); final limitY = pow(2, widthY); @@ -47,6 +48,7 @@ void testCompressionExhaustive(PartialProductGenerator pp) { 'vs expected $product\n') ..write(pp); } + compressor.compress(); final compressedValue = compressor.evaluate().$1; expect(compressedValue, equals(product), reason: 'Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $compressedValue ' @@ -59,11 +61,12 @@ void testCompressionExhaustive(PartialProductGenerator pp) { 'vs expected $product' '\n$pp'); - final a = compressor.sum.elements.first; - final b = compressor.sum.elements.last; + final a = compressor.extractRow(0); + final b = compressor.extractRow(1); final adder = ParallelPrefixAdder(a, b); - final adderValue = adder.sum.value.toBigInt().toSigned(compressor.width); + final adderValue = + adder.sum.value.toBigInt().toSigned(compressor.columns.length); expect(adderValue, equals(product), reason: 'Fail: $i($X)[$widthX] * $j($Y)[$widthY]: ' '$adderValue vs expected $product' @@ -73,7 +76,7 @@ void testCompressionExhaustive(PartialProductGenerator pp) { } void main() { - test('exhaustive compression evaluate: square radix-4, all SignExtension', + test('exhaustive compression evaluate: square radix-4, just CompactRect', () async { stdout.write('\n'); @@ -122,8 +125,9 @@ void main() { final pp = PartialProductGeneratorCompactRectSignExtension(a, b, encoder, signed: signed); expect(pp.evaluate(), equals(BigInt.from(av * bv))); - final compressor = AddendCompressor(pp); - // expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); + final compressor = ColumnCompressor(pp); + expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); + compressor.compress(); expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); } }); @@ -152,7 +156,9 @@ void main() { final pp = PartialProductGeneratorCompactRectSignExtension(a, b, encoder, signed: signed); expect(pp.evaluate(), equals(BigInt.from(av * bv))); - final compressor = AddendCompressor(pp); + final compressor = ColumnCompressor(pp); + expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); + compressor.compress(); expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); } }); @@ -182,7 +188,7 @@ void main() { final pp = PartialProductGeneratorCompactRectSignExtension(a, b, encoder, signed: signed); expect(pp.evaluate(), equals(BigInt.from(av * bv))); - final compressor = AddendCompressor(pp); + final compressor = ColumnCompressor(pp)..compress(); expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); } }); From 448b4e9ccca5ab4862fd81fb2ff849a6c4ffd9df Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Wed, 4 Sep 2024 18:53:03 -0700 Subject: [PATCH 35/38] format issue --- lib/src/arithmetic/evaluate_compressor.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/arithmetic/evaluate_compressor.dart b/lib/src/arithmetic/evaluate_compressor.dart index a459e6e8..46574347 100644 --- a/lib/src/arithmetic/evaluate_compressor.dart +++ b/lib/src/arithmetic/evaluate_compressor.dart @@ -59,7 +59,6 @@ extension EvaluateLiveColumnCompressor on ColumnCompressor { return (accum.toSigned(width), ts); } - /// Return a string representing the compression tree in its current state String representation() { final ts = StringBuffer(); From 741e78ef9640fb2b064b3e1073be746aa7dfe170 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Wed, 4 Sep 2024 20:42:19 -0700 Subject: [PATCH 36/38] commit to force git to run the job --- doc/components/multiplier.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/components/multiplier.md b/doc/components/multiplier.md index 56cf1ac9..e98ab5da 100644 --- a/doc/components/multiplier.md +++ b/doc/components/multiplier.md @@ -136,8 +136,7 @@ Here is an example of using the `CompressionTreeMultiplyAccumulate`: b.put(3); c.put(5); - final multiplier = CompressionTreeMultiplyAccumulate( - a, b, c, radix, signed: true); + final multiplier = CompressionTreeMultiplyAccumulate(a, b, c, radix, signed: true); final accumulate = multiplier.accumulate; From b32b72580376ab3a919db94b3a2bf967edf45629 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Wed, 4 Sep 2024 22:19:48 -0700 Subject: [PATCH 37/38] remove unused import --- lib/src/arithmetic/addend_compressor.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/arithmetic/addend_compressor.dart b/lib/src/arithmetic/addend_compressor.dart index ae8780c6..51e29619 100644 --- a/lib/src/arithmetic/addend_compressor.dart +++ b/lib/src/arithmetic/addend_compressor.dart @@ -13,7 +13,6 @@ import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; -import 'package:rohd_hcl/src/utils.dart'; /// Base class for bit-level column compressor function abstract class BitCompressor extends Module { From 9f7d84d102855d86659f965ac383c8a9afaa8e68 Mon Sep 17 00:00:00 2001 From: "Desmond A. Kirkpatrick" Date: Wed, 4 Sep 2024 23:36:51 -0700 Subject: [PATCH 38/38] another unused import --- lib/src/arithmetic/addend_compressor.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/src/arithmetic/addend_compressor.dart b/lib/src/arithmetic/addend_compressor.dart index 51e29619..81723e88 100644 --- a/lib/src/arithmetic/addend_compressor.dart +++ b/lib/src/arithmetic/addend_compressor.dart @@ -7,8 +7,6 @@ // 2024 June 04 // Author: Desmond Kirkpatrick -import 'dart:io'; - import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart';