Skip to content

Commit

Permalink
Small improvements for Architecture.md, TreeExample.md (#327)
Browse files Browse the repository at this point in the history
  • Loading branch information
chykon committed Mar 30, 2023
1 parent 15404c5 commit a30b4c6
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 121 deletions.
3 changes: 3 additions & 0 deletions .github/configs/mlc_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -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$"
}
]
}
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
56 changes: 0 additions & 56 deletions doc/Architecture.md

This file was deleted.

55 changes: 55 additions & 0 deletions doc/architecture.md
Original file line number Diff line number Diff line change
@@ -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`.
124 changes: 61 additions & 63 deletions doc/TreeExample.md → doc/tree_example.md
Original file line number Diff line number Diff line change
@@ -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<Logic> _seq = [];
Logic get out => output('out');
TreeOfTwoInputModules(List<Logic> 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<Logic> 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);
}
}
Expand All @@ -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<Logic>.generate(16, (index) => Logic(width: 8)),
(Logic a, Logic b) => mux(a > b, a, b)
);
final tree = TreeOfTwoInputModules(
List<Logic>.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
////////////////////
Expand All @@ -109,35 +86,56 @@ 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));
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
```

0 comments on commit a30b4c6

Please sign in to comment.