Skip to content
Open
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
35 changes: 35 additions & 0 deletions lib/src/arithmetic/floating_point/fft/butterfly.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// 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 {
late final ComplexFloatingPoint outA;
late final ComplexFloatingPoint outB;

Butterfly({
required ComplexFloatingPoint inA,
required ComplexFloatingPoint inB,
required ComplexFloatingPoint twiddleFactor,
super.name = 'butterfly',
}) {
final _inA = inA.clone()..gets(addInput('inA', inA, width: inA.width));
final _inB = inA.clone()..gets(addInput('inB', inB, width: inA.width));
final _twiddleFactor = inA.clone()
..gets(
addInput('twiddleFactor', twiddleFactor, width: twiddleFactor.width),
);

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

final temp = _twiddleFactor.multiplier(_inB);

outALogic <= _inA.adder(temp.negated);
outBLogic <= _inA.adder(temp);

outA = inA.clone()..gets(outALogic);
outB = inA.clone()..gets(outBLogic);
}
}
181 changes: 181 additions & 0 deletions lib/src/arithmetic/floating_point/fft/fft_stage.dart
Copy link
Contributor

Choose a reason for hiding this comment

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

remember to export components that are intended to be part of the public API for ROHD-HCL

Copy link
Contributor

Choose a reason for hiding this comment

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

Please add user guide descriptions on how to use new components in the doc/ directory (including links to the pages)

Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// Copyright (C) 2025 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause

import 'dart:math';

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';

class BadFFTStage extends Module {
Copy link
Contributor

Choose a reason for hiding this comment

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

why "Bad"? :)

Copy link
Author

Choose a reason for hiding this comment

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

It's not fully pipelined

Copy link
Contributor

Choose a reason for hiding this comment

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

