diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 46966b608..a74c02848 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,7 +48,7 @@ You must have [Dart](https://dart.dev/) installed on your system to use ROHD and #### On your own system -[Visual Studio Code (VSCode)](https://code.visualstudio.com/) is a great IDE for development. You can find installation instructions for VSCode here: +[Visual Studio Code (VSCode)](https://code.visualstudio.com/) is a great IDE for development. You can find installation instructions for VSCode here: The Dart extension extends VSCode with support for the Dart programming language and provides tools for effectively editing, refactoring and running. Check out the detailed information: @@ -131,7 +131,7 @@ Please include the SPDX tag near the top of any new files you create: Here is an example of a recommended file header template: ```dart -// Copyright (C) 2021-2023 Intel Corporation +// Copyright (C) 2021-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // example.dart diff --git a/README.md b/README.md index 5e16d4999..de8710db1 100644 --- a/README.md +++ b/README.md @@ -52,5 +52,5 @@ Some examples of component categories include: ---------------- -Copyright (C) 2023 Intel Corporation +Copyright (C) 2023-2024 Intel Corporation SPDX-License-Identifier: BSD-3-Clause diff --git a/confapp/README.md b/confapp/README.md index e9511b074..fad42662b 100644 --- a/confapp/README.md +++ b/confapp/README.md @@ -11,5 +11,5 @@ flowchart TD; ---------------- -Copyright (C) 2023 Intel Corporation +Copyright (C) 2023-2024 Intel Corporation SPDX-License-Identifier: BSD-3-Clause diff --git a/confapp/lib/app.dart b/confapp/lib/app.dart index 08abcd637..a24fefc92 100644 --- a/confapp/lib/app.dart +++ b/confapp/lib/app.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // app.dart diff --git a/confapp/lib/hcl/cubit/component_cubit.dart b/confapp/lib/hcl/cubit/component_cubit.dart index 596f21c0d..f92d53ea5 100644 --- a/confapp/lib/hcl/cubit/component_cubit.dart +++ b/confapp/lib/hcl/cubit/component_cubit.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // component_cubit.dart diff --git a/confapp/lib/hcl/cubit/system_verilog_cubit.dart b/confapp/lib/hcl/cubit/system_verilog_cubit.dart index 509cd1fc1..e40eb8149 100644 --- a/confapp/lib/hcl/cubit/system_verilog_cubit.dart +++ b/confapp/lib/hcl/cubit/system_verilog_cubit.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // system_verilog_cubit.dart diff --git a/confapp/lib/hcl/hcl.dart b/confapp/lib/hcl/hcl.dart index fb907b30a..0c258afc1 100644 --- a/confapp/lib/hcl/hcl.dart +++ b/confapp/lib/hcl/hcl.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause export 'view/hcl_page.dart'; diff --git a/confapp/lib/hcl/view/hcl_page.dart b/confapp/lib/hcl/view/hcl_page.dart index 9b070de1e..fb0d236bf 100644 --- a/confapp/lib/hcl/view/hcl_page.dart +++ b/confapp/lib/hcl/view/hcl_page.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // hcl_page.dart diff --git a/confapp/lib/hcl/view/hcl_view.dart b/confapp/lib/hcl/view/hcl_view.dart index ec5073cbd..9cbc76cd6 100644 --- a/confapp/lib/hcl/view/hcl_view.dart +++ b/confapp/lib/hcl/view/hcl_view.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // hcl_view.dart diff --git a/confapp/lib/hcl/view/screen/content_widget.dart b/confapp/lib/hcl/view/screen/content_widget.dart index 69e6d2b99..011726707 100644 --- a/confapp/lib/hcl/view/screen/content_widget.dart +++ b/confapp/lib/hcl/view/screen/content_widget.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // content_widget.dart diff --git a/confapp/lib/hcl/view/screen/sidebar_widget.dart b/confapp/lib/hcl/view/screen/sidebar_widget.dart index 135ca160f..f4969a472 100644 --- a/confapp/lib/hcl/view/screen/sidebar_widget.dart +++ b/confapp/lib/hcl/view/screen/sidebar_widget.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // sidebar_widget.dart diff --git a/confapp/lib/hcl_bloc_observer.dart b/confapp/lib/hcl_bloc_observer.dart index ed8b9a773..a8c0eec03 100644 --- a/confapp/lib/hcl_bloc_observer.dart +++ b/confapp/lib/hcl_bloc_observer.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // hcl_bloc_observer.dart diff --git a/confapp/lib/main.dart b/confapp/lib/main.dart index 1422d02d9..5eee94444 100644 --- a/confapp/lib/main.dart +++ b/confapp/lib/main.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // main.dart diff --git a/confapp/test/example_component.dart b/confapp/test/example_component.dart index 70307558b..930e9de19 100644 --- a/confapp/test/example_component.dart +++ b/confapp/test/example_component.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // example_component.dart diff --git a/confapp/test/hcl/view/hcl_page_test.dart b/confapp/test/hcl/view/hcl_page_test.dart index 4e06abf47..716a5b8c8 100644 --- a/confapp/test/hcl/view/hcl_page_test.dart +++ b/confapp/test/hcl/view/hcl_page_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // hcl_page_test.dart diff --git a/doc/README.md b/doc/README.md index 7533a346f..1ea46548c 100644 --- a/doc/README.md +++ b/doc/README.md @@ -3,7 +3,7 @@ Below is a list of components grouped by category. Ones with links are documented and completed, while others are still in planning or development stages. -Some in-development items will have opened issues, as well. Feel free to create a pull request or file issues to add more ideas to this list. If you plan to develop and contribute a component, please be sure to open an issue so that there's not multiple people working on the same thing. Make sure to check if someone else has an open issue for a certain component before starting. +Some in-development items will have opened issues, as well. Feel free to create a pull request or file issues to add more ideas to this list. If you plan to develop and contribute a component, please be sure to open an issue so that there are not multiple people working on the same thing. Make sure to check if someone else has an open issue for a certain component before starting. - Encoders & Decoders - [1-hot to Binary](./components/onehot.md) @@ -25,8 +25,8 @@ Some in-development items will have opened issues, as well. Feel free to create - Find maximum - Find N'th pattern from the start/end - Count - - [Count bit occurence](./components/count.md) - - Count pattern occurence + - [Count bit occurrence](./components/count.md) + - Count pattern occurrence - Detection - [Edge detection](./components/edge_detector.md) - Sort @@ -34,9 +34,14 @@ Some in-development items will have opened issues, as well. Feel free to create - Arithmetic - [Prefix Trees](./components/parallel_prefix_operations.md) - [Adders](./components/adder.md) + - [Sign Magnitude Adder](./components/adder.md#ripple-carry-adder) - Subtractors + - [One's Complement Adder Subtractor](./components/adder.md#ones-complement-adder-subtractor) - Multipliers - [Pipelined Integer Multiplier](./components/multiplier.md#carry-save-multiplier) + - [Compression Tree Multiplier](./components/multiplier.md#compression-tree-multiplier) + - [Compression Tree Multiply-Accumulate](./components/multiplier.md#compression-tree-multiply-accumulate) + - [Booth Encoding and Compression Components](./components/multiplier_components.md) - Dividers - Log - Square root diff --git a/doc/components/adder.md b/doc/components/adder.md index 4206fe639..49af3cf2b 100644 --- a/doc/components/adder.md +++ b/doc/components/adder.md @@ -1,14 +1,17 @@ # Adder -ROHD HCL provides an adder module to get the sum from a list of logic. As of now, we have +ROHD-HCL provides a set of adder modules to get the sum from a pair of Logic. As of now, we have - [Ripple Carry Adder](#ripple-carry-adder) +- [Parallel Prefix Adder](#parallel-prefix-adder) +- [One's Complement Adder Subtractor](#ones-complement-adder-subtractor) +- [Sign Magnitude Adder](#sign-magnitude-adder) ## Ripple Carry Adder A ripple carry adder is a digital circuit used for binary addition. 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. -The [`RippleCarryAdder`](https://intel.github.io/rohd-hcl/rohd_hcl/RippleCarryAdder-class.html) module in ROHD-HCL accept input `Logic`s a and b as the input pin and the name of the module `name`. Note that the width of the inputs must be the same or a `RohdHclException` will be thrown. +The [`RippleCarryAdder`](https://intel.github.io/rohd-hcl/rohd_hcl/RippleCarryAdder-class.html) module in ROHD-HCL accept input `Logic`s a and b as the input pin and the name of the module `name`. Note that the width of the inputs must be the same or a `RohdHclException` will be thrown. An example is shown below to add two inputs of signals that have 8-bits of width. @@ -22,3 +25,80 @@ b.put(5); final rippleCarryAdder = RippleCarryAdder(a, b); final sum = rippleCarryAdder.sum; ``` + +## Parallel Prefix Adder + +A parallel prefix adder is an adder that uses different varieties of a parallel prefix tree (see `Parallel Prefix Operations`) to efficiently connect a set of `Full Adder` circuits to form a complete adder. + +Here is an example of instantiating a `ParallelPrefixAdder`: + +```dart + 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 = ParallelPrefixAdder(a, b, ppGen: BrentKung.new); + + final sum = adder.sum; + + print('${sum.value.toBigInt()}'); +``` + +## One's Complement Adder Subtractor + +A ones-complement adder (and subtractor) is useful in efficient arithmetic operations as the +end-around carry can be bypassed and used later. + +The `OnesComplementAdder` can take a subtraction command as either a Logic `subtractIn` or a boolean `subtract` (the Logic overrides the boolean). If Logic `carry` is provided, the end-around carry is output on `carry` and the value will be one less than expected when `carry` is high. An `adderGen` adder function can be provided that generates your favorite internal adder (such as a parallel prefix adder). + +The output of `OnesComplementAdder` is a `sum` which is the magnitude and a `sign`. + +Here is an example of instantiating a `OnesComplementAdder` as a subtractor, but saving the `carry`: + +```dart + const width = 4; + final a = Logic(width: width); + final b = Logic(width: width); + + a.put(av); + b.put(bv); + final carry = Logic(); + final adder = OnesComplementAdder( + a, b, carryOut: carry, adderGen: RippleCarryAdder.new, + subtract: true); + final mag = adder.sum.value.toInt() + (carry.value.isZero ? 0 : 1)); + final out = (adder.sign.value.toInt() == 1 ? -mag : mag); +``` + +## Sign Magnitude Adder + +A sign magnitude adder is useful in situations where the sign of the addends is separated from their magnitude (e.g., not 2s complement), such as in floating point multipliers. The `SignMagnitudeAdder` inherits from `Adder` but adds the `Logic` inputs for the two operands. + +If you can supply the largest magnitude number first, then you can disable a comparator generation inside by declaring the `largestMagnitudeFirst` option as true. + +The `SignMagnitudeAdder` uses a `OnesComplementAdder` internally. + +Here is an example of instantiating a `SignMagnitudeAdder`: + +```dart + const width = 6; + final aSign = Logic(name: 'aSign'); + final a = Logic(name: 'a', width: width); + final bSign = Logic(name: 'bSign'); + final b = Logic(name: 'b', width: width); + + aSign.put(1); + a.put(24); + b.put(18); + bSign.put(0); + + final adder = SignMagnitudeAdder(aSign, a, bSign, b, adderGen: RippleCarryAdder.new, + largestMagnitudeFirst: true); + + final sum = adder.sum; + + print('${sum.value.toBigInt()}'); +``` diff --git a/doc/components/arbiter.md b/doc/components/arbiter.md index 2bded9d96..f8ffb6308 100644 --- a/doc/components/arbiter.md +++ b/doc/components/arbiter.md @@ -1,6 +1,6 @@ # Arbiters -ROHD HCL implements a generic `abstract` [`Arbiter`](https://intel.github.io/rohd-hcl/rohd_hcl/Arbiter-class.html) class that other arbiters can extend. It accepts a `List` of `requests`, where each request is a `1-bit` signal indicating that there is a request for a resource. The output `grants` is a `List` where each element corresponds to the request with the same index. The arbiter implementation decides how to select which request receives a grant. +ROHD-HCL implements a generic `abstract` [`Arbiter`](https://intel.github.io/rohd-hcl/rohd_hcl/Arbiter-class.html) class that other arbiters can extend. It accepts a `List` of `requests`, where each request is a `1-bit` signal indicating that there is a request for a resource. The output `grants` is a `List` where each element corresponds to the request with the same index. The arbiter implementation decides how to select which request receives a grant. ## Stateful Arbiter diff --git a/doc/components/binary_gray.md b/doc/components/binary_gray.md index 493921fc4..5d334f3ec 100644 --- a/doc/components/binary_gray.md +++ b/doc/components/binary_gray.md @@ -1,6 +1,6 @@ # Binary Gray -ROHD HCL provides a module to perform conversion on binary to gray and gray to binary. +ROHD-HCL provides a module to perform conversion on binary to gray and gray to binary. ## Binary-to-Gray diff --git a/doc/components/count.md b/doc/components/count.md index 1b56d85e6..e77d43a02 100644 --- a/doc/components/count.md +++ b/doc/components/count.md @@ -1,6 +1,6 @@ # Count -ROHD HCL comes with a `Count` component. The detailed API docs are available [here](https://intel.github.io/rohd-hcl/rohd_hcl/rohd_hcl-library.html). +ROHD-HCL comes with a `Count` component. The detailed API docs are available [here](https://intel.github.io/rohd-hcl/rohd_hcl/rohd_hcl-library.html). A `Count` will count all one(`1`)/zero(`0`) within a given Logic `bus`. @@ -12,7 +12,7 @@ This will return a Logic value labeled as `countOne` for `1` and `countZero` for ## Count One -To count all ones just pass in the `bus` with `countOne` as `true`. By default countOne is `true`. +To count all ones just pass in the `bus` with `countOne` as `true`. By default, `countOne` is `true`. ## Count Zero diff --git a/doc/components/edge_detector.md b/doc/components/edge_detector.md index 7984bd989..f89fb9137 100644 --- a/doc/components/edge_detector.md +++ b/doc/components/edge_detector.md @@ -1,3 +1,3 @@ # Edge Detection -The `EdgeDetector` is a simple utility to determine whether the current value of a 1-bit signal is different from the value in the previous cycle. It is a fully synchronous design, so it does not asynchronously detect edges. It optionally supports a reset, with an optional reset value. It can be configured to detect positive, negative, or "any" edges. +The `EdgeDetector` is a simple utility to determine whether the current value of a 1-bit signal is different from the value in the previous cycle. It is a fully synchronous design, so it does not asynchronously detect edges. It optionally supports a reset, with an optional reset value. Furthermore, it can be configured to detect positive, negative, or "any" edges. diff --git a/doc/components/fifo.md b/doc/components/fifo.md index ebd700497..dcade2d21 100644 --- a/doc/components/fifo.md +++ b/doc/components/fifo.md @@ -1,6 +1,6 @@ # FIFO -ROHD HCL comes with a simple FIFO (First In, First Out). The detailed API docs are available [here](https://intel.github.io/rohd-hcl/rohd_hcl/Fifo-class.html). +ROHD-HCL comes with a simple FIFO (First In, First Out). The detailed API docs are available [here](https://intel.github.io/rohd-hcl/rohd_hcl/Fifo-class.html). The underlying implementation uses a flop-based memory (see [`RegisterFile`](https://intel.github.io/rohd-hcl/rohd_hcl/RegisterFile-class.html)) to store data until it is ready to be popped, with independent read and write pointers. @@ -40,7 +40,7 @@ The FIFO comes with both a checker and a tracker that you can leverage in your t ### Checker -The `FifoChecker` is a ROHD-VF component which will watch for proper usge of a FIFO in your simulation. It is intended to check usage, not the internal workings of the FIFO, which are already pre-validated in the unit tests. This means it covers things like underflow, overflow, and that the FIFO is empty at the end of the test. +The `FifoChecker` is a ROHD-VF component which will watch for proper usage of a FIFO in your simulation. It is intended to check usage, not the internal workings of the FIFO, which are already pre-validated in the unit tests. This means it covers things like underflow, overflow, and that the FIFO is empty at the end of the test. ### Tracker diff --git a/doc/components/find.md b/doc/components/find.md index b71048ab5..4c650ac6f 100644 --- a/doc/components/find.md +++ b/doc/components/find.md @@ -1,6 +1,6 @@ # Find -ROHD HCL comes with a Find. The detailed API docs are available [here](https://intel.github.io/rohd-hcl/rohd_hcl/rohd_hcl-library.html). +ROHD-HCL comes with a Find. The detailed API docs are available [here](https://intel.github.io/rohd-hcl/rohd_hcl/rohd_hcl-library.html). A Find will search for first/nth occurrence of one(`1`)/zero(`0`) within a given Logic `bus`. The underlying implementation uses a `Count` to count 1's or 0's whenever a Logic `n` value @@ -10,7 +10,7 @@ It takes a Binary Logic `bus` and finds the position of any one or zero within t That is to say, By default a Find will go for finding the first occurrence when no `n` is passed. In addition, with `countOne` which is set as `true` by default to search only one (`1`). Both boolean `countOne` and Logic `n` are optional. Only Logic `bus` is mandatory argument. -This has an output pin named as `find`, for the index position on the occurrence searched (`1`s or `0`s) taken from the LSB (Least significant bit). +This has an output pin named as `find`, for the index position on the occurrence searched (`1`s or `0`s) taken from the LSB (Least Significant Bit). ## Find First diff --git a/doc/components/memory.md b/doc/components/memory.md index 45c194620..e1eae3ac9 100644 --- a/doc/components/memory.md +++ b/doc/components/memory.md @@ -1,6 +1,6 @@ # Memory -ROHD HCL provides a generic `abstract` [`Memory`](https://intel.github.io/rohd-hcl/rohd_hcl/Memory-class.html) class which accepts a dynamic number of `writePorts` and `readPorts`, where each port is of type [`DataPortInterface`](https://intel.github.io/rohd-hcl/rohd_hcl/DataPortInterface-class.html). A `DataPortInterface` is a simple interface with `en` and `addr` as `control` signals and `data` signal(s). In a write interface, all signals are in the same direction. In a read interface, the `control` signals are in the opposite direction of the `data` signal(s). +ROHD-HCL provides a generic `abstract` [`Memory`](https://intel.github.io/rohd-hcl/rohd_hcl/Memory-class.html) class which accepts a dynamic number of `writePorts` and `readPorts`, where each port is of type [`DataPortInterface`](https://intel.github.io/rohd-hcl/rohd_hcl/DataPortInterface-class.html). A `DataPortInterface` is a simple interface with `en` and `addr` as `control` signals and `data` signal(s). In a write interface, all signals are in the same direction. In a read interface, the `control` signals are in the opposite direction of the `data` signal(s). ## Masks diff --git a/doc/components/multiplier.md b/doc/components/multiplier.md index 91eccfa84..e98ab5dac 100644 --- a/doc/components/multiplier.md +++ b/doc/components/multiplier.md @@ -1,21 +1,44 @@ # Multiplier -ROHD HCL provides a multiplier module to get the product from a list of logic. As of now, we have +ROHD-HCL provides an abstract `Multiplier` module which multiplies two +numbers represented as two `Logic`s, potentially of different widths, +treating them as either signed (2s complement) or unsigned. It +produces the product as a `Logic` with width equal to the sum of the +widths of the inputs. As of now, we have the following implementations +of this abstract `Module`: - [Carry Save Multiplier](#carry-save-multiplier) +- [Compression Tree Multiplier](#compression-tree-multiplier) + +An additional kind of abstract module provided is a +`MultiplyAccumulate` module which multiplies two numbers represented +as two `Logic`s and adds the result to a third `Logic` with width +equal to the sum of the widths of the main inputs. We have a +high-performance implementation: + +- [Compression Tree Multiply Accumulate](#compression-tree-multiply-accumulate) + +The compression tree based arithmetic units are built from a set of components for Booth-encoding, column compression, and parallel prefix adders described in the [`Booth Encoding Multiplier Building Blocks`](./multiplier_components.md#booth-encoding-multiplier-building-blocks) section. ## Carry Save Multiplier -Carry save multiplier is a digital circuit used for performing multiplication operations. It is particularly useful in applications that require high speed multiplication, such as digital signal processing. +Carry save multiplier is a digital circuit used for performing multiplication operations. It +is particularly useful in applications that require high speed +multiplication, such as digital signal processing. -The [`CarrySaveMultiplier`](https://intel.github.io/rohd-hcl/rohd_hcl/CarrySaveMultiplier-class.html) module in ROHD-HCL accept input parameters the clock `clk` signal, reset `reset` signal, `Logic`s' a and b as the input pin and the name of the module `name`. Note that the width of the inputs must be the same or `RohdHclException` will be thrown. +The +[`CarrySaveMultiplier`](https://intel.github.io/rohd-hcl/rohd_hcl/CarrySaveMultiplier-class.html) +module in ROHD-HCL accept input parameters the clock `clk` signal, +reset `reset` signal, `Logic`s' a and b as the input pin and the name +of the module `name`. Note that the width of the inputs must be the +same or `RohdHclException` will be thrown. An example is shown below to multiply two inputs of signals that have 4-bits of width. ```dart -const widthLength = 4; -final a = Logic(name: 'a', width: widthLength); -final b = Logic(name: 'b', width: widthLength); +const bitWidth = 4; +final a = Logic(name: 'a', width: bitWidth); +final b = Logic(name: 'b', width: bitWidth); final reset = Logic(name: 'reset'); final clk = SimpleClockGenerator(10).clk; @@ -46,3 +69,76 @@ await waitCycles(csm.latency).then( Simulator.endSimulation(); ``` + +## Compression Tree Multiplier + +A compression tree multiplier is a digital circuit used for performing +multiplication operations, using Booth encoding to produce addends, a +compression tree for reducing addends to a final pair, and a final +adder generated from a parallel prefix tree option. It is particularly +useful in applications that require high speed multiplication, such as +digital signal processing. + +The parameters of the +`CompressionTreeMultiplier` are: + +- Two input terms `a` and `b` +- The radix used for Booth encoding (2, 4, 8, and 16 are currently supported) +- The type of `ParallelPrefix` tree used in the final `ParallelPrefixAdder` (optional) +- Whether the operands should be treated as signed (2s complement) or unsigned + +Here is an example of use of the `CompressionTreeMultiplier`: + +```dart + const widthA = 6; + const widthB = 9; + const radix = 8; + final a = Logic(name: 'a', width: widthA); + final b = Logic(name: 'b', width: widthB); + + a.put(15); + b.put(3); + + final multiplier = + CompressionTreeMultiplier(a, b, radix, signed: true); + + final product = multiplier.product; + + print('${product.value.toBigInt()}'); +``` + +## Compression Tree Multiply Accumulate + +A compression tree multiply accumulate is similar to a compress tree +multiplier, but it inserts an additional addend into the compression +tree to allow for accumulation into this third input. + +The parameters of the +`CompressionTreeMultiplyAccumulate` are: + +- Two input terms a and b +- The accumulate input term c +- The radix used for Booth encoding (2, 4, 8, and 16 are currently supported) +- The type of `ParallelPrefix` tree used in the final `ParallelPrefixAdder` (optional) +- Whether the operands should be treated as signed (2s complement) or unsigned + +Here is an example of using the `CompressionTreeMultiplyAccumulate`: + +```dart + const widthA = 6; + const widthB = 9; + const radix = 8; + final a = Logic(name: 'a', width: widthA); + final b = Logic(name: 'b', width: widthB); + final c = Logic(name: 'c', width: widthA + widthB); + + a.put(15); + b.put(3); + c.put(5); + + final multiplier = CompressionTreeMultiplyAccumulate(a, b, c, radix, signed: true); + + final accumulate = multiplier.accumulate; + + print('${accumulate.value.toBigInt()}'); +``` diff --git a/doc/components/multiplier_components.md b/doc/components/multiplier_components.md new file mode 100644 index 000000000..2d084b6f8 --- /dev/null +++ b/doc/components/multiplier_components.md @@ -0,0 +1,201 @@ +# Booth Encoding Multiplier Building Blocks + +The Compression Tree multipliers `CompressionTreeMultiplier` and `CompressionTreeMultiplyAccumulate` use a set of building blocks that can also be used for building up other multipliers and arithmetic circuits. These are from the family of Booth-encoding multipliers which are comprised of three major stages: + +1) Booth radix encoding (typically radix-4) generating partial products +2) Partial product array column compression to two addends +3) Final adder + +Each of these stages can be implemented with different algorithms and in ROHD-HCL, we provide the flexible building blocks for these three stages. + +- [Partial Product Generator](#partial-product-generator) +- [Compression Tree](#compression-tree) +- [Final Adder](#final-adder) + +## Introduction to Booth Encoding + +Think of the hand-multiplication process where you write down the multiplicand and the multiplier. Then, starting with the LSB of the multiplier (6), you would take each bit (0 1 1 0), and created a shifted version of the multiplicand (3 = 0 0 1 1) to write down as an addend row, shifting left 1 position per row. Once complete, you would add up all the rows. In the example below, the bits of the multiplier (6) select single multiples of multiplicand (3) shifted into a partial product matrix, which adds up to their product (18). + +```text + 0 0 1 1 (3) + 0 1 1 0 (6) + ========== + 0 0 0 0 + 0 0 1 1 + 0 0 1 1 + 0 0 0 0 + ==================== + 0 0 1 0 0 1 0 (18) +``` + +With Booth encoding, we take multiple adjacent bits of the multiplier (6) to form these rows. In the case that most closely matches hand-multiplication, radix-2 Booth encoding, we take two adjacent bit slices to create multiples [-1,-0, +0, +1] where a leading bit in the slice would indicate negation. These then select the appropriate multiple to shift into the row. So (6) = [0 1 1 0] gets sliced left-to-right (leading with a 0) to create multiple selectors: [0 0], [1 0], [1 1], [0 1]. These slices are radix encoded into multiple (±0, ±1) selectors as follows according to radix-2: + +| bit_i | bit_i-1 | multiple| +|:-----:|:-------:|:-------:| +| 0 | 0 | +0 | +| 0 | 1 | +1 | +| 1 | 0 | -1 | +| 1 | 1 | -0 | + +: Radix-2 Table + +These slices then select shifted multiples of the multiplicand (3 = 0 0 1 1) as follows. + +```text +row slice mult +00 [0 0] = +0 0 0 0 0 +01 [1 0] = -1 1 1 0 0 +02 [1 1] = -0 1 1 1 1 +03 [0 1] = +1 0 0 1 1 +``` + +A few things to note: first, that we are negating by 1s complement (so we need a -0) and second, these rows do not add up to (18: 10010). For Booth encoded rows to add up properly, they need to be in 2s complement form, and they need to be sign-extended. + + Here is the matrix with crude sign extension (this formatting is available from our `PartialProductGenerator` component). With 2s complementation, and sign bits folded in (note the LSB of each row has a sign term from the previous row), these addends are correctly formed and add to (18: 10010). + +```text + 7 6 5 4 3 2 1 0 +00 M=0 S=0: 0 0 0 0 0 0 0 0 : 00000000 = 0 (0) +01 M=1 S=1: 1 1 1 1 1 0 0 0 : 11111000 = 248 (-8) +02 M=0 S=1: 1 1 1 1 1 1 1 : 11111110 = 254 (-2) +03 M=1 S=0: 0 0 0 1 1 1 : 00011100 = 28 (28) +04 M= S= : 0 0 0 0 0 : 00000000 = 0 (0) +==================================== + 0 0 0 1 0 0 1 0 : 00010010 = 18 (18) + ``` + + There are more compact ways of doing sign-extension which result in far fewer additions. Here is an example of compact sign-extension: + +```text + 7 6 5 4 3 2 1 0 +00 M=0 S=0: 1 0 0 0 0 : 00010000 = 16 (16) +01 M=1 S=1: 0 1 0 0 0 : 00001000 = 8 (8) +02 M=0 S=1: 0 1 1 1 1 : 00011110 = 30 (30) +03 M=1 S=0: 1 1 0 1 1 1 : 11011100 = 220 (-36) +==================================== + 0 0 0 1 0 0 1 0 : 00010010 = 18 (18) +``` + +And of course, with higher radix-encoding, we select more bits at a time from the multiplier and therefore have fewer rows to add. Here is radix-4 Booth encoding for our example, slicing (6: 0110) radix$_{4}$[100]=-2 and radix$_{4}$[011]=2 as multiples: + +```text + 7 6 5 4 3 2 1 0 +00 M=2 S=1: 0 1 1 1 0 0 0 : 00111000 = 56 (56) +01 M=2 S=0: 1 1 0 1 1 0 1 : 11011010 = 218 (-38) +==================================== + 0 0 0 1 0 0 1 0 : 00010010 = 18 (18) +``` + +Note that radix-4 shifts by 2 positions each row, but with only two rows and with sign-extension adding an LSB bit, you only see a shift of 1 in row 1. + +## Partial Product Generator + +This building block creates a set of rows of partial products from a multiplicand and a multiplier. It maintains the partial products as a list of rows, which are themselves lists of Logic as well as a row shift value for each row to represent the starting column of the row's least-significant bit. Its primary inputs are the multiplicand, multiplier, `RadixEncoder`, whether the operands are signed, and the type of `SignExtension` to use in generating the partial product rows. + +The partial product generator produces a set of addends in shifted position to be added. The main output of the component is + +```dart + - List> partialProducts; + - rowShift = []; +``` + +### Radix Encoding + +An argument to the `PartialProductGenerator` is the `RadixEncoder` to be used. The [`RadixEncoder`] takes a single argument which is the radix (power of 2) to be used. + +Instead of using the 1's in the multiplier to select shifted versions of the multiplicand to add in a partial product matrix, radix-encoding will encode multiples of the multiplicand by examining adjacent bits of the multiplier. For radix-4, for example, for a multiplier of size M, instead of M rows of partial products, M/2 rows are formed by selecting from multiples [-2, -1, 0, 1, 2] of the multiplicand. These multiples are computed from an 3 bit slices, overlapped by 1 bit, of the multiplier. Higher radices use wider slices of the multiplier to encode fewer multiples and therefore fewer rows. + +| bit_i | bit_i-1 | bit_i-2 | multiple| +|:-----:|:-------:|:-------:|:-------:| +| 0 | 0 | 0 | +0 | +| 0 | 0 | 1 | 1 | +| 0 | 1 | 0 | 1 | +| 0 | 1 | 1 | 2 | +| 1 | 0 | 0 | 2 | +| 1 | 0 | 1 | -1 | +| 1 | 1 | 0 | -1 | +| 1 | 1 | 1 | -0 | + +: Radix-4 Table + +Our `RadixEncoder` module is general, creating selection tables for arbitrary Booth radices of powers of 2. Currently, we are limited to radix-16 because of challenges in creating the odd multiples efficiently, and there are more advanced techniques for efficiently generating higher radices than 16 than our current encoding/selection/partial-product generation scheme. + +### Sign Extension Option + +The `PartialProductGenerator` class also provides for sign extension with multiple options including `SignExtension.none` which is no sign extension for help in debugging, as well as `SignExtension.compactRect` which is a compact form which works for rectangular products where the multiplicand and multiplier can be of different widths. + +### Partial Product Visualization + +Creating new arithmetic building blocks from these components is tricky and visualizing intermediate results really helps. To that end, our `PartialProductGenerator` class has visualization extension `EvaluatePartialProduct` which help evaluate the current `Logic` values in array form during simulation to help with debug. The evaluation routine with the extension also adds the addends for you to help sanity check the partial product generation. The routine is `EvaluateLivePartialProduct.representation`. + +```text + 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +00 M= 2 S=1: 0 1 1 1 1 1 1 1 0 1 0 : 0000000001111111010 = 1018 (1018) +01 M= 1 S=0: 1 1 1 0 0 0 0 0 1 1 0 : 0000001110000011000 = 7192 (7192) +02 M= 0 S=0: 1 1 1 0 0 0 0 0 0 0 0 : 0001110000000000000 = 57344 (57344) +03 M= 0 S=0: 1 1 1 0 0 0 0 0 0 0 0 : 1110000000000000000 = 458752 (-65536) +====================================================================== + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 : 0000000000000010010 = 18 (18) +``` + +## Compression Tree + +Once you have a partial product matrix, you would like to add up the addends. Traditionally this is done using compression trees which instantiate 2:1 and 3:2 column compressors (or carry-save adders) to reduce the matrix to two addends. The final two addends are often added with an efficient final adder. + +Our `ColumnCompressor` class uses a delay-driven approach to efficiently compress the rows of the partial product matrix. Its only argument is a `PartialProductGenerator`, and it creates a list of `ColumnQueue`s containing the final two addends stored by column after compression. An `extractRow`routine can be used to extract the columns. `ColumnCompressor` currently has an extension `EvaluateColumnCompressor` which can be used to print out the compression progress. Here is the legend for these printouts. + +- ppR,C = partial product entry at row R, column C +- sR,C = sum term coming last from row R, column C +- cR,C = carry term coming last from row R, column C + +Compression Tree before: + +```text + pp5,11 pp5,10 pp5,9 pp5,8 pp5,7 pp5,6 pp5,5 pp5,4 pp4,3 pp3,2 pp2,1 pp1,0 + pp4,9 pp3,8 pp4,7 pp3,6 pp3,5 pp3,4 pp3,3 pp2,2 pp0,1 pp0,0 + pp4,8 pp3,7 pp4,6 pp4,5 pp4,4 pp1,3 pp1,2 pp1,1 + pp2,7 pp0,6 pp0,5 pp0,4 pp0,3 pp0,2 + pp2,6 pp2,5 pp2,4 pp2,3 + pp1,6 pp1,5 pp1,4 + + 1 1 0 0 0 0 0 0 0 1 1 0 110000000110 (3078) + 1 1 0 0 0 1 1 1 0 0 001100011100 (796) + 0 0 0 0 0 1 0 0 000000001000 (8) + 0 1 0 0 0 0 000001000000 (64) + 1 1 1 1 000001111000 (120) + 0 1 1 000000110000 (48) Total=18 +``` + +Compression Tree after compression: + +```text + pp5,11 pp5,10 s0,9 s0,8 s0,7 c0,5 c0,4 c0,3 s0,3 s0,2 pp0,1 pp1,0 + c0,9 c0,8 c0,7 c0,6 s0,6 s0,5 s0,4 s0,3 s0,2 s0,1 pp0,0 + + 1 1 1 1 1 0 1 0 0 1 0 0 111110100100 (4004) + 0 0 0 0 1 1 0 1 1 1 0 000001101110 (110) Total=18 +``` + +## Final Adder + +Any adder can be used as the final adder of the final two addends produced from compression. Typically, we use some for of parallel prefix adder for performance. + +## Compress Tree Multiplier Example + +Here is a code snippet that shows how these components can be used to create a multiplier. + +First the partial product generator is used, which has compact sign extension for rectangular products (`PartialProductGeneratorCompactRectSignExtension`) which we pass in the `RadixEncoder`, whether the operands are signed, and the kind of sign extension to use on the partial products. Note that sign extension is needed regardless of whether operands are signed or not due to Booth encoding. + +Next, we use the `ColumnCompressor` to compress the partial products into two final addends. + +We then choose a `ParallelPrefixAdder` using the `BrentKung` tree style to do the addition. We pass in the two extracted rows of the compressor. +Finally, we produce the product. + +```dart + final pp = + PartialProductGeneratorCompactRectSignExtension(a, b, RadixEncoder(radix), signed: true); + final compressor = ColumnCompressor(pp)..compress(); + final adder = ParallelPrefixAdder( + compressor.exractRow(0), compressor.extractRow(1), BrentKung.new); + product <= adder.sum.slice(a.width + b.width - 1, 0); +``` diff --git a/doc/components/onehot.md b/doc/components/onehot.md index fa42abf45..bcd549cb3 100644 --- a/doc/components/onehot.md +++ b/doc/components/onehot.md @@ -1,11 +1,11 @@ # One Hot Codecs -ROHD HCL implements a set of one hot encoder and decoders. +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 `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. +The encoders take a Logic bit-vector, with the constraint that only a single bit is set to '1' and outputs the bit position in binary. -The decoders take a Logic input representing the bit position to be set to '1', and returns a Logic bitvector with that bit position set to '1', and all others set to '0' +The decoders take a Logic input representing the bit position to be set to '1', and returns a Logic bit-vector with that bit position set to '1', and all others set to '0' [OneHotToBinary Schematic](https://intel.github.io/rohd-hcl/CaseOneHotToBinary.html) diff --git a/doc/components/parallel_prefix_operations.md b/doc/components/parallel_prefix_operations.md index 2c01f72ac..c69c2061b 100644 --- a/doc/components/parallel_prefix_operations.md +++ b/doc/components/parallel_prefix_operations.md @@ -4,15 +4,15 @@ Parallel prefix or 'scan' trees are useful for efficient implementation of computations which involve associative operators. They are used in computations like encoding, or-reduction, and addition. By leveraging advanced programming idioms, like -functors, allowing for passing of function that generates prefix trees -into an scan-based generator for that computation, we can have a wide +functors, allowing for passing of a function that generates prefix trees +into a scan-based generator for that computation, we can have a wide variety of that computation supported by our component library. For -example, we have tree patterns defined by ripple, Sklanksy, +example, we have tree patterns defined by ripple, Sklansky, Kogge-Stone, and Brent-Kung which gives us those four varieties of prefix reduction trees we can use across addition, or-reduction, and priority encoding. -ROHD HCL implements a set of parallel prefix compute operations using +ROHD-HCL implements a set of parallel prefix compute operations using different parallel prefix computation trees based on the ['ParallelPrefix'](https://intel.github.io/rohd-hcl/rohd_hcl/ParallelPrefix-class.html) node class. @@ -38,8 +38,8 @@ types: - ['Ripple'](https://intel.github.io/rohd-hcl/rohd_hcl/Ripple-class.html) - ['Sklansky](https://intel.github.io/rohd-hcl/rohd_hcl/Sklansky-class.html) -- ['KoggeStone'](https://intel.github.io/rohd-hcl/rohd_hcl/KoggeStone-class.html) -- ['BrentKung](https://intel.github.io/rohd-hcl/rohd_hcl/BrentKung-class.html) +- ['Kogge-Stone'](https://intel.github.io/rohd-hcl/rohd_hcl/KoggeStone-class.html) +- ['Brent-Kung](https://intel.github.io/rohd-hcl/rohd_hcl/BrentKung-class.html) Here is an example adder schematic: [ParallelPrefixAdder Schematic](https://intel.github.io/rohd-hcl/ParallelPrefixAdder.html) diff --git a/doc/components/parity.md b/doc/components/parity.md index 27fe9e287..08d7b4b15 100644 --- a/doc/components/parity.md +++ b/doc/components/parity.md @@ -1,6 +1,6 @@ # Parity -ROHD HCL implements a `Parity` component for error checking. For satisfying the functionality of Parity error checking in `Logic` data, ROHD HCL provides 2 `Module`, namely `ParityTransmitter` and `ParityReceiver`. +ROHD-HCL implements a `Parity` component for error checking. For satisfying the functionality of Parity error checking in `Logic` data, ROHD-HCL provides 2 `Module`, namely `ParityTransmitter` and `ParityReceiver`. ## Parity Transmitter diff --git a/doc/components/ready_valid_bfm.md b/doc/components/ready_valid_bfm.md index 74e3ebc96..26a3db764 100644 --- a/doc/components/ready_valid_bfm.md +++ b/doc/components/ready_valid_bfm.md @@ -6,7 +6,7 @@ The Ready/Valid BFM is a collection of [ROHD-VF](https://github.com/intel/rohd-v - When a receiver is able to accept something, it raises `ready`. - When both `valid` and `ready` are high, the transaction is accepted by both sides. -The main two components are the `ReadyValidTransmitterAgent` and `ReadyValidReceiverAgent`, which transmit and receive `data`, respectively. Any bundle of information can be mapped onto the `data` bus. Both agents provide a `blockRate` argument which controls a random weighted chance of preventing a transaction from occuring (either delaying a `valid` or dropping a `ready`). +The main two components are the `ReadyValidTransmitterAgent` and `ReadyValidReceiverAgent`, which transmit and receive `data`, respectively. Any bundle of information can be mapped onto the `data` bus. Both agents provide a `blockRate` argument which controls a random weighted chance of preventing a transaction from occurring (either delaying a `valid` or dropping a `ready`). Additionally, the `ReadyValidMonitor` can be placed on any ready/valid protocol to observe transactions that are accepted. The resulting `ReadyValidPacket`s can also be logged via the `ReadyValidTracker`. diff --git a/doc/components/rotate.md b/doc/components/rotate.md index 28f93167f..84d100083 100644 --- a/doc/components/rotate.md +++ b/doc/components/rotate.md @@ -1,6 +1,6 @@ # Rotate -ROHD HCL comes with a variety of utilities for performing rotations across +ROHD-HCL comes with a variety of utilities for performing rotations across - Direction (Left and Right) - Amount (Dynamic and Fixed), and @@ -38,7 +38,7 @@ A "non-fixed" version is implemented as a case statement with a static swizzle p ### Module Types -Rotations is also accessible via `Module` construction instead of `extension`s, if preferred. +Rotations are also accessible via `Module` construction instead of `extension`s, if preferred. If you want to rotate a signal by a fixed amount, you can use the "fixed" rotation modules: [`RotateLeftFixed`](https://intel.github.io/rohd-hcl/rohd_hcl/RotateLeftFixed-class.html) and [`RotateRightFixed`](https://intel.github.io/rohd-hcl/rohd_hcl/RotateRightFixed-class.html). diff --git a/doc/components/shift_register.md b/doc/components/shift_register.md index f4df3de04..634d8ff98 100644 --- a/doc/components/shift_register.md +++ b/doc/components/shift_register.md @@ -1,6 +1,6 @@ # Shift Register -The `ShiftRegister` in ROHD HCL is a configurable shift register including: +The `ShiftRegister` in ROHD-HCL is a configurable shift register including: - support for any width data - a configurable `depth` (which corresponds to the latency) diff --git a/doc/components/sort.md b/doc/components/sort.md index 9abec28e7..e15364159 100644 --- a/doc/components/sort.md +++ b/doc/components/sort.md @@ -1,6 +1,6 @@ # Sort -ROHD HCL provides a component to perform sort of a list of Logic. As of now, we have +ROHD-HCL provides a component to perform sort of a list of Logic. As of now, we have - [Bitonic Sort](#bitonic-sort) diff --git a/doc/components/standard_interfaces.md b/doc/components/standard_interfaces.md index acf807cb3..8bb3ddb49 100644 --- a/doc/components/standard_interfaces.md +++ b/doc/components/standard_interfaces.md @@ -1,6 +1,6 @@ # Standard Interfaces -ROHD HCL provides a set of standard interfaces using ROHD `Interface`s. This makes it easy to instantiate and connect common interfaces in a configurable way. +ROHD-HCL provides a set of standard interfaces using ROHD `Interface`s. This makes it easy to instantiate and connect common interfaces in a configurable way. ## APB diff --git a/example/example.dart b/example/example.dart index 0dec04bcd..ad4eabe05 100644 --- a/example/example.dart +++ b/example/example.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // example.dart diff --git a/gen/generate.dart b/gen/generate.dart index 8adda02b8..dc7bca2b9 100644 --- a/gen/generate.dart +++ b/gen/generate.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // generate.dart diff --git a/lib/rohd_hcl.dart b/lib/rohd_hcl.dart index 40df82852..9b1470232 100644 --- a/lib/rohd_hcl.dart +++ b/lib/rohd_hcl.dart @@ -1,10 +1,9 @@ // Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause -export 'src/adder.dart'; export 'src/arbiters/arbiters.dart'; +export 'src/arithmetic/arithmetic.dart'; export 'src/binary_gray.dart'; -export 'src/carry_save_mutiplier.dart'; export 'src/component_config/component_config.dart'; export 'src/count.dart'; export 'src/edge_detector.dart'; @@ -16,9 +15,6 @@ 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/parallel_prefix_operations.dart'; -export 'src/ripple_carry_adder.dart'; export 'src/rotate.dart'; export 'src/shift_register.dart'; export 'src/sort.dart'; diff --git a/lib/src/arbiters/arbiter.dart b/lib/src/arbiters/arbiter.dart index 1de80395f..8bbe3ccc4 100644 --- a/lib/src/arbiters/arbiter.dart +++ b/lib/src/arbiters/arbiter.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // arbiter.dart diff --git a/lib/src/arbiters/arbiters.dart b/lib/src/arbiters/arbiters.dart index 79b4eaeb0..d020c6b19 100644 --- a/lib/src/arbiters/arbiters.dart +++ b/lib/src/arbiters/arbiters.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause export 'arbiter.dart'; diff --git a/lib/src/arbiters/mask_round_robin_arbiter.dart b/lib/src/arbiters/mask_round_robin_arbiter.dart index b9bf01b6a..766f90829 100644 --- a/lib/src/arbiters/mask_round_robin_arbiter.dart +++ b/lib/src/arbiters/mask_round_robin_arbiter.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // mask_round_robin_arbiter.dart diff --git a/lib/src/arbiters/priority_arbiter.dart b/lib/src/arbiters/priority_arbiter.dart index f535e089a..df112920f 100644 --- a/lib/src/arbiters/priority_arbiter.dart +++ b/lib/src/arbiters/priority_arbiter.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // priority_arbiter.dart diff --git a/lib/src/arbiters/rotate_round_robin_arbiter.dart b/lib/src/arbiters/rotate_round_robin_arbiter.dart index 65d988116..80556b93b 100644 --- a/lib/src/arbiters/rotate_round_robin_arbiter.dart +++ b/lib/src/arbiters/rotate_round_robin_arbiter.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // rotate_round_robin_arbiter.dart diff --git a/lib/src/arbiters/round_robin_arbiter.dart b/lib/src/arbiters/round_robin_arbiter.dart index 82c690683..cc1d33b40 100644 --- a/lib/src/arbiters/round_robin_arbiter.dart +++ b/lib/src/arbiters/round_robin_arbiter.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // round_robin_arbiter.dart diff --git a/lib/src/arbiters/stateful_arbiter.dart b/lib/src/arbiters/stateful_arbiter.dart index c8bd22365..be62a012c 100644 --- a/lib/src/arbiters/stateful_arbiter.dart +++ b/lib/src/arbiters/stateful_arbiter.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // stateful_arbiter.dart diff --git a/lib/src/arithmetic/addend_compressor.dart b/lib/src/arithmetic/addend_compressor.dart new file mode 100644 index 000000000..81723e88f --- /dev/null +++ b/lib/src/arithmetic/addend_compressor.dart @@ -0,0 +1,254 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// addend_compressor.dart +// Column compression of partial prodcuts +// +// 2024 June 04 +// Author: Desmond Kirkpatrick + +import 'package:collection/collection.dart'; +import 'package:meta/meta.dart'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; + +/// Base class for bit-level column compressor function +abstract class BitCompressor extends Module { + /// Input bits to compress + @protected + late final Logic compressBits; + + /// The addition results [sum] including carry bit + Logic get sum => output('sum'); + + /// The carry results [carry]. + Logic get carry => output('carry'); + + /// Construct a column compressor + BitCompressor(Logic compressBits) { + this.compressBits = addInput( + 'compressBits', + compressBits, + width: compressBits.width, + ); + addOutput('sum'); + addOutput('carry'); + } +} + +/// 2-input column compressor (half-adder) +class Compressor2 extends BitCompressor { + /// Construct a 2-input compressor (half-adder) + Compressor2(super.compressBits) { + sum <= compressBits.xor(); + carry <= compressBits.and(); + } +} + +/// 3-input column compressor (full-adder) +class Compressor3 extends BitCompressor { + /// Construct a 3-input column compressor (full-adder) + Compressor3(super.compressBits) { + sum <= compressBits.xor(); + carry <= + mux(compressBits[0], compressBits.slice(2, 1).or(), + compressBits.slice(2, 1).and()); + } +} + +/// Compress terms +enum CompressTermType { + /// A carry term + carry, + + /// A sum term + sum, + + /// A partial product term (from the original matrix) + pp +} + +/// A compression term +class CompressTerm implements Comparable { + /// The type of term we have + late final CompressTermType type; + + /// The inputs that drove this Term + late final List inputs; + + /// The row of the terminal + final int row; + + /// The column of the term + final int col; + + /// The Logic wire of the term + final Logic logic; + + /// Estimated delay of the output of this CompessTerm + late double delay; + + /// Estimated delay of a Sum term + static const sumDelay = 1.0; + + /// Estimated delay of a Carry term + static const carryDelay = 0.75; + + /// CompressTerm constructor + CompressTerm(this.type, this.logic, this.inputs, this.row, this.col) { + delay = 0.0; + final deltaDelay = switch (type) { + CompressTermType.carry => carryDelay, + CompressTermType.sum => sumDelay, + CompressTermType.pp => 0.0 + }; + for (final i in inputs) { + if (i.delay + deltaDelay > delay) { + delay = i.delay + deltaDelay; + } + } + } + @override + int compareTo(Object other) { + if (other is! CompressTerm) { + throw Exception('Input must be of type CompressTerm '); + } + return delay > other.delay ? 1 : (delay < other.delay ? -1 : 0); + } + + /// Evaluate the logic value of a given CompressTerm. + LogicValue evaluate() { + late LogicValue value; + switch (type) { + case CompressTermType.pp: + value = logic.value; + case CompressTermType.sum: + // xor the eval of the terms + final termValues = [for (final term in inputs) term.evaluate()]; + final sum = termValues.swizzle().xor(); + value = sum; + case CompressTermType.carry: + final termValues = [for (final term in inputs) term.evaluate()]; + final termValuesInt = [ + for (var i = 0; i < termValues.length; i++) termValues[i].toInt() + ]; + + final count = (termValuesInt.isNotEmpty) + ? termValuesInt.reduce((c, term) => c + term) + : 0; + final majority = + (count > termValues.length ~/ 2 ? LogicValue.one : LogicValue.zero); + value = majority; + } + return value; + } + + @override + String toString() { + final str = StringBuffer(); + final ts = switch (type) { + CompressTermType.pp => 'pp', + CompressTermType.carry => 'c', + CompressTermType.sum => 's' + }; + str + ..write(ts) + ..write('$row,$col'); + return str.toString(); + } +} + +/// A column of partial product terms +typedef ColumnQueue = PriorityQueue; + +/// A column compressor +class ColumnCompressor { + /// Columns of partial product CompressTerms + + late final List columns; + + /// The partial product generator to be compressed + final PartialProductGenerator pp; + + /// Initialize a ColumnCompressor for a set of partial products + ColumnCompressor(this.pp) { + columns = List.generate(pp.maxWidth(), (i) => ColumnQueue()); + + for (var row = 0; row < pp.rows; row++) { + for (var col = 0; col < pp.partialProducts[row].length; col++) { + final trueColumn = pp.rowShift[row] + col; + final term = CompressTerm(CompressTermType.pp, + pp.partialProducts[row][col], [], row, trueColumn); + columns[trueColumn].add(term); + } + } + } + + /// Return the longest column length + int longestColumn() => + columns.reduce((a, b) => a.length > b.length ? a : b).length; + + /// Convert a row to a Logic bitvector + Logic extractRow(int row) { + final width = pp.maxWidth(); + + final rowBits = []; + for (var col = columns.length - 1; col >= 0; col--) { + final colList = columns[col].toList(); + if (row < colList.length) { + final value = colList[row].logic; + rowBits.add(value); + } + } + rowBits.addAll(List.filled(pp.rowShift[row], Const(0))); + return rowBits.swizzle().zeroExtend(width); + } + + /// Core iterator for column compressor routine + List _compressIter(int iteration) { + final terms = []; + for (var col = 0; col < columns.length; col++) { + final queue = columns[col]; + final depth = queue.length; + if (depth > iteration) { + if (depth > 2) { + final first = queue.removeFirst(); + final second = queue.removeFirst(); + final inputs = [first, second]; + BitCompressor compressor; + if (depth > 3) { + inputs.add(queue.removeFirst()); + compressor = + Compressor3([for (final i in inputs) i.logic].swizzle()); + } else { + compressor = + Compressor2([for (final i in inputs) i.logic].swizzle()); + } + final t = CompressTerm( + CompressTermType.sum, compressor.sum, inputs, 0, col); + terms.add(t); + columns[col].add(t); + if (col < columns.length - 1) { + final t = CompressTerm( + CompressTermType.carry, compressor.carry, inputs, 0, col); + columns[col + 1].add(t); + terms.add(t); + } + } + } + } + return terms; + } + + /// Compress the partial products array to two addends + void compress() { + final terms = []; + var iterations = longestColumn(); + while (iterations > 0) { + terms.addAll(_compressIter(iterations--)); + if (longestColumn() <= 2) { + break; + } + } + } +} diff --git a/lib/src/adder.dart b/lib/src/arithmetic/adder.dart similarity index 87% rename from lib/src/adder.dart rename to lib/src/arithmetic/adder.dart index 80ea05ab7..d1967be18 100644 --- a/lib/src/adder.dart +++ b/lib/src/arithmetic/adder.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // adder.dart @@ -15,13 +15,13 @@ import 'package:rohd_hcl/rohd_hcl.dart'; abstract class Adder extends Module { /// The input to the adder pin [a]. @protected - late final Logic a; + Logic get a => input('a'); /// The input to the adder pin [b]. @protected - late final Logic b; + Logic get b => input('b'); - /// The addition results [sum]. + /// The addition results in 2s complement form as [sum] Logic get sum => output('sum'); /// Takes in input [a] and input [b] and return the [sum] of the addition @@ -30,8 +30,8 @@ abstract class Adder extends Module { if (a.width != b.width) { throw RohdHclException('inputs of a and b should have same width.'); } - this.a = addInput('a', a, width: a.width); - this.b = addInput('b', b, width: b.width); + addInput('a', a, width: a.width); + addInput('b', b, width: b.width); addOutput('sum', width: a.width + 1); } } diff --git a/lib/src/arithmetic/arithmetic.dart b/lib/src/arithmetic/arithmetic.dart new file mode 100644 index 000000000..c454729d9 --- /dev/null +++ b/lib/src/arithmetic/arithmetic.dart @@ -0,0 +1,11 @@ +// Copyright (C) 2023-2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause + +export 'adder.dart'; +export 'carry_save_mutiplier.dart'; +export 'multiplier.dart'; +export 'multiplier_lib.dart'; +export 'ones_complement_adder.dart'; +export 'parallel_prefix_operations.dart'; +export 'ripple_carry_adder.dart'; +export 'sign_magnitude_adder.dart'; diff --git a/lib/src/carry_save_mutiplier.dart b/lib/src/arithmetic/carry_save_mutiplier.dart similarity index 95% rename from lib/src/carry_save_mutiplier.dart rename to lib/src/arithmetic/carry_save_mutiplier.dart index 36f291232..3a8363d14 100644 --- a/lib/src/carry_save_mutiplier.dart +++ b/lib/src/arithmetic/carry_save_mutiplier.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // carry_save_multiplier.dart @@ -42,7 +42,11 @@ class CarrySaveMultiplier extends Multiplier { CarrySaveMultiplier(super.a, super.b, {required Logic clk, required Logic reset, + required super.signed, super.name = 'carry_save_multiplier'}) { + if (a.width != b.width) { + throw RohdHclException('inputs of a and b should have same width.'); + } clk = addInput('clk', clk); reset = addInput('reset', reset); diff --git a/lib/src/arithmetic/evaluate_compressor.dart b/lib/src/arithmetic/evaluate_compressor.dart new file mode 100644 index 000000000..465743472 --- /dev/null +++ b/lib/src/arithmetic/evaluate_compressor.dart @@ -0,0 +1,78 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// addend_compressor.dart +// Column compression of partial prodcuts +// +// 2024 June 04 +// Author: Desmond Kirkpatrick + +import 'dart:io'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/src/arithmetic/multiplier_lib.dart'; +import 'package:rohd_hcl/src/utils.dart'; + +/// Debug routines for printing out ColumnCompressor during +/// simulation with live logic values +extension EvaluateLiveColumnCompressor on ColumnCompressor { + /// Evaluate the (un)compressed partial product array + /// logic=true will read the logic gate outputs at each level + /// printOut=true will print out the array in the StringBuffer + (BigInt, StringBuffer) evaluate({bool printOut = false, bool logic = false}) { + final ts = StringBuffer(); + final rows = longestColumn(); + final width = pp.maxWidth(); + + var accum = BigInt.zero; + for (var row = 0; row < rows; row++) { + final rowBits = []; + for (var col = columns.length - 1; col >= 0; col--) { + final colList = columns[col].toList(); + if (row < colList.length) { + final value = + logic ? colList[row].logic.value : (colList[row].evaluate()); + rowBits.add(value); + if (printOut) { + ts.write('\t${value.bitString}'); + } + } else if (printOut) { + ts.write('\t'); + } + } + rowBits.addAll(List.filled(pp.rowShift[row], LogicValue.zero)); + final val = rowBits.swizzle().zeroExtend(width).toBigInt(); + accum += val; + if (printOut) { + ts.write('\t${rowBits.swizzle().zeroExtend(width).bitString} ($val)'); + if (row == rows - 1) { + ts.write(' Total=${accum.toSigned(width)}\n'); + stdout.write(ts); + } else { + ts.write('\n'); + } + } + } + if (printOut) { + // We need this to be able to debug, but git lint flunks print + // print(ts); + } + return (accum.toSigned(width), ts); + } + + /// Return a string representing the compression tree in its current state + String representation() { + final ts = StringBuffer(); + for (var row = 0; row < longestColumn(); row++) { + for (var col = columns.length - 1; col >= 0; col--) { + final colList = columns[col].toList(); + if (row < colList.length) { + ts.write('\t${colList[row]}'); + } else { + ts.write('\t'); + } + } + ts.write('\n'); + } + return ts.toString(); + } +} diff --git a/lib/src/arithmetic/evaluate_partial_product.dart b/lib/src/arithmetic/evaluate_partial_product.dart new file mode 100644 index 000000000..e421d13d3 --- /dev/null +++ b/lib/src/arithmetic/evaluate_partial_product.dart @@ -0,0 +1,107 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// partial_product_generator.dart +// Partial Product matrix generation from Booth recoded multiplicand +// +// 2024 May 15 +// Author: Desmond Kirkpatrick + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// Debug routines for printing out partial product matrix during +/// simulation with live logic values +extension EvaluateLivePartialProduct on PartialProductGenerator { + /// Accumulate the partial products and return as BigInt + BigInt evaluate() { + final maxW = maxWidth(); + var accum = BigInt.from(0); + for (var row = 0; row < rows; row++) { + final pp = partialProducts[row].rswizzle().value; + final value = pp.zeroExtend(maxW) << rowShift[row]; + if (pp.isValid) { + accum += value.toBigInt(); + } + } + final sum = LogicValue.ofBigInt(accum, maxW).toBigInt(); + return signed ? sum.toSigned(maxW) : sum; + } + + /// Print out the partial product matrix + String representation() { + final str = StringBuffer(); + + final maxW = maxWidth(); + final nonSignExtendedPad = isSignExtended + ? 0 + : shift > 2 + ? shift - 1 + : 1; + // We will print encoding(1-hot multiples and sign) before each row + final shortPrefix = '99 ${'M='}99 S= : '.length + 3 * nonSignExtendedPad; + + // print bit position header + str.write(' ' * shortPrefix); + for (var i = maxW - 1; i >= 0; i--) { + final bits = i > 9 ? 2 : 1; + str + ..write('$i') + ..write(' ' * (3 - bits)); + } + str.write('\n'); + // Partial product matrix: rows of multiplicand multiples shift by + // rowshift[row] + for (var row = 0; row < rows; row++) { + final rowStr = (row < 10) ? '0$row' : '$row'; + if (row < encoder.rows) { + final encoding = encoder.getEncoding(row); + if (encoding.multiples.value.isValid) { + final first = encoding.multiples.value.firstOne() ?? -1; + final multiple = first + 1; + str.write('$rowStr M=' + '${multiple.toString().padLeft(2)} ' + 'S=${encoding.sign.value.toInt()}: '); + } else { + str.write(' ' * shortPrefix); + } + } else { + str.write('$rowStr ${'M='} S= : '); + } + final entry = partialProducts[row].reversed.toList(); + final prefixCnt = + maxW - (entry.length + rowShift[row]) + nonSignExtendedPad; + str.write(' ' * prefixCnt); + for (var col = 0; col < entry.length; col++) { + str.write('${entry[col].value.bitString} '); + } + final suffixCnt = rowShift[row]; + final value = entry.swizzle().value.zeroExtend(maxW) << suffixCnt; + final intValue = value.isValid ? value.toBigInt() : BigInt.from(-1); + str + ..write(' ' * suffixCnt) + ..write(': ${value.bitString}') + ..write(' = ${value.isValid ? intValue : ""}' + ' (${value.isValid ? intValue.toSigned(maxW) : ""})\n'); + } + // Compute and print binary representation from accumulated value + // Later: we will compare with a compression tree result + str + ..write('=' * (shortPrefix + 3 * maxW)) + ..write('\n') + ..write(' ' * shortPrefix); + + final sum = LogicValue.ofBigInt(evaluate(), maxW); + // print out the sum as a MSB-first bitvector + for (final elem in [for (var i = 0; i < maxW; i++) sum[i]].reversed) { + str.write('${elem.toInt()} '); + } + final val = evaluate(); + str.write(': ${sum.bitString} = ' + '${val.toUnsigned(maxW)}'); + if (isSignExtended) { + str.write(' ($val)\n\n'); + } + return str.toString(); + } +} diff --git a/lib/src/arithmetic/multiplicand_selector.dart b/lib/src/arithmetic/multiplicand_selector.dart new file mode 100644 index 000000000..b49d22177 --- /dev/null +++ b/lib/src/arithmetic/multiplicand_selector.dart @@ -0,0 +1,72 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// multiplicand_selector.dart +// Selection of muliples of the multiplicand for booth recoding +// +// 2024 May 15 +// Author: Desmond Kirkpatrick + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// A class accessing the multiples of the multiplicand at a position +class MultiplicandSelector { + /// radix of the selector + int radix; + + /// The bit shift of the selector (typically overlaps 1) + int shift; + + /// New width of partial products generated from the multiplicand + int get width => multiplicand.width + shift - 1; + + /// Access the multiplicand + Logic multiplicand = Logic(); + + /// Place to store multiples of the multiplicand + late LogicArray multiples; + + /// Generate required multiples of multiplicand + MultiplicandSelector(this.radix, this.multiplicand, {required bool signed}) + : shift = log2Ceil(radix) { + if (radix > 16) { + throw RohdHclException('Radices beyond 16 are not yet supported'); + } + final width = multiplicand.width + shift; + final numMultiples = radix ~/ 2; + multiples = LogicArray([numMultiples], width); + final extendedMultiplicand = signed + ? multiplicand.signExtend(width) + : multiplicand.zeroExtend(width); + + for (var pos = 0; pos < numMultiples; pos++) { + final ratio = pos + 1; + multiples.elements[pos] <= + switch (ratio) { + 1 => extendedMultiplicand, + 2 => extendedMultiplicand << 1, + 3 => (extendedMultiplicand << 2) - extendedMultiplicand, + 4 => extendedMultiplicand << 2, + 5 => (extendedMultiplicand << 2) + extendedMultiplicand, + 6 => (extendedMultiplicand << 3) - (extendedMultiplicand << 1), + 7 => (extendedMultiplicand << 3) - extendedMultiplicand, + 8 => extendedMultiplicand << 3, + _ => throw RohdHclException('Radix is beyond 16') + }; + } + } + + /// Retrieve the multiples of the multiplicand at current bit position + Logic getMultiples(int col) => [ + for (var i = 0; i < multiples.elements.length; i++) + multiples.elements[i][col] + ].swizzle().reversed; + + Logic _select(Logic multiples, RadixEncode encode) => + (encode.multiples & multiples).or() ^ encode.sign; + + /// Select the partial product term from the multiples using a RadixEncode + Logic select(int col, RadixEncode encode) => + _select(getMultiples(col), encode); +} diff --git a/lib/src/arithmetic/multiplier.dart b/lib/src/arithmetic/multiplier.dart new file mode 100644 index 000000000..1fde57422 --- /dev/null +++ b/lib/src/arithmetic/multiplier.dart @@ -0,0 +1,170 @@ +// Copyright (C) 2023-2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// multiplier.dart +// Abstract class of of multiplier module implementation. All multiplier module +// need to inherit this module to ensure consistency. +// +// 2023 May 29 +// Author: Yao Jing Quek + +import 'package:meta/meta.dart'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// An abstract class for all multiplier implementations. +abstract class Multiplier extends Module { + /// The input to the multiplier pin [a]. + @protected + late final Logic a; + + /// The input to the multiplier pin [b]. + @protected + late final Logic b; + + /// The multiplier treats operands and output as signed + bool signed; + + /// The multiplication results of the multiplier. + Logic get product; + + /// Take input [a] and input [b] and return the + /// [product] of the multiplication result. + Multiplier(Logic a, Logic b, {required this.signed, super.name}) { + this.a = addInput('a', a, width: a.width); + this.b = addInput('b', b, width: b.width); + } +} + +/// An abstract class for all multiply accumulate implementations. +abstract class MultiplyAccumulate extends Module { + /// The input to the multiplier pin [a]. + @protected + late final Logic a; + + /// The input to the multiplier pin [b]. + @protected + late final Logic b; + + /// The input to the addend pin [c]. + @protected + late final Logic c; + + /// The multiplier treats operands and output as signed + bool signed; + + /// The multiplication results of the multiply-accumulate. + Logic get accumulate; + + /// Take input [a] and input [b], compute their + /// product, add input [c] to produce the [accumulate] result. + MultiplyAccumulate(Logic a, Logic b, Logic c, + {required this.signed, super.name}) { + this.a = addInput('a', a, width: a.width); + this.b = addInput('b', b, width: b.width); + this.c = addInput('c', c, width: c.width); + } +} + +/// An implementation of an integer multiplier using compression trees +class CompressionTreeMultiplier extends Multiplier { + /// The final product of the multiplier module. + @override + Logic get product => output('product'); + + /// Construct a compression tree integer multipler with + /// a given radix and final adder functor + CompressionTreeMultiplier(super.a, super.b, int radix, + {ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppTree = KoggeStone.new, + super.signed = false}) + : super( + name: 'Compression Tree Multiplier: ' + 'R${radix}_${ppTree.call([ + Logic() + ], (a, b) => Logic()).runtimeType}') { + final product = addOutput('product', width: a.width + b.width); + final pp = PartialProductGeneratorCompactRectSignExtension( + a, b, RadixEncoder(radix), + signed: signed); + + final compressor = ColumnCompressor(pp)..compress(); + final adder = ParallelPrefixAdder( + compressor.extractRow(0), compressor.extractRow(1), + ppGen: ppTree); + product <= adder.sum.slice(a.width + b.width - 1, 0); + } +} + +/// An implementation of an integer multiply accumulate using compression trees +class CompressionTreeMultiplyAccumulate extends MultiplyAccumulate { + /// The final product of the multiplier module. + @override + Logic get accumulate => output('accumulate'); + + /// Construct a compression tree integer multipler with + /// a given radix and final adder functor + CompressionTreeMultiplyAccumulate(super.a, super.b, super.c, int radix, + {required super.signed, + ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppTree = KoggeStone.new}) + : super( + name: 'Compression Tree Multiply Accumulate: ' + 'R${radix}_${ppTree.call([Logic()], (a, b) => Logic()).name}') { + final accumulate = addOutput('accumulate', width: a.width + b.width + 1); + final pp = PartialProductGeneratorCompactRectSignExtension( + a, b, RadixEncoder(radix), + signed: signed); + + // TODO(desmonddak): This sign extension method for the additional + // addend may only work with CompactRectSignExtension + + final lastLength = + pp.partialProducts[pp.rows - 1].length + pp.rowShift[pp.rows - 1]; + + final sign = signed ? c[c.width - 1] : Const(0); + final l = [for (var i = 0; i < c.width; i++) c[i]]; + while (l.length < lastLength) { + l.add(sign); + } + l + ..add(~sign) + ..add(Const(1)); + + // For online evaluate in _ColumnCompressor to work, we need to + // insert the row rather than append it. + pp.partialProducts.insert(0, l); + pp.rowShift.insert(0, 0); + + final compressor = ColumnCompressor(pp)..compress(); + final adder = ParallelPrefixAdder( + compressor.extractRow(0), compressor.extractRow(1), + ppGen: ppTree); + accumulate <= adder.sum.slice(a.width + b.width - 1 + 1, 0); + } +} + +/// A MultiplyAccumulate which ignores the [c] term and applies the +/// multiplier function +class MutiplyOnly extends MultiplyAccumulate { + @override + Logic get accumulate => output('accumulate'); + + /// Construct a MultiplyAccumulate that only multiplies to enable + /// using the same tester with zero addend. + MutiplyOnly(super.a, super.b, super.c, + Multiplier Function(Logic a, Logic b) multiplyGenerator, + {super.signed = false}) // Will be overrwridden by multiplyGenerator + : super(name: 'Multiply Only: ${multiplyGenerator.call(a, b).name}') { + final accumulate = addOutput('accumulate', width: a.width + b.width + 1); + + final multiply = multiplyGenerator(a, b); + signed = multiply.signed; + + accumulate <= + (signed + ? [multiply.product[multiply.product.width - 1], multiply.product] + .swizzle() + : multiply.product.zeroExtend(accumulate.width)); + } +} diff --git a/lib/src/arithmetic/multiplier_encoder.dart b/lib/src/arithmetic/multiplier_encoder.dart new file mode 100644 index 000000000..cfe7727ef --- /dev/null +++ b/lib/src/arithmetic/multiplier_encoder.dart @@ -0,0 +1,194 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// muliplier_encoder.dart +// Generation of Booth Encoded partial products for multiplication +// +// 2024 May 15 +// Author: Desmond Kirkpatrick + +import 'dart:math'; + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// A bundle for the leaf radix compute nodes. This holds the multiples +/// of the multiplicand that are needed for encoding +class RadixEncode extends LogicStructure { + /// Which multiples need to be selected + final Logic multiples; + + /// 'sign' of multiple + final Logic sign; + + /// Structure for holding Radix Encoding + RadixEncode({required int numMultiples}) + : this._( + Logic(width: numMultiples, name: 'multiples'), Logic(name: 'sign')); + + RadixEncode._(this.multiples, this.sign, {String? name}) + : super([multiples, sign], name: name ?? 'RadixLogic'); + + @override + RadixEncode clone({String? name}) => + RadixEncode(numMultiples: multiples.width); +} + +/// Base interface for radix radixEncoder +class RadixEncoder { + /// The radix of the radixEncoder + int radix; + + /// Constructor for setting up a radix encoding block + RadixEncoder(this.radix) { + if (pow(2.0, log2Ceil(radix)) != radix) { + throw RohdHclException('radix must be a power of 2'); + } + } + + /// Encode a multiplier slice into the Booth encoded value + RadixEncode encode(Logic multiplierSlice) { + if (multiplierSlice.width != log2Ceil(radix) + 1) { + throw RohdHclException('multiplier slice width ${multiplierSlice.width}' + 'must be same length as log(radix)+1=${log2Ceil(radix) + 1}'); + } + final width = log2Ceil(radix) + 1; + final inputXor = Logic(width: width); + inputXor <= + (multiplierSlice ^ (multiplierSlice >>> 1)) + .slice(multiplierSlice.width - 1, 0); + + final multiples = []; + for (var i = 2; i < radix + 1; i += 2) { + final variantA = LogicValue.ofInt(i - 1, width); + final xorA = variantA ^ (variantA >>> 1); + final variantB = LogicValue.ofInt(i, width); + final xorB = variantB ^ (variantB >>> 1); + // Multiples don't agree on a bit position so we will skip that position + final multiplesDisagree = xorA ^ xorB; + // Where multiples agree, we need the sense or direction (1 or 0) + final senseMultiples = xorA & xorB; + + multiples.add([ + for (var j = 0; j < width - 1; j++) + if (multiplesDisagree[j].isZero) + if (senseMultiples[j].isZero) ~inputXor[j] else inputXor[j] + ].swizzle().and()); + } + + return RadixEncode._( + multiples.rswizzle(), multiplierSlice[multiplierSlice.width - 1]); + } +} + +/// A class that generates the Booth encoding of the multipler +class MultiplierEncoder { + /// Access the multiplier + final Logic multiplier; + + /// Number of row radixEncoders + late final int rows; + + /// The multiplier value, sign extended as appropriate to be divisible + /// by the RadixEncoder overlapping bitslices. + Logic _extendedMultiplier = Logic(); + late final RadixEncoder _encoder; + late final int _sliceWidth; + + /// Generate an encoding of the input multiplier + MultiplierEncoder(this.multiplier, RadixEncoder radixEncoder, + {required bool signed}) + : _encoder = radixEncoder, + _sliceWidth = log2Ceil(radixEncoder.radix) + 1 { + // Unsigned encoding wants to overlap past the multipler + if (signed) { + rows = + ((multiplier.width + (signed ? 0 : 1)) / log2Ceil(radixEncoder.radix)) + .ceil(); + } else { + rows = (((multiplier.width + 1) % (_sliceWidth - 1) == 0) ? 0 : 1) + + ((multiplier.width + 1) ~/ log2Ceil(radixEncoder.radix)); + } + // slices overlap by 1 and start at -1 + _extendedMultiplier = (signed + ? multiplier.signExtend(rows * (_sliceWidth - 1)) + : multiplier.zeroExtend(rows * (_sliceWidth - 1))); + } + + /// Retrieve the Booth encoding for the row + RadixEncode getEncoding(int row) { + if (row >= rows) { + throw RohdHclException('row $row is not < number of encoding rows $rows'); + } + final base = row * (_sliceWidth - 1); + final multiplierSlice = [ + if (row > 0) + _extendedMultiplier.slice(base + _sliceWidth - 2, base - 1) + else + [_extendedMultiplier.slice(base + _sliceWidth - 2, base), Const(0)] + .swizzle() + ]; + return _encoder.encode(multiplierSlice.first); + } +} + +/// A class accessing the multiples of the multiplicand at a position +class MultiplicandSelector { + /// radix of the selector + int radix; + + /// The bit shift of the selector (typically overlaps 1) + int shift; + + /// New width of partial products generated from the multiplicand + int get width => multiplicand.width + shift - 1; + + /// Access the multiplicand + Logic multiplicand = Logic(); + + /// Place to store multiples of the multiplicand + late LogicArray multiples; + + /// Generate required multiples of multiplicand + MultiplicandSelector(this.radix, this.multiplicand, {required bool signed}) + : shift = log2Ceil(radix) { + if (radix > 16) { + throw RohdHclException('Radices beyond 16 are not yet supported'); + } + final width = multiplicand.width + shift; + final numMultiples = radix ~/ 2; + multiples = LogicArray([numMultiples], width); + final extendedMultiplicand = signed + ? multiplicand.signExtend(width) + : multiplicand.zeroExtend(width); + + for (var pos = 0; pos < numMultiples; pos++) { + final ratio = pos + 1; + multiples.elements[pos] <= + switch (ratio) { + 1 => extendedMultiplicand, + 2 => extendedMultiplicand << 1, + 3 => (extendedMultiplicand << 2) - extendedMultiplicand, + 4 => extendedMultiplicand << 2, + 5 => (extendedMultiplicand << 2) + extendedMultiplicand, + 6 => (extendedMultiplicand << 3) - (extendedMultiplicand << 1), + 7 => (extendedMultiplicand << 3) - extendedMultiplicand, + 8 => extendedMultiplicand << 3, + _ => throw RohdHclException('Radix is beyond 16') + }; + } + } + + /// Retrieve the multiples of the multiplicand at current bit position + Logic getMultiples(int col) => [ + for (var i = 0; i < multiples.elements.length; i++) + multiples.elements[i][col] + ].swizzle().reversed; + + Logic _select(Logic multiples, RadixEncode encode) => + (encode.multiples & multiples).or() ^ encode.sign; + + /// Select the partial product term from the multiples using a RadixEncode + Logic select(int col, RadixEncode encode) => + _select(getMultiples(col), encode); +} diff --git a/lib/src/arithmetic/multiplier_lib.dart b/lib/src/arithmetic/multiplier_lib.dart new file mode 100644 index 000000000..d37063194 --- /dev/null +++ b/lib/src/arithmetic/multiplier_lib.dart @@ -0,0 +1,15 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// multiplier_lib.dart +// Consolidate exports of the main components for multiplication: encoder, +// selector, partial_product_generator, compressor +// +// 2024 August 9 +// Author: Desmond Kirkpatrick +// +// + +export './addend_compressor.dart'; +export './multiplier_encoder.dart'; +export './partial_product_generator.dart'; diff --git a/lib/src/arithmetic/ones_complement_adder.dart b/lib/src/arithmetic/ones_complement_adder.dart new file mode 100644 index 000000000..eead4be08 --- /dev/null +++ b/lib/src/arithmetic/ones_complement_adder.dart @@ -0,0 +1,79 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// ones_complement_adder.dart +// Implementation of a One's Complement Adder +// +// 2024 August 31 +// Author: Desmond Kirkpatrick + +import 'package:meta/meta.dart'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// An adder (and subtractor) [OnesComplementAdder] that operates on +/// ones-complement values. +class OnesComplementAdder extends Adder { + /// The sign of the result + Logic get sign => output('sign'); + + /// The end-around carry which should be added to the resulting [sum] + /// If the input [carryOut] is not null, this value is stored there. + /// Otherwise, the end-around carry is internally added to [sum] + Logic? get carryOut => tryOutput('carryOut'); + + @protected + Logic _sign = Logic(); + + /// [OnesComplementAdder] constructor with an adder functor [adderGen] + /// Either a Logic [subtractIn] or a boolean [subtract] can enable + /// subtraction, with [subtractIn] overriding [subtract]. If Logic [carryOut] + /// is provided as not null, then the end-around carry is not performed and is + /// left to the caller via the output [carryOut]. + OnesComplementAdder(super.a, super.b, + {Adder Function(Logic, Logic) adderGen = ParallelPrefixAdder.new, + Logic? subtractIn, + Logic? carryOut, + bool subtract = false}) + : super( + name: 'Ones Complement Adder: ' + '${adderGen.call(Logic(), Logic()).name}') { + if (subtractIn != null) { + subtractIn = addInput('subtractIn', subtractIn); + } + _sign = addOutput('sign'); + if (carryOut != null) { + addOutput('carryOut'); + carryOut <= this.carryOut!; + } + if ((subtractIn != null) & subtract) { + throw RohdHclException( + 'Subtraction is controlled by a non-null subtractIn: ' + 'subtract boolean is ignored'); + } + final doSubtract = subtractIn ?? (subtract ? Const(1) : Const(0)); + + final ax = a.zeroExtend(a.width); + final bx = b.zeroExtend(b.width); + + final adder = adderGen(ax, mux(doSubtract, ~bx, bx)); + + if (this.carryOut != null) { + this.carryOut! <= adder.sum[-1]; + } + final endAround = mux(doSubtract, adder.sum[-1], Const(0)); + final magnitude = adder.sum.slice(a.width - 1, 0); + + sum <= + mux( + doSubtract, + mux( + endAround, + [if (this.carryOut != null) magnitude else magnitude + 1] + .first, + ~magnitude) + .zeroExtend(sum.width), + adder.sum); + _sign <= mux(doSubtract, ~endAround, Const(0)); + } +} diff --git a/lib/src/parallel_prefix_operations.dart b/lib/src/arithmetic/parallel_prefix_operations.dart similarity index 69% rename from lib/src/parallel_prefix_operations.dart rename to lib/src/arithmetic/parallel_prefix_operations.dart index 7f1d5b381..e1af39542 100644 --- a/lib/src/parallel_prefix_operations.dart +++ b/lib/src/arithmetic/parallel_prefix_operations.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // parallel_prefix_operations.dart @@ -161,17 +161,39 @@ class ParallelPrefixOrScan extends Module { Logic get out => output('out'); /// OrScan constructor - ParallelPrefixOrScan( - Logic inp, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) - ppGen) { + ParallelPrefixOrScan(Logic inp, + {ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppGen = KoggeStone.new}) + : super( + name: 'ParallelPrefixOrScan: ${ppGen.call([ + Logic() + ], (a, b) => Logic()).name}') { inp = addInput('inp', inp, width: inp.width); - final u = - ppGen(List.generate(inp.width, (i) => inp[i]), (a, b) => a | b); + final u = ppGen(inp.elements, (a, b) => a | b); addOutput('out', width: inp.width) <= u.val.rswizzle(); } } +/// Priority Finder based on ParallelPrefix tree +class ParallelPrefixPriorityFinder extends Module { + /// Output [out] is the one-hot reduction to the first '1' in the Logic input + /// Search is from the LSB + Logic get out => output('out'); + + /// Priority Finder constructor + ParallelPrefixPriorityFinder(Logic inp, + {ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppGen = KoggeStone.new}) + : super( + name: 'ParallelPrefixFinder: ${ppGen.call([ + Logic() + ], (a, b) => Logic()).name}') { + inp = addInput('inp', inp, width: inp.width); + final u = ParallelPrefixOrScan(inp, ppGen: ppGen); + addOutput('out', width: inp.width) <= (u.out & ~(u.out << Const(1))); + } +} + /// Priority Encoder based on ParallelPrefix tree class ParallelPrefixPriorityEncoder extends Module { /// Output [out] is the bit position of the first '1' in the Logic input @@ -179,36 +201,41 @@ class ParallelPrefixPriorityEncoder extends Module { Logic get out => output('out'); /// PriorityEncoder constructor - ParallelPrefixPriorityEncoder( - Logic inp, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) - ppGen) { + ParallelPrefixPriorityEncoder(Logic inp, + {ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppGen = KoggeStone.new}) + : super( + name: 'ParallelPrefixEncoder: ${ppGen.call([ + Logic() + ], (a, b) => Logic()).name}') { inp = addInput('inp', inp, width: inp.width); - final u = ParallelPrefixOrScan(inp, ppGen); - addOutput('out', width: inp.width) <= (u.out & ~(u.out << Const(1))); + addOutput('out', width: log2Ceil(inp.width)); + final u = ParallelPrefixPriorityFinder(inp, ppGen: ppGen); + out <= OneHotToBinary(u.out).binary; } } /// Adder based on ParallelPrefix tree -class ParallelPrefixAdder extends Module { - /// Output [out] the arithmetic sum of the two Logic inputs - Logic get out => output('out'); - +class ParallelPrefixAdder extends Adder { /// Adder constructor - ParallelPrefixAdder(Logic a, Logic b, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) ppGen, - {super.definitionName}) { - a = addInput('a', a, width: a.width); - b = addInput('b', b, width: b.width); + ParallelPrefixAdder(super.a, super.b, + {ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppGen = KoggeStone.new}) + : super( + name: 'ParallelPrefixAdder: ${ppGen.call([ + Logic() + ], (a, b) => Logic()).name}') { final u = ppGen( - // generate, propagate or generate List.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.generate(a.width, - (i) => (i == 0) ? a[i] ^ b[i] : a[i] ^ b[i] ^ u.val[i - 1][1]) - .rswizzle(); + sum <= + [ + u.val[a.width - 1][1], + List.generate(a.width, + (i) => (i == 0) ? a[i] ^ b[i] : a[i] ^ b[i] ^ u.val[i - 1][1]) + .rswizzle() + ].swizzle(); } } @@ -218,13 +245,15 @@ class ParallelPrefixIncr extends Module { Logic get out => output('out'); /// Increment constructor - ParallelPrefixIncr( - Logic inp, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) - ppGen) { + ParallelPrefixIncr(Logic inp, + {ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppGen = KoggeStone.new}) + : super( + name: 'ParallelPrefixIncr: ${ppGen.call([ + Logic() + ], (a, b) => Logic()).name}') { inp = addInput('inp', inp, width: inp.width); - final u = ppGen(List.generate(inp.width, (i) => inp[i]), - (lhs, rhs) => rhs & lhs); + final u = ppGen(inp.elements, (lhs, rhs) => rhs & lhs); addOutput('out', width: inp.width) <= (List.generate( inp.width, (i) => ((i == 0) ? ~inp[i] : inp[i] ^ u.val[i - 1])) @@ -238,13 +267,15 @@ class ParallelPrefixDecr extends Module { Logic get out => output('out'); /// Decrement constructor - ParallelPrefixDecr( - Logic inp, - ParallelPrefix Function(List, Logic Function(Logic, Logic)) - ppGen) { + ParallelPrefixDecr(Logic inp, + {ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppGen = KoggeStone.new}) + : super( + name: 'ParallelPrefixDecr: ${ppGen.call([ + Logic() + ], (a, b) => Logic()).name}') { inp = addInput('inp', inp, width: inp.width); - final u = ppGen(List.generate(inp.width, (i) => ~inp[i]), - (lhs, rhs) => rhs & lhs); + final u = ppGen((~inp).elements, (lhs, rhs) => rhs & lhs); addOutput('out', width: inp.width) <= (List.generate( inp.width, (i) => ((i == 0) ? ~inp[i] : inp[i] ^ u.val[i - 1])) diff --git a/lib/src/arithmetic/partial_product_generator.dart b/lib/src/arithmetic/partial_product_generator.dart new file mode 100644 index 000000000..2f4efeedc --- /dev/null +++ b/lib/src/arithmetic/partial_product_generator.dart @@ -0,0 +1,271 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// partial_product_generator.dart +// Partial Product matrix generation from Booth recoded multiplicand +// +// 2024 May 15 +// Author: Desmond Kirkpatrick + +import 'dart:math'; + +import 'package:meta/meta.dart'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// A [PartialProductGenerator] class that generates a set of partial products. +/// Essentially a set of +/// shifted rows of [Logic] addends generated by Booth recoding and +/// manipulated by sign extension, before being compressed +abstract class PartialProductGenerator { + /// Get the shift increment between neighboring product rows + int get shift => selector.shift; + + /// The actual shift in each row. This value will be modified by the + /// sign extension routine used when folding in a sign bit from another + /// row + final rowShift = []; + + /// rows of partial products + int get rows => partialProducts.length; + + /// The multiplicand term + Logic get multiplicand => selector.multiplicand; + + /// The multiplier term + Logic get multiplier => encoder.multiplier; + + /// Partial Products output. Generated by selector and extended by sign + /// extension routines + late final List> partialProducts; + + /// Encoder for the full multiply operand + late final MultiplierEncoder encoder; + + /// Selector for the multiplicand which uses the encoder to index into + /// multiples of the multiplicand and generate partial products + late final MultiplicandSelector selector; + + /// Operands are signed + final bool signed; + + /// Used to avoid sign extending more than once + bool isSignExtended = false; + + /// Construct a [PartialProductGenerator] -- the partial product matrix + PartialProductGenerator( + Logic multiplicand, Logic multiplier, RadixEncoder radixEncoder, + {required this.signed}) { + encoder = MultiplierEncoder(multiplier, radixEncoder, signed: signed); + selector = + MultiplicandSelector(radixEncoder.radix, multiplicand, signed: signed); + + if (multiplicand.width < selector.shift) { + throw RohdHclException('multiplicand width must be greater than ' + '${selector.shift}'); + } + if (multiplier.width < (selector.shift + (signed ? 1 : 0))) { + throw RohdHclException('multiplier width must be greater than ' + '${selector.shift + (signed ? 1 : 0)}'); + } + _build(); + signExtend(); + } + + /// Perform sign extension (defined in child classes) + @protected + void signExtend(); + + /// Setup the partial products array (partialProducts and rowShift) + void _build() { + partialProducts = >[]; + for (var row = 0; row < encoder.rows; row++) { + partialProducts.add(List.generate( + selector.width, (i) => selector.select(i, encoder.getEncoding(row)))); + } + for (var row = 0; row < rows; row++) { + rowShift.add(row * shift); + } + } + + /// Return the actual largest width of all rows + int maxWidth() { + var maxW = 0; + for (var row = 0; row < rows; row++) { + final entry = partialProducts[row]; + if (entry.length + rowShift[row] > maxW) { + maxW = entry.length + rowShift[row]; + } + } + return maxW; + } +} + +/// A Partial Product Generator with no sign extension +class PartialProductGeneratorNoSignExtension extends PartialProductGenerator { + /// Construct a basic Partial Product Generator + PartialProductGeneratorNoSignExtension( + super.multiplicand, super.multiplier, super.radixEncoder, + {required super.signed}); + + @override + void signExtend() {} +} + +/// A Partial Product Generator using Brute Sign Extension +class PartialProductGeneratorCompactRectSignExtension + extends PartialProductGenerator { + /// Construct a compact rect sign extending Partial Product Generator + PartialProductGeneratorCompactRectSignExtension( + super.multiplicand, super.multiplier, super.radixEncoder, + {required super.signed}); + + void _addStopSignFlip(List addend, Logic sign) { + if (signed) { + addend.last = ~addend.last; + } else { + addend.add(sign); + } + } + + void _addStopSign(List addend, Logic sign) { + if (signed) { + addend.last = sign; + } else { + addend.add(sign); + } + } + + /// Sign extend the PP array using stop bits without adding a row + /// This routine works with different widths of multiplicand/multiplier, + /// an extension of Mohanty, B.K., Choubey designed by + /// Desmond A. Kirkpatrick + @override + void signExtend() { + if (isSignExtended) { + throw RohdHclException('Partial Product array already sign-extended'); + } + isSignExtended = true; + + final lastRow = rows - 1; + final firstAddend = partialProducts[0]; + final lastAddend = partialProducts[lastRow]; + + final firstRowQStart = selector.width - (signed ? 1 : 0); + final lastRowSignPos = shift * lastRow; + + final align = firstRowQStart - lastRowSignPos; + + final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; + + // Compute propgation info for folding sign bits into main rows + final propagate = + List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); + + for (var row = 0; row < rows; row++) { + propagate[row].add(signs[row]); + for (var col = 0; col < 2 * (shift - 1); col++) { + propagate[row].add(partialProducts[row][col]); + } + // Last row has extend sign propagation to Q start + if (row == lastRow) { + var col = 2 * (shift - 1); + while (propagate[lastRow].length <= align) { + propagate[lastRow].add(partialProducts[row][col++]); + } + } + // Now compute the propagation logic + for (var col = 1; col < propagate[row].length; col++) { + propagate[row][col] = propagate[row][col] & propagate[row][col - 1]; + } + } + + // Compute 'm', the prefix of each row to carry the sign of the next row + final m = + List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); + for (var row = 0; row < rows; row++) { + for (var c = 0; c < shift - 1; c++) { + m[row].add(partialProducts[row][c] ^ propagate[row][c]); + } + m[row].addAll(List.filled(shift - 1, Logic())); + } + while (m[lastRow].length < align) { + m[lastRow].add(Logic()); + } + for (var i = shift - 1; i < m[lastRow].length; i++) { + m[lastRow][i] = + lastAddend[i] ^ (i < align ? propagate[lastRow][i] : Const(0)); + } + + final remainders = List.filled(rows, Logic()); + for (var row = 0; row < lastRow; row++) { + remainders[row] = propagate[row][shift - 1]; + } + remainders[lastRow] = propagate[lastRow][align > 0 ? align : 0]; + + // Merge 'm' into the LSBs of each addend + for (var row = 0; row < rows; row++) { + final addend = partialProducts[row]; + if (row > 0) { + final mLimit = (row == lastRow) ? align : shift - 1; + for (var i = 0; i < mLimit; i++) { + addend[i] = m[row][i]; + } + // Stop bits + _addStopSignFlip(addend, ~signs[row]); + addend + ..insert(0, remainders[row - 1]) + ..addAll(List.filled(shift - 1, Const(1))); + rowShift[row] -= 1; + } else { + // First row + for (var i = 0; i < shift - 1; i++) { + firstAddend[i] = m[0][i]; + } + } + } + + // Insert the lastRow sign: Either in firstRow's Q if there is a + // collision or in another row if it lands beyond the Q sign extension + + final firstSign = signed ? firstAddend.last : signs[0]; + final lastSign = remainders[lastRow]; + // Compute Sign extension MSBs for firstRow + final qLen = shift + 1; + final insertSignPos = (align > 0) ? 0 : -align; + final q = List.filled(min(qLen, insertSignPos), firstSign, growable: true); + if (insertSignPos < qLen) { + // At sign insertion position + q.add(firstSign ^ lastSign); + if (insertSignPos == qLen - 1) { + q[insertSignPos] = ~q[insertSignPos]; + q.add(~(firstSign | q[insertSignPos])); + } else { + q + ..addAll(List.filled(qLen - insertSignPos - 2, firstSign & ~lastSign)) + ..add(~(firstSign & ~lastSign)); + } + } + + if (-align >= q.length) { + q.last = ~firstSign; + } + _addStopSign(firstAddend, q[0]); + firstAddend.addAll(q.getRange(1, q.length)); + + if (-align >= q.length) { + final finalCarryRelPos = + lastRowSignPos - selector.width - shift + (signed ? 1 : 0); + final finalCarryRow = (finalCarryRelPos / shift).floor(); + final curRowLength = + partialProducts[finalCarryRow].length + rowShift[finalCarryRow]; + + partialProducts[finalCarryRow] + ..addAll(List.filled(lastRowSignPos - curRowLength, Const(0))) + ..add(remainders[lastRow]); + } + if (shift == 1) { + lastAddend.add(Const(1)); + } + } +} diff --git a/lib/src/arithmetic/partial_product_test_sign_extend.dart b/lib/src/arithmetic/partial_product_test_sign_extend.dart new file mode 100644 index 000000000..6a1ffe369 --- /dev/null +++ b/lib/src/arithmetic/partial_product_test_sign_extend.dart @@ -0,0 +1,277 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// partial_product_test_sign_extend.dart +// Partial Product Genereator sign extension methods. +// +// 2024 May 15 +// Author: Desmond Kirkpatrick + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// Methods for sign extending the [PartialProductGenerator] +enum SignExtension { + /// No sign extension + none, + + /// Brute force sign extend each row to the full width of the product + brute, + + /// Extend using stop bits in each row (and an extra row for final sign) + stop, + + /// Fold in last row sign bit (Mohanty, B.K., Choubey, A.) + compact, + + /// Sign folding that works for rectangular partial products + compactRect +} + +/// Used to test different sign extension methods +typedef PPGFunction = PartialProductGenerator + Function(Logic a, Logic b, RadixEncoder radixEncoder, {bool signed}); + +/// Used to test different sign extension methods +PPGFunction curryPartialProductGenerator(SignExtension signExtension) => + (a, b, encoder, {signed = false}) => switch (signExtension) { + SignExtension.none => PartialProductGeneratorNoSignExtension( + a, b, encoder, + signed: signed), + SignExtension.brute => PartialProductGeneratorBruteSignExtension( + a, b, encoder, + signed: signed), + SignExtension.stop => PartialProductGeneratorStopBitsSignExtension( + a, b, encoder, + signed: signed), + SignExtension.compact => PartialProductGeneratorCompactSignExtension( + a, b, encoder, + signed: signed), + SignExtension.compactRect => + PartialProductGeneratorCompactRectSignExtension(a, b, encoder, + signed: signed), + }; + +/// These other sign extensions are for asssisting with testing and debugging. +/// More robust and simpler sign extensions in case +/// complex sign extension routines obscure other bugs. + +/// A Partial Product Generator using Brute Sign Extension +class PartialProductGeneratorBruteSignExtension + extends PartialProductGenerator { + /// Construct a brute-force sign extending Partial Product Generator + PartialProductGeneratorBruteSignExtension( + super.multiplicand, super.multiplier, super.radixEncoder, + {required super.signed}); + + /// Fully sign extend the PP array: useful for reference only + @override + void signExtend() { + if (isSignExtended) { + throw RohdHclException('Partial Product array already sign-extended'); + } + isSignExtended = true; + final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; + for (var row = 0; row < rows; row++) { + final addend = partialProducts[row]; + final sign = signed ? addend.last : signs[row]; + addend.addAll(List.filled((rows - row) * shift, sign)); + if (row > 0) { + addend + ..insertAll(0, List.filled(shift - 1, Const(0))) + ..insert(0, signs[row - 1]); + rowShift[row] -= shift; + } + } + // Insert carry bit in extra row + partialProducts.add(List.generate(selector.width, (i) => Const(0))); + partialProducts.last.insert(0, signs[rows - 2]); + rowShift.add((rows - 2) * shift); + } +} + +/// A Partial Product Generator using Brute Sign Extension +class PartialProductGeneratorCompactSignExtension + extends PartialProductGenerator { + /// Construct a compact sign extending Partial Product Generator + PartialProductGeneratorCompactSignExtension( + super.multiplicand, super.multiplier, super.radixEncoder, + {required super.signed}); + + /// Sign extend the PP array using stop bits without adding a row. + @override + void signExtend() { + // An implementation of + // Mohanty, B.K., Choubey, A. Efficient Design for Radix-8 Booth Multiplier + // and Its Application in Lifting 2-D DWT. Circuits Syst Signal Process 36, + // 1129–1149 (2017). https://doi.org/10.1007/s00034-016-0349-9 + if (isSignExtended) { + throw RohdHclException('Partial Product array already sign-extended'); + } + isSignExtended = true; + + final lastRow = rows - 1; + final firstAddend = partialProducts[0]; + final lastAddend = partialProducts[lastRow]; + var alignRow0Sign = selector.width - + shift * lastRow - + ((shift > 1) + ? 1 + : signed + ? 1 + : 0); + + if (alignRow0Sign < 0) { + alignRow0Sign = 0; + } + + final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; + + final propagate = + List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); + for (var row = 0; row < rows; row++) { + propagate[row].add(signs[row]); + for (var col = 0; col < 2 * (shift - 1); col++) { + propagate[row].add(partialProducts[row][col]); + } + for (var col = 1; col < propagate[row].length; col++) { + propagate[row][col] = propagate[row][col] & propagate[row][col - 1]; + } + } + final m = + List.generate(rows, (i) => List.filled(0, Logic(), growable: true)); + for (var row = 0; row < rows; row++) { + for (var c = 0; c < shift - 1; c++) { + m[row].add(partialProducts[row][c] ^ propagate[row][c]); + } + m[row].addAll(List.filled(shift - 1, Logic())); + } + + for (var i = shift - 1; i < m[lastRow].length; i++) { + m[lastRow][i] = lastAddend[i] ^ + (i < alignRow0Sign ? propagate[lastRow][i] : Const(0)); + } + + final remainders = List.filled(rows, Logic()); + for (var row = 0; row < lastRow; row++) { + remainders[row] = propagate[row][shift - 1]; + } + remainders[lastRow] <= propagate[lastRow][alignRow0Sign]; + + // Compute Sign extension for row==0 + final firstSign = signed ? firstAddend.last : signs[0]; + final q = [ + firstSign ^ remainders[lastRow], + ~(firstSign & ~remainders[lastRow]), + ]; + q.insertAll(1, List.filled(shift - 1, ~q[1])); + + for (var row = 0; row < rows; row++) { + final addend = partialProducts[row]; + if (row > 0) { + final mLimit = (row == lastRow) ? 2 * (shift - 1) : shift - 1; + for (var i = 0; i < mLimit; i++) { + addend[i] = m[row][i]; + } + // Stop bits + if (signed) { + addend.last = ~addend.last; + } else { + addend.add(~signs[row]); + } + addend + ..insert(0, remainders[row - 1]) + ..addAll(List.filled(shift - 1, Const(1))); + rowShift[row] -= 1; + } else { + for (var i = 0; i < shift - 1; i++) { + firstAddend[i] = m[0][i]; + } + if (signed) { + firstAddend.last = q[0]; + } else { + firstAddend.add(q[0]); + } + firstAddend.addAll(q.getRange(1, q.length)); + } + } + if (shift == 1) { + lastAddend.add(Const(1)); + } + } +} + +/// A Partial Product Generator using Brute Sign Extension +class PartialProductGeneratorStopBitsSignExtension + extends PartialProductGenerator { + /// Construct a stop bits sign extending Partial Product Generator + PartialProductGeneratorStopBitsSignExtension( + super.multiplicand, super.multiplier, super.radixEncoder, + {required super.signed}); + + /// Sign extend the PP array using stop bits + /// If possible, fold the final carry into another row (only when rectangular + /// enough that carry bit lands outside another row). + /// This technique can then be combined with a first-row extension technique + /// for folding in the final carry. + @override + void signExtend() { + if (isSignExtended) { + throw RohdHclException('Partial Product array already sign-extended'); + } + isSignExtended = true; + + final finalCarryPos = shift * (rows - 1); + final finalCarryRelPos = finalCarryPos - selector.width - shift; + final finalCarryRow = + ((encoder.multiplier.width > selector.multiplicand.width) && + (finalCarryRelPos > 0)) + ? (finalCarryRelPos / shift).floor() + : 0; + + final signs = [for (var r = 0; r < rows; r++) encoder.getEncoding(r).sign]; + for (var row = 0; row < rows; row++) { + final addend = partialProducts[row]; + final sign = signed ? addend.last : signs[row]; + if (row == 0) { + if (signed) { + addend.addAll(List.filled(shift - 1, sign)); // signed only? + } else { + addend.addAll(List.filled(shift, sign)); + } + addend.add(~sign); + } else { + if (signed) { + addend.last = ~sign; + } else { + addend.add(~sign); + } + addend + ..addAll(List.filled(shift - 1, Const(1))) + ..insertAll(0, List.filled(shift - 1, Const(0))) + ..insert(0, signs[row - 1]); + rowShift[row] -= shift; + } + } + + if (finalCarryRow > 0) { + final extensionRow = partialProducts[finalCarryRow]; + extensionRow + ..addAll(List.filled( + finalCarryPos - (extensionRow.length + rowShift[finalCarryRow]), + Const(0))) + ..add(signs[rows - 1]); + } else if (signed) { + // Create an extra row to hold the final carry bit + partialProducts + .add(List.filled(selector.width, Const(0), growable: true)); + partialProducts.last.insert(0, signs[rows - 2]); + rowShift.add((rows - 2) * shift); + + // Hack for radix-2 + if (shift == 1) { + partialProducts.last.last = ~partialProducts.last.last; + } + } + } +} diff --git a/lib/src/ripple_carry_adder.dart b/lib/src/arithmetic/ripple_carry_adder.dart similarity index 91% rename from lib/src/ripple_carry_adder.dart rename to lib/src/arithmetic/ripple_carry_adder.dart index 0c2575122..a5fe3d383 100644 --- a/lib/src/ripple_carry_adder.dart +++ b/lib/src/arithmetic/ripple_carry_adder.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // ripple_carry_adder.dart @@ -19,17 +19,16 @@ 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'}) { - Logic carry = Const(0); - + Logic? carry; final sumList = []; for (var i = 0; i < a.width; i++) { - final fullAdder = FullAdder(a: a[i], b: b[i], carryIn: carry); + final fullAdder = FullAdder(a: a[i], b: b[i], carryIn: carry ?? Const(0)); carry = fullAdder.carryOut; sumList.add(fullAdder.sum); } - sumList.add(carry); + sumList.add(carry!); sum <= sumList.rswizzle(); } diff --git a/lib/src/arithmetic/sign_magnitude_adder.dart b/lib/src/arithmetic/sign_magnitude_adder.dart new file mode 100644 index 000000000..008dcd984 --- /dev/null +++ b/lib/src/arithmetic/sign_magnitude_adder.dart @@ -0,0 +1,55 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// sign_magnitude_adder.dart +// Implementation of a One's Complement Adder +// +// 2024 August 8 +// Author: Desmond Kirkpatrick + +import 'package:meta/meta.dart'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// A [SignMagnitudeAdder] performsa addition on values in sign/magnitude format. +class SignMagnitudeAdder extends Adder { + /// The sign of the first input + Logic aSign; + + /// The sign of the second input + Logic bSign; + + /// The sign of the result + Logic get sign => output('sign'); + + @protected + Logic _sign = Logic(); + + /// Largest magnitude argument is provided in [a] or if equal + /// the argument with a negative sign. + bool largestMagnitudeFirst; + + /// [SignMagnitudeAdder] constructor with an adder functor [adderGen] + ///Inputs are (sign, magnitude) pairs: ([aSign], [a]) and ([bSign], [b]). + /// If the caller can guarantee that the larger magnitude value + /// is provided first in [a], then they can set [largestMagnitudeFirst] + /// too 'true' to avoid a comparator. + // TODO(desmonddak): this adder may need a carry-in for rounding + SignMagnitudeAdder(this.aSign, super.a, this.bSign, super.b, + Adder Function(Logic, Logic) adderGen, + {this.largestMagnitudeFirst = false}) + : super( + name: 'Sign Magnitude Adder: ' + '${adderGen.call(Logic(), Logic()).name}') { + aSign = addInput('aSign', aSign); + bSign = addInput('bSign', bSign); + _sign = addOutput('sign'); + + final bLarger = a.lt(b) | (a.eq(b) & bSign.gt(aSign)); + + _sign <= (largestMagnitudeFirst ? aSign : mux(bLarger, bSign, aSign)); + final adder = OnesComplementAdder(a, b, + subtractIn: aSign ^ bSign, adderGen: adderGen); + sum <= adder.sum; + } +} diff --git a/lib/src/binary_gray.dart b/lib/src/binary_gray.dart index 77fb6253e..ef280c245 100644 --- a/lib/src/binary_gray.dart +++ b/lib/src/binary_gray.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // binary_gray.dart diff --git a/lib/src/component_config/component_config.dart b/lib/src/component_config/component_config.dart index 2d7d78e12..a655c0678 100644 --- a/lib/src/component_config/component_config.dart +++ b/lib/src/component_config/component_config.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause export 'components/components.dart'; diff --git a/lib/src/component_config/components/component_registry.dart b/lib/src/component_config/components/component_registry.dart index f25d00b0a..6d3665e1d 100644 --- a/lib/src/component_config/components/component_registry.dart +++ b/lib/src/component_config/components/component_registry.dart @@ -24,4 +24,5 @@ List get componentRegistry => [ EdgeDetectorConfigurator(), FindConfigurator(), ParallelPrefixAdderConfigurator(), + CompressionTreeMultiplierConfigurator(), ]; diff --git a/lib/src/component_config/components/components.dart b/lib/src/component_config/components/components.dart index 899a8ec0a..32c33fda9 100644 --- a/lib/src/component_config/components/components.dart +++ b/lib/src/component_config/components/components.dart @@ -2,6 +2,7 @@ // SPDX-License-Identifier: BSD-3-Clause export 'config_carry_save_multiplier.dart'; +export 'config_compression_tree_multiplier.dart'; export 'config_ecc.dart'; export 'config_edge_detector.dart'; export 'config_fifo.dart'; diff --git a/lib/src/component_config/components/config_carry_save_multiplier.dart b/lib/src/component_config/components/config_carry_save_multiplier.dart index 9ab4fd4b5..96374d80a 100644 --- a/lib/src/component_config/components/config_carry_save_multiplier.dart +++ b/lib/src/component_config/components/config_carry_save_multiplier.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_carry_save_multiplier.dart @@ -16,19 +16,20 @@ class CarrySaveMultiplierConfigurator extends Configurator { /// A knob controlling the width of the inputs to a [CarrySaveMultiplier]. final IntConfigKnob logicWidthKnob = IntConfigKnob(value: 8); + /// A knob controling the sign of a [CarrySaveMultiplier] + final signChoiceKnob = ChoiceConfigKnob([false, true], value: false); + @override final String name = 'Carry Save Multiplier'; @override CarrySaveMultiplier createModule() => CarrySaveMultiplier( - Logic(width: logicWidthKnob.value), - Logic(width: logicWidthKnob.value), - clk: Logic(), - reset: Logic(), - ); + Logic(width: logicWidthKnob.value), Logic(width: logicWidthKnob.value), + clk: Logic(), reset: Logic(), signed: true); @override late final Map> knobs = UnmodifiableMapView({ 'Width': logicWidthKnob, + 'Sign': signChoiceKnob, }); } diff --git a/lib/src/component_config/components/config_compression_tree_multiplier.dart b/lib/src/component_config/components/config_compression_tree_multiplier.dart new file mode 100644 index 000000000..2f6fbbeb9 --- /dev/null +++ b/lib/src/component_config/components/config_compression_tree_multiplier.dart @@ -0,0 +1,60 @@ +// Copyright (C) 2023-2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// config_compression_tree_multiplier.dart +// Configurator for a Compression Tree Multiplier. +// +// 2024 August 7 +// Author: Desmond Kirkpatrick + +import 'dart:collection'; + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// A [Configurator] for [CompressionTreeMultiplier]s. +class CompressionTreeMultiplierConfigurator extends Configurator { + /// Map from Type to Function for Parallel Prefix generator + static Map, Logic Function(Logic, Logic))> + generatorMap = { + Ripple: Ripple.new, + Sklansky: Sklansky.new, + KoggeStone: KoggeStone.new, + BrentKung: BrentKung.new + }; + + /// Controls the type of [ParallelPrefix] tree used in the adder. + final prefixTreeKnob = + ChoiceConfigKnob(generatorMap.keys.toList(), value: KoggeStone); + + /// Controls the Booth encoding radix of the multiplier.! + final radixKnob = ChoiceConfigKnob( + [2, 4, 8, 16], + value: 4, + ); + + /// Controls the width of the multiplicand.! + final IntConfigKnob multiplicandWidthKnob = IntConfigKnob(value: 5); + + /// Controls the width of the multiplier.! + final IntConfigKnob multiplierWidthKnob = IntConfigKnob(value: 5); + + @override + Module createModule() => CompressionTreeMultiplier( + Logic(name: 'a', width: multiplicandWidthKnob.value), + Logic(name: 'b', width: multiplierWidthKnob.value), + radixKnob.value, + ppTree: generatorMap[prefixTreeKnob.value]!); + + @override + late final Map> knobs = UnmodifiableMapView({ + 'Tree type': prefixTreeKnob, + 'Radix': radixKnob, + 'Multiplicand width': multiplicandWidthKnob, + 'Multiplier width': multiplierWidthKnob, + }); + + @override + final String name = 'Comp. Tree Multiplier'; +} diff --git a/lib/src/component_config/components/config_fifo.dart b/lib/src/component_config/components/config_fifo.dart index 2750129de..a304bec17 100644 --- a/lib/src/component_config/components/config_fifo.dart +++ b/lib/src/component_config/components/config_fifo.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_priority_arbiter.dart diff --git a/lib/src/component_config/components/config_one_hot.dart b/lib/src/component_config/components/config_one_hot.dart index 322189b39..536890092 100644 --- a/lib/src/component_config/components/config_one_hot.dart +++ b/lib/src/component_config/components/config_one_hot.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_rotate.dart diff --git a/lib/src/component_config/components/config_parallel_prefix_adder.dart b/lib/src/component_config/components/config_parallel_prefix_adder.dart index 193072078..0cda4da18 100644 --- a/lib/src/component_config/components/config_parallel_prefix_adder.dart +++ b/lib/src/component_config/components/config_parallel_prefix_adder.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023-24 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_parallel-prefix_adder.dart @@ -35,7 +35,7 @@ class ParallelPrefixAdderConfigurator extends Configurator { Module createModule() => ParallelPrefixAdder( Logic(name: 'a', width: dataWidthKnob.value), Logic(name: 'b', width: dataWidthKnob.value), - generatorMap[prefixTreeKnob.value]!); + ppGen: generatorMap[prefixTreeKnob.value]!); @override late final Map> knobs = UnmodifiableMapView({ diff --git a/lib/src/component_config/components/config_priority_arbiter.dart b/lib/src/component_config/components/config_priority_arbiter.dart index b9a4e950b..480c16dfc 100644 --- a/lib/src/component_config/components/config_priority_arbiter.dart +++ b/lib/src/component_config/components/config_priority_arbiter.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_priority_arbiter.dart diff --git a/lib/src/component_config/components/config_rf.dart b/lib/src/component_config/components/config_rf.dart index 2c18de2d7..939a719af 100644 --- a/lib/src/component_config/components/config_rf.dart +++ b/lib/src/component_config/components/config_rf.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_rf.dart diff --git a/lib/src/component_config/components/config_ripple_carry_adder.dart b/lib/src/component_config/components/config_ripple_carry_adder.dart index 7e87516a0..8256ad3f1 100644 --- a/lib/src/component_config/components/config_ripple_carry_adder.dart +++ b/lib/src/component_config/components/config_ripple_carry_adder.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_rippler_carry_adder.dart diff --git a/lib/src/component_config/components/config_rotate.dart b/lib/src/component_config/components/config_rotate.dart index d26b47a73..f31c08150 100644 --- a/lib/src/component_config/components/config_rotate.dart +++ b/lib/src/component_config/components/config_rotate.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_rotate.dart diff --git a/lib/src/component_config/components/config_round_robin_arbiter.dart b/lib/src/component_config/components/config_round_robin_arbiter.dart index 8d2c443e4..1d9d7bfe7 100644 --- a/lib/src/component_config/components/config_round_robin_arbiter.dart +++ b/lib/src/component_config/components/config_round_robin_arbiter.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_round_robin_arbiter.dart diff --git a/lib/src/component_config/components/config_sort.dart b/lib/src/component_config/components/config_sort.dart index d1a978591..3f49d7b69 100644 --- a/lib/src/component_config/components/config_sort.dart +++ b/lib/src/component_config/components/config_sort.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_sort.dart diff --git a/lib/src/component_config/config_knobs/choice_config_knob.dart b/lib/src/component_config/config_knobs/choice_config_knob.dart index 2a97fea45..8ae85949d 100644 --- a/lib/src/component_config/config_knobs/choice_config_knob.dart +++ b/lib/src/component_config/config_knobs/choice_config_knob.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // choice_config_knob.dart @@ -20,14 +20,18 @@ class ChoiceConfigKnob extends ConfigKnob { /// Creates a new knob to with the specified default [value] of the available /// [choices]. - ChoiceConfigKnob(this.choices, {required super.value}) - : assert(choices.contains(value), - 'Default value should be one of the choices.'); + ChoiceConfigKnob(this.choices, {required super.value}) { + if (!choices.contains(value)) { + throw RohdHclException('Default value should be one of the choices.'); + } + } @override set value(T newValue) { - assert(choices.contains(newValue), - 'New value should be one of the available choices.'); + if (!choices.contains(newValue)) { + throw RohdHclException( + 'New value should be one of the available choices.'); + } super.value = newValue; } diff --git a/lib/src/component_config/config_knobs/config_knob.dart b/lib/src/component_config/config_knobs/config_knob.dart index a8b3bb4a0..b76c13bba 100644 --- a/lib/src/component_config/config_knobs/config_knob.dart +++ b/lib/src/component_config/config_knobs/config_knob.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_knob.dart diff --git a/lib/src/component_config/config_knobs/config_knobs.dart b/lib/src/component_config/config_knobs/config_knobs.dart index de14e7431..6f33c5ac3 100644 --- a/lib/src/component_config/config_knobs/config_knobs.dart +++ b/lib/src/component_config/config_knobs/config_knobs.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause export 'choice_config_knob.dart'; diff --git a/lib/src/component_config/config_knobs/group_of_knobs_knob.dart b/lib/src/component_config/config_knobs/group_of_knobs_knob.dart index f0e29700e..c6e7363f3 100644 --- a/lib/src/component_config/config_knobs/group_of_knobs_knob.dart +++ b/lib/src/component_config/config_knobs/group_of_knobs_knob.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // group_of_knobs_knob.dart diff --git a/lib/src/component_config/config_knobs/int_config_knob.dart b/lib/src/component_config/config_knobs/int_config_knob.dart index 8d0c018ef..e5a707941 100644 --- a/lib/src/component_config/config_knobs/int_config_knob.dart +++ b/lib/src/component_config/config_knobs/int_config_knob.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // int_config_knob.dart diff --git a/lib/src/component_config/config_knobs/list_of_knobs_knob.dart b/lib/src/component_config/config_knobs/list_of_knobs_knob.dart index afb816902..5204e7468 100644 --- a/lib/src/component_config/config_knobs/list_of_knobs_knob.dart +++ b/lib/src/component_config/config_knobs/list_of_knobs_knob.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // list_of_knobs_knob.dart diff --git a/lib/src/component_config/config_knobs/string_config_knob.dart b/lib/src/component_config/config_knobs/string_config_knob.dart index dcdcca76c..fc90d6689 100644 --- a/lib/src/component_config/config_knobs/string_config_knob.dart +++ b/lib/src/component_config/config_knobs/string_config_knob.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // string_config_knob.dart diff --git a/lib/src/component_config/config_knobs/toggle_config_knob.dart b/lib/src/component_config/config_knobs/toggle_config_knob.dart index 58b5022e4..e3e251989 100644 --- a/lib/src/component_config/config_knobs/toggle_config_knob.dart +++ b/lib/src/component_config/config_knobs/toggle_config_knob.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // config_knob.dart diff --git a/lib/src/component_config/configurator.dart b/lib/src/component_config/configurator.dart index da4751a36..259eaea26 100644 --- a/lib/src/component_config/configurator.dart +++ b/lib/src/component_config/configurator.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // configurator.dart @@ -51,12 +51,14 @@ abstract class Configurator { /// Loads the configuration from a serialized JSON representation. void loadJson(String json) { final decoded = jsonDecode(json) as Map; - assert(decoded['name'] == name, 'Expect name to be the same.'); + if (decoded['name'] != name) { + throw RohdHclException('Expect name to be the same.'); + } for (final decodedKnob in (decoded['knobs'] as Map).entries) { - assert(knobs.containsKey(decodedKnob.key), - 'Expect knobs in JSON to exist in configurator.'); - + if (!knobs.containsKey(decodedKnob.key)) { + throw RohdHclException('Expect name to be the same.'); + } knobs[decodedKnob.key]! .loadJson(decodedKnob.value as Map); } diff --git a/lib/src/encodings/binary_to_one_hot.dart b/lib/src/encodings/binary_to_one_hot.dart index 08983c178..98b954b64 100644 --- a/lib/src/encodings/binary_to_one_hot.dart +++ b/lib/src/encodings/binary_to_one_hot.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // binary_to_one_hot.dart diff --git a/lib/src/encodings/case_one_hot_to_binary.dart b/lib/src/encodings/case_one_hot_to_binary.dart index d464ee717..27f61ff32 100644 --- a/lib/src/encodings/case_one_hot_to_binary.dart +++ b/lib/src/encodings/case_one_hot_to_binary.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // case_one_hot_to_binary.dart @@ -22,8 +22,10 @@ class CaseOneHotToBinary extends OneHotToBinary { /// 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() { + : super.base() { + if (onehot.width >= 32) { + throw RohdHclException('Should not be used for large widths.'); + } Combinational([ Case(onehot, conditionalType: ConditionalType.unique, [ for (var i = 0; i < onehot.width; i++) diff --git a/lib/src/encodings/encodings.dart b/lib/src/encodings/encodings.dart index 815955317..570c7ae2f 100644 --- a/lib/src/encodings/encodings.dart +++ b/lib/src/encodings/encodings.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause export 'binary_to_one_hot.dart'; diff --git a/lib/src/encodings/one_hot_to_binary.dart b/lib/src/encodings/one_hot_to_binary.dart index 1038ecd81..51ccefef0 100644 --- a/lib/src/encodings/one_hot_to_binary.dart +++ b/lib/src/encodings/one_hot_to_binary.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // one_hot_to_binary.dart @@ -32,8 +32,10 @@ abstract class OneHotToBinary extends Module { {bool generateError = false, String name = 'one_hot_to_binary'}) { final isSmall = onehot.width <= 8; - assert(!(!isSmall && generateError), - 'Tree implementation does not generate error signal.'); + if (!isSmall && generateError) { + throw RohdHclException( + 'Tree implementation does not generate error signal.'); + } return isSmall ? CaseOneHotToBinary(onehot, generateError: generateError, name: name) diff --git a/lib/src/encodings/tree_one_hot_to_binary.dart b/lib/src/encodings/tree_one_hot_to_binary.dart index 9cf05cc8c..d6df0ecb2 100644 --- a/lib/src/encodings/tree_one_hot_to_binary.dart +++ b/lib/src/encodings/tree_one_hot_to_binary.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // tree_one_hot_to_binary.dart diff --git a/lib/src/error_checking/error_checker.dart b/lib/src/error_checking/error_checker.dart index ea1a5d62f..3c6a61c90 100644 --- a/lib/src/error_checking/error_checker.dart +++ b/lib/src/error_checking/error_checker.dart @@ -8,6 +8,7 @@ import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; /// A transmitter for data which includes a [code] for checking and possibly /// correcting data at the receiving end. @@ -97,8 +98,11 @@ abstract class ErrorCheckingReceiver extends Module { {required int codeWidth, required this.supportsErrorCorrection, required super.name, - super.definitionName}) - : assert(codeWidth > 0, 'Must provide non-empty code.') { + super.definitionName}) { + if (codeWidth <= 0) { + throw RohdHclException('Must provide non-empty code.'); + } + this.transmission = addInput('transmission', transmission, width: transmission.width); diff --git a/lib/src/exceptions.dart b/lib/src/exceptions.dart index d6d8e4e9f..ba7b52546 100644 --- a/lib/src/exceptions.dart +++ b/lib/src/exceptions.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // exceptions.dart diff --git a/lib/src/fifo.dart b/lib/src/fifo.dart index 08f0d4dd8..ce19e23fa 100644 --- a/lib/src/fifo.dart +++ b/lib/src/fifo.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // fifo.dart @@ -92,9 +92,10 @@ class Fifo extends Module { if (depth <= 0) { throw RohdHclException('Depth must be at least 1.'); } - - assert(_addrWidth > 0, - 'Assumption that address width is non-zero in implementation'); + if (_addrWidth <= 0) { + throw RohdHclException( + 'Assumption that address width is non-zero in implementation'); + } addInput('clk', clk); addInput('reset', reset); diff --git a/lib/src/find.dart b/lib/src/find.dart index e9ad6e66e..63243f197 100644 --- a/lib/src/find.dart +++ b/lib/src/find.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // find.dart diff --git a/lib/src/interfaces/apb.dart b/lib/src/interfaces/apb.dart index 2aa23858f..b41df2bc0 100644 --- a/lib/src/interfaces/apb.dart +++ b/lib/src/interfaces/apb.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb.dart diff --git a/lib/src/interfaces/interfaces.dart b/lib/src/interfaces/interfaces.dart index 14df1afec..45914619e 100644 --- a/lib/src/interfaces/interfaces.dart +++ b/lib/src/interfaces/interfaces.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause export 'apb.dart'; diff --git a/lib/src/memory/memories.dart b/lib/src/memory/memories.dart index dc51cd894..dec8f2107 100644 --- a/lib/src/memory/memories.dart +++ b/lib/src/memory/memories.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause export 'memory.dart'; diff --git a/lib/src/memory/memory.dart b/lib/src/memory/memory.dart index 80a6b35f8..c0545c1e6 100644 --- a/lib/src/memory/memory.dart +++ b/lib/src/memory/memory.dart @@ -27,8 +27,10 @@ class MaskedDataPortInterface extends DataPortInterface { Logic get mask => port('mask'); /// Constructs a [DataPortInterface] with mask. - MaskedDataPortInterface(super.dataWidth, super.addrWidth) - : assert(dataWidth % 8 == 0, 'The data width must be byte-granularity') { + MaskedDataPortInterface(super.dataWidth, super.addrWidth) { + if (dataWidth % 8 != 0) { + throw RohdHclException('The data width must be byte-granularity'); + } setPorts([ Port('mask', dataWidth ~/ 8), ], [ @@ -122,17 +124,23 @@ abstract class Memory extends Module { Memory(Logic clk, Logic reset, List writePorts, List readPorts, {super.name = 'memory'}) - : assert(!(writePorts.isEmpty && readPorts.isEmpty), - 'Must specify at least one read port or one write port.'), - numWrites = writePorts.length, + : numWrites = writePorts.length, numReads = readPorts.length, dataWidth = (writePorts.isNotEmpty) ? writePorts[0].dataWidth - : readPorts[0].dataWidth, // at least one of these must exist + : (readPorts.isNotEmpty) + ? readPorts[0].dataWidth + : 0, // at least one of these must exist addrWidth = (writePorts.isNotEmpty) ? writePorts[0].addrWidth - : readPorts[0].addrWidth // at least one of these must exist + : (readPorts.isNotEmpty) + ? readPorts[0].addrWidth + : 0 // at least one of these must exist { + if (writePorts.isEmpty && readPorts.isEmpty) { + throw RohdHclException( + 'Must specify at least one read port or one write port.'); + } if (readLatency < 0) { throw RohdHclException('Read latency must be non-negative.'); } diff --git a/lib/src/memory/register_file.dart b/lib/src/memory/register_file.dart index 6a59cb037..5d7742afb 100644 --- a/lib/src/memory/register_file.dart +++ b/lib/src/memory/register_file.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // register_file.dart diff --git a/lib/src/models/apb_bfm/abp_completer.dart b/lib/src/models/apb_bfm/abp_completer.dart index 550525663..a0058c33a 100644 --- a/lib/src/models/apb_bfm/abp_completer.dart +++ b/lib/src/models/apb_bfm/abp_completer.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb_completer.dart diff --git a/lib/src/models/apb_bfm/apb_bfm.dart b/lib/src/models/apb_bfm/apb_bfm.dart index 5c6f78b0d..3acc63375 100644 --- a/lib/src/models/apb_bfm/apb_bfm.dart +++ b/lib/src/models/apb_bfm/apb_bfm.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause export 'abp_completer.dart'; diff --git a/lib/src/models/apb_bfm/apb_compliance_checker.dart b/lib/src/models/apb_bfm/apb_compliance_checker.dart index 20d6b965f..a9aa311d1 100644 --- a/lib/src/models/apb_bfm/apb_compliance_checker.dart +++ b/lib/src/models/apb_bfm/apb_compliance_checker.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb_completer.dart diff --git a/lib/src/models/apb_bfm/apb_monitor.dart b/lib/src/models/apb_bfm/apb_monitor.dart index 6f757bea6..a1b679fbd 100644 --- a/lib/src/models/apb_bfm/apb_monitor.dart +++ b/lib/src/models/apb_bfm/apb_monitor.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb_monitor.dart diff --git a/lib/src/models/apb_bfm/apb_packet.dart b/lib/src/models/apb_bfm/apb_packet.dart index 2daeb8f32..77e326493 100644 --- a/lib/src/models/apb_bfm/apb_packet.dart +++ b/lib/src/models/apb_bfm/apb_packet.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb_packet.dart diff --git a/lib/src/models/apb_bfm/apb_requester.dart b/lib/src/models/apb_bfm/apb_requester.dart index 1bfc02955..428c534cf 100644 --- a/lib/src/models/apb_bfm/apb_requester.dart +++ b/lib/src/models/apb_bfm/apb_requester.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb_requester.dart diff --git a/lib/src/models/apb_bfm/apb_requester_driver.dart b/lib/src/models/apb_bfm/apb_requester_driver.dart index 5ac8b828e..42a3eda18 100644 --- a/lib/src/models/apb_bfm/apb_requester_driver.dart +++ b/lib/src/models/apb_bfm/apb_requester_driver.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb_requester_driver.dart diff --git a/lib/src/models/ready_valid_bfm/ready_valid_transmitter_driver.dart b/lib/src/models/ready_valid_bfm/ready_valid_transmitter_driver.dart index 270d73de3..f1ecdcc5f 100644 --- a/lib/src/models/ready_valid_bfm/ready_valid_transmitter_driver.dart +++ b/lib/src/models/ready_valid_bfm/ready_valid_transmitter_driver.dart @@ -1,9 +1,17 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// ready_valid_transmitter_agent.dart +// An agent for transmitting over a ready/valid protocol. +// +// 2024 January 5 +// Author: Max Korbel + import 'dart:async'; import 'dart:math'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -import 'package:rohd_hcl/src/models/ready_valid_bfm/ready_valid_packet.dart'; import 'package:rohd_vf/rohd_vf.dart'; /// An [Agent] for transmitting over a ready/valid protocol. @@ -70,7 +78,9 @@ class ReadyValidTransmitterDriver } else { valid.inject(1); - assert(pkt.data.width == data.width, 'Data widths should match.'); + if (pkt.data.width != data.width) { + throw RohdHclException('Data widths should match.'); + } data.inject(pkt.data); // wait for it to be accepted diff --git a/lib/src/models/sparse_memory_storage.dart b/lib/src/models/sparse_memory_storage.dart index 6aced4552..21bf3d8db 100644 --- a/lib/src/models/sparse_memory_storage.dart +++ b/lib/src/models/sparse_memory_storage.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // sparse_memory_storage.dart diff --git a/lib/src/multiplier.dart b/lib/src/multiplier.dart deleted file mode 100644 index 15a820aa2..000000000 --- a/lib/src/multiplier.dart +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2023 Intel Corporation -// SPDX-License-Identifier: BSD-3-Clause -// -// multiplier.dart -// Abstract class of of multiplier module implementation. All multiplier module -// need to inherit this module to ensure consistency. -// -// 2023 May 29 -// Author: Yao Jing Quek - -import 'package:meta/meta.dart'; -import 'package:rohd/rohd.dart'; -import 'package:rohd_hcl/rohd_hcl.dart'; - -/// An abstract class for all multiplier implementation. -abstract class Multiplier extends Module { - /// The input to the multiplier pin [a]. - @protected - late final Logic a; - - /// The input to the multiplier pin [b]. - @protected - late final Logic b; - - /// The multiplication results of the multiplier. - Logic get product; - - /// Take input [a] and input [b] and return the - /// [product] of the multiplication result. - Multiplier(Logic a, Logic b, {super.name}) { - if (a.width != b.width) { - throw RohdHclException('inputs of a and b should have same width.'); - } - this.a = addInput('a', a, width: a.width); - this.b = addInput('b', b, width: b.width); - } -} diff --git a/lib/src/rotate.dart b/lib/src/rotate.dart index 1dd37950b..f7ef3923f 100644 --- a/lib/src/rotate.dart +++ b/lib/src/rotate.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // rotate.dart @@ -166,10 +166,11 @@ extension RotateLogic on Logic { Logic _rotate(dynamic rotateAmount, {required RotateDirection direction, int? maxAmount}) { if (rotateAmount is int) { - assert( - maxAmount == null || rotateAmount <= maxAmount, - 'If `maxAmount` is provided with an integer `amount`,' - ' it should meet the restriction.'); + if (!(maxAmount == null || rotateAmount <= maxAmount)) { + throw RohdHclException( + 'If `maxAmount` is provided with an integer `amount`,' + ' it should meet the restriction.'); + } return direction == RotateDirection.left ? RotateLeftFixed(this, rotateAmount).rotated diff --git a/lib/src/shift_register.dart b/lib/src/shift_register.dart index 6757bfc2e..e567ab6d1 100644 --- a/lib/src/shift_register.dart +++ b/lib/src/shift_register.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // shift_register.dart diff --git a/lib/src/sort.dart b/lib/src/sort.dart index a5db2b3cb..d350ea64a 100644 --- a/lib/src/sort.dart +++ b/lib/src/sort.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // sort.dart diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 4e2c70813..e27b7f083 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -1,10 +1,52 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // utils.dart // Various utilities helpful for working with the component library import 'dart:math'; +import 'package:rohd/rohd.dart'; /// Computes the bit width needed to store [w] addresses. int log2Ceil(int w) => (log(w) / log(2)).ceil(); + +/// This extension will eventually move to ROHD once it is proven useful +extension LogicValueBitString on LogicValue { + /// Simplest version of bit string representation as shorthand + String get bitString => toString(includeWidth: false); +} + +/// This extension will eventually move to ROHD once it is proven useful +extension LogicValueMajority on LogicValue { + /// Compute the unary majority on LogicValue + bool majority() { + if (!isValid) { + return false; + } + final zero = LogicValue.filled(width, LogicValue.zero); + var shiftedValue = this; + var result = 0; + while (shiftedValue != zero) { + result += (shiftedValue[0] & LogicValue.one == LogicValue.one) ? 1 : 0; + shiftedValue >>>= 1; + } + return result > (width ~/ 2); + } + + /// Compute the first One find operation on LogicValue, returning its position + int? firstOne() { + if (!isValid) { + return null; + } + var shiftedValue = this; + var result = 0; + while (shiftedValue[0] != LogicValue.one) { + result++; + if (result == width) { + return null; + } + shiftedValue >>>= 1; + } + return result; + } +} diff --git a/test/apb_bfm_test.dart b/test/apb_bfm_test.dart index 151a8243c..d80a65536 100644 --- a/test/apb_bfm_test.dart +++ b/test/apb_bfm_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb_bfm_test.dart diff --git a/test/apb_test.dart b/test/apb_test.dart index 2c344c5cc..ea658a276 100644 --- a/test/apb_test.dart +++ b/test/apb_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb_test.dart diff --git a/test/arithmetic/addend_compressor_test.dart b/test/arithmetic/addend_compressor_test.dart new file mode 100644 index 000000000..c147026cf --- /dev/null +++ b/test/arithmetic/addend_compressor_test.dart @@ -0,0 +1,195 @@ +// Copyright (C) 2023-2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// addend_compressor_test.dart +// Tests for the select interface of Booth encoding +// +// 2024 June 04 +// Author: Desmond Kirkpatrick + +import 'dart:io'; +import 'dart:math'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_hcl/src/arithmetic/evaluate_compressor.dart'; +import 'package:rohd_hcl/src/arithmetic/evaluate_partial_product.dart'; +import 'package:rohd_hcl/src/arithmetic/partial_product_test_sign_extend.dart'; +import 'package:test/test.dart'; + +void testCompressionExhaustive(PartialProductGenerator pp) { + final widthX = pp.selector.multiplicand.width; + final widthY = pp.encoder.multiplier.width; + + final compressor = ColumnCompressor(pp); + + final limitX = pow(2, widthX); + final limitY = pow(2, widthY); + for (var i = 0; i < limitX; i++) { + for (var j = 0; j < limitY; j++) { + final X = pp.signed + ? BigInt.from(i).toSigned(widthX) + : BigInt.from(i).toUnsigned(widthX); + final Y = pp.signed + ? BigInt.from(j).toSigned(widthY) + : BigInt.from(j).toUnsigned(widthY); + final product = X * Y; + + pp.multiplicand.put(X); + pp.multiplier.put(Y); + final value = pp.evaluate(); + expect(value, equals(product), + reason: 'Fail: $i($X) * $j($Y): $value ' + 'vs expected $product' + '\n$pp'); + final evaluateValue = compressor.evaluate(); + if (evaluateValue.$1 != product) { + stdout + ..write('Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $evaluateValue ' + 'vs expected $product\n') + ..write(pp); + } + compressor.compress(); + final compressedValue = compressor.evaluate().$1; + expect(compressedValue, equals(product), + reason: 'Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $compressedValue ' + 'vs expected $product' + '\n$pp'); + final compressedLogicValue = compressor.evaluate(logic: true).$1; + expect(compressedLogicValue, equals(product), + reason: + 'Fail: $i($X)[$widthX] * $j($Y)[$widthY]: $compressedLogicValue ' + 'vs expected $product' + '\n$pp'); + + final a = compressor.extractRow(0); + final b = compressor.extractRow(1); + + final adder = ParallelPrefixAdder(a, b); + final adderValue = + adder.sum.value.toBigInt().toSigned(compressor.columns.length); + expect(adderValue, equals(product), + reason: 'Fail: $i($X)[$widthX] * $j($Y)[$widthY]: ' + '$adderValue vs expected $product' + '\n$pp'); + } + } +} + +void main() { + test('exhaustive compression evaluate: square radix-4, just CompactRect', + () async { + stdout.write('\n'); + + for (final signed in [false, true]) { + for (var radix = 4; radix < 8; radix *= 2) { + final encoder = RadixEncoder(radix); + // stdout.write('encoding with radix=$radix\n'); + final shift = log2Ceil(encoder.radix); + for (var width = shift + 1; width < 2 * shift + 1; width++) { + for (final signExtension in SignExtension.values) { + if (signExtension != SignExtension.compactRect) { + continue; + } + final ppg = curryPartialProductGenerator(signExtension); + final pp = ppg(Logic(name: 'X', width: width), + Logic(name: 'Y', width: width), encoder, + signed: signed); + + testCompressionExhaustive(pp); + } + } + } + } + }); + test('single compressor evaluate multiply', () async { + const widthX = 6; + const widthY = 9; + final a = Logic(name: 'a', width: widthX); + final b = Logic(name: 'b', width: widthY); + + const av = 4; + const bv = 14; + for (final signed in [true, false]) { + final bA = signed + ? BigInt.from(av).toSigned(widthX) + : BigInt.from(av).toUnsigned(widthX); + final bB = signed + ? BigInt.from(bv).toSigned(widthY) + : BigInt.from(bv).toUnsigned(widthY); + + // Set these so that printing inside module build will have Logic values + a.put(bA); + b.put(bB); + const radix = 2; + final encoder = RadixEncoder(radix); + final pp = PartialProductGeneratorCompactRectSignExtension(a, b, encoder, + signed: signed); + expect(pp.evaluate(), equals(BigInt.from(av * bv))); + final compressor = ColumnCompressor(pp); + expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); + compressor.compress(); + expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); + } + }); + test('single compressor evaluate', () async { + const widthX = 6; + const widthY = 6; + final a = Logic(name: 'a', width: widthX); + final b = Logic(name: 'b', width: widthY); + + const av = 3; + const bv = 6; + for (final signed in [true]) { + final bA = signed + ? BigInt.from(av).toSigned(widthX) + : BigInt.from(av).toUnsigned(widthX); + final bB = signed + ? BigInt.from(bv).toSigned(widthY) + : BigInt.from(bv).toUnsigned(widthY); + + // Set these so that printing inside module build will have Logic values + a.put(bA); + b.put(bB); + const radix = 2; + final encoder = RadixEncoder(radix); + + final pp = PartialProductGeneratorCompactRectSignExtension(a, b, encoder, + signed: signed); + expect(pp.evaluate(), equals(BigInt.from(av * bv))); + final compressor = ColumnCompressor(pp); + expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); + compressor.compress(); + expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); + } + }); + + test('example multiplier', () async { + const widthX = 10; + const widthY = 10; + final a = Logic(name: 'a', width: widthX); + final b = Logic(name: 'b', width: widthY); + + const av = 37; + const bv = 6; + for (final signed in [true]) { + final bA = signed + ? BigInt.from(av).toSigned(widthX) + : BigInt.from(av).toUnsigned(widthX); + final bB = signed + ? BigInt.from(bv).toSigned(widthY) + : BigInt.from(bv).toUnsigned(widthY); + + // Set these so that printing inside module build will have Logic values + a.put(bA); + b.put(bB); + const radix = 8; + final encoder = RadixEncoder(radix); + + final pp = PartialProductGeneratorCompactRectSignExtension(a, b, encoder, + signed: signed); + expect(pp.evaluate(), equals(BigInt.from(av * bv))); + final compressor = ColumnCompressor(pp)..compress(); + expect(compressor.evaluate().$1, equals(BigInt.from(av * bv))); + } + }); +} diff --git a/test/arithmetic/adder_test.dart b/test/arithmetic/adder_test.dart new file mode 100644 index 000000000..1e0af79e0 --- /dev/null +++ b/test/arithmetic/adder_test.dart @@ -0,0 +1,335 @@ +// Copyright (C) 2023-2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// adder_test.dart +// Tests for the Adder interface. +// +// 2024 April 4 +// Author: Desmond Kirkpatrick + +import 'dart:math'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:test/test.dart'; + +void checkAdder(Adder 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(), + // ignore: invalid_use_of_protected_member + // equals((aB + bB) & ((BigInt.one << adder.a.width) - BigInt.one))); + equals(aB + bB)); + expect(adder.sum.value.toBigInt(), equals(aB + bB)); +} + +void testExhaustive(int n, Adder 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); + checkAdder(mod, av, bv); + } + } + }); +} + +void testAdderRandom(int n, int nSamples, Adder Function(Logic a, Logic b) fn) { + final a = Logic(name: 'a', width: n); + final b = Logic(name: 'b', width: n); + + final adder = fn(a, b); + test('random: ${adder.name}_W${a.width}_I$nSamples', () async { + await adder.build(); + + for (var i = 0; i < nSamples; ++i) { + final aa = Random().nextLogicValue(width: n); + final bb = Random().nextLogicValue(width: n); + checkAdder(adder, aa, bb); + } + }); +} + +void checkSignMagnitudeAdder(SignMagnitudeAdder adder, LogicValue aSign, + LogicValue aMagnitude, LogicValue bSign, LogicValue bMagnitude) { + // ignore: invalid_use_of_protected_member + adder.aSign.put(aSign); + // ignore: invalid_use_of_protected_member + adder.bSign.put(bSign); + + // ignore: invalid_use_of_protected_member + adder.a.put(aMagnitude); + // ignore: invalid_use_of_protected_member + adder.b.put(bMagnitude); + + final computedVal = (adder.sign.value == LogicValue.one) + ? -adder.sum.value.toBigInt() + : adder.sum.value.toBigInt(); + + final aValue = (aSign == LogicValue.one) + ? -aMagnitude.toBigInt() + : aMagnitude.toBigInt(); + final bValue = (bSign == LogicValue.one) + ? -bMagnitude.toBigInt() + : bMagnitude.toBigInt(); + final expectSign = (aValue + bValue).sign; + final expectedMag = (aValue + bValue).abs(); + + final expectVal = expectSign == -1 ? -expectedMag : expectedMag; + expect(computedVal, equals(expectVal)); +} + +void testExhaustiveSignMagnitude(int n, Adder Function(Logic a, Logic b) fn, + {bool operandsArePresorted = true}) { + final aSign = Logic(name: 'aSign'); + final a = Logic(name: 'a', width: n); + final bSign = Logic(name: 'bSign'); + final b = Logic(name: 'b', width: n); + + final adder = SignMagnitudeAdder(aSign, a, bSign, b, fn, + largestMagnitudeFirst: operandsArePresorted); + test( + 'exhaustive Sign Magnitude: ' + '${adder.name}_W${a.width}_N$operandsArePresorted', () { + for (var i = 0; i < pow(2, n); i += 1) { + for (var j = 0; j < pow(2, n); j += 1) { + final bI = BigInt.from(i).toSigned(n); + final bJ = BigInt.from(j).toSigned(n); + + final bigger = bI; + final smaller = bJ; + // When equal, we want the negative one first to produce 1 1...1 + if (operandsArePresorted & + ((bI.abs() < bJ.abs()) || (bI.abs() == bJ.abs() && (bI > bJ)))) { + continue; + } else { + final biggerSign = bigger.abs() != bigger ? 1 : 0; + final smallerSign = smaller.abs() != smaller ? 1 : 0; + + final biggerSignLv = LogicValue.of(biggerSign, width: 1); + final smallerSignLv = LogicValue.of(smallerSign, width: 1); + + final biggerLv = LogicValue.of(bigger.abs(), width: n); + final smallerLv = LogicValue.of(smaller.abs(), width: n); + + checkSignMagnitudeAdder( + adder, biggerSignLv, biggerLv, smallerSignLv, smallerLv); + } + } + } + }); +} + +void testRandomSignMagnitude( + int width, int nSamples, Adder Function(Logic a, Logic b) fn, + {bool sortOperands = true}) { + final aSign = Logic(name: 'aSign'); + final a = Logic(name: 'a', width: width); + final bSign = Logic(name: 'bSign'); + final b = Logic(name: 'b', width: width); + + final adder = SignMagnitudeAdder(aSign, a, bSign, b, fn, + largestMagnitudeFirst: sortOperands); + test('random Sign Magnitude: ${adder.name}_W${a.width}_N$sortOperands', + () async { + await adder.build(); + + for (var i = 0; i < nSamples; ++i) { + final aa = Random().nextLogicValue(width: width); + final av = aa.toBigInt().toSigned(width); + final bb = Random().nextLogicValue(width: width); + final bv = bb.toBigInt().toSigned(width); + + final bigger = av; + final smaller = bv; + // When equal, we want the negative one first to produce 1 1...1 + if (sortOperands & + ((av.abs() < bv.abs()) || (av.abs() == bv.abs() && (av > bv)))) { + continue; + } else { + final biggerSign = bigger.abs() != bigger ? 1 : 0; + final smallerSign = smaller.abs() != smaller ? 1 : 0; + + final biggerSignLv = LogicValue.of(biggerSign, width: 1); + final smallerSignLv = LogicValue.of(smallerSign, width: 1); + + final biggerLv = LogicValue.of(bigger.abs(), width: width); + final smallerLv = LogicValue.of(smaller.abs(), width: width); + + checkSignMagnitudeAdder( + adder, biggerSignLv, biggerLv, smallerSignLv, smallerLv); + } + } + }); +} + +void main() { + tearDown(() async { + await Simulator.reset(); + }); + + final generators = [Ripple.new, Sklansky.new, KoggeStone.new, BrentKung.new]; + + group('adderRandom', () { + for (final n in [64, 64, 65]) { + testAdderRandom(n, 30, RippleCarryAdder.new); + for (final ppGen in generators) { + testAdderRandom( + n, 30, (a, b) => ParallelPrefixAdder(a, b, ppGen: ppGen)); + } + } + }); + group('exhaustive', () { + testExhaustive(4, RippleCarryAdder.new); + for (final ppGen in generators) { + testExhaustive(4, (a, b) => ParallelPrefixAdder(a, b, ppGen: ppGen)); + } + }); + group('SignMagnitude random', () { + for (final ppGen in generators) { + testRandomSignMagnitude(4, 30, RippleCarryAdder.new); + testRandomSignMagnitude(4, 30, RippleCarryAdder.new, sortOperands: false); + testRandomSignMagnitude( + 4, 30, (a, b) => ParallelPrefixAdder(a, b, ppGen: ppGen)); + testRandomSignMagnitude( + 4, 30, (a, b) => ParallelPrefixAdder(a, b, ppGen: ppGen), + sortOperands: false); + } + }); + group('SignMagnitude exhaustive', () { + for (final ppGen in generators) { + testExhaustiveSignMagnitude(4, RippleCarryAdder.new); + testExhaustiveSignMagnitude(4, RippleCarryAdder.new, + operandsArePresorted: false); + testExhaustiveSignMagnitude( + 4, (a, b) => ParallelPrefixAdder(a, b, ppGen: ppGen)); + testExhaustiveSignMagnitude( + 4, (a, b) => ParallelPrefixAdder(a, b, ppGen: ppGen), + operandsArePresorted: false); + } + }); + test('trivial parallel prefix 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 = ParallelPrefixAdder(a, b, ppGen: BrentKung.new); + + final sum = adder.sum; + expect(sum.value.toBigInt(), equals(BigInt.from(18 + 24))); + }); + test('trivial sign magnitude adder test', () async { + const width = 6; + final aSign = Logic(name: 'aSign'); + final a = Logic(name: 'a', width: width); + final bSign = Logic(name: 'bSign'); + final b = Logic(name: 'b', width: width); + + aSign.put(0); + a.put(24); + bSign.put(1); + b.put(18); + + final adder = SignMagnitudeAdder(aSign, a, bSign, b, RippleCarryAdder.new, + largestMagnitudeFirst: true); + + final sum = adder.sum; + expect(sum.value.toBigInt(), equals(BigInt.from(24 - 18))); + aSign.put(1); + a.put(24); + bSign.put(0); + b.put(18); + + expect(-sum.value.toBigInt(), equals(BigInt.from(18 - 24))); + }); + + test('ones complement with boolean subtract', () { + const width = 2; + final a = Logic(width: width); + final b = Logic(width: width); + + for (final subtract in [false, true]) { + for (var av = 0; av < pow(2, width); av++) { + for (var bv = 0; bv < pow(2, width); bv++) { + a.put(av); + b.put(bv); + final carry = Logic(); + final adder = + OnesComplementAdder(a, b, carryOut: carry, subtract: subtract); + final mag = adder.sum.value.toInt() + + (subtract ? (carry.value.isZero ? 0 : 1) : 0); + final out = (adder.sign.value.toInt() == 1 ? -mag : mag); + + final expected = [if (subtract) av - bv else av + bv].first; + expect(out, equals(expected)); + } + } + } + }); + + test('ones complement with Logic subtract', () { + const width = 2; + final a = Logic(width: width); + final b = Logic(width: width); + + for (final subtractIn in [Const(0), Const(1)]) { + for (var av = 0; av < pow(2, width); av++) { + for (var bv = 0; bv < pow(2, width); bv++) { + a.put(av); + b.put(bv); + final carry = Logic(); + final adder = OnesComplementAdder(a, b, + subtractIn: subtractIn, + carryOut: carry, + adderGen: RippleCarryAdder.new); + final mag = adder.sum.value.toInt() + + (subtractIn.value == LogicValue.one + ? (carry.value.isZero ? 0 : 1) + : 0); + final out = (adder.sign.value.toInt() == 1 ? -mag : mag); + + final expected = [ + if (subtractIn.value == LogicValue.one) av - bv else av + bv + ].first; + expect(out, equals(expected)); + } + } + } + }); + test('trivial sign magnitude with onescomplement adder test', () async { + const width = 8; + final aSign = Logic(name: 'aSign'); + final a = Logic(name: 'a', width: width); + final bSign = Logic(name: 'bSign'); + final b = Logic(name: 'b', width: width); + + aSign.put(1); + a.put(24); + b.put(6); + bSign.put(0); + + final adder = OnesComplementAdder(a, b, + adderGen: RippleCarryAdder.new, subtract: true); + + final sum = adder.sum; + // print('${adder.sign.value.toInt()} ${sum.value.toInt()}'); + expect(-sum.value.toBigInt(), equals(BigInt.from(6 - 24))); + }); +} diff --git a/test/carry_save_multiplier_test.dart b/test/arithmetic/carry_save_multiplier_test.dart similarity index 90% rename from test/carry_save_multiplier_test.dart rename to test/arithmetic/carry_save_multiplier_test.dart index 614bea00b..9cffff90b 100644 --- a/test/carry_save_multiplier_test.dart +++ b/test/arithmetic/carry_save_multiplier_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // carry_save_mutiplier_test.dart @@ -25,7 +25,8 @@ void main() { final clk = SimpleClockGenerator(10).clk; final reset = Logic(name: 'reset'); - expect(() => CarrySaveMultiplier(clk: clk, reset: reset, a, b), + expect( + () => CarrySaveMultiplier(clk: clk, reset: reset, signed: true, a, b), throwsA(const TypeMatcher())); }); @@ -36,7 +37,7 @@ void main() { final reset = Logic(name: 'reset'); final clk = SimpleClockGenerator(10).clk; - final csm = CarrySaveMultiplier(clk: clk, reset: reset, a, b); + final csm = CarrySaveMultiplier(clk: clk, reset: reset, signed: true, a, b); await csm.build(); diff --git a/test/arithmetic/multiplier_encoder_test.dart b/test/arithmetic/multiplier_encoder_test.dart new file mode 100644 index 000000000..c575a3f33 --- /dev/null +++ b/test/arithmetic/multiplier_encoder_test.dart @@ -0,0 +1,249 @@ +// Copyright (C) 2023-2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// multiplier_encoder_test.dart +// Tests for Booth encoding +// +// 2024 May 21 +// Author: Desmond Kirkpatrick + +import 'dart:io'; +import 'dart:math'; + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_hcl/src/arithmetic/evaluate_partial_product.dart'; +import 'package:rohd_hcl/src/arithmetic/partial_product_test_sign_extend.dart'; +import 'package:test/test.dart'; + +void checkEvaluateExhaustive(PartialProductGenerator pp) { + final widthX = pp.selector.multiplicand.width; + final widthY = pp.encoder.multiplier.width; + + final limitX = pow(2, widthX); + final limitY = pow(2, widthY); + for (var i = BigInt.zero; i < BigInt.from(limitX); i += BigInt.one) { + for (var j = BigInt.zero; j < BigInt.from(limitY); j += BigInt.one) { + final X = pp.signed ? i.toSigned(widthX) : i.toUnsigned(widthX); + final Y = pp.signed ? j.toSigned(widthY) : j.toUnsigned(widthY); + pp.multiplicand.put(X); + pp.multiplier.put(Y); + final value = pp.evaluate(); + expect(value, equals(X * Y), + reason: '$X * $Y = $value should be ${X * Y}'); + } + } +} + +void checkEvaluateRandom(PartialProductGenerator pp, int nSamples) { + final widthX = pp.selector.multiplicand.width; + final widthY = pp.encoder.multiplier.width; + + for (var i = 0; i < nSamples; ++i) { + final rX = Random().nextLogicValue(width: widthX).toBigInt(); + final rY = Random().nextLogicValue(width: widthY).toBigInt(); + final X = pp.signed ? rX.toSigned(widthX) : rX; + final Y = pp.signed ? rY.toSigned(widthY) : rY; + pp.multiplicand.put(X); + pp.multiplier.put(Y); + final value = pp.evaluate(); + expect(value, equals(X * Y), reason: '$X * $Y = $value should be ${X * Y}'); + } +} + +void main() { + test('single MAC partial product test', () async { + final encoder = RadixEncoder(16); + const widthX = 8; + const widthY = 18; + + const i = 1478; + const j = 9; + const k = 0; + + final X = BigInt.from(i).toSigned(widthX); + final Y = BigInt.from(j).toSigned(widthY); + final Z = BigInt.from(k).toSigned(widthX + widthY); + // print('X=$X Y=$Y, Z=$Z'); + final product = X * Y + Z; + + final logicX = Logic(name: 'X', width: widthX); + final logicY = Logic(name: 'Y', width: widthY); + final logicZ = Logic(name: 'Z', width: widthX + widthY); + logicX.put(X); + logicY.put(Y); + logicZ.put(Z); + final pp = PartialProductGeneratorCompactRectSignExtension( + logicX, logicY, encoder, + signed: true); + + final lastLength = + pp.partialProducts[pp.rows - 1].length + pp.rowShift[pp.rows - 1]; + + final sign = logicZ[logicZ.width - 1]; + // for unsigned versus signed testing + // final sign = signed ? logicZ[logicZ.width - 1] : Const(0); + final l = [for (var i = 0; i < logicZ.width; i++) logicZ[i]]; + while (l.length < lastLength) { + l.add(sign); + } + l + ..add(~sign) + ..add(Const(1)); + // print(pp.representation()); + + pp.partialProducts.insert(0, l); + pp.rowShift.insert(0, 0); + // print(pp.representation()); + + if (pp.evaluate() != product) { + stdout.write('Fail: $X * $Y: ${pp.evaluate()} vs expected $product\n'); + } + expect(pp.evaluate(), equals(product)); + }); + + test('majority function', () async { + expect(LogicValue.ofBigInt(BigInt.from(7), 5).majority(), true); + expect(LogicValue.ofBigInt(BigInt.from(7) << 1, 5).majority(), true); + expect(LogicValue.ofBigInt(BigInt.from(11) << 1, 5).majority(), true); + expect(LogicValue.ofBigInt(BigInt.from(9) << 1, 5).majority(), false); + expect(LogicValue.ofBigInt(BigInt.from(7) << 3, 7).majority(), false); + }); + + // This is a two-minute exhaustive but quick test + test('exhaustive partial product evaluate: square radix-4, all extension', + () async { + for (final signed in [false, true]) { + for (var radix = 4; radix < 8; radix *= 2) { + const radix = 4; + final encoder = RadixEncoder(radix); + final shift = log2Ceil(encoder.radix); + final minWidth = shift + (signed ? 1 : 0); + for (var width = minWidth; width < shift * 2 + 1; width++) { + for (final signExtension in SignExtension.values) { + if (signExtension == SignExtension.none) { + continue; + } + final ppg = curryPartialProductGenerator(signExtension); + final pp = ppg(Logic(name: 'X', width: width), + Logic(name: 'Y', width: width), encoder, + signed: signed); + + checkEvaluateExhaustive(pp); + } + } + } + } + }); + + test('full rectangular test,', () async { + for (final signed in [false, true]) { + for (var radix = 2; radix < 32; radix *= 2) { + final encoder = RadixEncoder(radix); + final shift = log2Ceil(encoder.radix); + for (var width = 3 + shift + 1; width < 3 + shift * 2 + 1; width++) { + for (var skew = -3; skew < shift * 2; skew++) { + // Only some sign extension routines have rectangular support + // Commented out rectangular extension routines for speedup + for (final signExtension in [ + SignExtension.brute, + SignExtension.stop, + SignExtension.compactRect + ]) { + final ppg = curryPartialProductGenerator(signExtension); + final pp = ppg(Logic(name: 'X', width: width), + Logic(name: 'Y', width: width + skew), encoder, + signed: signed); + checkEvaluateRandom(pp, 20); + } + } + } + } + } + }); + + test('Rectangle Q collision tests,', () async { + // These collide with the normal q extension bits + // These are unsigned tests + + final alignTest = [ + [(2, 5, 0), (4, 7, 1), (8, 7, 2), (16, 9, 3)], + [(2, 5, 1), (4, 6, 2), (8, 9, 3), (16, 8, 4)], + [(2, 5, 2), (4, 7, 3), (8, 8, 4), (16, 11, 5)], + [(4, 6, 4), (8, 7, 5), (16, 10, 6)], + [(8, 9, 6), (16, 9, 7)], + [(16, 8, 8)], + ]; + for (final alignList in alignTest) { + for (final align in alignList) { + final radix = align.$1; + final encoder = RadixEncoder(radix); + final width = align.$2; + + final skew = align.$3; + final X = BigInt.from(29).toUnsigned(width); + final Y = BigInt.from(2060).toUnsigned(width + skew); + final product = X * Y; + + final pp = PartialProductGeneratorCompactRectSignExtension( + Logic(name: 'X', width: width), + Logic(name: 'Y', width: width + skew), + encoder, + signed: false); + + pp.multiplicand.put(X); + pp.multiplier.put(Y); + expect(pp.evaluate(), equals(product)); + checkEvaluateRandom(pp, 100); + } + } + }); + + test('minimum width verification,', () async { + for (final signed in [false, true]) { + for (var radix = 2; radix < 32; radix *= 2) { + final encoder = RadixEncoder(radix); + final shift = log2Ceil(encoder.radix); + final width = shift + (signed ? 1 : 0); + const skew = 0; + // Only some sign extension routines have rectangular support + // Commented out rectangular extension routines for speedup + for (final signExtension in SignExtension.values) { + if (signExtension == SignExtension.none) { + continue; + } + final ppg = curryPartialProductGenerator(signExtension); + final pp = ppg(Logic(name: 'X', width: width), + Logic(name: 'Y', width: width + skew), encoder, + signed: signed); + checkEvaluateRandom(pp, 100); + } + } + } + }); + test('minimum rectangular width verification,', () async { + for (final signed in [false, true]) { + for (var radix = 2; radix < 32; radix *= 2) { + final encoder = RadixEncoder(radix); + final shift = log2Ceil(encoder.radix); + final width = shift; + final skew = (signed ? 1 : 0); + // Only some sign extension routines have rectangular support + // Commented out rectangular extension routines for speedup + for (final signExtension in [ + SignExtension.brute, + SignExtension.stop, + SignExtension.compactRect + ]) { + { + final ppg = curryPartialProductGenerator(signExtension); + final pp = ppg(Logic(name: 'X', width: width), + Logic(name: 'Y', width: width + skew), encoder, + signed: signed); + checkEvaluateExhaustive(pp); + } + } + } + } + }); +} diff --git a/test/arithmetic/multiplier_test.dart b/test/arithmetic/multiplier_test.dart new file mode 100644 index 000000000..3eef9a358 --- /dev/null +++ b/test/arithmetic/multiplier_test.dart @@ -0,0 +1,236 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// multiplier_test.dart +// Test Multiplier and MultiplerAccumulate: CompressionTree implementations +// +// 2024 August 7 +// Author: Desmond Kirkpatrick + +import 'dart:math'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:test/test.dart'; + +// Inner test of a multipy accumulate unit +void checkMultiplyAccumulate( + MultiplyAccumulate mod, BigInt bA, BigInt bB, BigInt bC) { + final golden = bA * bB + bC; + // ignore: invalid_use_of_protected_member + mod.a.put(bA); + // ignore: invalid_use_of_protected_member + mod.b.put(bB); + // ignore: invalid_use_of_protected_member + mod.c.put(bC); + + final result = mod.signed + ? mod.accumulate.value.toBigInt().toSigned(mod.accumulate.width) + : mod.accumulate.value.toBigInt().toUnsigned(mod.accumulate.width); + expect(result, equals(golden)); +} + +// Random testing of a mutiplier or multiplier/accumulate unit +void testMultiplyAccumulateRandom(int width, int iterations, + MultiplyAccumulate Function(Logic a, Logic b, Logic c) fn) { + final a = Logic(name: 'a', width: width); + final b = Logic(name: 'b', width: width); + final c = Logic(name: 'c', width: width * 2); + final mod = fn(a, b, c); + test('random_${mod.name}_S${mod.signed}_W${width}_I$iterations', () async { + final multiplyOnly = mod is MutiplyOnly; + await mod.build(); + final signed = mod.signed; + final value = Random(47); + for (var i = 0; i < iterations; i++) { + final bA = signed + ? value.nextLogicValue(width: width).toBigInt().toSigned(width) + : value.nextLogicValue(width: width).toBigInt().toUnsigned(width); + final bB = signed + ? value.nextLogicValue(width: width).toBigInt().toSigned(width) + : value.nextLogicValue(width: width).toBigInt().toUnsigned(width); + final bC = multiplyOnly + ? BigInt.zero + : signed + ? value.nextLogicValue(width: width).toBigInt().toSigned(width) + : value.nextLogicValue(width: width).toBigInt().toUnsigned(width); + checkMultiplyAccumulate(mod, bA, bB, bC); + } + }); +} + +// Exhaustive testing of a mutiplier or multiplier/accumulate unit +void testMultiplyAccumulateExhaustive( + int width, MultiplyAccumulate Function(Logic a, Logic b, Logic c) fn) { + final a = Logic(name: 'a', width: width); + final b = Logic(name: 'b', width: width); + final c = Logic(name: 'c', width: 2 * width); + final mod = fn(a, b, c); + test('exhaustive_${mod.name}_S${mod.signed}_W$width', () async { + await mod.build(); + final signed = mod.signed; + final multiplyOnly = mod is MutiplyOnly; + + final cLimit = multiplyOnly ? 1 : (1 << (2 * width)); + + for (var aa = 0; aa < (1 << width); ++aa) { + for (var bb = 0; bb < (1 << width); ++bb) { + for (var cc = 0; cc < cLimit; ++cc) { + final bA = signed + ? BigInt.from(aa).toSigned(width) + : BigInt.from(aa).toUnsigned(width); + final bB = signed + ? BigInt.from(bb).toSigned(width) + : BigInt.from(bb).toUnsigned(width); + final bC = multiplyOnly + ? BigInt.zero + : signed + ? BigInt.from(cc).toSigned(2 * width) + : BigInt.from(cc).toUnsigned(2 * width); + checkMultiplyAccumulate(mod, bA, bB, bC); + } + } + } + }); +} + +typedef MultiplyAccumulateCallback = MultiplyAccumulate Function( + Logic a, Logic b, Logic c); + +typedef MultiplierCallback = Multiplier Function(Logic a, Logic b); + +void main() { + tearDown(() async { + await Simulator.reset(); + }); + + MultiplierCallback curryCompressionTreeMultiplier( + int radix, + ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppTree, + {required bool signed}) => + (a, b) => CompressionTreeMultiplier(a, b, radix, + ppTree: ppTree, signed: signed); + + MultiplyAccumulateCallback curryMultiplierAsMultiplyAccumulate( + int radix, + ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppTree, + {required bool signed}) => + (a, b, c) => MutiplyOnly(a, b, c, + curryCompressionTreeMultiplier(radix, ppTree, signed: signed)); + + MultiplyAccumulateCallback curryMultiplyAccumulate( + int radix, + ParallelPrefix Function(List, Logic Function(Logic, Logic)) + ppTree, + {required bool signed}) => + (a, b, c) => CompressionTreeMultiplyAccumulate(a, b, c, radix, + ppTree: ppTree, signed: signed); + + group('Curried Test of Compression Tree Multiplier', () { + for (final signed in [false, true]) { + for (final radix in [2, 16]) { + for (final width in [5, 6]) { + for (final ppTree in [KoggeStone.new, BrentKung.new]) { + testMultiplyAccumulateRandom( + width, + 10, + curryMultiplierAsMultiplyAccumulate(radix, ppTree, + signed: signed)); + } + } + } + } + }); + + group('Curried Test of Compression Tree Multiplier Accumulate', () { + for (final signed in [false, true]) { + for (final radix in [2, 16]) { + for (final width in [5, 6]) { + for (final ppTree in [KoggeStone.new, BrentKung.new]) { + testMultiplyAccumulateRandom(width, 10, + curryMultiplyAccumulate(radix, ppTree, signed: signed)); + } + } + } + } + }); + + test('single mac', () async { + const width = 8; + final a = Logic(name: 'a', width: width); + final b = Logic(name: 'b', width: width); + final c = Logic(name: 'c', width: 2 * width); + + const av = 0; + const bv = 0; + const cv = -512; + for (final signed in [true, false]) { + final bA = signed + ? BigInt.from(av).toSigned(width) + : BigInt.from(av).toUnsigned(width); + final bB = signed + ? BigInt.from(bv).toSigned(width) + : BigInt.from(bv).toUnsigned(width); + final bC = signed + ? BigInt.from(cv).toSigned(2 * width) + : BigInt.from(cv).toUnsigned(width * 2); + + // Set these so that printing inside module build will have Logic values + a.put(bA); + b.put(bB); + c.put(bC); + + final mod = CompressionTreeMultiplyAccumulate(a, b, c, 4, signed: signed); + checkMultiplyAccumulate(mod, bA, bB, bC); + } + }); + + test('single rectangular mac', () async { + const widthA = 6; + const widthB = 9; + final a = Logic(name: 'a', width: widthA); + final b = Logic(name: 'b', width: widthB); + final c = Logic(name: 'c', width: widthA + widthB); + + const av = 0; + const bv = 0; + const cv = -512; + for (final signed in [true, false]) { + final bA = signed + ? BigInt.from(av).toSigned(widthA) + : BigInt.from(av).toUnsigned(widthA); + final bB = signed + ? BigInt.from(bv).toSigned(widthB) + : BigInt.from(bv).toUnsigned(widthB); + final bC = signed + ? BigInt.from(cv).toSigned(widthA + widthB) + : BigInt.from(cv).toUnsigned(widthA + widthB); + + // Set these so that printing inside module build will have Logic values + a.put(bA); + b.put(bB); + c.put(bC); + + final mod = CompressionTreeMultiplyAccumulate(a, b, c, 4, signed: signed); + checkMultiplyAccumulate(mod, bA, bB, bC); + } + }); + test('trivial compression tree multiply-accumulate test', () async { + const widthA = 6; + const widthB = 6; + const radix = 8; + final a = Logic(name: 'a', width: widthA); + final b = Logic(name: 'b', width: widthB); + final c = Logic(name: 'c', width: widthA + widthB); + + a.put(15); + b.put(3); + c.put(5); + + final multiplier = + CompressionTreeMultiplyAccumulate(a, b, c, radix, signed: true); + final accumulate = multiplier.accumulate; + expect(accumulate.value.toBigInt(), equals(BigInt.from(15 * 3 + 5))); + }); +} diff --git a/test/parallel_prefix_operations_test.dart b/test/arithmetic/parallel_prefix_operations_test.dart similarity index 58% rename from test/parallel_prefix_operations_test.dart rename to test/arithmetic/parallel_prefix_operations_test.dart index 09da58354..e965960c7 100644 --- a/test/parallel_prefix_operations_test.dart +++ b/test/arithmetic/parallel_prefix_operations_test.dart @@ -1,21 +1,22 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // -// parallel-prefix_operations.dart +// parallel_prefix_operations.dart // Implementation of operations using various parallel-prefix trees. // // 2023 Sep 29 // Author: Desmond Kirkpatrick -import 'dart:math'; +// ignore_for_file: avoid_print + import 'package:rohd/rohd.dart'; -import 'package:rohd_hcl/src/parallel_prefix_operations.dart'; +import 'package:rohd_hcl/src/arithmetic/parallel_prefix_operations.dart'; import 'package:test/test.dart'; void testOrScan(int n, ParallelPrefixOrScan Function(Logic a) fn) { - test('or_scan_$n', () async { - final inp = Logic(name: 'inp', width: n); - final mod = fn(inp); + final inp = Logic(name: 'inp', width: n); + final mod = fn(inp); + test('or_scan_${n}_${mod.name}', () async { await mod.build(); int computeOrScan(int j) { @@ -42,14 +43,14 @@ void testOrScan(int n, ParallelPrefixOrScan Function(Logic a) fn) { }); } -void testPriorityEncoder( - int n, ParallelPrefixPriorityEncoder Function(Logic a) fn) { - test('priority_encoder_$n', () async { - final inp = Logic(name: 'inp', width: n); - final mod = fn(inp); +void testPriorityFinder( + int n, ParallelPrefixPriorityFinder Function(Logic a) fn) { + final inp = Logic(name: 'inp', width: n); + final mod = fn(inp); + test('priority_finder_${n}_${mod.name}', () async { await mod.build(); - int computePriorityEncoding(int j) { + int computePriorityLocation(int j) { for (var i = 0; i < n; ++i) { if (((1 << i) & j) != 0) { return 1 << i; @@ -61,69 +62,47 @@ void testPriorityEncoder( // put/expect testing for (var j = 0; j < (1 << n); ++j) { - final golden = computePriorityEncoding(j); + final golden = computePriorityLocation(j); inp.put(j); final result = mod.out.value.toInt(); - // print("priority_encoder: $j ${result} ${golden}"); + // print('priority_encoder: $j $result $golden'); expect(result, equals(golden)); } }); } -void testAdder(int n, ParallelPrefixAdder Function(Logic a, Logic b) fn) { - test('adder_$n', () async { - final a = Logic(name: 'a', width: n); - final b = Logic(name: 'b', width: n); - - final mod = fn(a, b); +void testPriorityEncoder( + int n, ParallelPrefixPriorityEncoder Function(Logic a) fn) { + final inp = Logic(name: 'inp', width: n); + final mod = fn(inp); + test('priority_encoder_${n}_${mod.name}', () async { await mod.build(); - int computeAdder(int aa, int bb) => (aa + bb) & ((1 << n) - 1); - - // put/expect testing - - for (var aa = 0; aa < (1 << n); ++aa) { - for (var bb = 0; bb < (1 << n); ++bb) { - final golden = computeAdder(aa, bb); - a.put(aa); - b.put(bb); - final result = mod.out.value.toInt(); - //print("adder: $aa $bb $result $golden"); - expect(result, equals(golden)); + int computePriorityEncoding(int j) { + for (var i = 0; i < n; ++i) { + if (((1 << i) & j) != 0) { + return i; + } } + return 0; } - }); -} -void testAdderRandom( - int n, int nSamples, ParallelPrefixAdder Function(Logic a, Logic b) fn) { - test('adder_$n', () async { - final a = Logic(name: 'a', width: n); - final b = Logic(name: 'b', width: n); - - final mod = fn(a, b); - await mod.build(); - - LogicValue computeAdder(LogicValue aa, LogicValue bb) => - (aa + bb) & LogicValue.ofBigInt(BigInt.from((1 << n) - 1), n); // put/expect testing - for (var i = 0; i < nSamples; ++i) { - final aa = Random().nextLogicValue(width: n); - final bb = Random().nextLogicValue(width: n); - final golden = computeAdder(aa, bb); - a.put(aa); - b.put(bb); - final result = mod.out.value; + for (var j = 0; j < (1 << n); ++j) { + final golden = computePriorityEncoding(j); + inp.put(j); + final result = mod.out.value.toInt(); + // print('priority_encoder: $j $result $golden'); expect(result, equals(golden)); } }); } void testIncr(int n, ParallelPrefixIncr Function(Logic a) fn) { - test('incr_$n', () async { - final inp = Logic(name: 'inp', width: n); - final mod = fn(inp); + final inp = Logic(name: 'inp', width: n); + final mod = fn(inp); + test('incr_${n}_${mod.name}', () async { await mod.build(); int computeIncr(int aa) => (aa + 1) & ((1 << n) - 1); @@ -141,9 +120,9 @@ void testIncr(int n, ParallelPrefixIncr Function(Logic a) fn) { } void testDecr(int n, ParallelPrefixDecr Function(Logic a) fn) { - test('decr_$n', () async { - final inp = Logic(name: 'inp', width: n); - final mod = fn(inp); + final inp = Logic(name: 'inp', width: n); + final mod = fn(inp); + test('decr_${n}_${mod.name}', () async { await mod.build(); int computeDecr(int aa) => (aa - 1) % (1 << n); @@ -178,32 +157,25 @@ void main() { group('or_scan', () { for (final n in [7, 8, 9]) { for (final ppGen in generators) { - testOrScan(n, (inp) => ParallelPrefixOrScan(inp, ppGen)); + testOrScan(n, (inp) => ParallelPrefixOrScan(inp, ppGen: ppGen)); } } }); - group('priority_encoder', () { + group('priority_finder', () { for (final n in [7, 8, 9]) { for (final ppGen in generators) { - testPriorityEncoder( - n, (inp) => ParallelPrefixPriorityEncoder(inp, ppGen)); - } - } - }); - - group('adder', () { - for (final n in [3, 4, 5]) { - for (final ppGen in generators) { - testAdder(n, (a, b) => ParallelPrefixAdder(a, b, ppGen)); + testPriorityFinder( + n, (inp) => ParallelPrefixPriorityFinder(inp, ppGen: ppGen)); } } }); - group('adderRandom', () { - for (final n in [127, 128, 129]) { + group('priority_encoder', () { + for (final n in [7, 8, 9]) { for (final ppGen in generators) { - testAdderRandom(n, 10, (a, b) => ParallelPrefixAdder(a, b, ppGen)); + testPriorityEncoder( + n, (inp) => ParallelPrefixPriorityEncoder(inp, ppGen: ppGen)); } } }); @@ -211,7 +183,7 @@ void main() { group('incr', () { for (final n in [7, 8, 9]) { for (final ppGen in generators) { - testIncr(n, (inp) => ParallelPrefixIncr(inp, ppGen)); + testIncr(n, (inp) => ParallelPrefixIncr(inp, ppGen: ppGen)); } } }); @@ -219,8 +191,10 @@ void main() { group('decr', () { for (final n in [7, 8, 9]) { for (final ppGen in generators) { - testDecr(n, (inp) => ParallelPrefixDecr(inp, ppGen)); + testDecr(n, (inp) => ParallelPrefixDecr(inp, ppGen: ppGen)); } } }); + + // Note: all ParallelPrefixAdders are tested in adder_test.dart } diff --git a/test/ripple_carry_adder_test.dart b/test/arithmetic/ripple_carry_adder_test.dart similarity index 98% rename from test/ripple_carry_adder_test.dart rename to test/arithmetic/ripple_carry_adder_test.dart index 1856438da..0c26c4fe7 100644 --- a/test/ripple_carry_adder_test.dart +++ b/test/arithmetic/ripple_carry_adder_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // ripple_carry_adder_test.dart diff --git a/test/binary_gray_test.dart b/test/binary_gray_test.dart index 685cbd77b..b983d3224 100644 --- a/test/binary_gray_test.dart +++ b/test/binary_gray_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // binary_gray_test.dart diff --git a/test/bitonic_sort_test.dart b/test/bitonic_sort_test.dart index edbe6c55b..d5ebfc249 100644 --- a/test/bitonic_sort_test.dart +++ b/test/bitonic_sort_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // bitonic_sort_test.dart diff --git a/test/configurator_test.dart b/test/configurator_test.dart index 8cd6155ac..4a4c23281 100644 --- a/test/configurator_test.dart +++ b/test/configurator_test.dart @@ -125,9 +125,9 @@ void main() { test('should return both Int knobs to be configured', () { final multiplier = CarrySaveMultiplierConfigurator(); - for (final element in multiplier.knobs.values.toList()) { - expect(element, isA()); - } + expect(multiplier.knobs.values.whereType().length, 1); + expect(multiplier.knobs.values.whereType>().length, + 1); }); test('should return rtl code when invoke generate() with default value', diff --git a/test/count_test.dart b/test/count_test.dart index 1e0c10ace..a744d9739 100644 --- a/test/count_test.dart +++ b/test/count_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // count_test.dart diff --git a/test/find_test.dart b/test/find_test.dart index ad323da86..6eff6e219 100644 --- a/test/find_test.dart +++ b/test/find_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // find_test.dart diff --git a/test/memory_test.dart b/test/memory_test.dart index 5e3318312..7d6cf5e2c 100644 --- a/test/memory_test.dart +++ b/test/memory_test.dart @@ -379,7 +379,7 @@ void main() { RegisterFile(Logic(), Logic(), [], []); fail('Should have failed'); // ignore: avoid_catching_errors - } on AssertionError catch (_) {} + } on RohdHclException catch (_) {} }); }); } diff --git a/test/one_hot_test.dart b/test/one_hot_test.dart index 86eda9994..d25c143cf 100644 --- a/test/one_hot_test.dart +++ b/test/one_hot_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // one_hot_test.dart diff --git a/test/ready_valid_bfm_test.dart b/test/ready_valid_bfm_test.dart index f43acec84..1acf33e79 100644 --- a/test/ready_valid_bfm_test.dart +++ b/test/ready_valid_bfm_test.dart @@ -1,3 +1,12 @@ +// Copyright (C) 2023-2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// rotate_test.dart +// Tests for rotating +// +// 2023 February 17 +// Author: Max Korbel + import 'dart:async'; import 'dart:convert'; import 'dart:io'; diff --git a/test/rotate_test.dart b/test/rotate_test.dart index ad9bfc1c1..b9d6e74c7 100644 --- a/test/rotate_test.dart +++ b/test/rotate_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // rotate_test.dart diff --git a/test/sparse_memory_storage_test.dart b/test/sparse_memory_storage_test.dart index f7f1edc55..b4d2147c3 100644 --- a/test/sparse_memory_storage_test.dart +++ b/test/sparse_memory_storage_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // sparse_memory_storage_test.dart diff --git a/tool/converters/json_html.sh b/tool/converters/json_html.sh index a02706332..a966e761b 100755 --- a/tool/converters/json_html.sh +++ b/tool/converters/json_html.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # jsonToHtml.sh diff --git a/tool/converters/verilog_html.sh b/tool/converters/verilog_html.sh index df8c718dd..79cf0ea4b 100755 --- a/tool/converters/verilog_html.sh +++ b/tool/converters/verilog_html.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # verilogToJSON.sh diff --git a/tool/converters/verilog_json.sh b/tool/converters/verilog_json.sh index f1ff0e75d..81d036923 100755 --- a/tool/converters/verilog_json.sh +++ b/tool/converters/verilog_json.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # verilogToJSON.sh diff --git a/tool/generate_coverage.sh b/tool/generate_coverage.sh index d41964aab..ad1b0dc71 100755 --- a/tool/generate_coverage.sh +++ b/tool/generate_coverage.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2022-2023 Intel Corporation +# Copyright (C) 2022-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # generate_coverage.sh diff --git a/tool/gh_actions/analyze_flutter_source.sh b/tool/gh_actions/analyze_flutter_source.sh index 0279e78e2..15381d464 100755 --- a/tool/gh_actions/analyze_flutter_source.sh +++ b/tool/gh_actions/analyze_flutter_source.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # analyze_flutter_source.sh diff --git a/tool/gh_actions/analyze_source.sh b/tool/gh_actions/analyze_source.sh index 8fc260b6b..9a86dfc43 100755 --- a/tool/gh_actions/analyze_source.sh +++ b/tool/gh_actions/analyze_source.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2022 Intel Corporation +# Copyright (C) 2022-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # analyze_source.sh diff --git a/tool/gh_actions/create_htmls.sh b/tool/gh_actions/create_htmls.sh index fadd709b1..2238da1bd 100755 --- a/tool/gh_actions/create_htmls.sh +++ b/tool/gh_actions/create_htmls.sh @@ -1,5 +1,8 @@ #!/bin/bash +# Copyright (C) 2022-2024 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause + # Scavenged from Nic30:d3-hwschematic #cp -r doc/d3-hwschematic-assets doc/api/ diff --git a/tool/gh_actions/hcl_site_generation_build.sh b/tool/gh_actions/hcl_site_generation_build.sh index ff1a63152..52b7de54e 100755 --- a/tool/gh_actions/hcl_site_generation_build.sh +++ b/tool/gh_actions/hcl_site_generation_build.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # hcl_site_generation_build.sh diff --git a/tool/gh_actions/install_d3_hwschematic.sh b/tool/gh_actions/install_d3_hwschematic.sh index 57a6c3423..dba3a7cd7 100755 --- a/tool/gh_actions/install_d3_hwschematic.sh +++ b/tool/gh_actions/install_d3_hwschematic.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # install_d3_hwschematic.sh diff --git a/tool/gh_actions/install_dependencies.sh b/tool/gh_actions/install_dependencies.sh index 7aef34082..ce2f10118 100755 --- a/tool/gh_actions/install_dependencies.sh +++ b/tool/gh_actions/install_dependencies.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2022-2023 Intel Corporation +# Copyright (C) 2022-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # install_dependencies.sh diff --git a/tool/gh_actions/install_dependencies_generator_site.sh b/tool/gh_actions/install_dependencies_generator_site.sh index a5c81e53d..12026319d 100755 --- a/tool/gh_actions/install_dependencies_generator_site.sh +++ b/tool/gh_actions/install_dependencies_generator_site.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # install_dependencies_generator_site.sh diff --git a/tool/gh_actions/install_opencadsuite.sh b/tool/gh_actions/install_opencadsuite.sh index 952f49e55..d48df7c40 100755 --- a/tool/gh_actions/install_opencadsuite.sh +++ b/tool/gh_actions/install_opencadsuite.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # install_opencadsuite.sh diff --git a/tool/gh_actions/run_flutter_tests.sh b/tool/gh_actions/run_flutter_tests.sh index 464c3cff5..c71d4d345 100755 --- a/tool/gh_actions/run_flutter_tests.sh +++ b/tool/gh_actions/run_flutter_tests.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # run_flutter_tests.sh diff --git a/tool/gh_actions/run_tests.sh b/tool/gh_actions/run_tests.sh index e34354891..8ec0d557a 100755 --- a/tool/gh_actions/run_tests.sh +++ b/tool/gh_actions/run_tests.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2022-2023 Intel Corporation +# Copyright (C) 2022-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # run_tests.sh diff --git a/tool/gh_actions/verify_formatting.sh b/tool/gh_actions/verify_formatting.sh index e69c52248..2947583a6 100755 --- a/tool/gh_actions/verify_formatting.sh +++ b/tool/gh_actions/verify_formatting.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2022-2023 Intel Corporation +# Copyright (C) 2022-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # verify_formatting.sh diff --git a/tool/gh_codespaces/install_dart.sh b/tool/gh_codespaces/install_dart.sh index abbe39a0c..0f013974e 100755 --- a/tool/gh_codespaces/install_dart.sh +++ b/tool/gh_codespaces/install_dart.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # install_dart.sh diff --git a/tool/gh_codespaces/install_flutter.sh b/tool/gh_codespaces/install_flutter.sh index c850d89b7..1d32ef578 100755 --- a/tool/gh_codespaces/install_flutter.sh +++ b/tool/gh_codespaces/install_flutter.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023-24 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # install_flutter.sh diff --git a/tool/gh_codespaces/run_setup.sh b/tool/gh_codespaces/run_setup.sh index 3e92b9575..70a564d62 100755 --- a/tool/gh_codespaces/run_setup.sh +++ b/tool/gh_codespaces/run_setup.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # run_setup.sh diff --git a/tool/run_checks.sh b/tool/run_checks.sh index 9c449ec46..402a154b1 100755 --- a/tool/run_checks.sh +++ b/tool/run_checks.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2022-2023 Intel Corporation +# Copyright (C) 2022-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # run_checks.sh diff --git a/tool/synthesize.sh b/tool/synthesize.sh index 1c625820a..12998686d 100755 --- a/tool/synthesize.sh +++ b/tool/synthesize.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2024 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # synthesize.sh