Skip to content

Commit

Permalink
Enhancements for modifying and printing partial product arrays. (#102)
Browse files Browse the repository at this point in the history
* Enhancements for modifying and printing partial product arrays.

* Updated partialProduct printout refactored to use listString (replaces vecString, missnamed):

* listString improved to handle both ASCII and markdown in one function, 

* listString  partialProduct.setAbsolute, .setAbsoluteAll, .getAbsolute .getAbsoluteAll  tests added
  • Loading branch information
desmonddak authored Oct 10, 2024
1 parent 87783b8 commit f2b0458
Show file tree
Hide file tree
Showing 11 changed files with 576 additions and 196 deletions.
55 changes: 40 additions & 15 deletions doc/components/multiplier_components.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Think of the hand-multiplication process where you write down the multiplicand a

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|
| Bit i | Bit i-1 | Multiple|
|:-----:|:-------:|:-------:|
| 0 | 0 | +0 |
| 0 | 1 | +1 |
Expand Down Expand Up @@ -105,7 +105,7 @@ An argument to the `PartialProductGenerator` is the `RadixEncoder` 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 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 radixes use wider slices of the multiplier to encode fewer multiples and therefore fewer rows.

| bit_i | bit_i-1 | bit_i-2 | multiple|
| Bit i | Bit i-1 | Bit i-2 | Multiple|
|:-----:|:-------:|:-------:|:-------:|
| 0 | 0 | 0 | +0 |
| 0 | 0 | 1 | 1 |
Expand All @@ -118,35 +118,60 @@ 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 radixes 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 radixes 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.

If customization is needed beyond sign extension options, routines are provided that allow for fixed customization of bit positions, or conditional (mux based on a Logic) form.

```dart
final ppg = PartialProductGenerator(a,b);
ppg.setAbsolute(row, col, logic);
ppg.setAbsoluteAll(row, col, List<Logic>);
ppg.muxAbsolute(row, col, condition, logic);
ppg.muxAbsoluteAll(row, col, condition, List<logic>);
```

### 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`. Here 'S' or 's' represent a sign bit extension (positive polarity) with 'S' representing '1', 's' representing 0. 'I' and 'i' represent an inverted sign bit.

```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)
18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
00 M= 2 S=1 i S S 1 1 1 1 1 1 0 0 0 = 2040 (2040)
01 M= 2 S=0 1 I 0 0 0 0 0 0 1 1 0 1 = 6170 (6170)
02 M= 0 S=0 1 I 0 0 0 0 0 0 0 0 0 0 = 24576 (24576)
03 M= 0 S=0 1 I 0 0 0 0 0 0 0 0 0 0 = 98304 (98304)
04 M= 0 S=0 1 I 0 0 0 0 0 0 0 0 0 0 = 393216 (-131072)
=====================================================================
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 = 18 (18)
```

You can also generate a Markdown form of the same matrix:

| R | M | S| 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | value|
|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--|
|00| 2| 1||||||||$\overline0$|$\underline1$|$\underline1$|1|1|1|1|1|1|0|0|0| 2040 (2040)|
|01| 2| 0|||||||1|$\overline1$|0|0|0|0|0|0|1|1|0|1|| 6170 (6170)|
|02| 0| 0|||||1|$\overline1$|0|0|0|0|0|0|0|0|0|0|||| 24576 (24576)|
|03| 0| 0|||1|$\overline1$|0|0|0|0|0|0|0|0|0|0|||||| 98304 (98304)|
|04| 0| 0|1|$\overline1$|0|0|0|0|0|0|0|0|0|0|||||||| 393216 (-131072)|
||||0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |1 |0 |0 |1 |0 |18 (18)|

Here $\underline 1$ or $\underline 0$ represent a sign bit extension (positive polarity),
whereas $\overline 1$ or $\overline 0$ represents a negative polarity sign bit.

## 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 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 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
- cR,C = carry term coming last from row R, column C
- `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:

Expand Down Expand Up @@ -208,7 +233,7 @@ The `vecString` extension provides a basic string printer with an optional `head

`alignHigh` controls the highest (toward MSB) alignment column of the output whereas `alignLow` controls the lower limit (toward the LSB).

`sepPos' is optional and allows you to set a marker for a separator in the number.
`sepPos` is optional and allows you to set a marker for a separator in the number.
`sepChar` is the separation character you wish to use (do not use '|' with Markdown formatting.)

```dart
Expand Down
5 changes: 4 additions & 1 deletion lib/src/arithmetic/addend_compressor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,10 @@ class ColumnCompressor {
}
}
rowBits.addAll(List.filled(pp.rowShift[row], Const(0)));
return rowBits.swizzle().zeroExtend(width);
if (width > rowBits.length) {
return rowBits.swizzle().zeroExtend(width);
}
return rowBits.swizzle().getRange(0, width);
}

