Skip to content
Closed
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
225 changes: 225 additions & 0 deletions lib/src/arithmetic/signals/complex_fixed_point_logic.dart
Copy link
Contributor

Choose a reason for hiding this comment

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

When you add new components/files that should be publicly visible, make sure to export them (e.g. see lib/src/arithmetic/signals/signals.dart, or the appropriate one per-file)

Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
// Copyright (C) 2021-2024 Intel Corporation
Copy link
Contributor

Choose a reason for hiding this comment

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

Try to keep the headers consistent and accurate:

  • Make sure you update copyright years
  • Most files should have some header describing what the file is, and you can put your name as the author for files you create if you wish

See other files or CONTRIBUTING.md for examples

// SPDX-License-Identifier: BSD-3-Clause

import 'dart:math';

import 'package:rohd/rohd.dart';
import 'package:rohd_hcl/src/arithmetic/signals/signals.dart';
import 'package:rohd_hcl/src/arithmetic/values/complex_fixed_point_value.dart';
import 'package:rohd_hcl/src/exceptions.dart';

class ComplexFixedPoint extends Logic {
final bool signed;

final int integerBits;
Copy link
Contributor

@mkorbel1 mkorbel1 Jun 9, 2025

Choose a reason for hiding this comment

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

for consistency (including with #208), better might be to call *Width


final int fractionalBits;

static int _fixedPointWidth(
bool signed,
int integerBits,
int fractionalBits,
) =>
signed ? 1 + integerBits + fractionalBits : integerBits + fractionalBits;

static int _complexFixedPointWidth(
bool signed,
int integerBits,
int fractionalBits,
) =>
2 * _fixedPointWidth(signed, integerBits, fractionalBits);

ComplexFixedPoint({
required this.signed,
required this.integerBits,
required this.fractionalBits,
super.name,
super.naming,
}) : assert(integerBits > 0),
assert(fractionalBits > 0),
assert(max(integerBits, fractionalBits) > 0),
super(
width: _complexFixedPointWidth(signed, integerBits, fractionalBits),
) {}

ComplexFixedPoint.of(Logic signal,
{required this.signed,
required this.integerBits,
required this.fractionalBits})
: super(
width:
_complexFixedPointWidth(signed, integerBits, fractionalBits)) {
this <= signal;
}

static ComplexFixedPoint fromPartsUnsafe(Logic realPart, Logic imaginaryPart,
bool signed, int integerBits, int fractionalBits) {
final result = ComplexFixedPoint(
signed: signed,
integerBits: integerBits,
fractionalBits: fractionalBits);

result._realPart() <= realPart;
result._imaginaryPart() <= imaginaryPart;

return result;
}

static ComplexFixedPoint fromParts(
FixedPoint realPart, FixedPoint imaginaryPart) {
assert(realPart.signed == imaginaryPart.signed);
assert(realPart.m == imaginaryPart.m);
assert(realPart.n == imaginaryPart.n);

final result = ComplexFixedPoint(
signed: realPart.signed,
integerBits: realPart.m,
fractionalBits: realPart.n);

result._realPart() <= realPart;
result._imaginaryPart() <= imaginaryPart;

return result;
}

void _verifyCompatible(ComplexFixedPoint other) {
if ((signed != other.signed) |
(integerBits != other.integerBits) |
(fractionalBits != other.fractionalBits)) {
throw RohdHclException('Inputs are not comparable.');
}
}

Logic _realPart() =>
getRange(0, _fixedPointWidth(signed, integerBits, fractionalBits));
Copy link
Contributor

Choose a reason for hiding this comment

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

Conceptually, the getRange function creates a new module which extracts the requested bits from the original signal and produces an output that results from that operation. Assigning to that output (as you did in some functions above) doesn't make sense and you'll (hopefully) get an error or at least X.

I think a more convenient way to structure a grouping of two FixedPoints would be to create a LogicStructure which contains two FixedPoints as elements. Then they can be individually referenced, assigned, etc. without needing to extract bits each time or manage widths yourself.


FixedPoint realPart() => FixedPoint.of(_realPart(),
signed: signed, m: integerBits, n: fractionalBits);

Logic _imaginaryPart() =>
getRange(_fixedPointWidth(signed, integerBits, fractionalBits), width);

FixedPoint imaginaryPart() => FixedPoint.of(_imaginaryPart(),
signed: signed, m: integerBits, n: fractionalBits);

@override
void put(dynamic val, {bool fill = false}) {
if (val is ComplexFixedPointValue) {
if ((signed != val.realPart.signed) |
(integerBits != val.realPart.m) |
(fractionalBits != val.realPart.n)) {
throw RohdHclException('Value is not compatible with signal.');
}

realPart().put(val.realPart);
imaginaryPart().put(val.imaginaryPart);
} else {
throw RohdHclException('Only ComplexFixedPointValue is allowed');
}
}

@override
Logic lt(dynamic other) {
if (other is! ComplexFixedPoint) {
throw RohdHclException('Input must be complex fixed point signal.');
}
_verifyCompatible(other);
return realPart().lt(other.realPart()) &
imaginaryPart().lt(other.imaginaryPart());
}

@override
Logic lte(dynamic other) {
if (other is! ComplexFixedPoint) {
throw RohdHclException('Input must be complex fixed point signal.');
}
_verifyCompatible(other);
return realPart().lte(other.realPart()) &
imaginaryPart().lte(other.imaginaryPart());
}

@override
Logic gt(dynamic other) {
if (other is! ComplexFixedPoint) {
throw RohdHclException('Input must be complex fixed point signal.');
}
_verifyCompatible(other);
return realPart().gt(other.realPart()) &
imaginaryPart().gt(other.imaginaryPart());
}

@override
Logic gte(dynamic other) {
if (other is! ComplexFixedPoint) {
throw RohdHclException('Input must be complex fixed point signal.');
}
_verifyCompatible(other);
return realPart().gte(other.realPart()) &
imaginaryPart().gte(other.imaginaryPart());
}

Logic _add(dynamic other) {
if (other is! ComplexFixedPoint) {
throw RohdHclException('Input must be complex fixed point signal.');
}
_verifyCompatible(other);
return fromPartsUnsafe(
realPart() + other.realPart(),
imaginaryPart() + other.imaginaryPart(),
signed,
integerBits + 1,
fractionalBits);
}

Logic _multiply(dynamic other) {
if (other is! ComplexFixedPoint) {
throw RohdHclException('Input must be complex fixed point signal.');
}
_verifyCompatible(other);

// assert((realPart() + imaginaryPart()).width ==
// (other.realPart() + other.imaginaryPart()).width);

// use only 3 multipliers: https://mathworld.wolfram.com/ComplexMultiplication.html
// final abcd = (realPart() + imaginaryPart()) *
// (other.realPart() + other.imaginaryPart());
final ri = realPart() + imaginaryPart();
final ri2 = other.realPart() + other.imaginaryPart();

final abcd = ri * ri2;
final ac = (realPart() * other.realPart()).signExtend(abcd.width);
final bd = (imaginaryPart() * other.imaginaryPart()).signExtend(abcd.width);
return fromPartsUnsafe((ac - bd).signExtend(abcd.width + 2), abcd - ac - bd,
signed, 2 * (integerBits + 1) + 3, fractionalBits * 2);
}

@override
Logic operator >(dynamic other) => gt(other);

@override
Logic operator >=(dynamic other) => gte(other);

@override
Logic operator +(dynamic other) => _add(other);

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

@override
Logic eq(dynamic other) {
if (other is! ComplexFixedPoint) {
throw RohdHclException('Input must be complex fixed point signal.');
}
_verifyCompatible(other);
return super.eq(other);
}

@override
Logic neq(dynamic other) {
if (other is! ComplexFixedPoint) {
throw RohdHclException('Input must be complex fixed point signal.');
}
_verifyCompatible(other);
return super.neq(other);
}
}
22 changes: 22 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,25 @@ class FixedPoint extends Logic {
}
}

