Skip to content

Commit

Permalink
Reorganize one-hot encodings (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkorbel1 authored Dec 28, 2023
1 parent 1719f5d commit e62be8c
Show file tree
Hide file tree
Showing 12 changed files with 259 additions and 151 deletions.
2 changes: 1 addition & 1 deletion doc/components/onehot.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

ROHD HCL implements a set of one hot encoder and decoders.

For example, we have an encoder [`BinaryToOneHot`](https://intel.github.io/rohd-hcl/rohd_hcl/BinaryToOneHot-class.html) class, and a couple of implementations of decoder classes like [`OneHotToBinary`](https://intel.github.io/rohd-hcl/rohd_hcl/OneHotToBinary-class.html) and a more performant [`TreeBinaryToOneHot`](https://intel.github.io/rohd-hcl/rohd_hcl/TreeOneHotToBinary-class.html).
For example, we have an encoder [`BinaryToOneHot`](https://intel.github.io/rohd-hcl/rohd_hcl/BinaryToOneHot-class.html) class, and a couple of implementations of decoder classes like `CaseOneHotToBinary` and a more performant `TreeBinaryToOneHot`. The `OneHotToBinary` default constructor will select an implementation based on the width of the input.

The encoders take a Logic bitvector, with the constraint that only a single bit is set to '1' and outputs the bit position in binary.

Expand Down
2 changes: 1 addition & 1 deletion lib/rohd_hcl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ export 'src/arbiters/arbiters.dart';
export 'src/carry_save_mutiplier.dart';
export 'src/component_config/component_config.dart';
export 'src/count.dart';
export 'src/encodings/encodings.dart';
export 'src/exceptions.dart';
export 'src/fifo.dart';
export 'src/find.dart';
export 'src/interfaces/interfaces.dart';
export 'src/memory/memories.dart';
export 'src/models/models.dart';
export 'src/multiplier.dart';
export 'src/one_hot.dart';
export 'src/parity.dart';
export 'src/ripple_carry_adder.dart';
export 'src/rotate.dart';
Expand Down
25 changes: 25 additions & 0 deletions lib/src/encodings/binary_to_one_hot.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// binary_to_one_hot.dart
// Implementation of one hot codec from binary to one-hot.
//
// 2023 February 24
// Author: Desmond Kirkpatrick

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

/// Encodes a binary number into one-hot.
class BinaryToOneHot extends Module {
/// The [encoded] one-hot result.
Logic get encoded => output('encoded');

/// Constructs a [Module] which encodes a 2's complement number [binary]
/// into a one-hot, or thermometer code
BinaryToOneHot(Logic binary, {super.name = 'binary_to_one_hot'}) {
binary = addInput('binary', binary, width: binary.width);
addOutput('encoded', width: pow(2, binary.width).toInt());
encoded <= Const(1, width: encoded.width) << binary;
}
}
43 changes: 43 additions & 0 deletions lib/src/encodings/case_one_hot_to_binary.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// case_one_hot_to_binary.dart
// Implementation of one hot codec from one hot to binary via case statements
//
// 2023 February 24
// Author: Desmond Kirkpatrick

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

/// Decodes a one-hot number into binary using a case block.
class CaseOneHotToBinary extends OneHotToBinary {
/// Constructs a [Module] which decodes a one-hot number [onehot] into a 2s
/// complement number [binary] by encoding the position of the '1' using a
/// [Case] block.
///
/// It is not recommended to use this for very large-width [onehot]s since it
/// will create many [CaseItem]s. If the width is more than ~8, try using the
/// [TreeOneHotToBinary]. The implementation does not support widths exceeding
/// the maximum width of an `int`.
CaseOneHotToBinary(super.onehot,
{super.generateError = false, super.name = 'one_hot_to_binary'})
: assert(onehot.width < 32, 'Should not be used for large widths.'),
super.base() {
Combinational([
Case(onehot, conditionalType: ConditionalType.unique, [
for (var i = 0; i < onehot.width; i++)
CaseItem(
Const(BigInt.from(1) << i, width: onehot.width),
[
binary < Const(i, width: binary.width),
if (generateError) error! < 0,
],
)
], defaultItem: [
binary < Const(0, width: binary.width),
if (generateError) error! < 1,
])
]);
}
}
7 changes: 7 additions & 0 deletions lib/src/encodings/encodings.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause

export 'binary_to_one_hot.dart';
export 'case_one_hot_to_binary.dart';
export 'one_hot_to_binary.dart';
export 'tree_one_hot_to_binary.dart';
60 changes: 60 additions & 0 deletions lib/src/encodings/one_hot_to_binary.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// one_hot_to_binary.dart
// Abstract definition of one hot codec for one hot to binary
//
// 2023 February 24
// Author: Desmond Kirkpatrick

import 'dart:math';

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

/// Decodes a one-hot number into binary using a case block.
abstract class OneHotToBinary extends Module {
/// The [binary] decoded result.
Logic get binary => output('binary');

/// Whether there was an [error] in getting the result.
///
/// Only exists if [generateError] is `true`.
Logic? get error => tryOutput('error');

/// If `true`, then the [error] output will be generated.
final bool generateError;

/// By default, creates an instance of a [CaseOneHotToBinary] for smaller
/// widths and a [TreeOneHotToBinary] for larger widths.
factory OneHotToBinary(Logic onehot,
{bool generateError = false, String name = 'one_hot_to_binary'}) {
final isSmall = onehot.width <= 8;

assert(!(!isSmall && generateError),
'Tree implementation does not generate error signal.');

return isSmall
? CaseOneHotToBinary(onehot, generateError: generateError, name: name)
: TreeOneHotToBinary(onehot, name: name);
}

/// The [input] of this instance.
///
/// Should only be used by implementations, since it uses an [input].
@protected
Logic get onehot => input('onehot');

/// Constructs a [Module] which decodes a one-hot number [onehot] into a 2s
/// complement number [binary] by encoding the position of the '1'.
OneHotToBinary.base(Logic onehot,
{this.generateError = false, super.name = 'one_hot_to_binary'}) {
onehot = addInput('onehot', onehot, width: onehot.width);
addOutput('binary', width: max(log2Ceil(onehot.width), 1));

if (generateError) {
addOutput('error');
}
}
}
51 changes: 51 additions & 0 deletions lib/src/encodings/tree_one_hot_to_binary.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// tree_one_hot_to_binary.dart
// Implementation of one hot codec from one hot to binary via a tree
//
// 2023 February 24
// Author: Desmond Kirkpatrick

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

/// Module for binary-tree recursion for decoding one-hot.
class TreeOneHotToBinary extends OneHotToBinary {
/// Top level module for computing binary to one-hot using recursion
TreeOneHotToBinary(super.onehot, {super.name = 'tree_one_hot_to_binary'})
: super.base() {
binary <= _NodeOneHotToBinary(onehot).binary;
}
}

/// Internal class for binary-tree recursion for decoding one-hot
class _NodeOneHotToBinary extends Module {
/// The [binary] decoded result.
Logic get binary => output('binary');

/// Build a shorter-input module for recursion
/// (borrowed from Chisel OHToUInt)
_NodeOneHotToBinary(Logic onehot) : super(name: 'node_one_hot_to_binary') {
final wid = onehot.width;
onehot = addInput('onehot', onehot, width: wid);

if (wid <= 2) {
addOutput('binary');
//Log2 of 2-bit quantity
if (wid == 2) {
binary <= onehot[1];
} else {
binary <= Const(0, width: 1);
}
} else {
final mid = 1 << (log2Ceil(wid) - 1);
addOutput('binary', width: log2Ceil(mid + 1));
final hi = onehot.getRange(mid).zeroExtend(mid);
final lo = onehot.getRange(0, mid).zeroExtend(mid);
final recurse = lo | hi;
final response = _NodeOneHotToBinary(recurse).binary;
binary <= [hi.or(), response].swizzle();
}
}
}
6 changes: 3 additions & 3 deletions lib/src/find.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Find extends Module {

/// [error] is an getter for error in Find
/// When your find is not found it will result in error `1`
Logic get error => output('error');
Logic? get error => tryOutput('error');

/// If `true`, then the [error] output will be generated.
final bool generateError;
Expand Down Expand Up @@ -67,15 +67,15 @@ class Find extends Module {
}

final oneHotBinary =
OneHotToBinary(oneHotList.rswizzle(), generateError: true);
OneHotToBinary(oneHotList.rswizzle(), generateError: generateError);
// Upon search complete, we get the position value in binary `bin` form
final bin = oneHotBinary.binary;
addOutput('index', width: bin.width);
index <= bin;

if (generateError) {
addOutput('error');
error <= oneHotBinary.error;
error! <= oneHotBinary.error!;
}
}
}
111 changes: 0 additions & 111 deletions lib/src/one_hot.dart

This file was deleted.

2 changes: 1 addition & 1 deletion lib/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@

import 'dart:math';

/// Compute the bit width needed to store w addresses
/// Computes the bit width needed to store [w] addresses.
int log2Ceil(int w) => (log(w) / log(2)).ceil();
Loading

0 comments on commit e62be8c

Please sign in to comment.