Skip to content

Commit

Permalink
Extrema Component (#93)
Browse files Browse the repository at this point in the history
  • Loading branch information
robtorx authored Sep 20, 2024
1 parent 22f6154 commit 317092f
Show file tree
Hide file tree
Showing 8 changed files with 358 additions and 2 deletions.
3 changes: 1 addition & 2 deletions doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ Some in-development items will have opened issues, as well. Feel free to create
- [Shift register](./components/shift_register.md)
- Find
- [Find N'th bit (0 or 1) from the start/end](./components/find.md#find-nth)
- Find minimum
- Find maximum
- [Extrema](./components/extrema.md)
- Find N'th pattern from the start/end
- Count
- [Count bit occurrence](./components/count.md)
Expand Down
73 changes: 73 additions & 0 deletions doc/components/extrema.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Extrema

ROHD-HCL provides a component to find the extrema of a list of Logic.

The component `Extrema` will determine an extrema (maximum or minimum) and their position in a list of `Logic`s.

## Description

`Extrema` will take in an input containing a `List<Logic>` along with a parameter `max`.

* `max` is a boolean indicating whether to find the maximum value (`true`), or the minimum value (`false`). Default is `true`.

`Extrema` will then ouput two `Logic` signals:

* `index` The index or position of the first extrema in the list.

* `index` will be a `Logic` with the smallest width needed to represent the largest possible index in the list.

* If multiple instances of the same extrema exists in the list, the index of the first instance will be returned.

* `val` The value of the extrema.

* `val` will be a `Logic` with a width equal to the largest width of any element in the list.

The `List<Logic>` may contain `Logic`s of any width. They will all be considered positive unsigned numbers.

`Extrema` will throw an exception if the `Logic` list is empty.

## Example Usage

Dart code:

```dart
void main() {
// Example list of Logic signals.
final signals = [
Logic(width: 8)..put(LogicValue.of([LogicValue.one, LogicValue.zero])), //0b10
Logic(width: 4)..put(LogicValue.ofInt(13, 4)), //0xD
Logic()..put(LogicValue.one), //0b1
Logic(width: 8)..put(LogicValue.ofString('00001101')), // 0xD
Logic(width: 4)..put(LogicValue.ofString('0001')), //0b1
];
// Create an Extrema module to find the minimum value.
final findMin = Extrema(signals, max: false);
await findMin.build();
// Create an Extrema module to find the first maximum value.
final findMax = Extrema(signals);
await findMax.build();
// Assign the integer representation of the value of the index to a variable.
final x = findMax.index.value.toInt();
print('x equals findMax index: $x');
// print the index and value of the minimum as values
print('findMin index as a value: ${findMin.index.value}');
print('findMin value as a value: ${findMin.val.value}');
// print the index and value of the maximum as values
print('findMax index as a value: ${findMax.index.value}');
print('findMax val as a value: ${findMax.val.value}');
}
```

Console output:

```console
x equals findMax index: 1
findMin index as a value: 3'h2
findMin value as a value: 8'h1
findMax index as a value: 3'h1
findMax val as a value: 8'hd
```
1 change: 1 addition & 0 deletions lib/rohd_hcl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export 'src/edge_detector.dart';
export 'src/encodings/encodings.dart';
export 'src/error_checking/error_checking.dart';
export 'src/exceptions.dart';
export 'src/extrema.dart';
export 'src/fifo.dart';
export 'src/find.dart';
export 'src/interfaces/interfaces.dart';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ List<Configurator> get componentRegistry => [
FindConfigurator(),
ParallelPrefixAdderConfigurator(),
CompressionTreeMultiplierConfigurator(),
ExtremaConfigurator(),
];
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 @@ -5,6 +5,7 @@ export 'config_carry_save_multiplier.dart';
export 'config_compression_tree_multiplier.dart';
export 'config_ecc.dart';
export 'config_edge_detector.dart';
export 'config_extrema.dart';
export 'config_fifo.dart';
export 'config_find.dart';
export 'config_one_hot.dart';
Expand Down
47 changes: 47 additions & 0 deletions lib/src/component_config/components/config_extrema.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// config_extrema.dart
// Configurator for extrema.
//
// 2024 September 16
// Author: Roberto Torres <[email protected]>
//

import 'dart:collection';

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

/// A [Configurator] for [Extrema].
class ExtremaConfigurator extends Configurator {
/// A knob controlling the number of logics to compare.
final IntConfigKnob signalsKnob = IntConfigKnob(value: 4);

/// A knob controlling the width of each element to sort.
final IntConfigKnob logicWidthKnob = IntConfigKnob(value: 8);

/// A knob controlling whether to find Max or Min.
final ToggleConfigKnob maxKnob = ToggleConfigKnob(value: true);

@override
late final Map<String, ConfigKnob<dynamic>> knobs = UnmodifiableMapView({
'Length of list (number of elements)': signalsKnob,
'Element width (for all elements)': logicWidthKnob,
'Find maximum (uncheck for minimum)': maxKnob,
});

@override
Module createModule() {
final signals = List.generate(
signalsKnob.value, (index) => Logic(width: logicWidthKnob.value));

return Extrema(
signals,
max: maxKnob.value,
);
}

@override
final String name = 'Extrema';
}
78 changes: 78 additions & 0 deletions lib/src/extrema.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// extrema.dart
// Implementation of finding extremas (max or min) of signals.
//
// 2024 September 16
// Author: Roberto Torres <[email protected]>
//

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

/// Determines the extremas (maximum or minimum) of a List<[Logic]>.
class Extrema extends Module {
/// The [index] of the extrema.
Logic get index => output('index');

/// The [val] of the extrema.
Logic get val => output('val');

/// Finds an extrema of List<[Logic]> [signals]. Inputs need not be the same
/// width, and will all be considered positive unsigned numbers.
///
/// If [max] is `true`, will find maximum value, else will find minimum.
///
/// Outputs the [index] and [val] of the extrema in the list of [signals].
Extrema(List<Logic> signals, {bool max = true}) {
// List to consume inputs internally.
final logics = <Logic>[];

// Adds input for every element in the signals list, to logics.
for (var i = 0; i < signals.length; i++) {
logics.add(addInput('signal$i', signals[i], width: signals[i].width));
}

// Check if list is empty
if (logics.isEmpty) {
throw RohdHclException('List cannot be empty.');
}

// Find the max width of all inputs.
var maxWidth = 0;
for (var i = 0; i < logics.length; i++) {
if (logics[i].width > maxWidth) {
maxWidth = logics[i].width;
}
}

// Check max width and prepend with 0s. Will make all inputs same width.
for (var i = 0; i < logics.length; i++) {
if (logics[i].width < maxWidth) {
logics[i] = logics[i].zeroExtend(maxWidth);
}
}

// Find indexWidth, initialize extremaIndex and extremaVal.
final indexWidth = log2Ceil(logics.length);
Logic extremaIndex = Const(0, width: indexWidth);
var extremaVal = logics[0];

// If max is true, find max value. Else, find min value.
for (var i = 1; i < logics.length; i++) {
final compareVal =
max ? logics[i].gt(extremaVal) : logics[i].lt(extremaVal);
extremaVal = Logic(name: 'muxOut$i', width: maxWidth)
..gets(mux(compareVal, logics[i], extremaVal));
extremaIndex = mux(compareVal, Const(i, width: indexWidth), extremaIndex);
}

// Generate outputs here.
addOutput('index', width: extremaIndex.width);
index <= extremaIndex;

addOutput('val', width: extremaVal.width);
val <= extremaVal;
}
}
156 changes: 156 additions & 0 deletions test/extrema_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// extrema_test.dart
// Tests for extrema.
//
// 2024 September 16
// Author: Roberto Torres <[email protected]>
//

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

void main() {
tearDown(() async {
await Simulator.reset();
});
test('Extrema of a list of Logics, all same width.', () async {
// Create a list of Logic objects with different values.
final logics = [
Logic(width: 8)..put(LogicValue.ofString('01101101')), // 109 in decimal
Logic(width: 8)..put(LogicValue.ofString('00010100')), // 20 in decimal
Logic(width: 8)..put(LogicValue.ofString('00000011')), // 3 in decimal
Logic(width: 8)..put(LogicValue.ofString('00001111')) // 15 in decimal
];

// Create an instance of Extrema to use for finding first minimum.
final findMin = Extrema(logics, max: false);
await findMin.build();

// Create an instance of FindMax.
final findMax = Extrema(logics);
await findMax.build();

// Verify the min value and index
expect(findMin.val.value.toInt(), equals(3));
expect(findMin.index.value.toInt(), equals(2));

// Verify the max value and index.
expect(findMax.val.value.toInt(), equals(109));
expect(findMax.index.value.toInt(), equals(0));
});

test('Extrema of a list of Logics, different widths.', () async {
// Create a list of Logic objects with different values.
final logics = [
Logic(width: 8)..put(LogicValue.ofString('01101101')), // 109 in decimal
Logic(width: 4)..put(LogicValue.ofString('0101')), // 5 in decimal
Logic(width: 8)..put(LogicValue.ofString('00010100')), // 20 in decimal
Logic(width: 2)..put(LogicValue.ofString('11')), // 3 in decimal
Logic(width: 8)..put(LogicValue.ofString('00001111')) // 15 in decimal
];

// Create an instance of FindMin.
final findMin = Extrema(logics, max: false);
await findMin.build();

// Create an instance of FindMax.
final findMax = Extrema(logics);
await findMax.build();

// Verify the min value and index
expect(findMin.val.value.toInt(), equals(3));
expect(findMin.index.value.toInt(), equals(3));

// Verify the max value and index.
expect(findMax.val.value.toInt(), equals(109));
expect(findMax.index.value.toInt(), equals(0));
});

test('List with same extrema including min in first and last.', () async {
// Create a list of Logic objects with different values.
final logics = [
Logic(width: 8)..put(LogicValue.ofString('00000011')), // 3 in decimal
Logic(width: 4)..put(LogicValue.ofString('1101')), // 13 in decimal
Logic(width: 8)..put(LogicValue.ofString('00000100')), // 4 in decimal
Logic(width: 2)..put(LogicValue.ofString('11')), // 3 in decimal
Logic(width: 8)..put(LogicValue.ofString('00001100')), // 12 in decimal
Logic(width: 6)..put(LogicValue.ofString('001101')), // 13 in decimal
Logic(width: 8)..put(LogicValue.ofString('00000011')), // 3 in decimal
];

// Create an instance of FindMin.
final findMin = Extrema(logics, max: false);
await findMin.build();

// Create an instance of FindMax.
final findMax = Extrema(logics);
await findMax.build();

// Verify the min value and index
expect(findMin.val.value.toInt(), equals(3));
expect(findMin.index.value.toInt(), equals(0));

// Verify the max value and index.
expect(findMax.val.value.toInt(), equals(13));
expect(findMax.index.value.toInt(), equals(1));
});

test('List with same extrema including max in first and last.', () async {
// Create a list of Logic objects with different values.
final logics = [
Logic(width: 8)..put(LogicValue.ofString('000001101')), // 13 in decimal
Logic(width: 4)..put(LogicValue.ofString('1101')), // 13 in decimal
Logic(width: 8)..put(LogicValue.ofString('00000100')), // 4 in decimal
Logic(width: 2)..put(LogicValue.ofString('11')), // 3 in decimal
Logic(width: 8)..put(LogicValue.ofString('00001100')), // 12 in decimal
Logic(width: 6)..put(LogicValue.ofString('000011')), // 3 in decimal
Logic(width: 8)..put(LogicValue.ofString('000001101')), // 13 in decimal
];

// Create an instance of FindMin.
final findMin = Extrema(logics, max: false);
await findMin.build();

// Create an instance of FindMax.
final findMax = Extrema(logics);
await findMax.build();

// Verify the min value and index
expect(findMin.val.value.toInt(), equals(3));
expect(findMin.index.value.toInt(), equals(3));

// Verify the max value and index.
expect(findMax.val.value.toInt(), equals(13));
expect(findMax.index.value.toInt(), equals(0));
});

test('List containing one element.', () async {
// Create a list of Logic objects with different values
final logics = [
Logic(width: 4)..put(LogicValue.ofString('1100')), // 12 in decimal
];

// Create an instance of FindMin.
final findMin = Extrema(logics, max: false);
await findMin.build();

// Create an instance of FindMax
final findMax = Extrema(logics);
await findMax.build();

// Verify the minimum value and index
expect(findMax.val.value.toInt(), equals(12));
expect(findMax.index.value.toInt(), equals(0));
});

test('Empty List.', () async {
// Create a list of Logic objects with
final logics = List<Logic>.empty();

// Try to create an instance of Extrema
expect(() => Extrema(logics), throwsA(isA<RohdHclException>()));
});
}

0 comments on commit 317092f

Please sign in to comment.