Logic _add(dynamic other) {
_verifyCompatible(other);
if (signed) {
final sum = Add(getRange(0, width - 1), other.getRange(0, width - 1)).sum;
final difference =
Subtract(getRange(0, width - 1), other.getRange(0, width - 1)).sum;

condition[0] <= this[-1];
condition[1] <= other[-1];

// mux(this[-1], mux(other[-1], ), mux())

FixedPoint.of(sum, signed: signed, m: m + 1, n: n);
Copy link
Contributor

Choose a reason for hiding this comment

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

looks incomplete?

} else {
final sum = Add(this, other).sum;
return FixedPoint.of(sum, signed: signed, m: m + 1, n: n);
}
}

/// Multiply
Logic _multiply(dynamic other) {
_verifyCompatible(other);
Expand All @@ -138,6 +157,9 @@ class FixedPoint extends Logic {
@override
Logic operator >=(dynamic other) => gte(other);

@override
Logic operator +(dynamic other) => _add(other);

/// multiply
@override
Logic operator *(dynamic other) => _multiply(other);
Expand Down
20 changes: 20 additions & 0 deletions lib/src/arithmetic/values/complex_fixed_point_value.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (C) 2024-2025 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause

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

@immutable
Copy link
Contributor

Choose a reason for hiding this comment

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

i think you'll want to implement == and hashCode as well

class ComplexFixedPointValue {
final FixedPointValue realPart;
final FixedPointValue imaginaryPart;

ComplexFixedPointValue({
required this.realPart,
required this.imaginaryPart,
}) : assert(realPart.signed == imaginaryPart.signed),
assert(realPart.m == imaginaryPart.m),
assert(realPart.n == imaginaryPart.n);
}
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) {
Copy link
Contributor

Choose a reason for hiding this comment

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

you could make this private (_bitReverse) if this isn't something we want to expose in the library. Alternatively, you could use LogicValue's reversed to do the work (which also then would cover values >64 bits as well)

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];
}
}
}
Loading
Loading