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

Floating Point Square Root Module with Variable Odd Mantissa and No Rounding #188

Merged
merged 5 commits into from
Mar 20, 2025
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
4 changes: 4 additions & 0 deletions doc/components/fixed_point.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,7 @@ Currently, the FloatToFixed converter, when in lossy mode, is not performing any
## Float8ToFixed

This component converts an 8-bit floating-point (FP8) representation ([FloatingPoint8E4M3Value](https://intel.github.io/rohd-hcl/rohd_hcl/FloatingPoint8E4M3Value-class.html) or [FloatingPoint8E5M2Value](https://intel.github.io/rohd-hcl/rohd_hcl/FloatingPoint8E5M2Value-class.html)) to a signed fixed-point representation. This component offers using the same hardware for both FP8 formats. Therefore, both input and output are of type [Logic](https://intel.github.io/rohd/rohd/Logic-class.html) and can be cast from/to floating point/fixed point by the producer/consumer based on the selected `mode`. Infinities and NaN's are not supported. The output width is 33bits to accommodate [FloatingPoint8E5M2Value](https://intel.github.io/rohd-hcl/rohd_hcl/FloatingPoint8E5M2Value-class.html) without loss.

## FixedPointSqrt

This component computes the square root of a 3.x fixed-point value, returning a result in the same format. The square root value is rounded to the ordered number of bits. The integral part must be 3 bits, and the fractional part may be any odd value <= 51. Even numbers of bits are currently not supported, integral bits in numbers other than 3 are currently not supported.
11 changes: 11 additions & 0 deletions doc/components/floating_point.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ Currently, the [FloatingPointAdderSimple](https://intel.github.io/rohd-hcl/rohd_

A second [FloatingPointAdderRound](https://intel.github.io/rohd-hcl/rohd_hcl/FloatingPointAdderRound-class.html) component is available which does perform rounding. It is based on "Delay-Optimized Implementation of IEEE Floating-Point Addition", by Peter-Michael Seidel and Guy Even, using an R-path and an N-path to process far-apart exponents and use rounding and an N-path for exponents within 2 and subtraction, which is exact. If you pass in an optional clock, a pipe stage will be added to help optimize frequency; an optional reset and enable are can control the pipe stage.

## FloatingPointSqrt

A very basic [FloatingPointSqrtSimple] component is available which does not perform any
rounding and does not support DeNorm numbers. It also only operates on variable mantissas of an odd value (1,3,5,etc) but these odd mantissas can be of variable length up to 51. It takes one
[FloatingPoint](https://intel.github.io/rohd-hcl/rohd_hcl/FloatingPoint-class.html) [LogicStructure](https://intel.github.io/rohd/rohd/LogicStructure-class.html) and
performs a square root on it, returning the [FloatingPoint](https://intel.github.io/rohd-hcl/rohd_hcl/FloatingPoint-class.html) value on the output.

Currently, the [FloatingPointSqrtSimple](https://intel.github.io/rohd-hcl/rohd_hcl/FloatingPointSqrtSimple-class.html) is close in accuracy (as it has no rounding) and is not
optimized for circuit performance, but provides the key functionalities of floating-point square root. Still, this component is a starting point for more realistic
floating-point components that leverage the the logical [FloatingPoint](https://intel.github.io/rohd-hcl/rohd_hcl/FloatingPoint-class.html) and literal [FloatingPointValue](https://intel.github.io/rohd-hcl/rohd_hcl/FloatingPointValue-class.html) type abstractions.

## FloatingPointMultiplier

A very basic [FloatingPointMultiplierSimple] component is available which does not perform any rounding. It takes two [FloatingPoint](https://intel.github.io/rohd-hcl/rohd_hcl/FloatingPoint-class.html) [LogicStructure](https://intel.github.io/rohd/rohd/LogicStructure-class.html)s and multiplies them, returning a normalized [FloatingPoint](https://intel.github.io/rohd-hcl/rohd_hcl/FloatingPoint-class.html) on the output 'product'.
Expand Down
1 change: 1 addition & 0 deletions lib/src/arithmetic/arithmetic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export 'arithmetic_utils.dart';
export 'carry_save_mutiplier.dart';
export 'compound_adder.dart';
export 'divider.dart';
export 'fixed_sqrt.dart';
export 'fixed_to_float.dart';
export 'float_to_fixed.dart';
export 'floating_point/floating_point.dart';
Expand Down
102 changes: 102 additions & 0 deletions lib/src/arithmetic/fixed_sqrt.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright (C) 2025 Intel Corporation
// SPDX-License-Indentifier: BSD-3-Clause
//
// fixed_point_sqrt.dart
// An abstract base class defining the API for floating-point square root.
//
// 2025 March 3
// Authors: James Farwell <james.c.farwell@intel.com>,
// Stephen Weeks <stephen.weeks@intel.com>

// An abstract API for fixed point square root.
import 'package:meta/meta.dart';
import 'package:rohd/rohd.dart';
import 'package:rohd_hcl/rohd_hcl.dart';

/// Base class for fixed-point square root
abstract class FixedPointSqrtBase extends Module {
/// Width of the input and output fields.
final int width;

/// The value [a], named this way to allow for a local variable 'a'.
@protected
late final FixedPoint a;

/// getter for the computed output.
late final FixedPoint sqrt = a.clone(name: 'sqrt')..gets(output('sqrt'));

/// Square root a fixed point number [a], returning result in [sqrt].
FixedPointSqrtBase(FixedPoint a,
{super.name = 'fixed_point_square_root', String? definitionName})
: width = a.width,
super(
definitionName:
definitionName ?? 'FixedPointSquareRoot${a.width}') {
this.a = a.clone(name: 'a')..gets(addInput('a', a, width: a.width));

addOutput('sqrt', width: width);
}
}

/// Implementation
/// Algorithm explained here;
/// https://projectf.io/posts/square-root-in-verilog/
class FixedPointSqrt extends FixedPointSqrtBase {
/// Constructor
FixedPointSqrt(super.a) {
if (a.signed) {
throw RohdHclException('Signed values not supported');
}

Logic solution =
FixedPoint(signed: a.signed, name: 'solution', m: a.m + 1, n: a.n + 1);
Logic remainder =
FixedPoint(signed: a.signed, name: 'remainder', m: a.m + 1, n: a.n + 1);
Logic subtractionValue =
FixedPoint(signed: a.signed, name: 'subValue', m: a.m + 1, n: a.n + 1);
Logic aLoc =
FixedPoint(signed: a.signed, name: 'aLoc', m: a.m + 1, n: a.n + 1);

solution = Const(0, width: aLoc.width);
remainder = Const(0, width: aLoc.width);
subtractionValue = Const(0, width: aLoc.width);
aLoc = [Const(0), a, Const(0)].swizzle();

final outputSqrt = a.clone(name: 'sqrt');
output('sqrt') <= outputSqrt;

// loop once through input value
for (var i = 0; i < ((width + 2) >> 1); i++) {
// append bits from a, two at a time
remainder = [
remainder.slice(width + 2 - 3, 0),
aLoc.slice(aLoc.width - 1 - (i * 2), aLoc.width - 2 - (i * 2))
].swizzle();
subtractionValue =
[solution.slice(width + 2 - 3, 0), Const(1, width: 2)].swizzle();
solution = [
solution.slice(width + 2 - 2, 0),
subtractionValue.lte(remainder)
].swizzle();
remainder = mux(subtractionValue.lte(remainder),
remainder - subtractionValue, remainder);
}

// loop again to finish remainder
for (var i = 0; i < ((width + 2) >> 1) - 1; i++) {
// don't try to append bits from a, they are done
remainder =
[remainder.slice(width + 2 - 3, 0), Const(0, width: 2)].swizzle();
subtractionValue =
[solution.slice(width + 2 - 3, 0), Const(1, width: 2)].swizzle();
solution = [
solution.slice(width + 2 - 2, 0),
subtractionValue.lte(remainder)
].swizzle();
remainder = mux(subtractionValue.lte(remainder),
remainder - subtractionValue, remainder);
}
solution = solution + 1;
outputSqrt <= solution.slice(aLoc.width - 1, aLoc.width - a.width);
}
}
2 changes: 2 additions & 0 deletions lib/src/arithmetic/floating_point/floating_point.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ export 'floating_point_converter.dart';
export 'floating_point_multiplier.dart';
export 'floating_point_multiplier_simple.dart';
export 'floating_point_rounding.dart';
export 'floating_point_sqrt.dart';
export 'floating_point_sqrt_simple.dart';
export 'floating_point_utilities.dart';
81 changes: 81 additions & 0 deletions lib/src/arithmetic/floating_point/floating_point_sqrt.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (C) 2025 Intel Corporation
// SPDX-License-Indentifier: BSD-3-Clause
//
// floating_point_sqrt.dart
// An abstract base class defining the API for floating-point square root.
//
// 2025 March 3
// Authors: James Farwell <james.c.farwell@intel.com>,
// Stephen Weeks <stephen.weeks@intel.com>,
// Curtis Anderson <curtis.anders@intel.com>

import 'package:meta/meta.dart';
import 'package:rohd/rohd.dart';
import 'package:rohd_hcl/rohd_hcl.dart';

/// An abstract API for floating point square root.
abstract class FloatingPointSqrt<FpType extends FloatingPoint> extends Module {
/// Width of the output exponent field.
final int exponentWidth;

/// Width of the output mantissa field.
final int mantissaWidth;

/// The [clk] : if a non-null clock signal is passed in, a pipestage is added
/// to the square root to help optimize frequency.
/// Plumbed for future work for pipelining, currently unsupported.
@protected
late final Logic? clk;

/// Optional [reset], used only if a [clk] is not null to reset the pipeline
/// flops.
/// Plumbed for future work for pipelining, currently unsupported.
@protected
late final Logic? reset;

/// Optional [enable], used only if a [clk] is not null to enable the pipeline
/// flops.
/// Plumbed for future work for pipelining, currently unsupported.
@protected
late final Logic? enable;

/// The value [a], named this way to allow for a local variable 'a'.
@protected
late final FpType a;

/// getter for the computed [FloatingPoint] output.
late final FloatingPoint sqrt = (a.clone(name: 'sqrt') as FpType)
..gets(output('sqrt'));

/// getter for the [error] output.
late final Logic error = Logic(name: 'error')..gets(output('error'));

/// Square root a floating point number [a], returning result in [sqrt].
/// - [clk], [reset], [enable] are optional inputs to control a pipestage
/// (only inserted if [clk] is provided)
FloatingPointSqrt(FpType a,
{Logic? clk,
Logic? reset,
Logic? enable,
super.name = 'floating_point_square_root',
String? definitionName})
: exponentWidth = a.exponent.width,
mantissaWidth = a.mantissa.width,
super(
definitionName: definitionName ??
'FloatingPointSquareRoot_E${a.exponent.width}'
'M${a.mantissa.width}') {
this.clk = (clk != null) ? addInput('clk', clk) : null;
this.reset = (reset != null) ? addInput('reset', reset) : null;
this.enable = (enable != null) ? addInput('enable', enable) : null;
this.a = (a.clone(name: 'a') as FpType)
..gets(addInput('a', a, width: a.width));

addOutput('sqrt', width: exponentWidth + mantissaWidth + 1);
addOutput('error');
}

/// Pipelining helper that uses the context for signals clk/enable/reset
Logic localFlop(Logic input) =>
condFlop(clk, input, en: enable, reset: reset);
}
112 changes: 112 additions & 0 deletions lib/src/arithmetic/floating_point/floating_point_sqrt_simple.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright (C) 2025 Intel Corporation
// SPDX-License-Indentifier: BSD-3-Clause
//
// floating_point_sqrt.dart
// An abstract base class defining the API for floating-point square root.
//
// 2025 March 4
// Authors: James Farwell <james.c.farwell@intel.com>,
// Stephen Weeks <stephen.weeks@intel.com>,
// Curtis Anderson <curtis.anders@intel.com>

import 'package:rohd/rohd.dart';
import 'package:rohd_hcl/rohd_hcl.dart';

/// An square root module for FloatingPoint values
class FloatingPointSqrtSimple<FpType extends FloatingPoint>
extends FloatingPointSqrt<FpType> {
/// Square root one floating point number [a], returning results
/// [sqrt] and [error]
FloatingPointSqrtSimple(super.a,
{super.clk,
super.reset,
super.enable,
super.name = 'floatingpoint_square_root_simple'})
: super(
definitionName: 'FloatingPointSquareRootSimple_'
'E${a.exponent.width}M${a.mantissa.width}') {
final outputSqrt = FloatingPoint(
exponentWidth: exponentWidth,
mantissaWidth: mantissaWidth,
name: 'sqrt');
output('sqrt') <= outputSqrt;
late final error = output('error');

// check to see if we do sqrt at all or just return a
final isInf = a.isAnInfinity.named('isInf');
final isNaN = a.isNaN.named('isNan');
final isZero = a.isAZero.named('isZero');
final isDeNormal = ~a.isNormal.named('isDenorm');
final enableSqrt =
~((isInf | isNaN | isZero | isDeNormal) | a.sign).named('enableSqrt');

// debias the exponent
final deBiasAmt = (1 << a.exponent.width - 1) - 1;

// deBias math
final deBiasExp = a.exponent - deBiasAmt;

// shift exponent
final shiftedExp = [deBiasExp[-1], deBiasExp.slice(a.exponent.width - 1, 1)]
.swizzle()
.named('deBiasExp');

// check if exponent was odd
final isExpOdd = deBiasExp[0];

// use fixed sqrt unit
final aFixed = FixedPoint(signed: false, m: 3, n: a.mantissa.width);
aFixed <= [Const(1, width: 3), a.mantissa.getRange(0)].swizzle();

// mux if we shift left by 1 if exponent was odd
final aFixedAdj = aFixed.clone()
..gets(mux(isExpOdd, [aFixed.slice(-2, 0), Const(0)].swizzle(), aFixed)
.named('oddMantissaMux'));

// mux to choose if we do square root or not
final fixedSqrt = aFixedAdj.clone()
..gets(mux(enableSqrt, FixedPointSqrt(aFixedAdj).sqrt, aFixedAdj)
.named('sqrtMux'));

// convert back to floating point representation
final fpSqrt = FixedToFloat(fixedSqrt,
exponentWidth: a.exponent.width, mantissaWidth: a.mantissa.width);

// final calculation results
Combinational([
error < Const(0),
If.block([
Iff(isInf & ~a.sign, [
outputSqrt < outputSqrt.inf(),
]),
ElseIf(isInf & a.sign, [
outputSqrt < outputSqrt.inf(negative: true),
error < Const(1),
]),
ElseIf(isNaN, [
outputSqrt < outputSqrt.nan,
]),
ElseIf(isZero, [
outputSqrt.sign < a.sign,
outputSqrt.exponent < a.exponent,
outputSqrt.mantissa < a.mantissa,
]),
ElseIf(a.sign, [
outputSqrt < outputSqrt.nan,
error < Const(1),
]),
ElseIf(isDeNormal, [
outputSqrt.sign < a.sign,
outputSqrt.exponent < a.exponent,
outputSqrt.mantissa < a.mantissa,
error < Const(1),
]),
Else([
outputSqrt.sign < a.sign,
outputSqrt.exponent < (shiftedExp + deBiasAmt),
outputSqrt.mantissa < fpSqrt.float.mantissa,
])
])
]);
}
}
11 changes: 11 additions & 0 deletions lib/src/arithmetic/signals/fixed_point_logic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,13 @@ class FixedPoint extends Logic {
}
}

/// Multiply
Logic _multiply(dynamic other) {
_verifyCompatible(other);
final product = Multiply(this, other).out;
return FixedPoint.of(product, signed: false, m: 2 * m, n: 2 * n);
}

/// Greater-than.
@override
Logic operator >(dynamic other) => gt(other);
Expand All @@ -131,6 +138,10 @@ class FixedPoint extends Logic {
@override
Logic operator >=(dynamic other) => gte(other);

/// multiply
@override
Logic operator *(dynamic other) => _multiply(other);

@override
Logic eq(dynamic other) {
_verifyCompatible(other);
Expand Down
Loading