-
Notifications
You must be signed in to change notification settings - Fork 24
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
parallel prefix operations #51
Changes from 6 commits
10ba13d
3eb2576
300b118
c7c617f
1f79ee9
e29402f
9984f8a
e947354
be6fb15
f9b65b8
0941a1a
50d9898
51905db
f3dfe50
bf3eb49
8bc8ee0
1e2e42b
b4d0297
155ae9b
6d91fae
b78aba6
492d6e8
6c51f4d
d6e0700
bd93c4c
8c18efc
06e1a6f
ab25690
2fce1ec
675e028
fbe11ae
6696b3c
7c7d888
90eaede
38f01a8
aabbbac
2a2993d
60a8878
55c6d61
6d7082d
de2f6e6
2e7e435
1423819
0c26868
6eb11ff
5a0b1b0
78e2c8f
529a9d5
7e6fd8a
6601e6b
4bbe8a8
171bdbc
659037e
b198e3c
837712a
8065052
9d335e0
ddce8ed
c71c351
7f73322
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,7 @@ doc/api/ | |
tmp* | ||
*.vcd | ||
.vscode/ | ||
*~ | ||
|
||
# Exceptions | ||
!.vscode/extensions.json | ||
!.vscode/extensions.json |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Parallel Prefix Operations | ||
|
||
ROHD HCL implements a set of parallel prefix compute operations using different parallel prefix computation trees based on the ['ParallePrefix'] node class carrying carry/save or generate/propagate bits. | ||
|
||
For example, we have unary operations like a word-level 'or' [`OrScan`] class, and a priority encoder [`PriorityEncoder`] class which computes the position of the first bit set to '1'. We have simple unary arithmetic operations like an increment [`PPIncr`] class, and a decrement [`PPDecr`] class. Finally, we have a binary adder [`PPAdder`] class. For background on basic parallel prefix adder structures, see <https://en.wikipedia.org/wiki/Kogge%E2%80%93Stone_adder>. | ||
mkorbel1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Each of these operations can be implemented with different ['ParallelPrefix'] tree structures: | ||
|
||
desmonddak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- ['Ripple'] | ||
- ['Sklansky] | ||
- ['KoggeStone'] | ||
- ['BrentKung] | ||
|
||
[ParallelPrefixAdder (BrentKung) Schematic] | ||
|
||
[ParallelPrefixPriorityEncoder (KoggeStone) Schematic] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Copyright (C) 2023 Intel Corporation | ||
// SPDX-License-Identifier: BSD-3-Clause | ||
// | ||
// one_hot_gen.dart | ||
// Generate one_hot codecs. | ||
// | ||
// 2023 Oct 02 | ||
// Author: Desmond Kirkpatrick <[email protected]> | ||
|
||
import 'dart:io'; | ||
import 'package:rohd/rohd.dart'; | ||
import 'package:rohd_hcl/src/parallel_prefix_operations.dart'; | ||
|
||
Future<void> parallelPrefixGen() async { | ||
const n = 8; | ||
final a = Logic(name: 'a', width: n); | ||
final b = Logic(name: 'b', width: n); | ||
|
||
final generators = [Ripple.new, Sklansky.new, KoggeStone.new, BrentKung.new]; | ||
final names = ['Ripple', 'Sklansky', 'KoggeStone', 'BrentKung']; | ||
var i = 0; | ||
for (final ppGen in generators) { | ||
final m1 = ParallelPrefixAdder(a, b, ppGen); | ||
await m1.build(); | ||
File('build/${m1.definitionName}_${names[i]}.v').writeAsStringSync(m1 | ||
.generateSynth() | ||
.replaceAll(m1.definitionName, '${m1.definitionName}_${names[i]}')); | ||
final m2 = ParallelPrefixPriorityEncoder(a, ppGen); | ||
await m2.build(); | ||
File('build/${m2.definitionName}_${names[i]}.v').writeAsStringSync(m2 | ||
.generateSynth() | ||
.replaceAll(m2.definitionName, '${m2.definitionName}_${names[i]}')); | ||
i = i + 1; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,7 @@ | |
// Implementation of one hot codec for Logic | ||
// | ||
// 2023 February 24 | ||
// Author: Desmond Kirkpatrick | ||
// Author: Desmond Kirkpatrick <[email protected]> | ||
|
||
import 'dart:math'; | ||
import 'package:rohd/rohd.dart'; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you need to add this file to |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
// Copyright (C) 2023 Intel Corporation | ||
// SPDX-License-Identifier: BSD-3-Clause | ||
// | ||
// parallel-prefix_operations.dart | ||
mkorbel1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Implementation of operators using various parallel-prefix trees. | ||
// | ||
// 2023 Sep 29 | ||
// Author: Desmond Kirkpatrick <[email protected]> | ||
// Borrowed from https://github.com/stevenmburns/rohd_sklansky.git | ||
|
||
import 'dart:math'; | ||
|
||
import 'package:collection/collection.dart'; | ||
import 'package:rohd/rohd.dart'; | ||
import 'package:rohd_hcl/rohd_hcl.dart'; | ||
|
||
/// This computes the power of 2 less than x | ||
int largestPow2LessThan(int x) => pow(2, log2Ceil(x) - 1).toInt(); | ||
desmonddak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// ParallePrefix is the core parallel prefix tree structure node | ||
mkorbel1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// The output is a List of multi-bit Logic vectors (typicall 2-bit) that | ||
mkorbel1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// represent things like carry-save or generate-propagate signaling in adder | ||
/// networks. Each node in a parallel prefix tree transforms a row of inputs | ||
/// to an equal length row of outputs of these multi-bit Logic values. | ||
class ParallelPrefix extends Module { | ||
final List<Logic> _oseq = []; | ||
|
||
/// Output sequence value | ||
List<Logic> get val => _oseq; | ||
mkorbel1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// ParallePrefix recursion | ||
ParallelPrefix(List<Logic> inps, String name) : super(name: name) { | ||
desmonddak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (inps.isEmpty) { | ||
throw Exception("Don't use {name} with an empty sequence"); | ||
} | ||
} | ||
} | ||
|
||
/// Ripple shaped ParallelPrefix tree | ||
class Ripple extends ParallelPrefix { | ||
/// Ripple constructor | ||
Ripple(List<Logic> inps, Logic Function(Logic, Logic) op) | ||
: super(inps, 'ripple') { | ||
final iseq = <Logic>[]; | ||
|
||
inps.forEachIndexed((i, el) { | ||
iseq.add(addInput('i$i', el, width: el.width)); | ||
_oseq.add(addOutput('o$i', width: el.width)); | ||
}); | ||
|
||
for (var i = 0; i < iseq.length; ++i) { | ||
if (i == 0) { | ||
_oseq[i] <= iseq[i]; | ||
} else { | ||
_oseq[i] <= op(_oseq[i - 1], iseq[i]); | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// Sklansky shaped ParallelPrefix tree | ||
class Sklansky extends ParallelPrefix { | ||
/// Sklansky constructor | ||
Sklansky(List<Logic> inps, Logic Function(Logic, Logic) op) | ||
: super(inps, 'sklansky') { | ||
final iseq = <Logic>[]; | ||
|
||
inps.forEachIndexed((i, el) { | ||
iseq.add(addInput('i$i', el, width: el.width)); | ||
_oseq.add(addOutput('o$i', width: el.width)); | ||
}); | ||
|
||
if (iseq.length == 1) { | ||
_oseq[0] <= iseq[0]; | ||
} else { | ||
final n = iseq.length; | ||
final m = largestPow2LessThan(n); | ||
final u = Sklansky(iseq.getRange(0, m).toList(), op).val; | ||
final v = Sklansky(iseq.getRange(m, n).toList(), op).val; | ||
u.forEachIndexed((i, el) { | ||
_oseq[i] <= el; | ||
}); | ||
v.forEachIndexed((i, el) { | ||
_oseq[m + i] <= op(u[m - 1], el); | ||
}); | ||
} | ||
} | ||
} | ||
|
||
/// KoggeStone shaped ParallelPrefix tree | ||
class KoggeStone extends ParallelPrefix { | ||
/// KoggeStone constructor | ||
KoggeStone(List<Logic> inps, Logic Function(Logic, Logic) op) | ||
: super(inps, 'kogge_stone') { | ||
final iseq = <Logic>[]; | ||
|
||
inps.forEachIndexed((i, el) { | ||
iseq.add(addInput('i$i', el, width: el.width)); | ||
_oseq.add(addOutput('o$i', width: el.width)); | ||
}); | ||
|
||
var skip = 1; | ||
|
||
while (skip < inps.length) { | ||
for (var i = inps.length - 1; i >= skip; --i) { | ||
iseq[i] = op(iseq[i - skip], iseq[i]); | ||
} | ||
skip *= 2; | ||
} | ||
|
||
iseq.forEachIndexed((i, el) { | ||
_oseq[i] <= el; | ||
}); | ||
} | ||
} | ||
|
||
/// BrentKung shaped ParallelPrefix tree | ||
class BrentKung extends ParallelPrefix { | ||
/// BrentKung constructor | ||
BrentKung(List<Logic> inps, Logic Function(Logic, Logic) op) | ||
: super(inps, 'brent_kung') { | ||
final iseq = <Logic>[]; | ||
|
||
inps.forEachIndexed((i, el) { | ||
iseq.add(addInput('i$i', el, width: el.width)); | ||
_oseq.add(addOutput('o$i', width: el.width)); | ||
}); | ||
|
||
// Reduce phase | ||
var skip = 2; | ||
while (skip <= inps.length) { | ||
for (var i = skip - 1; i < inps.length; i += skip) { | ||
iseq[i] = op(iseq[i - skip ~/ 2], iseq[i]); | ||
} | ||
skip *= 2; | ||
} | ||
|
||
// Prefix Phase | ||
skip = largestPow2LessThan(inps.length); | ||
while (skip > 2) { | ||
for (var i = 3 * (skip ~/ 2) - 1; i < inps.length; i += skip) { | ||
iseq[i] = op(iseq[i - skip ~/ 2], iseq[i]); | ||
} | ||
skip ~/= 2; | ||
} | ||
|
||
// Final row | ||
for (var i = 2; i < inps.length; i += 2) { | ||
iseq[i] = op(iseq[i - 1], iseq[i]); | ||
} | ||
|
||
iseq.forEachIndexed((i, el) { | ||
_oseq[i] <= el; | ||
}); | ||
} | ||
} | ||
|
||
/// Or scan based on ParallelPrefix tree | ||
class ParallelPrefixOrScan extends Module { | ||
/// Output [out] is the or of bits of the input | ||
Logic get out => output('out'); | ||
|
||
/// OrScan constructor | ||
ParallelPrefixOrScan( | ||
Logic inp, | ||
ParallelPrefix Function(List<Logic>, Logic Function(Logic, Logic)) | ||
ppGen) { | ||
inp = addInput('inp', inp, width: inp.width); | ||
final u = | ||
ppGen(List<Logic>.generate(inp.width, (i) => inp[i]), (a, b) => a | b); | ||
addOutput('out', width: inp.width) <= u.val.rswizzle(); | ||
} | ||
} | ||
|
||
/// Priority Encoder based on ParallelPrefix tree | ||
class ParallelPrefixPriorityEncoder extends Module { | ||
/// Output [out] is the bit position of the first '1' in the Logic input | ||
/// Search is counted from the LSB | ||
Logic get out => output('out'); | ||
|
||
/// PriorityEncoder constructor | ||
ParallelPrefixPriorityEncoder( | ||
Logic inp, | ||
ParallelPrefix Function(List<Logic>, Logic Function(Logic, Logic)) | ||
ppGen) { | ||
inp = addInput('inp', inp, width: inp.width); | ||
final u = ParallelPrefixOrScan(inp, ppGen); | ||
addOutput('out', width: inp.width) <= (u.out & ~(u.out << Const(1))); | ||
} | ||
} | ||
|
||
/// Adder based on ParallelPrefix tree | ||
class ParallelPrefixAdder extends Module { | ||
/// Output [out] the arithmetic sum of the two Logic inputs | ||
Logic get out => output('out'); | ||
|
||
/// Adder constructor | ||
ParallelPrefixAdder( | ||
Logic a, | ||
Logic b, | ||
ParallelPrefix Function(List<Logic>, Logic Function(Logic, Logic)) | ||
ppGen) { | ||
a = addInput('a', a, width: a.width); | ||
b = addInput('b', b, width: b.width); | ||
final u = ppGen( | ||
// generate, propagate or generate | ||
List<Logic>.generate( | ||
a.width, (i) => [a[i] & b[i], a[i] | b[i]].swizzle()), | ||
(lhs, rhs) => [rhs[1] | rhs[0] & lhs[1], rhs[0] & lhs[0]].swizzle()); | ||
addOutput('out', width: a.width) <= | ||
List<Logic>.generate(a.width, | ||
(i) => (i == 0) ? a[i] ^ b[i] : a[i] ^ b[i] ^ u.val[i - 1][1]) | ||
.rswizzle(); | ||
} | ||
} | ||
|
||
/// Incrementer based on ParallelPrefix tree | ||
class ParallelPrefixIncr extends Module { | ||
/// Output is '1' added to the Logic input | ||
Logic get out => output('out'); | ||
|
||
/// Increment constructor | ||
ParallelPrefixIncr( | ||
Logic inp, | ||
ParallelPrefix Function(List<Logic>, Logic Function(Logic, Logic)) | ||
ppGen) { | ||
inp = addInput('inp', inp, width: inp.width); | ||
final u = ppGen(List<Logic>.generate(inp.width, (i) => inp[i]), | ||
(lhs, rhs) => rhs & lhs); | ||
addOutput('out', width: inp.width) <= | ||
(List<Logic>.generate( | ||
inp.width, (i) => ((i == 0) ? ~inp[i] : inp[i] ^ u.val[i - 1])) | ||
.rswizzle()); | ||
} | ||
} | ||
|
||
/// Decrementer based on ParallelPrefix tree | ||
class ParallelPrefixDecr extends Module { | ||
/// Output is '1' subtracted from the Logic input | ||
Logic get out => output('out'); | ||
|
||
/// Decrement constructor | ||
ParallelPrefixDecr( | ||
Logic inp, | ||
ParallelPrefix Function(List<Logic>, Logic Function(Logic, Logic)) | ||
ppGen) { | ||
inp = addInput('inp', inp, width: inp.width); | ||
final u = ppGen(List<Logic>.generate(inp.width, (i) => ~inp[i]), | ||
(lhs, rhs) => rhs & lhs); | ||
addOutput('out', width: inp.width) <= | ||
(List<Logic>.generate( | ||
inp.width, (i) => ((i == 0) ? ~inp[i] : inp[i] ^ u.val[i - 1])) | ||
.rswizzle()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ environment: | |
sdk: '>=2.18.0 <3.0.0' | ||
|
||
dependencies: | ||
collection: ^1.18.0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NOTE: need to update open-source dependencies prior to merge (I'll take care of it, just marking it here so I don't forget) |
||
meta: ^1.9.1 | ||
rohd: ^0.5.0 | ||
rohd_vf: ^0.5.0 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,7 @@ | |
// Test of one_hot codec. | ||
// | ||
// 2023 February 24 | ||
// Author: Desmond Kirkpatrick | ||
// Author: Desmond Kirkpatrick <[email protected]> | ||
|
||
import 'dart:math'; | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's weird that this (and some other) files are showing up as a diff from
main
. If you just do a merge or rebase withmain
does this go away?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The merge failed so it required manual changes. The PR is quite stale, so there are more and more conflicts.
Some files were moved in main (e.g. one_hot.dart), so I removed them from this branch.
We should go through a list of changed files before final merge and make sure this branch is not changing files we do not expect.