Skip to content

Commit

Permalink
Async Reset Improvements (intel#533)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkorbel1 authored Dec 16, 2024
1 parent 4ed61b8 commit ea893bb
Show file tree
Hide file tree
Showing 21 changed files with 1,155 additions and 246 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@
- Breaking: Updated APIs for `Synthesizer.synthesize` and down the stack to use a `Function` to calculate the instance type of a module instead of a `Map` look-up table.
- Added `srcConnections` API to `Logic` to make it easier to trace drivers of subtypes of `Logic` which contain multiple drivers.
- Breaking: `Const` constructor updated so that specified `width` takes precedence over the inherent width of a provided `LogicValue` `val`.
- Added flags to support an `asyncReset` option in places where sequential reset automation was already present.
- Breaking: `Sequential` has new added strictness checking when triggers and non-triggers change simultaneously (in the same `Simulator` tick) when it may be unpredictable how the hardware would synthesize, driving `X`s on outputs instead of just picking an order. Descriptions that imply asynchronous resets are predictable and therefore unaffected.
- Breaking: injected actions in the `Simulator` can now occur in either the `mainTick` or `clkStable` phases. This API will generally continue to work as expected and as it always has, but in some scenarios could slightly change the behavior of existing testbenches.
- Breaking: `Simulator.run` now yields execution of the Dart event loop prior to beginning the simulation. This makes actions taken before starting the simulation more predictable, but may slightly change behavior in existing testbenches that relied on a potential delay.
- Improved error and exception messages.
- Fixed a bug where asynchronous events could sometimes show up late in generated waveforms from `WaveDumper`.
- Added support for negative edge triggers to `Sequential.multi` for cases where synthesis may interpret an inverted `posedge` as different from a `negedge`.
- Fixed a bug where `resetValues` would not take effect in `Pipeline`s.
- Fixed a bug where a multi-triggered `Sequential` may not generate X's if one trigger is valid and another trigger is invalid.

## 0.5.3

Expand Down
2 changes: 1 addition & 1 deletion doc/user_guide/_get-started/01-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,6 @@ If you're thinking "SystemVerilog is just fine, I don't need something new", it

Try out Dart instantly from your browser here (it supports ROHD too!): <https://dartpad.dev/?null_safety=true>

See some Dart language samples here: <https://dart.dev/samples>
See some Dart language samples here: <https://dart.dev/language>

For more information on Dart and tutorials, see <https://dart.dev/> and <https://dart.dev/overview>
49 changes: 27 additions & 22 deletions example/fir_filter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// allow `print` messages (disable lint):
// ignore_for_file: avoid_print

import 'dart:async';
import 'dart:io';
import 'package:rohd/rohd.dart';

Expand Down Expand Up @@ -85,7 +86,7 @@ Future<void> main({bool noPrint = false}) async {

// Generate a simple clock. This will run along by itself as
// the Simulator goes.
final clk = SimpleClockGenerator(5).clk;
final clk = SimpleClockGenerator(10).clk;

// 4-cycle delay coefficients.
final firFilter =
Expand All @@ -105,39 +106,43 @@ Future<void> main({bool noPrint = false}) async {

// Now let's try simulating!

// Let's set the initial setting.
en.put(0);
resetB.put(0);
inputVal.put(1);

// Attach a waveform dumper.
if (!noPrint) {
WaveDumper(firFilter);
}

// Raise enable at time 5.
Simulator.registerAction(5, () => en.put(1));
// Let's set the initial setting.
en.inject(0);
resetB.inject(0);
inputVal.inject(1);

// Set a maximum time for the simulation so it doesn't keep running forever.
Simulator.setMaxSimTime(200);

// Kick off the simulation.
unawaited(Simulator.run());

await clk.nextPosedge;

// Raise resetB at time 10.
Simulator.registerAction(10, () => resetB.put(1));
// Raise enable
await clk.nextPosedge;
en.inject(1);

// Raise resetB
await clk.nextPosedge;
resetB.inject(1);

// Plan the input sequence.
for (var i = 1; i < 10; i++) {
Simulator.registerAction(5 + i * 4, () => inputVal.put(i));
await clk.nextPosedge;
inputVal.inject(i);
}

// Print a message when we're done with the simulation!
Simulator.registerAction(100, () {
if (!noPrint) {
print('Simulation completed!');
}
});

// Set a maximum time for the simulation so it doesn't keep running forever.
Simulator.setMaxSimTime(100);

// Kick off the simulation.
await Simulator.run();
await Simulator.endSimulation();
if (!noPrint) {
print('Simulation completed!');
}

// We can take a look at the waves now.
if (!noPrint) {
Expand Down
85 changes: 52 additions & 33 deletions example/oven_fsm.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2023 Intel Corporation
// Copyright (C) 2023-2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// oven_fsm.dart
Expand All @@ -10,6 +10,8 @@
// ignore_for_file: avoid_print

// Import the ROHD package.
import 'dart:async';

import 'package:rohd/rohd.dart';

// Import the counter module implement in example.dart.
Expand Down Expand Up @@ -58,17 +60,15 @@ class OvenModule extends Module {
Logic get led => output('led');

// This oven module receives a `button` and a `reset` input from runtime.
OvenModule(Logic button, Logic reset) : super(name: 'OvenModule') {
OvenModule(Logic button, Logic reset, Logic clk) : super(name: 'OvenModule') {
// Register inputs and outputs of the module in the constructor.
// Module logic must consume registered inputs and output to registered
// outputs. `led` output also added as the output port.
button = addInput('button', button, width: button.width);
reset = addInput('reset', reset);
clk = addInput('clk', clk);
final led = addOutput('led', width: button.width);

// An internal clock generator.
final clk = SimpleClockGenerator(10).clk;

// Register local signals, `counterReset` and `en`
// for Counter module.
final counterReset = Logic(name: 'counter_reset');
Expand Down Expand Up @@ -184,16 +184,26 @@ class OvenModule extends Module {
FiniteStateMachine<OvenState> get ovenStateMachine => _oven;
}

/// A helper function to wait for a number of cycles.
Future<void> waitCycles(Logic clk, int numCycles) async {
for (var i = 0; i < numCycles; i++) {
await clk.nextPosedge;
}
}

Future<void> main({bool noPrint = false}) async {
// Signals `button` and `reset` that mimic user's behaviour of button pressed
// and reset.
//
// Width of button is 2 because button is represent by 2-bits signal.
// Width of button is 2 because button is represented by a 2-bit signal.
final button = Logic(name: 'button', width: 2);
final reset = Logic(name: 'reset');

// A clock generator.
final clk = SimpleClockGenerator(10).clk;

// Build an Oven Module and passed the `button` and `reset`.
final oven = OvenModule(button, reset);
final oven = OvenModule(button, reset, clk);

// Generate a Mermaid FSM diagram and save as the name `oven_fsm.md`.
// Note that the extension of the files is recommend as .md or .mmd.
Expand All @@ -210,14 +220,22 @@ Future<void> main({bool noPrint = false}) async {

// Now let's try simulating!

// Let's start off with asserting reset to Oven.
reset.inject(1);
// Set a maximum time for the simulation so it doesn't keep running forever.
Simulator.setMaxSimTime(300);

// Attach a waveform dumper so we can see what happens.
if (!noPrint) {
WaveDumper(oven, outputPath: 'oven.vcd');
}

// Kick off the simulation.
unawaited(Simulator.run());

await clk.nextPosedge;

// Let's start off with asserting reset to Oven.
reset.inject(1);

if (!noPrint) {
// We can listen to the streams on LED light changes based on time.
oven.led.changed.listen((event) {
Expand All @@ -227,38 +245,39 @@ Future<void> main({bool noPrint = false}) async {
// Print the Simulator time when the LED light changes.
print('@t=${Simulator.time}, LED changed to: $ledVal');
});

button.changed.listen((event) {
final buttonVal = Button.values[event.newValue.toInt()].name;
print('@t=${Simulator.time}, Button changed to: $buttonVal');
});
}

// Drop reset at time 25.
Simulator.registerAction(25, () => reset.put(0));
await waitCycles(clk, 2);

// Press button start => `00` at time 25.
Simulator.registerAction(25, () {
button.put(Button.start.value);
});
// Drop reset
reset.inject(0);

// Press button pause => `01` at time 50.
Simulator.registerAction(50, () {
button.put(Button.pause.value);
});
// Press button start => `00`
button.inject(Button.start.value);

// Press button resume => `10` at time 70.
Simulator.registerAction(70, () {
button.put(Button.resume.value);
});
await waitCycles(clk, 3);

// Print a message when we're done with the simulation!
Simulator.registerAction(120, () {
if (!noPrint) {
print('Simulation completed!');
}
});
// Press button pause => `01`
button.inject(Button.pause.value);

// Set a maximum time for the simulation so it doesn't keep running forever.
Simulator.setMaxSimTime(120);
await waitCycles(clk, 3);

// Kick off the simulation.
await Simulator.run();
// Press button resume => `10`
button.inject(Button.resume.value);

await waitCycles(clk, 8);

await Simulator.endSimulation();

// Print a message when we're done with the simulation!
if (!noPrint) {
print('Simulation completed!');
}

// We can take a look at the waves now
if (!noPrint) {
Expand Down
13 changes: 7 additions & 6 deletions lib/src/exceptions/name/invalid_reserved_name_exception.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2022-2023 Intel Corporation
// Copyright (C) 2022-2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// invalid_reserved_name_exception.dart
Expand All @@ -11,11 +11,12 @@ import 'package:rohd/src/exceptions/rohd_exception.dart';

/// An exception that thrown when a reserved name is invalid.
class InvalidReservedNameException extends RohdException {
/// Display error [message] on invalid reserved name.
/// An exception with an error [message] for an invalid reserved name.
///
/// Creates a [InvalidReservedNameException] with an optional error [message].
InvalidReservedNameException(
[super.message = 'Reserved Name need to follow proper naming '
'convention if reserved'
' name set to true']);
InvalidReservedNameException(String name)
: super('The name "$name" was reserved but does not follow'
' safe naming conventions. '
'Generally, reserved names should be valid variable identifiers'
' in languages such as Dart and SystemVerilog.');
}
19 changes: 13 additions & 6 deletions lib/src/finite_state_machine.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,28 @@ class FiniteStateMachine<StateIdentifier> {
/// Width of the state.
final int _stateWidth;

/// If `true`, the [reset] signal is asynchronous.
final bool asyncReset;

/// Creates an finite state machine for the specified list of [_states], with
/// an initial state of [resetState] (when synchronous [reset] is high) and
/// transitions on positive [clk] edges.
FiniteStateMachine(
Logic clk,
Logic reset,
StateIdentifier resetState,
List<State<StateIdentifier>> states,
) : this.multi([clk], reset, resetState, states);
List<State<StateIdentifier>> states, {
bool asyncReset = false,
}) : this.multi([clk], reset, resetState, states, asyncReset: asyncReset);

/// Creates an finite state machine for the specified list of [_states], with
/// an initial state of [resetState] (when synchronous [reset] is high) and
/// transitions on positive edges of any of [_clks].
/// an initial state of [resetState] (when [reset] is high) and transitions on
/// positive edges of any of [_clks].
///
/// If [asyncReset] is `true`, the [reset] signal is asynchronous.
FiniteStateMachine.multi(
this._clks, this.reset, this.resetState, this._states)
this._clks, this.reset, this.resetState, this._states,
{this.asyncReset = false})
: _stateWidth = _logBase(_states.length, 2),
currentState =
Logic(name: 'currentState', width: _logBase(_states.length, 2)),
Expand Down Expand Up @@ -147,7 +154,7 @@ class FiniteStateMachine<StateIdentifier> {
])
]);

Sequential.multi(_clks, reset: reset, resetValues: {
Sequential.multi(_clks, reset: reset, asyncReset: asyncReset, resetValues: {
currentState: _stateValueLookup[_stateLookup[resetState]]
}, [
currentState < nextState,
Expand Down
2 changes: 1 addition & 1 deletion lib/src/interfaces/interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class Interface<TagType> {
Logic port(String name) => _ports.containsKey(name)
? _ports[name]!
: throw PortDoesNotExistException(
'Port named "$name" not found on this interface.');
'Port named "$name" not found on this interface: $this.');

/// Provides the [port] named [name] if it exists, otherwise `null`.
Logic? tryPort(String name) => _ports[name];
Expand Down
Loading

0 comments on commit ea893bb

Please sign in to comment.