diff --git a/.github/configs/mlc_config.json b/.github/configs/mlc_config.json index 419310c1b..cc2567981 100644 --- a/.github/configs/mlc_config.json +++ b/.github/configs/mlc_config.json @@ -2,6 +2,9 @@ "ignorePatterns": [ { "pattern": "^https://www.cadence.com/en_US/home/tools/digital-design-and-signoff/synthesis/stratus-high-level-synthesis.html$" + }, + { + "pattern": "^https://github.com/intel/rohd/blob/main/doc/tree_example.md$" } ] } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f81bc3052..f38e1a480 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -123,7 +123,7 @@ Please include the SPDX tag near the top of any new files you create: /// SPDX-License-Identifier: BSD-3-Clause ``` -You may find that reading the [Architecture](doc/Architecture.md) document will be helpful to becoming familiar with the design of the ROHD framework. +You may find that reading the [Architecture](doc/architecture.md) document will be helpful to becoming familiar with the design of the ROHD framework. ### Creating a New Package diff --git a/README.md b/README.md index f7544d77e..34e08c297 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ You can find an executable version of this counter example in [example/example.d ### A more complex example -See a more advanced example of a logarithmic-depth tree of arbitrary functionality at [doc/TreeExample.md](https://github.com/intel/rohd/blob/main/doc/TreeExample.md). +See a more advanced example of a logarithmic-depth tree of arbitrary functionality at [doc/tree_example.md](https://github.com/intel/rohd/blob/main/doc/tree_example.md). You can find an executable version of the tree example in [example/tree.dart](https://github.com/intel/rohd/blob/main/example/tree.dart). diff --git a/doc/Architecture.md b/doc/Architecture.md deleted file mode 100644 index d3b893ce9..000000000 --- a/doc/Architecture.md +++ /dev/null @@ -1,56 +0,0 @@ - -# Architecture - -This document describes the organization and architecture of the ROHD framework. It is not necessary to fully understand all this detail in order to use ROHD, but it could be helpful for debug or contributing. - -## Major Concepts - -### Logic and LogicValue - -The `Logic` is the fundamental "wire" that connects signals throughout a hardware design. It behaves very similarly to a `logic` in SystemVerilog. It has a fixed width determined at the time of construction. At any point in time, it has one value of type `LogicValue`. A `Logic` can be connected to up to one source, and any number of destinations. All connections must be the same width. - -Any time the source of a `Logic` changes, it propogates the change outwards to its destinations. There are various events that can be subscribed to related to signal value transitions on `Logic`. - -A `LogicValue` represents a multi-bit (including 0-bit and 1-bit) 4-value (`1`, `0`, `x`, `z`) static value. It is immutable. - -### Module - -The `Module` is the fundamental building block of hardware designs in ROHD. They have clearly defined inputs and outputs, and all logic contained within the module should connect either/both from inputs and to outputs. The ROHD framework will determine at `build()` time which logic sits within which `Module`. Any functional operation, whether a simple gate or a large module, is implemented as a `Module`. - -Every `Module` defines its own functionality. This could be through composition of other `Module`s, or through custom functional definition. For a custom functionality to be convertable to an output (e.g. SystemVerilog), it has to explicitly define how to convert it (via `CustomVerilog` or `InlineVerilog`). Any time the input of a custom functionality `Module` toggles, the outputs should correspondingly change, if necessary. - -### Simulator - -The `Simulator` acts as a staticly accessible driver of the overal simulation. Anything can register arbitrary `Function`s to be executed at any timestamp that has not already occurred. The `Simulator` does not need to understand much about the functionality of a design; rather, the `Module`s and `Logic`s are responsible for propogating changes throughout. - -### Synthesizer - -A separate type of object responsible for taking a `Module` and converting it to some output, such as SystemVerilog. - -## Organization - -All the code for the ROHD framework library is in lib/src/, with lib/rohd.dart exporting the main stuff for usage. - -### collections - -Software collections that are useful for high-performance internal implementation details in ROHD. - -### exceptions - -Exceptions that the ROHD framework may throw. - -### modules - -Contains a collection of `Module` implementations that can be used as primitive building blocks for ROHD designs. - -### synthesizers - -Contains logic for synthesizing `Module`s into some output. It is structured to maximize reusability across different output types (including those not yet supported). - -### utilities - -Various generic objects and classes that may be useful in different areas of ROHD. - -### values - -Definitions for things related to `LogicValue`. diff --git a/doc/architecture.md b/doc/architecture.md new file mode 100644 index 000000000..ce71f1b26 --- /dev/null +++ b/doc/architecture.md @@ -0,0 +1,55 @@ +# Architecture + +This document describes the organization and architecture of the ROHD framework. It is not necessary to fully understand all this detail in order to use ROHD, but it could be helpful for debug or contributing. + +## Major Concepts + +### Logic and LogicValue + +The `Logic` is the fundamental "wire" that connects signals throughout a hardware design. It behaves very similarly to a `logic` in SystemVerilog. It has a fixed width determined at the time of construction. At any point in time, it has one value of type `LogicValue`. A `Logic` can be connected to up to one source, and any number of destinations. All connections must be the same width. + +Any time the source of a `Logic` changes, it propagates the change outwards to its destinations. There are various events that can be subscribed to related to signal value transitions on `Logic`. + +A `LogicValue` represents a multi-bit (including 0-bit and 1-bit) 4-value (`1`, `0`, `x`, `z`) static value. It is immutable. + +### Module + +The `Module` is the fundamental building block of hardware designs in ROHD. They have clearly defined inputs and outputs, and all logic contained within the module should connect either/both from inputs and to outputs. The ROHD framework will determine at `build()` time which logic sits within which `Module`. Any functional operation, whether a simple gate or a large module, is implemented as a `Module`. + +Every `Module` defines its own functionality. This could be through composition of other `Module`s, or through custom functional definition. For a custom functionality to be convertable to an output (e.g. SystemVerilog), it has to explicitly define how to convert it (via `CustomVerilog` or `InlineVerilog`). Any time the input of a custom functionality `Module` toggles, the outputs should correspondingly change, if necessary. + +### Simulator + +The `Simulator` acts as a statically accessible driver of the overall simulation. Anything can register arbitrary `Function`s to be executed at any timestamp that has not already occurred. The `Simulator` does not need to understand much about the functionality of a design; rather, the `Module`s and `Logic`s are responsible for propagating changes throughout. + +### Synthesizer + +A separate type of object responsible for taking a `Module` and converting it to some output, such as SystemVerilog. + +## Organization + +All the code for the ROHD framework library is in `lib/src/`, with `lib/rohd.dart` exporting the main stuff for usage. + +### collections + +Software collections that are useful for high-performance internal implementation details in ROHD. + +### exceptions + +Exceptions that the ROHD framework may throw. + +### modules + +Contains a collection of `Module` implementations that can be used as primitive building blocks for ROHD designs. + +### synthesizers + +Contains logic for synthesizing `Module`s into some output. It is structured to maximize reusability across different output types (including those not yet supported). + +### utilities + +Various generic objects and classes that may be useful in different areas of ROHD. + +### values + +Definitions for things related to `LogicValue`. diff --git a/doc/TreeExample.md b/doc/tree_example.md similarity index 68% rename from doc/TreeExample.md rename to doc/tree_example.md index e7e9a12fb..0f32a8379 100644 --- a/doc/TreeExample.md +++ b/doc/tree_example.md @@ -1,28 +1,33 @@ - -The below example demonstrates some aspects of the power of ROHD where writing equivalent design code in SystemVerilog can be challenging or impossible. The example is a port from an example used by Chisel. +The below example demonstrates some aspects of the power of ROHD where writing equivalent design code in SystemVerilog can be challenging or impossible. The example is a port from an example used by Chisel. The ROHD module `TreeOfTwoInputModules` is a succinct representation a logarithmic-height tree of arbitrary two-input/one-output modules. ```dart class TreeOfTwoInputModules extends Module { - final Logic Function(Logic a, Logic b) _op; final List _seq = []; Logic get out => output('out'); - TreeOfTwoInputModules(List seq, this._op) : super(name: 'tree_of_two_input_modules') { - if(seq.isEmpty) throw Exception("Don't use TreeOfTwoInputModules with an empty sequence"); + TreeOfTwoInputModules(List seq, this._op) + : super(name: 'tree_of_two_input_modules') { + if (seq.isEmpty) { + throw Exception("Don't use TreeOfTwoInputModules with an empty sequence"); + } - for(var i = 0; i < seq.length; i++) { + for (var i = 0; i < seq.length; i++) { _seq.add(addInput('seq$i', seq[i], width: seq[i].width)); } addOutput('out', width: seq[0].width); - if(_seq.length == 1) { + if (_seq.length == 1) { out <= _seq[0]; } else { - var a = TreeOfTwoInputModules(_seq.getRange(0, _seq.length~/2).toList(), _op).out; - var b = TreeOfTwoInputModules(_seq.getRange(_seq.length~/2, _seq.length).toList(), _op).out; + final a = TreeOfTwoInputModules( + _seq.getRange(0, _seq.length ~/ 2).toList(), _op) + .out; + final b = TreeOfTwoInputModules( + _seq.getRange(_seq.length ~/ 2, _seq.length).toList(), _op) + .out; out <= _op(a, b); } } @@ -32,71 +37,43 @@ class TreeOfTwoInputModules extends Module { Some interesting things to note: - The constructor for `TreeOfTwoInputModules` accepts two arguments: - - `seq` is a Dart `List` of arbitrary length of input elements. The module dynamically assigns the input and output widths of the module to match the width of the input elements. Additionally, the total number of inputs to the module is dynamically determined at run time. - - `_op` is a Dart `Function` (in Dart, `Function`s are first-class and can be stored in variables). It expects a function which takes two `Logic` inputs and provides one `Logic` output. -- This module recursively instantiates itself, but with different numbers of inputs each time. The same module implementation can have a variable number of inputs and different logic without any explicit parameterization. + - `seq` is a Dart `List` of arbitrary length of input elements. The module dynamically assigns the input and output widths of the module to match the width of the input elements. Additionally, the total number of inputs to the module is dynamically determined at run time. + - `_op` is a Dart `Function` (in Dart, `Function`s are first-class and can be stored in variables). It expects a function which takes two `Logic` inputs and provides one `Logic` output. +- This module recursively instantiates itself, but with different numbers of inputs each time. The same module implementation can have a variable number of inputs and different logic without any explicit parameterization. You could instantiate this module with some code such as: ```dart -var tree = TreeOfTwoInputModules( - List.generate(16, (index) => Logic(width: 8)), - (Logic a, Logic b) => mux(a > b, a, b) -); +final tree = TreeOfTwoInputModules( + List.generate(16, (index) => Logic(width: 8)), + (a, b) => mux(a > b, a, b)); ``` -This instantiation code generates a list of sixteen 8-bit logic signals. The operation to be performed (`_op`) is to create a `mux` which returns `a` if `a` is greater than `b`, otherwise `b`. Therefore, this instantiation creates a logarithmic-height tree of modules which outputs the largest 8-bit value. Note that `mux` also needs no parameters, as it can automatically determine the appropriate size of its outputs based on the inputs. +This instantiation code generates a list of sixteen 8-bit logic signals. The operation to be performed (`_op`) is to create a `mux` which returns `a` if `a` is greater than `b`, otherwise `b`. Therefore, this instantiation creates a logarithmic-height tree of modules which outputs the largest 8-bit value. Note that `mux` also needs no parameters, as it can automatically determine the appropriate size of its outputs based on the inputs. -A SystemVerilog implementation of this requires numerous module definitions and substantially more code. Below is an output of the ROHD-generated SystemVerilog: +A SystemVerilog implementation of this requires numerous module definitions and substantially more code. Below is an output of the ROHD-generated SystemVerilog: ```verilog -module TreeOfTwoInputModules_3( +module TreeOfTwoInputModules( input logic [7:0] seq0, -input logic [7:0] seq1, -input logic [7:0] seq2, -input logic [7:0] seq3, -input logic [7:0] seq4, -input logic [7:0] seq5, -input logic [7:0] seq6, -input logic [7:0] seq7, -input logic [7:0] seq8, -input logic [7:0] seq9, -input logic [7:0] seq10, -input logic [7:0] seq11, -input logic [7:0] seq12, -input logic [7:0] seq13, -input logic [7:0] seq14, -input logic [7:0] seq15, output logic [7:0] out ); -logic [7:0] out_1; -logic [7:0] out_0; - -assign out = (out_1 > out_0) ? out_1 : out_0; // mux -TreeOfTwoInputModules_2 tree_of_two_input_modules(.seq0(seq0),.seq1(seq1),.seq2(seq2),.seq3(seq3),.seq4(seq4),.seq5(seq5),.seq6(seq6),.seq7(seq7),.out(out_1)); -TreeOfTwoInputModules_2 tree_of_two_input_modules_0(.seq0(seq8),.seq1(seq9),.seq2(seq10),.seq3(seq11),.seq4(seq12),.seq5(seq13),.seq6(seq14),.seq7(seq15),.out(out_0)); -endmodule : TreeOfTwoInputModules_3 +assign out = seq0; +endmodule : TreeOfTwoInputModules //////////////////// -module TreeOfTwoInputModules_2( +module TreeOfTwoInputModules_0( input logic [7:0] seq0, input logic [7:0] seq1, -input logic [7:0] seq2, -input logic [7:0] seq3, -input logic [7:0] seq4, -input logic [7:0] seq5, -input logic [7:0] seq6, -input logic [7:0] seq7, output logic [7:0] out ); logic [7:0] out_1; logic [7:0] out_0; - assign out = (out_1 > out_0) ? out_1 : out_0; // mux -TreeOfTwoInputModules_1 tree_of_two_input_modules(.seq0(seq0),.seq1(seq1),.seq2(seq2),.seq3(seq3),.out(out_1)); -TreeOfTwoInputModules_1 tree_of_two_input_modules_0(.seq0(seq4),.seq1(seq5),.seq2(seq6),.seq3(seq7),.out(out_0)); -endmodule : TreeOfTwoInputModules_2 +TreeOfTwoInputModules tree_of_two_input_modules(.seq0(seq0),.out(out_1)); +TreeOfTwoInputModules tree_of_two_input_modules_0(.seq0(seq1),.out(out_0)); +endmodule : TreeOfTwoInputModules_0 //////////////////// @@ -109,7 +86,6 @@ output logic [7:0] out ); logic [7:0] out_1; logic [7:0] out_0; - assign out = (out_1 > out_0) ? out_1 : out_0; // mux TreeOfTwoInputModules_0 tree_of_two_input_modules(.seq0(seq0),.seq1(seq1),.out(out_1)); TreeOfTwoInputModules_0 tree_of_two_input_modules_0(.seq0(seq2),.seq1(seq3),.out(out_0)); @@ -117,27 +93,49 @@ endmodule : TreeOfTwoInputModules_1 //////////////////// -module TreeOfTwoInputModules_0( +module TreeOfTwoInputModules_2( input logic [7:0] seq0, input logic [7:0] seq1, +input logic [7:0] seq2, +input logic [7:0] seq3, +input logic [7:0] seq4, +input logic [7:0] seq5, +input logic [7:0] seq6, +input logic [7:0] seq7, output logic [7:0] out ); logic [7:0] out_1; logic [7:0] out_0; - assign out = (out_1 > out_0) ? out_1 : out_0; // mux -TreeOfTwoInputModules tree_of_two_input_modules(.seq0(seq0),.out(out_1)); -TreeOfTwoInputModules tree_of_two_input_modules_0(.seq0(seq1),.out(out_0)); -endmodule : TreeOfTwoInputModules_0 +TreeOfTwoInputModules_1 tree_of_two_input_modules(.seq0(seq0),.seq1(seq1),.seq2(seq2),.seq3(seq3),.out(out_1)); +TreeOfTwoInputModules_1 tree_of_two_input_modules_0(.seq0(seq4),.seq1(seq5),.seq2(seq6),.seq3(seq7),.out(out_0)); +endmodule : TreeOfTwoInputModules_2 //////////////////// -module TreeOfTwoInputModules( +module TreeOfTwoInputModules_3( input logic [7:0] seq0, +input logic [7:0] seq1, +input logic [7:0] seq2, +input logic [7:0] seq3, +input logic [7:0] seq4, +input logic [7:0] seq5, +input logic [7:0] seq6, +input logic [7:0] seq7, +input logic [7:0] seq8, +input logic [7:0] seq9, +input logic [7:0] seq10, +input logic [7:0] seq11, +input logic [7:0] seq12, +input logic [7:0] seq13, +input logic [7:0] seq14, +input logic [7:0] seq15, output logic [7:0] out ); - -assign out = seq0; - -endmodule : TreeOfTwoInputModules +logic [7:0] out_1; +logic [7:0] out_0; +assign out = (out_1 > out_0) ? out_1 : out_0; // mux +TreeOfTwoInputModules_2 tree_of_two_input_modules(.seq0(seq0),.seq1(seq1),.seq2(seq2),.seq3(seq3),.seq4(seq4),.seq5(seq5),.seq6(seq6),.seq7(seq7),.out(out_1)); +TreeOfTwoInputModules_2 tree_of_two_input_modules_0(.seq0(seq8),.seq1(seq9),.seq2(seq10),.seq3(seq11),.seq4(seq12),.seq5(seq13),.seq6(seq14),.seq7(seq15),.out(out_0)); +endmodule : TreeOfTwoInputModules_3 ```