diff --git a/docs/_data/navigation.yml b/docs/_data/navigation.yml index 6d84613..3ab1977 100644 --- a/docs/_data/navigation.yml +++ b/docs/_data/navigation.yml @@ -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" diff --git a/docs/_docs/A02-logical_signals.md b/docs/_docs/A02-logical_signals.md index 93997fd..77d1fc5 100644 --- a/docs/_docs/A02-logical_signals.md +++ b/docs/_docs/A02-logical_signals.md @@ -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.). diff --git a/docs/_docs/A08-modules.md b/docs/_docs/A08-modules.md index 07066a3..a48f9dc 100644 --- a/docs/_docs/A08-modules.md +++ b/docs/_docs/A08-modules.md @@ -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`, 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`, 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: @@ -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 diff --git a/docs/_docs/A13-custom-module-behavior-inline-system-verilog.md b/docs/_docs/A13-custom-module-behavior-inline-system-verilog.md index 389613c..5d4186a 100644 --- a/docs/_docs/A13-custom-module-behavior-inline-system-verilog.md +++ b/docs/_docs/A13-custom-module-behavior-inline-system-verilog.md @@ -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. @@ -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(); @@ -49,9 +49,8 @@ class NotGate extends Module with InlineSystemVerilog { @override String inlineVerilog(Map 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'; } diff --git a/docs/_docs/A22-logic-nets.md b/docs/_docs/A22-logic-nets.md new file mode 100644 index 0000000..f4b2985 --- /dev/null +++ b/docs/_docs/A22-logic-nets.md @@ -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; + } +} +``` diff --git a/docs/_forum/rohd-forum-minutes.md b/docs/_forum/rohd-forum-minutes.md index 2803024..16edbe7 100644 --- a/docs/_forum/rohd-forum-minutes.md +++ b/docs/_forum/rohd-forum-minutes.md @@ -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 diff --git a/rohd b/rohd index f74ad35..b39abb2 160000 --- a/rohd +++ b/rohd @@ -1 +1 @@ -Subproject commit f74ad35301b0b5ecce50af8e579354089e6be958 +Subproject commit b39abb2cd02692ac362f2b29dec111acbc469b6f