Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Floatingpoint #97

Merged
merged 19 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,14 @@ Some in-development items will have opened issues, as well. Feel free to create
- Sort
- [Bitonic sort](./components/sort.md#bitonic-sort)
- Arithmetic
- [Prefix Trees](./components/parallel_prefix_operations.md)
- [Prefix Trees](./components/parallel_prefix_operations.md) Several efficient components that leverage a variety of parallel prefix trees such as Ripple, Kogge-Stone, Sklansky, and Brent-Kung tree types.
- [Priority Encoder](./components/parallel_prefix_operations.md)
- [Or-scan](./components/parallel_prefix_operations.md)
- [Incrementer](./components/parallel_prefix_operations.md)
- [Decrementer](./components/parallel_prefix_operations.md)
- [Adders](./components/adder.md)
- [Sign Magnitude Adder](./components/adder.md#ripple-carry-adder)
- [Parallel Prefix Adder](./components/parallel_prefix_operations.md)
- Subtractors
- [One's Complement Adder Subtractor](./components/adder.md#ones-complement-adder-subtractor)
- Multipliers
Expand All @@ -48,11 +53,13 @@ Some in-development items will have opened issues, as well. Feel free to create
- Square root
- Inverse square root
- Floating point
- [Floating-Point Value Types](./components/floating_point.md)
- Double (64-bit)
- Float (32-bit)
- BFloat16 (16-bit)
- BFloat8 (8-bit)
- BFloat4 (4-bit)
- [Simple Floating-Point Adder](./components/floating_point.md#floatingpointadder)
- Fixed point
- Binary-Coded Decimal (BCD)
- [Rotate](./components/rotate.md)
Expand Down
37 changes: 37 additions & 0 deletions doc/components/floating_point.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Floating-Point Components

Floating-point operations require meticulous precision, and have standards like [IEEE-754](<https://standards.ieee.org/ieee/754/6210/>) which govern them. To support floating-point components, we have created a parallel to `Logic`/`LogicValue` which are part of [ROHD](<https://intel.github.io/rohd-website/>). Here, `FloatingPoint` is the `Logic` wire in a component that carries `FloatingPointValue` literal values. An important distinction is that these classes are parameterized to create arbitrary size floating-point values.

## FloatingPointValue

The `FloatingPointValue` class comprises the sign, exponent, and mantissa `LogicValue`s that represent a floating-point number. `FloatingPointValue`s can be converted to and from Dart native `Double`s, as well as constructed from integer and string representations of their fields. They can be operated on (+, -, *, /) and compared.

A `FloatingPointValue` has a mantissa in $[0,2)$ with

$$0 <= exponent <= maxExponent$$

A normal `isNormal` `FloatingPointValue` has:

$$minExponent <= exponent <= maxExponent$$

And a mantissa in the range of $[1,2)$. Subnormal numbers are represented with a zero exponent and leading zeros in the mantissa capture the negative exponent value.

The various IEEE constants representing corner cases of the field of floating-point values for a given size of `FloatingPointValue`: infinities, zeros, limits for normal (e.g. mantissa in the range of $[1,2])$ and sub-normal numbers (zero exponent, and mantissa <1).

Appropriate string representations, comparison operations, and operators are available. The usefulness of `FloatingPointValue` is in the testing of `FloatingPoint` components, where we can leverage the abstraction of a floating-point value type to drive and compare floating-point values operated upon by floating-point components.

As 32-bit single precision and 64-bit double-precision floating-point types are most common, we have `FloatingPoint32Value` and `FloatingPoint64Value` subclasses with direct converters from Dart native Double.

Finally, we have a `FloatingPointValue` random generator for testing purposes, generating valid floating-point types, optionally constrained to normal range (mantissa in $[1, 2)$).

## FloatingPoint

The `FloatingPoint` type is a `LogicStructure` which comprises the `Logic` bits for the sign, exponent, and mantissa used in hardware floating-point. These types are provided to simplify and abstract the declaration and manipulation of floating-point types in hardware. This type is parameterized like `FloatingPointValue`, for exponent and mantissa width.

Again, like `FloatingPointValue`, `FloatingPoint64` and `FloatingPoint32` subclasses are provided as these are the most common floating-point number types.

## FloatingPointAdder

A very basic `FloatingPointAdder` component is available which does not perform any rounding. It takes two `FloatingPoint` `LogicStructure`s and adds them, returning a normalized `FloatingPoint` on the output. An option on input is the type of `ParallelPrefixTree` used in the internal addition of the mantissas.

Currently, the `FloatingPointAdder` is close in accuracy (as it has no rounding) and is not optimized for circuit performance, but only provides the key functionalities of alignment, addition, and normalization. Still, this component is a starting point for more realistic floating-point components that leverage the logical `FloatingPoint` and literal `FloatingPointValue` type abstractions.
2 changes: 1 addition & 1 deletion doc/components/multiplier.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ Here is an example of use of the `CompressionTreeMultiplier`:

## Compression Tree Multiply Accumulate

A compression tree multiply accumulate is similar to a compress tree
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.

Expand Down
39 changes: 38 additions & 1 deletion doc/components/multiplier_components.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ The partial product generator produces a set of addends in shifted position to b

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 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.
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|
|:-----:|:-------:|:-------:|:-------:|
Expand Down Expand Up @@ -199,3 +199,40 @@ Finally, we produce the product.
compressor.exractRow(0), compressor.extractRow(1), BrentKung.new);
product <= adder.sum.slice(a.width + b.width - 1, 0);
```

## Utility: Aligned Vector Formatting

We provide an extension on `LogicValue` which permits formatting of binary vectors in an aligned way to help with debugging arithmetic components.

The `vecString` extension provides a basic string printer with an optional `header` flag for bit numbering. A `prefix` value can be used to specify the name lengths to be used to keep vectors aligned.

`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.
`sepChar` is the separation character you wish to use (do not use '|' with Markdown formatting.)

```dart
final ref = FloatingPoint64Value.fromDouble(3.14159);
print(ref.mantissa
.vecString('pi', alignHigh: 55, alignLow: 40, header: true, sepPos: 52));
```

Produces

```text
54 53 52* 51 50 49 48 47 46 45 44 43 42 41 40
pi * 1 0 0 1 0 0 1 0 0 0 0 1
```

The routine also allows for output in Markdown format:

```dart
print(ref.mantissa.vecString('pi',
alignHigh: 58, alignLow: 40, header: true, sepPos: 52, markDown: true));
```

producing:

| Name | 54 | 53 | 52* | 51 | 50 | 49 | 48 | 47 | 46 | 45 | 44 | 43 | 42 | 41 | 40 |
|:--:|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:---|
|pi|||* | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
1 change: 1 addition & 0 deletions lib/src/arithmetic/arithmetic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
export 'adder.dart';
export 'carry_save_mutiplier.dart';
export 'divider.dart';
export 'floating_point/floating_point.dart';
export 'multiplier.dart';
export 'multiplier_lib.dart';
export 'ones_complement_adder.dart';
Expand Down
118 changes: 118 additions & 0 deletions lib/src/arithmetic/arithmetic_utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// floating_point_test.dart
// Tests of Floating Point stuff
//
// 2024 August 30
// Author: Desmond A Kirkpatrick <desmond.a.kirkpatrick@intel.com

// ignore_for_file: avoid_print

import 'dart:math';

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.
/// [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.
/// 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.
desmonddak marked this conversation as resolved.
Show resolved Hide resolved
/// The output can have space by setting [extraSpace]
String vecString(String name,
{int prefix = 10,
int? alignHigh,
int? sepPos,
bool header = false,
String sepChar = '*',
int alignLow = 0,
int extraSpace = 0,
bool markDown = false}) {
final str = StringBuffer();
final minHigh = min(alignHigh ?? width, width);
final length = BigInt.from(minHigh).toString().length + extraSpace;
// ignore: cascade_invocations
const hdrSep = '| ';
const hdrSepStart = '| ';
const hdrSepEnd = '|';

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

if (header) {
str.write(markDown ? '$hdrSepStart Name' : ' ' * prefix);

for (var col = highLimit; col >= alignLow; col--) {
final chars = BigInt.from(col).toString().length + extraSpace;
if (sepPos != null && sepPos == col) {
str
..write(
markDown ? ' $hdrSep' : ' ' * (length - chars + 1 + extraSpace))
..write('$col$sepChar')
..write(markDown ? ' $hdrSep' : '');
} else if (sepPos != null && sepPos == col + 1) {
if (sepPos == max(alignHigh ?? width, width)) {
str
..write(sepChar)
..write(markDown ? ' $hdrSep' : ' ' * (length - chars - 1));
}
str.write('${' ' * (length - chars + extraSpace + 0)}$col');
} else {
str
..write(
markDown ? ' $hdrSep' : ' ' * (length - chars + 1 + extraSpace))
..write('$col');
}
}
str.write(markDown ? ' $hdrSepEnd\n' : '\n');
if (markDown) {
str.write(markDown ? '|:--:' : ' ' * prefix);
for (var col = highLimit; col >= alignLow; col--) {
str.write('|:--');
}
str.write('-|\n');
}
}
const dataSepStart = '|';
const dataSep = '| ';
const dataSepEnd = '|';
final String strPrefix;
strPrefix = markDown
? '$dataSepStart $name'
: (name.length <= prefix)
? name.padRight(prefix)
: name.substring(0, prefix);
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;
if (sepPos != null && sepPos == pos) {
str.write(
markDown ? ' $dataSep$v $sepChar' : '${' ' * length}$v$sepChar');
} else if (sepPos != null && sepPos == pos + 1) {
if (sepPos == minHigh) {
str.write(sepChar);
}
str
..write(markDown ? ' $dataSep' : ' ' * (length - 1))
..write(v);
} else {
str
..write(markDown ? ' $dataSep' : ' ' * length)
..write(v);
}
}
if (markDown) {
str.write(' $dataSepEnd');
}
return str.toString();
}
}
5 changes: 1 addition & 4 deletions lib/src/arithmetic/evaluate_compressor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ extension EvaluateLiveColumnCompressor on ColumnCompressor {
}
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)');
Expand All @@ -52,10 +53,6 @@ extension EvaluateLiveColumnCompressor on ColumnCompressor {
}
}
}
if (printOut) {
// We need this to be able to debug, but git lint flunks print
// print(ts);
}
return (accum.toSigned(width), ts);
}

