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 11 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
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
138 changes: 138 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,138 @@
// 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 results (+1) in 2s complement form as [sum1]
mkorbel1 marked this conversation as resolved.
Show resolved Hide resolved
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 MockCompoundAdder extends CompoundAdder {
mkorbel1 marked this conversation as resolved.
Show resolved Hide resolved
/// Constructs a [CompoundAdder].
MockCompoundAdder(super.a, super.b, {super.name = 'mock_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.slice(blockStartIdx + blockWidth - 1, blockStartIdx);
mkorbel1 marked this conversation as resolved.
Show resolved Hide resolved
blockB <= b.slice(blockStartIdx + blockWidth - 1, blockStartIdx);
// Build ripple-carry adders for 0 and 1 carryin values
final fullAdder0 =
RippleCarryAdderC(blockA, blockB, Const(0), name: 'block0_${i}');
final fullAdder1 =
RippleCarryAdderC(blockA, blockB, 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();
}
}
18 changes: 17 additions & 1 deletion lib/src/arithmetic/ripple_carry_adder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,26 @@ import 'package:rohd_hcl/rohd_hcl.dart';
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'}) {
final adder = RippleCarryAdderC(a, b, Const(0));
sum <= adder.sum;
}
}

/// An [RippleCarryAdderC] is a digital circuit used for binary addition with
/// exposed carry signals.
/// It consists of a series of full adders connected in a chain, with the carry
/// output of each adder linked to the carry input of the next one. Starting
/// from the least significant bit (LSB) to most significant bit (MSB), the
/// adder sequentially adds corresponding bits of two binary numbers.
class RippleCarryAdderC extends Adder {
mkorbel1 marked this conversation as resolved.
Show resolved Hide resolved
/// Constructs an n-bit adder based on inputs List of inputs.
RippleCarryAdderC(super.a, super.b, Logic carryIn,
{super.name = 'ripple_carry_adder_carry_in'}) {
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);

carry = fullAdder.carryOut;
sumList.add(fullAdder.sum);
Expand Down
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.from(1)));
mkorbel1 marked this conversation as resolved.
Show resolved Hide resolved
}

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, MockCompoundAdder.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));
}
});
}
104 changes: 104 additions & 0 deletions test/arithmetic/ripple_carry_adder_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,108 @@ void main() {
expect(rippleCarryAdder.sum.value[widthLength], equals(LogicValue.one));
});
});
group('ripple carry adder with carryIn', () {
test('should throw exception if toSum Logics have diferent width.', () {
final a = Logic(name: 'a', width: 8);
final b = Logic(name: 'b', width: 16);
final ci = Logic(name: 'carry_in');

expect(() => RippleCarryAdderC(a, b, ci),
throwsA(const TypeMatcher<RohdHclException>()));
});

test('should return correct value for ripple carry adder.', () async {
final a = Logic(name: 'a', width: 8);
final b = Logic(name: 'b', width: 8);
final ci = Logic(name: 'carry_in');

final lvA = Random(5).nextInt(128);
final lvB = Random(10).nextInt(128);
final lvC = Random(1).nextInt(2);

a.put(lvA);
b.put(lvB);
ci.put(lvC);

final rippleCarryAdder = RippleCarryAdderC(a, b, ci);
await rippleCarryAdder.build();

expect(rippleCarryAdder.sum.value.toInt(), equals(lvA + lvB + lvC));
});

test('should return 0 when a , b and carryIn are all 0.', () async {
final a = Logic(name: 'a', width: 10)..put(0);
final b = Logic(name: 'b', width: 10)..put(0);
final carryIn = Logic(name: 'carry_in')..put(0);

final rippleCarryAdder = RippleCarryAdderC(a, b, carryIn);
await rippleCarryAdder.build();

expect(rippleCarryAdder.sum.value.toInt(), equals(0));
});

test('should return one of the value when one of the input is 0.',
() async {
const valA = 10;
final a = Logic(name: 'a', width: 10)..put(valA);
final b = Logic(name: 'b', width: 10)..put(0);
final carryIn = Logic(name: 'carry_in')..put(0);

final rippleCarryAdder = RippleCarryAdderC(a, b, carryIn);
await rippleCarryAdder.build();

expect(rippleCarryAdder.sum.value.toInt(), equals(valA));
});

test('should return correct value when random numbers is given.', () async {
final a = Logic(name: 'a', width: 10);
final b = Logic(name: 'b', width: 10);
final carryIn = Logic(name: 'carry_in');

final rippleCarryAdder = RippleCarryAdderC(a, b, carryIn);
await rippleCarryAdder.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);
final randC = rand.nextInt(2);
a.put(randA);
b.put(randB);
carryIn.put(randC);
expect(
rippleCarryAdder.sum.value.toInt(), equals(randA + randB + randC));
}
});

test('should return correct value when carry bit is non-zero.', () async {
const widthLength = 4;
final a = Logic(name: 'a', width: widthLength)..put(1 << widthLength - 1);
final b = Logic(name: 'b', width: widthLength)..put(1 << widthLength - 1);
final carryIn = Logic(name: 'carry_in')..put(1);

final rippleCarryAdder = RippleCarryAdderC(a, b, carryIn);
await rippleCarryAdder.build();

expect(rippleCarryAdder.sum.value.toInt(), (1 << a.width) + 1);
expect(rippleCarryAdder.sum.value.width, a.width + 1);
expect(rippleCarryAdder.sum.value[a.width], equals(LogicValue.one));
});

test('should return correct value when overflow from int to Big Int.',
() async {
const widthLength = 64;
final a = Logic(name: 'a', width: widthLength)..put(1 << widthLength - 1);
final b = Logic(name: 'b', width: widthLength)..put(1 << widthLength - 1);
final carryIn = Logic(name: 'carry_in')..put(1);

final rippleCarryAdder = RippleCarryAdderC(a, b, carryIn);
await rippleCarryAdder.build();

expect(rippleCarryAdder.sum.value.toBigInt(),
(BigInt.one << a.width) + BigInt.from(1));
expect(rippleCarryAdder.sum.value.width, a.width + 1);
expect(rippleCarryAdder.sum.value[widthLength], equals(LogicValue.one));
});
});
}
Loading