maybe "multi-cycle" is a better name or something (if that's what it is)?

final int logStage;
final int exponentWidth;
final int mantissaWidth;
Logic clk;
Logic reset;
Logic go;
DataPortInterface inputSamplesA;
DataPortInterface inputSamplesB;
DataPortInterface twiddleFactorROM;

late final Logic done;

BadFFTStage({
Copy link
Contributor

Choose a reason for hiding this comment

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

please do write doc comments

required this.logStage,
required this.exponentWidth,
required this.mantissaWidth,
required this.clk,
required this.reset,
required this.go,
required this.inputSamplesA,
required this.inputSamplesB,
required this.twiddleFactorROM,
required DataPortInterface outputSamplesA,
required DataPortInterface outputSamplesB,
super.name = 'badfftstage',
}) : assert(go.width == 1),
assert(
inputSamplesA.dataWidth == 2 * (1 + exponentWidth + mantissaWidth),
),
assert(
inputSamplesB.dataWidth == 2 * (1 + exponentWidth + mantissaWidth),
) {
clk = addInput('clk', clk);
reset = addInput('reset', reset);
go = addInput('go', go);
final doneInner = Logic(name: '_done');
done = addOutput('done')..gets(doneInner);
final en = (go & ~done).named('enable');

inputSamplesA = addInterfacePorts(
inputSamplesA,
inputTags: [DataPortGroup.data],
outputTags: [DataPortGroup.control],
uniquify: (name) => 'inputSamplesA$name',
);
inputSamplesB = addInterfacePorts(
inputSamplesB,
inputTags: [DataPortGroup.data],
outputTags: [DataPortGroup.control],
uniquify: (name) => 'inputSamplesB$name',
);
twiddleFactorROM = addInterfacePorts(
twiddleFactorROM,
inputTags: [DataPortGroup.data],
outputTags: [DataPortGroup.control],
uniquify: (name) => 'twiddleFactorROM$name',
);

outputSamplesA = addInterfacePorts(
outputSamplesA,
inputTags: [DataPortGroup.control],
outputTags: [DataPortGroup.data],
uniquify: (name) => 'outputSamplesA$name',
);
outputSamplesB = addInterfacePorts(
outputSamplesB,
inputTags: [DataPortGroup.control],
outputTags: [DataPortGroup.data],
uniquify: (name) => 'outputSamplesB$name',
);

final outputSamplesWritePortA = DataPortInterface(
inputSamplesA.dataWidth,
inputSamplesA.addrWidth,
);
final outputSamplesWritePortB = DataPortInterface(
inputSamplesA.dataWidth,
inputSamplesA.addrWidth,
);
final outputSamplesReadPortA = DataPortInterface(
inputSamplesA.dataWidth,
inputSamplesA.addrWidth,
);
final outputSamplesReadPortB = DataPortInterface(
inputSamplesA.dataWidth,
inputSamplesA.addrWidth,
);

final n = 1 << inputSamplesA.addrWidth;
RegisterFile(
clk,
reset,
[outputSamplesWritePortA, outputSamplesWritePortB],
[outputSamplesReadPortA, outputSamplesReadPortB],
numEntries: n,
name: 'outputSamplesBuffer',
);
outputSamplesA.data <= outputSamplesReadPortA.data;
outputSamplesReadPortA.en <= outputSamplesA.en;
outputSamplesReadPortA.addr <= outputSamplesA.addr;
outputSamplesB.data <= outputSamplesReadPortB.data;
outputSamplesReadPortB.en <= outputSamplesB.en;
outputSamplesReadPortB.addr <= outputSamplesB.addr;

final log2Length = inputSamplesA.addrWidth;
final m = 1 << logStage;
final mShift = log2Ceil(m);

final i = Counter.ofLogics(
[flop(clk, en)],
clk: clk,
reset: reset | (go & doneInner),
width: max(log2Length - 1, 1),
maxValue: n ~/ 2,
name: 'i',
);
doneInner <= i.equalsMax;

final k = ((i.count >> (mShift - 1)) << mShift).named('k');
final j = (i.count & Const((m >> 1) - 1, width: i.width)).named('j');

// 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
final addressA = (k + j).named('addressA');
final addressB = (addressA + m ~/ 2).named('addressB');
inputSamplesA.addr <= addressA;
inputSamplesA.en <= en;
inputSamplesB.addr <= addressB;
inputSamplesB.en <= en;
twiddleFactorROM.addr <= j;
twiddleFactorROM.en <= en;

final butterfly = Butterfly(
inA: ComplexFloatingPoint.of(
inputSamplesA.data,
exponentWidth: exponentWidth,
mantissaWidth: mantissaWidth,
),
inB: ComplexFloatingPoint.of(
inputSamplesB.data,
exponentWidth: exponentWidth,
mantissaWidth: mantissaWidth,
),
twiddleFactor: ComplexFloatingPoint.of(
twiddleFactorROM.data,
exponentWidth: exponentWidth,
mantissaWidth: mantissaWidth,
),
);

outputSamplesWritePortA.addr <= addressA;
outputSamplesWritePortA.en <= en;
outputSamplesWritePortB.addr <= addressB;
outputSamplesWritePortB.en <= en;

Sequential(
clk,
[
outputSamplesWritePortA.data < butterfly.outA.named('butterflyOutA'),
outputSamplesWritePortB.data < butterfly.outB.named('butterflyOutB'),
],
reset: reset);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// 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.of(
Logic input, {
required int exponentWidth,
required int mantissaWidth,
String? name,
}) : this._internal(
realPart: FloatingPoint(
exponentWidth: exponentWidth,
mantissaWidth: mantissaWidth,
name: _nameJoin(name, 're'),
)..gets(input.getRange(0, 1 + exponentWidth + mantissaWidth)),
imaginaryPart: FloatingPoint(
exponentWidth: exponentWidth,
mantissaWidth: mantissaWidth,
name: _nameJoin(name, 'im'),
)..gets(
input.getRange(1 + exponentWidth + mantissaWidth, input.width)),
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'));
}
Loading
Loading