/// Core iterator for column compressor routine
Expand Down
121 changes: 94 additions & 27 deletions lib/src/arithmetic/arithmetic_utils.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// floating_point_test.dart
// Tests of Floating Point stuff
// arithmetic_utils.dart
// Utlities for arithmetic visualization
//
// 2024 August 30
// Author: Desmond A Kirkpatrick <[email protected]
Expand All @@ -15,34 +15,44 @@ import 'package:rohd/rohd.dart';
import 'package:rohd_hcl/rohd_hcl.dart';

/// Helper evaluation methods for printing aligned arithmetic bitvectors.
extension NumericVector on LogicValue {
/// Print aligned bitvector with an optional header.
extension LogicList on List<Logic> {
/// Print aligned bitvector with an optional header from List<Logic>.
/// [name] is printed at the LHS of the line, trimmed by [prefix].
/// [prefix] is the distance from the margin bebore the vector is printed.
/// You can align with longer bitvectors by stating the length [alignHigh].
/// [alignLow] will trim the vector below this bit position.
/// [alignHigh] is highest column (MSB) to which to align
/// [alignLow] will trim the vector below this bit position (LSB).
/// [shift] will allow you to shift your list positions
/// You can insert a separator [sepChar] at position [sepPos].
/// A header can be printed by setting [header] to true.
/// Markdown format can be produced by setting [markDown] to true.
/// The output can have space by setting [extraSpace]
String vecString(String name,
/// if [intValue] is true, then the integer value (signed version in parens)
/// will be printed at the end of the vector.
String listString(String name,
{int prefix = 10,
int? alignHigh,
int? sepPos,
bool header = false,
String sepChar = '*',
int alignLow = 0,
int extraSpace = 0,
int shift = 0,
bool intValue = false,
bool markDown = false}) {
final str = StringBuffer();
final minHigh = min(alignHigh ?? width, width);
final length = BigInt.from(minHigh).toString().length + extraSpace;
final maxHigh = max(alignHigh ?? length, length);
final minHigh = min(alignHigh ?? length, length) - shift;
final colWidth = BigInt.from(maxHigh).toString().length + extraSpace;
// ignore: cascade_invocations
const hdrSep = '| ';
const hdrSepStart = '| ';
const hdrSepEnd = '|';

final highLimit = ((alignHigh ?? width) - width) + width - 1;
if (markDown && sepChar.contains('|')) {
throw RohdHclException('markDown cannot use | as a sepChar');
}

final highLimit = ((alignHigh ?? length) - length) + length - 1;

if (header) {
str.write(markDown ? '$hdrSepStart Name' : ' ' * prefix);
Expand All @@ -51,30 +61,38 @@ extension NumericVector on LogicValue {
final chars = BigInt.from(col).toString().length + extraSpace;
if (sepPos != null && sepPos == col) {
str
..write(
markDown ? ' $hdrSep' : ' ' * (length - chars + 1 + extraSpace))
..write(markDown
? ' $hdrSep'
: ' ' * (colWidth - chars + 1 + extraSpace))
..write('$col$sepChar')
..write(markDown ? ' $hdrSep' : '');
} else if (sepPos != null && sepPos == col + 1) {
if (sepPos == max(alignHigh ?? width, width)) {
if (sepPos == max(alignHigh ?? length, length)) {
str
..write(sepChar)
..write(markDown ? ' $hdrSep' : ' ' * (length - chars - 1));
..write(markDown ? ' $hdrSep' : ' ' * (colWidth - chars - 1));
}
str.write('${' ' * (length - chars + extraSpace + 0)}$col');
str.write('${' ' * (colWidth - chars + extraSpace + 0)}$col');
} else {
str
..write(
markDown ? ' $hdrSep' : ' ' * (length - chars + 1 + extraSpace))
..write(markDown
? ' $hdrSep'
: ' ' * (colWidth - chars + 1 + extraSpace))
..write('$col');
}
}
str.write(markDown ? ' $hdrSepEnd\n' : '\n');
str
..write(intValue & markDown ? hdrSepEnd : '')
..write(markDown ? hdrSepEnd : '')
..write('\n');
if (markDown) {
str.write(markDown ? '|:--:' : ' ' * prefix);
str.write(markDown ? '|:--' : ' ' * prefix);
for (var col = highLimit; col >= alignLow; col--) {
str.write('|:--');
}
if (intValue) {
str.write('-|:--');
}
str.write('-|\n');
}
}
Expand All @@ -87,29 +105,78 @@ extension NumericVector on LogicValue {
: (name.length <= prefix)
? name.padRight(prefix)
: name.substring(0, prefix);

// Column calculations for overlapping vector with range
final startCol = highLimit;
final endCol = alignLow;
final startPos = length + shift - 1;
final endPos = shift;
final startOvl = min(startCol, startPos);
final endOvl = max(endCol, endPos);
final startIdx = startOvl - shift;
final endIdx = endOvl - shift;

final emptyLeft = (startCol - max(alignLow - 1, startOvl)).toInt();
final emptyRight = min(highLimit + 1, endPos) - endCol;
str
..write(strPrefix)
..write((markDown ? dataSep : ' ' * (length + 1)) *
((alignHigh ?? width) - width));
for (var col = alignLow; col < minHigh; col++) {
final pos = minHigh - 1 - col + alignLow;
final v = this[pos].bitString;
..write((markDown ? dataSepStart : ' ' * (colWidth + 1)) * emptyLeft);

for (var pos = startIdx; pos >= endIdx; pos--) {
final bit = this[pos];
final String v;
if (bit is SignBit) {
if (bit.value == LogicValue.zero) {
v = markDown
? bit.inverted
? r'$\overline 0$'
: r'$\underline 0$'
: bit.inverted
? 'i'
: 's';
} else {
v = markDown
? bit.inverted
? r'$\overline 1$'
: r'$\underline 1$'
: bit.inverted
? 'I'
: 'S';
}
} else {
v = this[pos].value.bitString;
}
if (sepPos != null && sepPos == pos) {
str.write(
markDown ? ' $dataSep$v $sepChar' : '${' ' * length}$v$sepChar');
markDown ? ' $dataSep$v $sepChar' : '${' ' * colWidth}$v$sepChar');
} else if (sepPos != null && sepPos == pos + 1) {
if (sepPos == minHigh) {
str.write(sepChar);
}
str
..write(markDown ? ' $dataSep' : ' ' * (length - 1))
..write(markDown ? ' $dataSep' : ' ' * (colWidth - 1))
..write(v);
} else {
str
..write(markDown ? ' $dataSep' : ' ' * length)
..write(markDown ? ' $dataSep' : ' ' * colWidth)
..write(v);
}
}

str.write(markDown ? dataSepStart : ' ' * emptyRight * (colWidth + 1));
if (intValue) {
final vec = (shift >= 0)
? this.rswizzle().zeroExtend(maxHigh) << shift
: this.rswizzle().zeroExtend(maxHigh) >>> -shift;

final vecC = vec.slice(highLimit, 0);
final v = vecC.value.toBigInt().toUnsigned(maxHigh);
final spacer =
colWidth - ((sepPos != null) && (sepPos == endCol) ? 1 : 0);
str
..write(markDown ? dataSep : '${' ' * spacer} = ')
..write('$v (${v.toSigned(maxHigh)})');
}
if (markDown) {
str.write(' $dataSepEnd');
}
Expand Down
7 changes: 5 additions & 2 deletions lib/src/arithmetic/evaluate_compressor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,14 @@ extension EvaluateLiveColumnCompressor on ColumnCompressor {
}
}
rowBits.addAll(List.filled(pp.rowShift[row], LogicValue.zero));
final val = rowBits.swizzle().zeroExtend(width).toBigInt();
final rowBitsExtend = rowBits.length < width
? rowBits.swizzle().zeroExtend(width)
: rowBits.swizzle();
final val = rowBitsExtend.toBigInt();

accum += val;
if (printOut) {
ts.write('\t${rowBits.swizzle().zeroExtend(width).bitString} ($val)');
ts.write('\t${rowBitsExtend.bitString} ($val)');
if (row == rows - 1) {
ts.write(' Total=${accum.toSigned(width)}\n');
stdout.write(ts);
Expand Down
Loading

0 comments on commit f2b0458

Please sign in to comment.