Expand Down
71 changes: 70 additions & 1 deletion lib/src/arithmetic/evaluate_partial_product.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ extension EvaluateLivePartialProduct on PartialProductGenerator {
str.write(' ' * shortPrefix);
}
} else {
str.write('$rowStr ${'M='} S= : ');
str.write('$rowStr ${'M='} S= : ');
}
final entry = partialProducts[row].reversed.toList();
final prefixCnt =
Expand Down Expand Up @@ -104,4 +104,73 @@ extension EvaluateLivePartialProduct on PartialProductGenerator {
}
return str.toString();
}

/// Print out the partial product matrix
String markdown() {
final str = StringBuffer();

final maxW = maxWidth();
// print bit position header
str.write('| R | M | S');
for (var i = maxW - 1; i >= 0; i--) {
str.write('| $i ');
}
str
..write('| bitvector | value|\n')
..write('|:--:' * 3);
for (var i = maxW - 1; i >= 0; i--) {
str.write('|:--:');
}
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| '
'$multiple| '
'${encoding.sign.value.toInt()}');
} else {
str.write('| | |');
}
} else {
str.write('|$rowStr | |');
}
final entry = partialProducts[row].reversed.toList();
str.write('| ' * (maxW - (entry.length + rowShift[row])));
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 : "<invalid>"}'
' (${value.isValid ? intValue.toSigned(maxW) : "<invalid>"})|\n');
}
// Compute and print binary representation from accumulated value
// Later: we will compare with a compression tree result
str.write('||\n');

final sum = LogicValue.ofBigInt(evaluate(), maxW);
// print out the sum as a MSB-first bitvector
str.write('|||');
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)');
}
str.write('|\n');
return str.toString();
}
}
6 changes: 6 additions & 0 deletions lib/src/arithmetic/floating_point/floating_point.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause

export 'floating_point_adder.dart';
export 'floating_point_logic.dart';
export 'floating_point_value.dart';
Loading
Loading