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

Add CompoundAdder component #98

Merged
merged 36 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
2736160
Add compound_adder. Trivial Mock implementation
Sep 26, 2024
8cde073
Add compound carry-select adder
Oct 2, 2024
ca6cac1
Compound added extend Adder
Oct 2, 2024
fd8be7c
Comments
Oct 2, 2024
35a2229
Cleanup. Add bit splitter algorithm result check
Oct 2, 2024
1d1d345
Cleanup.
Oct 2, 2024
b98bf94
Merge pull request #1 from aasorokiin/as/compound_adder
aasorokiin Oct 2, 2024
0a6dd3f
Format
Oct 2, 2024
f0d08e5
Merge pull request #2 from aasorokiin/as/compound_adder
aasorokiin Oct 2, 2024
1a3137e
Fix spliter algorithm rename
Oct 2, 2024
3d4643a
Merge pull request #3 from aasorokiin/as/compound_adder
aasorokiin Oct 2, 2024
71d2e98
Add compound adder doc. Make ripple-carry adder carry input optional.…
Oct 3, 2024
e229a3d
Merge pull request #4 from aasorokiin/as/compound_adder
aasorokiin Oct 3, 2024
fb71c25
Add CompundAdder configurator
Oct 4, 2024
d423372
Merge pull request #5 from aasorokiin/as/compound_adder
aasorokiin Oct 4, 2024
25d718a
Doc markdown fixes.
Oct 4, 2024
9db0a59
Doc typo.
Oct 4, 2024
61f25f5
Merge pull request #6 from aasorokiin/as/compound_adder
aasorokiin Oct 4, 2024
f44c9a0
Doc markdown fixes.
Oct 5, 2024
e6ff19d
Doc markdown fixes.
Oct 5, 2024
945b6bd
Merge pull request #7 from aasorokiin/as/compound_adder
aasorokiin Oct 5, 2024
c6fa0dd
Doc markdown fixes.
Oct 7, 2024
22b9e5c
Doc markdown fixes.
Oct 7, 2024
d3cd6e3
Merge pull request #8 from aasorokiin/as/compound_adder
aasorokiin Oct 7, 2024
0269089
Compound Adder Configurator. Add adder type to the list of options
Oct 8, 2024
b0758d0
Compound Adder Configurator. Format
Oct 8, 2024
7dc857e
Merge pull request #9 from aasorokiin/as/compound_adder
aasorokiin Oct 8, 2024
f706b6b
Compound Adder Configurator. Added to registry
Oct 8, 2024
2433983
Merge pull request #10 from aasorokiin/as/compound_adder
aasorokiin Oct 9, 2024
90d68be
Compound Adder Configurator. Added to registry. Doc Markdown update
Oct 10, 2024
2fb31c7
Merge pull request #11 from aasorokiin/as/compound_adder
aasorokiin Oct 10, 2024
9c76e47
Update adder.md
aasorokiin Oct 10, 2024
a9b0af6
Update adder.md
aasorokiin Oct 10, 2024
5332674
Update adder.md
aasorokiin Oct 10, 2024
bcf274d
Update adder.md
aasorokiin Oct 10, 2024
03b3b2a
Merge pull request #12 from aasorokiin/as/compound_adder
aasorokiin Oct 10, 2024
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
28 changes: 28 additions & 0 deletions doc/components/adder.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ ROHD-HCL provides a set of adder modules to get the sum from a pair of Logic. As
- [Parallel Prefix Adder](#parallel-prefix-adder)
- [One's Complement Adder Subtractor](#ones-complement-adder-subtractor)
- [Sign Magnitude Adder](#sign-magnitude-adder)
- [Compound Adder](#compound-adder)

## Ripple Carry Adder

Expand Down Expand Up @@ -102,3 +103,30 @@ Here is an example of instantiating a `SignMagnitudeAdder`:

print('${sum.value.toBigInt()}');
```

## Compound Adder

A compound carry adder is a digital circuit used for binary addition. It produces sum and sum+1 outputs.
A trivial compound adder component `TrivialCompoundAdder` doesnt use any RTL code optimization.
Carry-select adder-based compound adder `CarrySelectCompoundAdder` uses carry-select adder as a basis. Like a carry-select adder it consists of a multiple blocks of two ripple-carry adders <https://en.wikipedia.org/wiki/Carry-select_adder>. But the first block has two ripple-carry adders and two separate carry-propagate chains are used to select sum and sum+1 output bits. Sum selecting chain starts from carry input 0 driven block and sum+1 selecting chain starts from carry input 1 driven block.
The delay of the adder is defined by combination ripple-carry adder and accumulated carry-select chain delay.

The `CarrySelectCompoundAdder` module in ROHD-HCL accept input `Logic`s a and b as the input pin and the name of the module `name`. Note that the width of the inputs must be the same or a `RohdHclException` will be thrown.
Compound adder generator provides two alogithms for splitting adder into ripple-carry blocks. `CarrySelectCompoundAdder.splitSelectAdderAlgorithm4Bit` algoritm splits adder into blocks of 4-bit ripple-carry adders with the first one width adjusted down. `CarrySelectCompoundAdder.splitSelectAdderAlgorithmSingleBlock` algorithm generates only one block of full bitwidth of the adder. Input List\<int\> Function(int adderFullWidth) `widthGen` should be used to specify custom adder splitting algorithm that returns a list of sub-adders width. The default one is `CarrySelectCompoundAdder.splitSelectAdderAlgorithmSingleBlock`.

An example is shown below to add two inputs of signals that have 8-bits of width.

```dart
final a = Logic(name: 'a', width: 8);
final b = Logic(name: 'b', width: 8);

a.put(5);
b.put(5);

final rippleCarryAdder = CarrySelectCompoundAdder(a, b);
final sum = rippleCarryAdder.sum;
final sum1 = rippleCarryAdder.sum1;

final rippleCarryAdder4BitBlock = CarrySelectCompoundAdder(a, b,
widthGen: CarrySelectCompoundAdder.splitSelectAdderAlgorithm4Bit);
```
1 change: 1 addition & 0 deletions lib/src/arithmetic/arithmetic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

export 'adder.dart';
export 'carry_save_mutiplier.dart';
export 'compound_adder.dart';
export 'divider.dart';
export 'multiplier.dart';
export 'multiplier_lib.dart';
Expand Down
139 changes: 139 additions & 0 deletions lib/src/arithmetic/compound_adder.dart
mkorbel1 marked this conversation as resolved.
Show resolved Hide resolved
mkorbel1 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Copyright (C) 2023-2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// compound_adder.dart
// Implementation of Compund Integer Adder Module
// (Output Sum and Sum1 which is Sum + 1).
//
// 2024 September
// Author: Anton Sorokin <anton.a.sorokin@intel.com>

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

/// An abstract class for all compound adder module implementations.
abstract class CompoundAdder extends Adder {
/// The addition result [sum] + 1 in 2s complement form as [sum1]
Logic get sum1 => output('sum1');

/// Takes in input [a] and input [b] and return the [sum] of the addition
/// result and [sum1] sum + 1.
/// The width of input [a] and [b] must be the same.
CompoundAdder(super.a, super.b, {super.name}) {
if (a.width != b.width) {
throw RohdHclException('inputs of a and b should have same width.');
}
addOutput('sum1', width: a.width + 1);
}
}

/// A trivial compound adder.
class TrivialCompoundAdder extends CompoundAdder {
/// Constructs a [CompoundAdder].
TrivialCompoundAdder(super.a, super.b,
{super.name = 'trivial_compound_adder'}) {
sum <= a.zeroExtend(a.width + 1) + b.zeroExtend(b.width + 1);
sum1 <= sum + 1;
}
}

/// Carry-select compound adder.
class CarrySelectCompoundAdder extends CompoundAdder {
/// Adder ripple-carry block size computation algorithm.
/// Generates only one carry-select block
/// Return list of carry-ripple block sizes starting from
/// the LSB connected one.
/// [adderWidth] is a whole width of adder.
static List<int> splitSelectAdderAlgorithmSingleBlock(int adderWidth) {
final splitData = <int>[adderWidth];
return splitData;
}

/// Adder ripple-carry block size computation algorithm.
/// Generates 4 bit carry-select blocks with 1st entry width adjusted down.
/// Return list of carry-ripple block sizes starting from
/// the LSB connected one.
/// [adderWidth] is a whole width of adder.
static List<int> splitSelectAdderAlgorithm4Bit(int adderWidth) {
final blockNb = (adderWidth / 4.0).ceil();
final firstBlockSize = adderWidth - (blockNb - 1) * 4;
final splitData = <int>[firstBlockSize];
for (var i = 1; i < blockNb; ++i) {
splitData.add(4);
}
return splitData;
}

/// Constructs a [CarrySelectCompoundAdder].
CarrySelectCompoundAdder(super.a, super.b,
{super.name = 'cs_compound_adder',
List<int> Function(int) widthGen =
splitSelectAdderAlgorithmSingleBlock}) {
// output bits lists
final sumList0 = <Logic>[];
final sumList1 = <Logic>[];
// carryout of previous ripple-carry adder block
// for sum and sum+1
Logic? carry0;
Logic? carry1;
// Get size of each ripple-carry adder block
final adderSplit = widthGen(a.width);
// 1st output bit index of each block
var blockStartIdx = 0;
for (var i = 0; i < adderSplit.length; ++i) {
// input width of current ripple-carry adder block
final blockWidth = adderSplit[i];
if (blockWidth <= 0) {
throw RohdHclException('non-positive ripple-carry adder block size.');
}
if (blockWidth + blockStartIdx > a.width) {
throw RohdHclException('oversized ripple-carry adders sequence.');
}
final blockA = Logic(name: 'block_${i}_a', width: blockWidth);
final blockB = Logic(name: 'block_${i}_b', width: blockWidth);
blockA <= a.getRange(blockStartIdx, blockStartIdx + blockWidth);
blockB <= b.getRange(blockStartIdx, blockStartIdx + blockWidth);
// Build ripple-carry adders for 0 and 1 carryin values
final fullAdder0 = RippleCarryAdder(blockA, blockB,
carryIn: Const(0), name: 'block0_$i');
final fullAdder1 = RippleCarryAdder(blockA, blockB,
carryIn: Const(1), name: 'block1_$i');
for (var bitIdx = 0; bitIdx < blockWidth; ++bitIdx) {
if (i == 0) {
// connect directly to respective sum output bit
sumList0.add(fullAdder0.sum[bitIdx]);
sumList1.add(fullAdder1.sum[bitIdx]);
} else {
final bitOut0 = Logic(name: 'bit0_${blockStartIdx + bitIdx}');
final bitOut1 = Logic(name: 'bit1_${blockStartIdx + bitIdx}');
// select adder output from adder matching carryin value
bitOut0 <=
mux(carry0!, fullAdder1.sum[bitIdx], fullAdder0.sum[bitIdx]);
bitOut1 <=
mux(carry1!, fullAdder1.sum[bitIdx], fullAdder0.sum[bitIdx]);
sumList0.add(bitOut0);
sumList1.add(bitOut1);
}
}
if (i == 0) {
// select carryout as a last bit of the adder
carry0 = fullAdder0.sum[blockWidth];
carry1 = fullAdder1.sum[blockWidth];
} else {
// select carryout depending on carryin (carryout of the previous block)
carry0 = mux(
carry0!, fullAdder1.sum[blockWidth], fullAdder0.sum[blockWidth]);
carry1 = mux(
carry1!, fullAdder1.sum[blockWidth], fullAdder0.sum[blockWidth]);
}
blockStartIdx += blockWidth;
}

// append carryout bit
sumList0.add(carry0!);
sumList1.add(carry1!);

sum <= sumList0.rswizzle();
sum1 <= sumList1.rswizzle();
}
}
9 changes: 7 additions & 2 deletions lib/src/arithmetic/ripple_carry_adder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,16 @@ import 'package:rohd_hcl/rohd_hcl.dart';
/// adder sequentially adds corresponding bits of two binary numbers.
class RippleCarryAdder extends Adder {
/// Constructs an n-bit adder based on inputs List of inputs.
RippleCarryAdder(super.a, super.b, {super.name = 'ripple_carry_adder'}) {
RippleCarryAdder(super.a, super.b,
{Logic? carryIn, super.name = 'ripple_carry_adder_carry_in'}) {
if (carryIn != null) {
carryIn = addInput('carry_in', carryIn, width: carryIn.width);
}
Logic? carry;
final sumList = <Logic>[];
for (var i = 0; i < a.width; i++) {
final fullAdder = FullAdder(a: a[i], b: b[i], carryIn: carry ?? Const(0));
final fullAdder =
FullAdder(a: a[i], b: b[i], carryIn: carry ?? (carryIn ?? Const(0)));

carry = fullAdder.carryOut;
sumList.add(fullAdder.sum);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ List<Configurator> get componentRegistry => [
ParallelPrefixAdderConfigurator(),
CompressionTreeMultiplierConfigurator(),
ExtremaConfigurator(),
CompoundAdderConfigurator(),
];
1 change: 1 addition & 0 deletions lib/src/component_config/components/components.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: BSD-3-Clause

export 'config_carry_save_multiplier.dart';
export 'config_compound_adder.dart';
export 'config_compression_tree_multiplier.dart';
export 'config_ecc.dart';
export 'config_edge_detector.dart';
Expand Down
40 changes: 40 additions & 0 deletions lib/src/component_config/components/config_compound_adder.dart
mkorbel1 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (C) 2023-2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// compound_adder.dart
// Configurator for a CompoundAdder.
//
// 2024 Cotober 1

import 'dart:collection';

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

/// A [Configurator] for [CompoundAdder].
class CompoundAdderConfigurator extends Configurator {
/// Map from Type to Adder generator
static Map<Type, CompoundAdder Function(Logic a, Logic b)> generatorMap = {
TrivialCompoundAdder: TrivialCompoundAdder.new,
CarrySelectCompoundAdder: CarrySelectCompoundAdder.new
};

/// A knob controlling the width of the inputs to the adder.
final IntConfigKnob logicWidthKnob = IntConfigKnob(value: 16);

/// Controls the type of [CompoundAdder].
final moduleTypeKnob = ChoiceConfigKnob(generatorMap.keys.toList(),
value: CarrySelectCompoundAdder);
@override
final String name = 'Compound Adder';

@override
late final Map<String, ConfigKnob<dynamic>> knobs = UnmodifiableMapView(
{'Width': logicWidthKnob, 'Adder Type': moduleTypeKnob});

@override
Module createModule() => generatorMap[moduleTypeKnob.value]!(
Logic(width: logicWidthKnob.value),
Logic(width: logicWidthKnob.value),
);
}
90 changes: 90 additions & 0 deletions test/arithmetic/compound_adder_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (C) 2023-2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// compound_adder_test.dart
// Tests for the Compound Adder interface.
//
// 2024 September 23
// Author: Anton Sorokin <anton.a.sorokin@intel.com>

import 'dart:math';

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

void checkCompoundAdder(CompoundAdder adder, LogicValue av, LogicValue bv) {
final aB = av.toBigInt();
final bB = bv.toBigInt();
// ignore: invalid_use_of_protected_member
adder.a.put(av);
// ignore: invalid_use_of_protected_member
adder.b.put(bv);

expect(adder.sum.value.toBigInt(), equals(aB + bB));
expect(adder.sum1.value.toBigInt(), equals(aB + bB + BigInt.one));
}

void testExhaustive(int n, CompoundAdder Function(Logic a, Logic b) fn) {
final a = Logic(name: 'a', width: n);
final b = Logic(name: 'b', width: n);

final mod = fn(a, b);
test(
'exhaustive: ${mod.name}_W$n'
'_G${fn.call(a, b).name}', () async {
await mod.build();

for (var aa = 0; aa < (1 << n); ++aa) {
for (var bb = 0; bb < (1 << n); ++bb) {
final av = LogicValue.of(BigInt.from(aa), width: n);
final bv = LogicValue.of(BigInt.from(bb), width: n);
checkCompoundAdder(mod, av, bv);
}
}
});
}

void main() {
tearDown(() async {
await Simulator.reset();
});
group('exhaustive', () {
testExhaustive(4, TrivialCompoundAdder.new);
testExhaustive(4, CarrySelectCompoundAdder.new);
});
test('trivial compound adder test', () async {
const width = 6;
final a = Logic(name: 'a', width: width);
final b = Logic(name: 'b', width: width);

a.put(18);
b.put(24);

final adder = CarrySelectCompoundAdder(a, b,
widthGen: CarrySelectCompoundAdder.splitSelectAdderAlgorithm4Bit);

final sum = adder.sum;
final sum1 = adder.sum1;
expect(sum.value.toBigInt(), equals(BigInt.from(18 + 24)));
expect(sum1.value.toBigInt(), equals(BigInt.from(18 + 24 + 1)));
});
test('should return correct value when random numbers are given.', () async {
final a = Logic(name: 'a', width: 10);
final b = Logic(name: 'b', width: 10);

final adder = CarrySelectCompoundAdder(a, b,
widthGen: CarrySelectCompoundAdder.splitSelectAdderAlgorithm4Bit);
await adder.build();

final rand = Random(5);
for (var i = 0; i < 100; i++) {
final randA = rand.nextInt(1 << a.width);
final randB = rand.nextInt(1 << a.width);
a.put(randA);
b.put(randB);
expect(adder.sum.value.toInt(), equals(randA + randB));
expect(adder.sum1.value.toInt(), equals(randA + randB + 1));
}
});
}
Loading
Loading