From 317092f0fa7fa0ca1f9c3fe801331036ada36cfb Mon Sep 17 00:00:00 2001 From: Roberto Torres <117673911+robtorx@users.noreply.github.com> Date: Fri, 20 Sep 2024 10:32:57 -0700 Subject: [PATCH] Extrema Component (#93) --- doc/README.md | 3 +- doc/components/extrema.md | 73 ++++++++ lib/rohd_hcl.dart | 1 + .../components/component_registry.dart | 1 + .../components/components.dart | 1 + .../components/config_extrema.dart | 47 ++++++ lib/src/extrema.dart | 78 +++++++++ test/extrema_test.dart | 156 ++++++++++++++++++ 8 files changed, 358 insertions(+), 2 deletions(-) create mode 100644 doc/components/extrema.md create mode 100644 lib/src/component_config/components/config_extrema.dart create mode 100644 lib/src/extrema.dart create mode 100644 test/extrema_test.dart diff --git a/doc/README.md b/doc/README.md index f76b7f5c9..2413aa04c 100644 --- a/doc/README.md +++ b/doc/README.md @@ -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) diff --git a/doc/components/extrema.md b/doc/components/extrema.md new file mode 100644 index 000000000..f7681870c --- /dev/null +++ b/doc/components/extrema.md @@ -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` 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` 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 +``` diff --git a/lib/rohd_hcl.dart b/lib/rohd_hcl.dart index efb817ba9..8cf3540a1 100644 --- a/lib/rohd_hcl.dart +++ b/lib/rohd_hcl.dart @@ -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'; diff --git a/lib/src/component_config/components/component_registry.dart b/lib/src/component_config/components/component_registry.dart index 5d452b111..638420096 100644 --- a/lib/src/component_config/components/component_registry.dart +++ b/lib/src/component_config/components/component_registry.dart @@ -27,4 +27,5 @@ List get componentRegistry => [ FindConfigurator(), ParallelPrefixAdderConfigurator(), CompressionTreeMultiplierConfigurator(), + ExtremaConfigurator(), ]; diff --git a/lib/src/component_config/components/components.dart b/lib/src/component_config/components/components.dart index 0d7e59a64..7d17f149d 100644 --- a/lib/src/component_config/components/components.dart +++ b/lib/src/component_config/components/components.dart @@ -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'; diff --git a/lib/src/component_config/components/config_extrema.dart b/lib/src/component_config/components/config_extrema.dart new file mode 100644 index 000000000..c4d6dfee3 --- /dev/null +++ b/lib/src/component_config/components/config_extrema.dart @@ -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 +// + +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> 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'; +} diff --git a/lib/src/extrema.dart b/lib/src/extrema.dart new file mode 100644 index 000000000..f35b4dd60 --- /dev/null +++ b/lib/src/extrema.dart @@ -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 +// + +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 signals, {bool max = true}) { + // List to consume inputs internally. + final logics = []; + + // 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; + } +} diff --git a/test/extrema_test.dart b/test/extrema_test.dart new file mode 100644 index 000000000..32bf19c2a --- /dev/null +++ b/test/extrema_test.dart @@ -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 +// + +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.empty(); + + // Try to create an instance of Extrema + expect(() => Extrema(logics), throwsA(isA())); + }); +}