Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
71 changes: 71 additions & 0 deletions lib/src/arithmetic/floating_point/fft/bad_fft.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (C) 2025 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause

import 'package:rohd/rohd.dart';

// class FFT extends Module {
// LogicArray get out => output('out') as LogicArray;
//
// FFT(Logic en, Logic clk, Logic reset, LogicArray input, {super.name = 'fft'})
// : assert(input.dimensions.length == 1) {
// final int length = input.dimensions[0];
// if ((length & (~(length - 1))) != length) {
// assert(false);
// }
// final int log2Length = log2Ceil(length);
//
// input = addInputArray(
// 'input_array',
// input,
// dimensions: input.dimensions, // it seems like these are needed
// elementWidth: input.elementWidth,
// numUnpackedDimensions: input.numUnpackedDimensions,
// );
//
// List<LogicArray> stageArrays = List.generate(
// log2Length + 1,
// (stage) => LogicArray(
// input.dimensions,
// input.elementWidth,
// name: 'stage${stage}Array',
// numUnpackedDimensions: input.numUnpackedDimensions,
// ),
// );
//
// LogicArray out = addOutputArray(
// 'out',
// dimensions: input.dimensions,
// elementWidth: input.elementWidth,
// numUnpackedDimensions: input.numUnpackedDimensions,
// );
// out <= stageArrays[log2Length];
//
// List<List<Conditional> Function(PipelineStageInfo)> fftStages = [];
//
// fftStages.add((p) => [stageArrays[0] < BitReverse(input).out]);
//
// for (var s = 1; s <= log2Length; s++) {
// final m = 1 << s;
// final mShift = log2Ceil(m);
//
// Counter i = Counter(en, reset, clk, width: log2Length - 1);
//
// Logic k = (i.val >> (mShift - 1)) << mShift;
// Logic j = (i.val & Const((m >> 1) - 1, width: i.width));
// }
//
// // ReadyValidPipeline()
//
// // for s = 1 to log(n) do
// // m ← 2s
// // ωm ← exp(−2πi/m)
// // for k = 0 to n-1 by m do
// // ω ← 1
// // for j = 0 to m/2 – 1 do
// // t ← ω A[k + j + m/2]
// // u ← A[k + j]
// // A[k + j] ← u + t
// // A[k + j + m/2] ← u – t
// // ω ← ω ωm
// }
// }
31 changes: 31 additions & 0 deletions lib/src/arithmetic/floating_point/fft/butterfly.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (C) 2025 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please update headers on the files to match the recommended style, including the name of the file, date authored, purpose of the file, etc.


import 'package:rohd/rohd.dart';
import 'package:rohd_hcl/src/arithmetic/signals/floating_point_logics/complex_floating_point_logic.dart';

