Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async Reset Improvements #533

Merged
merged 30 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a53bbb0
Add support for negedge triggers on Sequential
mkorbel1 Apr 17, 2024
8aa53cd
test for negedge trigger
mkorbel1 Apr 17, 2024
adb2909
add some extra tests
mkorbel1 Apr 17, 2024
67f94a7
Test flop with both edge triggers
mkorbel1 Apr 17, 2024
7b50033
Merge branch 'main' of github.com:intel/rohd into negedge
mkorbel1 Jun 4, 2024
a5dc0de
add notes to update pipeline and fsm
mkorbel1 Jun 4, 2024
166ab18
added asyncReset flag to places, started with failing async reset test
mkorbel1 Dec 3, 2024
a5c5734
Merge branch 'negedge' into asyncreset
mkorbel1 Dec 3, 2024
9904eb9
merge works with async and negedge
mkorbel1 Dec 3, 2024
f7cfa69
async reset sampling works
mkorbel1 Dec 4, 2024
7d3db7b
test different asyncreset mechanisms work
mkorbel1 Dec 4, 2024
6cf153c
test for non-identical trigger
mkorbel1 Dec 5, 2024
3b7f4e5
wip async checks
mkorbel1 Dec 10, 2024
e3663a9
check is printing at maybe the right time
mkorbel1 Dec 10, 2024
c6a760f
fix examples with new restriction
mkorbel1 Dec 12, 2024
7adb1d9
making progress on getting tests workign with new rules
mkorbel1 Dec 12, 2024
a05693e
fix multi trigger test, add some doc
mkorbel1 Dec 12, 2024
8ff97b1
fix another test with order of multiple triggers
mkorbel1 Dec 12, 2024
806ccc8
test with and without clock presence
mkorbel1 Dec 12, 2024
1aebf34
settling on solution that works
mkorbel1 Dec 12, 2024
2ffcde7
async reset simcompare with different flop approaches
mkorbel1 Dec 13, 2024
b39e183
async reset vcd test
mkorbel1 Dec 13, 2024
9a98948
improve invalid reserved name exception message
mkorbel1 Dec 13, 2024
1fe0b8e
cleanup and doc
mkorbel1 Dec 13, 2024
7a8bfbe
test fsm with async reset
mkorbel1 Dec 13, 2024
06f8422
test async reset in pipeline
mkorbel1 Dec 13, 2024
17c41f4
fix resetvalues in pipelines
mkorbel1 Dec 13, 2024
76a28f3
tweaks
mkorbel1 Dec 13, 2024
0c9def6
cleanup code review
mkorbel1 Dec 16, 2024
e64f4dc
fix dart.dev link in docs
mkorbel1 Dec 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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