Skip to content

Commit

Permalink
Update docs and user guide for LogicNets, etc. (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkorbel1 authored Jun 11, 2024
1 parent 8ae20a8 commit 8343b33
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 22 deletions.
2 changes: 2 additions & 0 deletions docs/_data/navigation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ docs:
url: /docs/logic-arrays/
- title: "Generating Outputs"
url: /docs/generation/
- title: "Logic Nets, In/Outs, and Tri-state Buffers"
url: /docs/logic-nets/
- title: "Additional Information"
children:
- title: "Contributing"
Expand Down
2 changes: 1 addition & 1 deletion docs/_docs/A02-logical_signals.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var bus = Logic(name: 'b', width: 8)

#### The value of a signal

You can access the current value of a signal using `value`. You cannot access this as part of synthesizable ROHD code. ROHD supports X and Z values and propogation. If the signal is valid (no X or Z in it), you can also convert it to an `int` with `value.toInt()` (ROHD will throw an exception otherwise). If the signal has more bits than a dart `int` (64 bits, usually), you need to use `value.toBigInt()` to get a `BigInt` (again, ROHD will throw an exception otherwise).
You can access the current value of a signal using `value`. You cannot access this as part of synthesizable ROHD code. ROHD supports X and Z values and propagation. If the signal is valid (no X or Z in it), you can also convert it to an `int` with `value.toInt()` (ROHD will throw an exception otherwise). If the signal has more bits than a dart `int` (64 bits, usually), you need to use `value.toBigInt()` to get a `BigInt` (again, ROHD will throw an exception otherwise).

The value of a `Logic` is of type [`LogicValue`](https://intel.github.io/rohd/rohd/LogicValue-class.html), with pre-defined constant bit values `x`, `z`, `one`, and `zero`. `LogicValue` has a number of built-in logical operations (like `&`, `|`, `^`, `+`, `-`, etc.).

Expand Down
18 changes: 9 additions & 9 deletions docs/_docs/A08-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,27 @@
title: "Modules"
permalink: /docs/modules/
excerpt: "Modules"
last_modified_at: 2022-12-06
last_modified_at: 2024-6-3
toc: true
---

[`Module`](https://intel.github.io/rohd/rohd/Module-class.html)s are similar to modules in SystemVerilog. They have inputs and outputs and logic that connects them. There are a handful of rules that *must* be followed when implementing a module.

1. All logic within a `Module` must consume only inputs (from the `input` or `addInput` methods) to the Module either directly or indirectly.
2. Any logic outside of a `Module` must consume the signals only via outputs (from the `output` or `addOutput` methods) of the Module.
3. Logic must be defined *before* the call to `super.build()`, which *always* must be called at the end of the `build()` method if it is overidden.
1. Inputs and inOuts (from `input`, `addInput`, `inOut` or `addInOut` methods, or their array equivalents) return *internal* copies of ports that should be used inside a `Module`. Signals should not be consumed directly from outside a `Module`. Internal module logic should consume the internal versions. Logic outside the `Module` can drive to (or receive from, in the case of inOut) that `Module` only through the external copies, i.e. arguments passed to `addInput`, `addInOut`, etc.
2. Outputs (from `output` or `addOutput`, or their array equivalents) are the only way logic outside of a `Module` can consume signals from that `Module`. There are no internal vs. external versions of `output`s, so they may be consumed inside of `Module`s as well.
3. All logic must be defined *before* the call to `super.build()`. Logic should not be further defined after build.

The reasons for these rules have to do with how ROHD is able to determine which logic and `Module`s exist within a given Module and how ROHD builds connectivity. If these rules are not followed, generated outputs (including waveforms and SystemVerilog) may be unpredictable.

You should strive to build logic within the constructor of your `Module` (directly or via method calls within the constructor). This way any code can utilize your `Module` immediately after creating it. **Be careful** to consume the registered `input`s and drive the registered `output`s of your module, and not the "raw" parameters.
You should strive to build logic within the constructor of your `Module` (directly or via method calls within the constructor). This way any code can utilize your `Module` immediately after creating it. **Be careful** to consume the registered `input`s and drive the registered `output`s of your module, and not the "raw" external arguments passed to the constructor.

It is legal to put logic within an override of the `build` function, but that forces users of your module to always call `build` before it will be functionally usable for simple simulation. If you put logic in `build()`, ensure you put the call to `super.build()` *at the end* of the method.

Note that the `build()` method returns a `Future<void>`, not just `void`. This is because the `build()` method is permitted to consume real wallclock time in some cases, for example for setting up cosimulation with another simulator. If you expect your build to consume wallclock time, make sure the `Simulator` is aware it needs to wait before proceeding.
Note that the `build()` method returns a `Future<void>`, not just `void`. This is because the `build()` method is permitted to consume real wall clock time in some cases, for example for setting up cosimulation with another simulator. Make sure the `build` completes before the simulation begins.

It is not necessary to put all logic directly within a class that extends Module. You can put synthesizable logic in other functions and classes, as long as the logic eventually connects to an input or output of a module if you hope to convert it to SystemVerilog. Except where there is a desire for the waveforms and SystemVerilog generated to have module hierarchy, it is not necessary to use submodules within modules instead of plain classes or functions.
It is not necessary to put all logic directly within a class that extends Module. You can put synthesizable logic in other functions and classes, as long as the logic eventually connects to an input or output of a module if you hope to convert it to SystemVerilog. Except where there is a desire for the debug hierarchy, waveforms, SystemVerilog generated, etc. to have equivalent module hierarchy, it is not necessary to use submodules within modules instead of plain classes or functions.

The `Module` base class has an optional String argument 'name' which is an instance name.
The `Module` base class has an optional String argument `name` which is an instance name.

`Module`s have the below basic structure:

Expand All @@ -31,7 +31,7 @@ The `Module` base class has an optional String argument 'name' which is an insta
class MyModule extends Module {
// constructor
MyModule(Logic in1, {String name='mymodule'}) : super(name: name) {
MyModule(Logic in1) {
// add inputs in the constructor, passing in the Logic it is connected to
// it's a good idea to re-set the input parameters,
// so you don't accidentally use the wrong one
Expand Down
17 changes: 8 additions & 9 deletions docs/_docs/A13-custom-module-behavior-inline-system-verilog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
title: "Custom module behavior with custom in-line SystemVerilog representation"
permalink: /docs/custom-module-behavior-inline-system-verilog/
excerpt: "Custom module behavior with custom in-line SystemVerilog representation"
last_modified_at: 2022-12-06
last_modified_at: 2024-6-3
toc: true
---

Many of the basic built-in gates in Dart implement custom behavior. An implementation of the NotGate is shown below as an example. There is different syntax for functions which can be inlined versus those which cannot (the ~ can be inlined). In this case, the `InlineSystemVerilog` mixin is used, but if it were not inlineable, you could use `CustomSystemVerilog`. Note that it is mandatory to provide an initial value computation when the module is first created for non-sequential modules.
Many of the basic built-in gates in Dart implement custom behavior. An implementation of the `NotGate` is shown below as an example. There is different syntax for functions which can be inlined versus those which cannot (the `~` can be inlined). In this case, the `InlineSystemVerilog` mixin is used, but if it were not inlineable, you could use the `SystemVerilog` mixin instead. Note that it is mandatory to provide an initial value computation when the module is first created for non-sequential modules.

```dart
/// A gate [Module] that performs bit-wise inversion.
Expand All @@ -18,17 +18,17 @@ class NotGate extends Module with InlineSystemVerilog {
late final String _outName;
/// The input to this [NotGate].
Logic get _in => input(_inName);
late final Logic _in = input(_inName);
/// The output of this [NotGate].
Logic get out => output(_outName);
late final Logic out = output(_outName);
/// Constructs a [NotGate] with [in_] as its input.
///
/// You can optionally set [name] to name this [Module].
NotGate(Logic in_, {super.name = 'not'}) {
_inName = Module.unpreferredName(in_.name);
_outName = Module.unpreferredName('${in_.name}_b');
_inName = Naming.unpreferredName(in_.name);
_outName = Naming.unpreferredName('${in_.name}_b');
addInput(_inName, in_, width: in_.width);
addOutput(_outName, width: in_.width);
_setup();
Expand All @@ -49,9 +49,8 @@ class NotGate extends Module with InlineSystemVerilog {
@override
String inlineVerilog(Map<String, String> inputs) {
if (inputs.length != 1) {
throw Exception('Gate has exactly one input.');
}
assert(inputs.length == 1, 'Gate has exactly one input.');
final a = inputs[_inName]!;
return '~$a';
}
Expand Down
41 changes: 41 additions & 0 deletions docs/_docs/A22-logic-nets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
title: "Logic Nets, In/Outs, and Tri-state Buffers"
permalink: /docs/logic-nets/
last_modified_at: 2024-6-3
toc: true
---

## Logic Nets

The `LogicNet` class is a type of `Logic` which supports multiple drivers and bidirectional signal propagation. If multiple drivers disagree on the signal value, then an `x` (contention) value will be generated on that signal. Therefore, typically only one driver on a set of connected `LogicNet`s should be non-floating. One should use [tri-state buffers](#tri-state-buffers) to control which driver is actively driving on a net. In general, usage of `LogicNet` can be used for places where external IO and analog driver behavior needs to be modelled. For example, an external IO may support one data bus for both read and write directions, so the IOs on either side would use tristate drivers to control whether each is reading from or writing to that bus.

Assignments between `LogicNet`s use the same `<=` or `gets` API as normal `Logic`s, but it applies automatically in both directions. Assignments from `LogicNet` to `Logic` will only apply in the direction of driving the `Logic`, and conversely, assignments from `Logic` to `LogicNet` will only apply in the direction of driving the `LogicNet`.

`LogicArray`s can also be nets by using the `LogicArray.net` constructor. The `isNet` accessor provides information on any `Logic` (including `LogicArray`s) about whether the signal will behave like a net (supporting multiple drivers).

## In/Out Ports

`Module`s support `inOut` ports via `addInOut`, which return objects of type `LogicNet`. There are also equivalent versions for `LogicArray`s. The API for `inOut` is similar to that of `input` -- there's an internal version to be used within a module, and the external version used for outside of the module.

## Tri-state Buffers

A tri-state buffer allows a driver to consider driving one of three states: driving 1, driving 0, and not driving (Z). This is useful for when you may want multiple things to drive the same net at different times. The `TriStateBuffer` module provides this capability in ROHD.

## Example

The below example shows a `Module` with an `inOut` port that is conditionally driven using a `TriStateBuffer`.

```dart
class ModWithInout extends Module {
/// A module which drives [toDrive] if [isDriver] is high onto [io], or
/// else leaves [io] floating (undriven) for others to drive it.
ModWithInout(Logic isDriver, Logic toDrive, LogicNet io)
: super(name: 'modwithinout') {
isDriver = addInput('isDriver', isDriver);
toDrive = addInput('toDrive', toDrive, width: toDrive.width);
io = addInOut('io', io, width: toDrive.width);
io <= TriStateBuffer(toDrive, enable: isDriver).out;
}
}
```
4 changes: 2 additions & 2 deletions docs/_forum/rohd-forum-minutes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ toc: true

### Future agenda topics

- Arrays and Structures
- Pair interfaces
- Discussion on HLS integration with ROHD
- LogicNets, in/out ports, and tri-state buffers
- ROHD Wave Viewer

### February 7, 2024 @ 8AM Pacific Time

Expand Down
2 changes: 1 addition & 1 deletion rohd
Submodule rohd updated 58 files
+1 −1 .github/workflows/build_devtool.yml
+5 −4 .github/workflows/general.yml
+2 −0 .gitignore
+26 −0 .pubignore
+23 −0 CHANGELOG.md
+1 −6 benchmark/many_seq_and_comb_benchmark.dart
+1 −1 doc/architecture.md
+1 −4 doc/tutorials/chapter_2/answers/exercise_2.dart
+1 −1 doc/user_guide/_docs/A02-logical_signals.md
+9 −9 doc/user_guide/_docs/A08-modules.md
+8 −9 doc/user_guide/_docs/A13-custom-module-behavior-inline-system-verilog.md
+41 −0 doc/user_guide/_docs/A22-logic-nets.md
+12 −9 lib/src/diagnostics/inspector_service.dart
+16 −0 lib/src/exceptions/module/invalid_hierarchy_exception.dart
+4 −1 lib/src/exceptions/module/module_exceptions.dart
+2 −2 lib/src/exceptions/module/port_does_not_exist_exception.dart
+23 −0 lib/src/exceptions/module/port_rules_violation_exception.dart
+18 −0 lib/src/exceptions/module/port_type_exception.dart
+15 −8 lib/src/external.dart
+57 −15 lib/src/interfaces/interface.dart
+68 −28 lib/src/interfaces/pair_interface.dart
+238 −61 lib/src/module.dart
+1 −1 lib/src/modules/bus.dart
+7 −9 lib/src/modules/clkgen.dart
+28 −18 lib/src/modules/conditional.dart
+2 −3 lib/src/modules/gates.dart
+1 −0 lib/src/modules/modules.dart
+88 −0 lib/src/modules/tristate.dart
+1 −1 lib/src/signals/const.dart
+97 −31 lib/src/signals/logic.dart
+114 −7 lib/src/signals/logic_array.dart
+73 −0 lib/src/signals/logic_net.dart
+23 −8 lib/src/signals/logic_structure.dart
+2 −0 lib/src/signals/signals.dart
+4 −3 lib/src/signals/wire.dart
+44 −0 lib/src/signals/wire_net.dart
+74 −30 lib/src/simulator.dart
+12 −7 lib/src/synthesizers/synth_builder.dart
+11 −4 lib/src/synthesizers/synthesis_result.dart
+3 −3 lib/src/synthesizers/synthesizer.dart
+491 −92 lib/src/synthesizers/systemverilog.dart
+1 −1 lib/src/utilities/config.dart
+68 −13 lib/src/utilities/simcompare.dart
+18 −0 lib/src/values/big_logic_value.dart
+37 −1 lib/src/values/filled_logic_value.dart
+34 −0 lib/src/values/logic_value.dart
+18 −0 lib/src/values/small_logic_value.dart
+2 −2 pubspec.yaml
+13 −1 test/conditionals_test.dart
+51 −9 test/interface_test.dart
+1 −4 test/invalid_latch_test.dart
+48 −0 test/logic_value_test.dart
+64 −6 test/module_test.dart
+4 −0 test/multimodule5_test.dart
+639 −0 test/net_test.dart
+12 −2 test/pair_interface_hier_test.dart
+33 −5 test/pair_interface_test.dart
+102 −7 test/sv_gen_test.dart

0 comments on commit 8343b33

Please sign in to comment.