class Butterfly extends Module {
final ComplexFloatingPoint inA;
final ComplexFloatingPoint inB;
final ComplexFloatingPoint twiddleFactor;

late final outA = inA.clone()..gets(output('outA'));
late final outB = inA.clone()..gets(output('outB'));

Butterfly(
{required this.inA,
required this.inB,
required this.twiddleFactor,
super.name = 'butterfly'}) {
addInput('inA', inA, width: inA.width);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

heads up, you'll want to use the signal returned by addInput or input for internal computation rather than the one provided in the constructor

addInput('inB', inB, width: inA.width);

final outA = addOutput('outA', width: inA.width);
final outB = addOutput('outB', width: inA.width);

final temp = twiddleFactor.multiplier(inB);

outB <= inA.adder(temp.negated);
outA <= inA.adder(temp);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright (C) 2024-2025 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause

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

class ComplexFloatingPoint extends LogicStructure {
final FloatingPoint realPart;

final FloatingPoint imaginaryPart;

static String _nameJoin(String? structName, String signalName) {
if (structName == null) {
return signalName;
}
return '${structName}_$signalName';
}

ComplexFloatingPoint({
required int exponentWidth,
required int mantissaWidth,
String? name,
}) : this._internal(
realPart: FloatingPoint(
exponentWidth: exponentWidth,
mantissaWidth: mantissaWidth,
name: _nameJoin(name, 're'),
),
imaginaryPart: FloatingPoint(
exponentWidth: exponentWidth,
mantissaWidth: mantissaWidth,
name: _nameJoin(name, 'im'),
),
name: name,
);

ComplexFloatingPoint._internal(
{required this.realPart, required this.imaginaryPart, super.name})
: assert(realPart.exponent.width == imaginaryPart.exponent.width),
assert(realPart.mantissa.width == imaginaryPart.mantissa.width),
super([realPart, imaginaryPart]);

@mustBeOverridden
@override
ComplexFloatingPoint clone({String? name}) => ComplexFloatingPoint(
exponentWidth: realPart.exponent.width,
mantissaWidth: realPart.mantissa.width,
name: name,
);

ComplexFloatingPoint adder(ComplexFloatingPoint other) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not override operator + and *?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to hide the fact that it's expensive

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the names of the functions seem unintuitive to me, maybe plus and times or addedTo and multipliedBy or something?

if we wanted to stay more consistent with other parts of the library, these could be their own components (classes, modules) e.g. ComplexFloatingPointMultiplier, perhaps with an argument that allows you to choose your own FloatingPointMultiplier implementation for the internals.

ComplexFloatingPoint._internal(
realPart: FloatingPointAdderSinglePath(realPart, other.realPart).sum,
imaginaryPart:
FloatingPointAdderSinglePath(imaginaryPart, other.imaginaryPart)
.sum,
name: _nameJoin(name, "adder"));

ComplexFloatingPoint multiplier(ComplexFloatingPoint other) {
// use only 3 multipliers: https://mathworld.wolfram.com/ComplexMultiplication.html
final ac = FloatingPointMultiplierSimple(realPart, other.realPart).product;
final bd = FloatingPointMultiplierSimple(imaginaryPart, other.imaginaryPart)
.product;
final abcd = FloatingPointMultiplierSimple(
FloatingPointAdderSinglePath(realPart, imaginaryPart).sum,
FloatingPointAdderSinglePath(other.realPart, other.imaginaryPart)
.sum)
.product;

return ComplexFloatingPoint._internal(
realPart: FloatingPointAdderSinglePath(ac, bd.negated()).sum,
imaginaryPart: FloatingPointAdderSinglePath(abcd,
FloatingPointAdderSinglePath(ac.negated(), bd.negated()).sum)
.sum,
name: _nameJoin(name, "multiplier"));
}

late final ComplexFloatingPoint negated = ComplexFloatingPoint._internal(
realPart: realPart.negated(),
imaginaryPart: imaginaryPart.negated(),
name: _nameJoin(name, "negated"));
}
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@ class FloatingPoint extends LogicStructure {
}
}

FloatingPoint negated() => FloatingPoint._(
~sign,
exponent.clone()..gets(exponent),
mantissa.clone()..gets(mantissa),
_explicitJBit);

/// Construct a FloatingPoint that represents infinity.
factory FloatingPoint.inf(
{required int exponentWidth,
Expand Down
44 changes: 44 additions & 0 deletions lib/src/bit_reversal.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (C) 2021-2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause

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

int bitReverse(int value, int bits) {
var reversed = 0;
for (var i = 0; i < bits; i++) {
reversed <<= 1;
reversed |= value & 1;
value >>= 1;
}
return reversed;
}

class BitReversal extends Module {
LogicArray get out => output('out') as LogicArray;

BitReversal(LogicArray input, {super.name = 'bit_reversal'})
: assert(input.dimensions.length == 1, 'Can only bit reverse 1D arrays') {
input = addInputArray(
'input_array',
input,
dimensions: input.dimensions, // it seems like these are needed
elementWidth: input.elementWidth,
numUnpackedDimensions: input.numUnpackedDimensions,
);

final out = addOutputArray(
'out',
dimensions: input.dimensions,
elementWidth: input.elementWidth,
numUnpackedDimensions: input.numUnpackedDimensions,
);

final length = input.dimensions[0];
final bits = log2Ceil(length);

for (var i = 0; i < length; i++) {
out.elements[bitReverse(i, bits)] <= input.elements[i];
}
}
}
46 changes: 46 additions & 0 deletions test/arithmetic/floating_point/butterfly_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (C) 2025 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause

import 'package:rohd/rohd.dart';
import 'package:rohd_hcl/rohd_hcl.dart';
import 'package:rohd_hcl/src/arithmetic/floating_point/fft/butterfly.dart';
import 'package:rohd_hcl/src/arithmetic/signals/floating_point_logics/complex_floating_point_logic.dart';
import 'package:test/test.dart';

ComplexFloatingPoint newComplex(double real, double imaginary) {
final realFP = FloatingPoint64();
final imaginaryFP = FloatingPoint64();

final realFPValue = FloatingPoint64Value.populator().ofDouble(real);
final imaginaryFPValue = FloatingPoint64Value.populator().ofDouble(imaginary);

realFP.put(realFPValue);
imaginaryFP.put(imaginaryFPValue);

final complex = ComplexFloatingPoint(
exponentWidth: realFP.exponent.width,
mantissaWidth: realFP.mantissa.width);
complex.realPart <= realFP;
complex.imaginaryPart <= imaginaryFP;

return complex;
}

void main() {
tearDown(() async {
await Simulator.reset();
});

test('butterfly unit test', () {
final a = newComplex(1.0, 2.0);
final b = newComplex(-3.0, -4.0);
final twiddle = newComplex(1.0, 0.0);

final butterfly = Butterfly(inA: a, inB: b, twiddleFactor: twiddle);

expect(butterfly.outA.realPart.floatingPointValue.toDouble(), -2.0);
expect(butterfly.outA.imaginaryPart.floatingPointValue.toDouble(), -2.0);
expect(butterfly.outB.realPart.floatingPointValue.toDouble(), 4.0);
expect(butterfly.outB.imaginaryPart.floatingPointValue.toDouble(), 6.0);
});
}
57 changes: 57 additions & 0 deletions test/arithmetic/floating_point/complex_floating_point_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (C) 2025 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause

import 'package:rohd/rohd.dart';
import 'package:rohd_hcl/rohd_hcl.dart';
import 'package:rohd_hcl/src/arithmetic/signals/floating_point_logics/complex_floating_point_logic.dart';
import 'package:test/test.dart';

ComplexFloatingPoint newComplex(double real, double imaginary) {
final realFP = FloatingPoint64();
final imaginaryFP = FloatingPoint64();

final realFPValue = FloatingPoint64Value.populator().ofDouble(real);
final imaginaryFPValue = FloatingPoint64Value.populator().ofDouble(imaginary);

realFP.put(realFPValue);
imaginaryFP.put(imaginaryFPValue);

final complex = ComplexFloatingPoint(
exponentWidth: realFP.exponent.width,
mantissaWidth: realFP.mantissa.width);
complex.realPart <= realFP;
complex.imaginaryPart <= imaginaryFP;

return complex;
}

void main() {
tearDown(() async {
await Simulator.reset();
});

test('complex constructor', () {
final complex = newComplex(1.23, 3.45);

expect(complex.realPart.floatingPointValue.toDouble(), 1.23);
expect(complex.imaginaryPart.floatingPointValue.toDouble(), 3.45);
});

test('complex addition', () {
final a = newComplex(1.0, 0.0);
final b = newComplex(0.0, -1.0);
final c = a.adder(b);

expect(c.realPart.floatingPointValue.toDouble(), 1.0);
expect(b.imaginaryPart.floatingPointValue.toDouble(), -1.0);
});

test('complex multiplication', () {
final a = newComplex(1.0, 2.0);
final b = newComplex(-3.0, -4.0);
final c = a.multiplier(b);

expect(c.realPart.floatingPointValue.toDouble(), 5.0);
expect(c.imaginaryPart.floatingPointValue.toDouble(), -10.0);
});
}
Loading