Skip to content

Commit

Permalink
Updates to FSM and Pipeline abstractions and documentation (#414)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkorbel1 authored Sep 20, 2023
1 parent 1d1cc0b commit f1c647a
Show file tree
Hide file tree
Showing 50 changed files with 424 additions and 201 deletions.
12 changes: 6 additions & 6 deletions doc/tutorials/chapter_8/02_finite_state_machine.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,15 @@ class OvenModule extends Module {
}
```

In ROHD, we can use `StateMachine` API library. The `StateMachine` constructs a simple FSM, using the `clk` and `reset` signals. Also accepts the `reset` state to transition to `resetState` along with the List of _states of the FSM. Later, we will also need to create a List of `state` and send to the StateMachine.
In ROHD, we can use `FiniteStateMachine` API library. The `FiniteStateMachine` constructs a simple FSM, using the `clk` and `reset` signals. Also accepts the `reset` state to transition to `resetState` along with the List of _states of the FSM. Later, we will also need to create a List of `state` and send to the `FiniteStateMachine`.

Let start by intitialize a variable called `_oven` that is `StateMachine` with `StateIdentifier` as `OvenState`.
Let start by intitialize a variable called `_oven` that is `FiniteStateMachine` with `StateIdentifier` as `OvenState`.

Besides, we can use a simple hashmap to map over the button and LED value to integer.

```dart
class OvenModule extends Module {
late StateMachine<OvenState> _oven;
late FiniteStateMachine<OvenState> _oven;
// A hashmap that represent button value
final Map<String, int> btnVal = {
Expand Down Expand Up @@ -99,7 +99,7 @@ Let also create an internal clock generator `clk` inside the module. This clk ge

```dart
class OvenModule extends Module {
late StateMachine<OvenState> _oven;
late FiniteStateMachine<OvenState> _oven;
Logic get led => output('led');
OvenModule(): super(name: 'OvenModule') {
Expand Down Expand Up @@ -235,12 +235,12 @@ final states = [
];
```

By now, you already have a list of `state` ready to be passed to the `StateMachine`. Let assign the the `state` to the StateMachine declared. Note that, we also passed `OvenState.standby` to the StateMachine to understand that is the State when reset signal is given.
By now, you already have a list of `state` ready to be passed to the `FiniteStateMachine`. Let assign the the `state` to the `FiniteStateMachine` declared. Note that, we also passed `OvenState.standby` to the `FiniteStateMachine` to understand that is the State when reset signal is given.

ROHD FSM abstraction come with state diagram generator using mermaid. We can create a markdown file using the function `generateDiagram()`. You can install mermaid extension in VSCode to preview the diagram.

```dart
_oven = StateMachine<OvenState>(clk, reset, OvenState.standby, states);
_oven = FiniteStateMachine<OvenState>(clk, reset, OvenState.standby, states);
_oven.generateDiagram(outputPath: 'doc/tutorials/chapter_8/oven_fsm.md');
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import 'package:rohd/rohd.dart';
enum ToyCapsuleState { idle, coinInserted, dispensing }

class ToyCapsuleFSM extends Module {
late StateMachine<ToyCapsuleState> _state;
late FiniteStateMachine<ToyCapsuleState> _state;

ToyCapsuleFSM(Logic clk, Logic reset, Logic btnDispense, Logic coin)
: super(name: 'toy_capsule_fsm') {
Expand Down Expand Up @@ -33,10 +33,10 @@ class ToyCapsuleFSM extends Module {
]),
];

_state = StateMachine(clk, reset, ToyCapsuleState.idle, states);
_state = FiniteStateMachine(clk, reset, ToyCapsuleState.idle, states);
}

StateMachine<ToyCapsuleState> get toyCapsuleStateMachine => _state;
FiniteStateMachine<ToyCapsuleState> get toyCapsuleStateMachine => _state;
Logic get toyCapsule => output('toy_capsule');
}

Expand Down
9 changes: 5 additions & 4 deletions doc/tutorials/chapter_8/oven_fsm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ enum OvenState { standby, cooking, paused, completed }

// Define a class OvenModule that extends ROHD's abstract Module class.
class OvenModule extends Module {
// A private variable with type StateMachine<OvenState> `_oven`.
// A private variable with type FiniteStateMachine<OvenState> `_oven`.
//
// Use `late` to indicate that the value will not be null
// and will be assign in the later section.
late StateMachine<OvenState> _oven;
late FiniteStateMachine<OvenState> _oven;

// A hashmap that represent button value
final Map<String, int> btnVal = {
Expand Down Expand Up @@ -157,8 +157,9 @@ class OvenModule extends Module {
])
];

// Assign the _oven StateMachine object to private variable declared.
_oven = StateMachine<OvenState>(clk, reset, OvenState.standby, states);
// Assign the _oven FiniteStateMachine object to private variable declared.
_oven =
FiniteStateMachine<OvenState>(clk, reset, OvenState.standby, states);

// 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 Down
158 changes: 109 additions & 49 deletions doc/user_guide/_docs/A15-fsm.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,119 @@
title: "Finite State Machines"
permalink: /docs/fsm/
excerpt: "Finite State Machines"
last_modified_at: 2022-12-06
last_modified_at: 2023-09-19
toc: true
---

ROHD has a built-in syntax for handling FSMs in a simple & refactorable way. The below example shows a 2 way Traffic light FSM. Note that [`StateMachine`](https://intel.github.io/rohd/rohd/StateMachine-class.html) consumes the `clk` and `reset` signals. Also accepts the reset state to transition to `resetState` along with the `List` of `states` of the FSM. You can also use this abstraction to generate a FSM diagram using [`generateDiagram`](https://intel.github.io/rohd/rohd/StateMachine/generateDiagram.html).
ROHD has a built-in syntax for handling Finite State Machines (FSM) in a simple & refactorable way. To illustrate, the below example shows a two-way traffic light controller FSM with some basic rules:

- The usual traffic light colors: green means go, yellow means slow, red means stop
- Only either north/south or east/west traffic is allowed at a given time
- Always transition through a yellow light before going to red
- Trigger a transition between north/south and east/west flowing when there is traffic waiting at a red light

With the ROHD [`FiniteStateMachine`](https://intel.github.io/rohd/rohd/FiniteStateMachine-class.html), we can just describe the state machine architecturally and let ROHD take care of the low-level implementation details.

First, let's define our states ("flowing" means green for them, red for the other; "slowing" means yellow for them, red for the other). For simplicity, we just refer to "north" and "east" instead of "north/south" and "east/west".

```dart
enum LightStates { northFlowing, northSlowing, eastFlowing, eastSlowing }
```

Now let's make some representation for different cases of traffic wishing to go through the intersection. Let's also add some helper functions to compute whether there's pending traffic in each direction, since the "both" case is true for either one.

```dart
class TrafficTestModule extends Module {
TrafficTestModule(Logic traffic, Logic reset) {
traffic = addInput('traffic', traffic, width: traffic.width);
var northLight = addOutput('northLight', width: traffic.width);
var eastLight = addOutput('eastLight', width: traffic.width);
var clk = SimpleClockGenerator(10).clk;
reset = addInput('reset', reset);
var states = [
State<LightStates>(LightStates.northFlowing, events: {
traffic.eq(Direction.noTraffic()): LightStates.northFlowing,
traffic.eq(Direction.northTraffic()): LightStates.northFlowing,
traffic.eq(Direction.eastTraffic()): LightStates.northSlowing,
traffic.eq(Direction.both()): LightStates.northSlowing,
}, actions: [
northLight < LightColor.green(),
eastLight < LightColor.red(),
]),
State<LightStates>(LightStates.northSlowing, events: {
traffic.eq(Direction.noTraffic()): LightStates.eastFlowing,
traffic.eq(Direction.northTraffic()): LightStates.eastFlowing,
traffic.eq(Direction.eastTraffic()): LightStates.eastFlowing,
traffic.eq(Direction.both()): LightStates.eastFlowing,
}, actions: [
northLight < LightColor.yellow(),
eastLight < LightColor.red(),
]),
State<LightStates>(LightStates.eastFlowing, events: {
traffic.eq(Direction.noTraffic()): LightStates.eastSlowing,
traffic.eq(Direction.northTraffic()): LightStates.eastSlowing,
traffic.eq(Direction.eastTraffic()): LightStates.eastFlowing,
traffic.eq(Direction.both()): LightStates.eastSlowing,
}, actions: [
northLight < LightColor.red(),
eastLight < LightColor.green(),
]),
State<LightStates>(LightStates.eastSlowing, events: {
traffic.eq(Direction.noTraffic()): LightStates.northFlowing,
traffic.eq(Direction.northTraffic()): LightStates.northFlowing,
traffic.eq(Direction.eastTraffic()): LightStates.northFlowing,
traffic.eq(Direction.both()): LightStates.northFlowing,
}, actions: [
northLight < LightColor.red(),
eastLight < LightColor.yellow(),
]),
];
StateMachine<LightStates>(clk, reset, LightStates.northFlowing, states);
}
enum TrafficPresence {
noTraffic(0),
northTraffic(1),
eastTraffic(2),
both(3);
final int value;
const TrafficPresence(this.value);
static Logic isEastActive(Logic dir) =>
dir.eq(TrafficPresence.eastTraffic.value) |
dir.eq(TrafficPresence.both.value);
static Logic isNorthActive(Logic dir) =>
dir.eq(TrafficPresence.northTraffic.value) |
dir.eq(TrafficPresence.both.value);
}
```

Now, let's define the encoding of outputs (the color of the light).

```dart
enum LightColor {
green(0),
yellow(1),
red(2);
final int value;
const LightColor(this.value);
}
```

Now we can go ahead and describe the set of states for our state machine. Note that for each state, we describe a few things:

- Identify the name of the state (one of `LightStates`)
- Define `events` that cause a transition to another state. If none of the events occur, it will stay in the same state by default, but that's configurable via the `defaultNextState`. In this case, if there's traffic waiting at a red light, we start transitioning. If we're yellow, go to red state next.
- Define `actions` that should occur when in that state. In this case, we set the light colors based on the state.

```dart
final states = <State<LightStates>>[
State(LightStates.northFlowing, events: {
TrafficPresence.isEastActive(traffic): LightStates.northSlowing,
}, actions: [
northLight < LightColor.green.value,
eastLight < LightColor.red.value,
]),
State(
LightStates.northSlowing,
events: {},
defaultNextState: LightStates.eastFlowing,
actions: [
northLight < LightColor.yellow.value,
eastLight < LightColor.red.value,
],
),
State(
LightStates.eastFlowing,
events: {
TrafficPresence.isNorthActive(traffic): LightStates.eastSlowing,
},
actions: [
northLight < LightColor.red.value,
eastLight < LightColor.green.value,
],
),
State(
LightStates.eastSlowing,
events: {},
defaultNextState: LightStates.northFlowing,
actions: [
northLight < LightColor.red.value,
eastLight < LightColor.yellow.value,
],
),
];
```

Now, constructing the state machine itself is easy. LEt's start at the north/south flowing state.

```dart
FiniteStateMachine<LightStates>(
clk,
reset,
LightStates.northFlowing,
states,
);
```

This state machine is now functional and synthesizable into SystemVerilog!

You can even generate a mermaid diagram for the state machine using the [`generateDiagram`](https://intel.github.io/rohd/rohd/FiniteStateMachine/generateDiagram.html) API.

You can see a full executable example of this state machine in [`test/fsm_test.dart`](https://github.com/intel/rohd/blob/main/test/fsm_test.dart) in the ROHD repository.
13 changes: 7 additions & 6 deletions example/oven_fsm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ enum LEDLight {

// Define a class OvenModule that extends ROHD's abstract Module class.
class OvenModule extends Module {
// A private variable with type StateMachine<OvenState> `_oven`.
// A private variable with type FiniteStateMachine<OvenState> `_oven`.
//
// Use `late` to indicate that the value will not be null
// and will be assign in the later section.
late StateMachine<OvenState> _oven;
late FiniteStateMachine<OvenState> _oven;

// We can expose an LED light output as a getter to retrieve it value.
Logic get led => output('led');
Expand Down Expand Up @@ -175,12 +175,13 @@ class OvenModule extends Module {
])
];

// Assign the _oven StateMachine object to private variable declared.
_oven = StateMachine<OvenState>(clk, reset, OvenState.standby, states);
// Assign the _oven FiniteStateMachine object to private variable declared.
_oven =
FiniteStateMachine<OvenState>(clk, reset, OvenState.standby, states);
}

// An ovenStateMachine that represent in getter.
StateMachine<OvenState> get ovenStateMachine => _oven;
// An oven FiniteStateMachine that represent in getter.
FiniteStateMachine<OvenState> get ovenStateMachine => _oven;
}

Future<void> main({bool noPrint = false}) async {
Expand Down
3 changes: 2 additions & 1 deletion lib/rohd.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
// Copyright (C) 2021-2023 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause

export 'src/exceptions/exceptions.dart';
export 'src/external.dart';
export 'src/finite_state_machine.dart';
export 'src/interfaces/interfaces.dart';
export 'src/module.dart';
export 'src/modules/modules.dart';
export 'src/signals/signals.dart';
export 'src/simulator.dart';
export 'src/state_machine.dart';
export 'src/swizzle.dart';
export 'src/synthesizers/synthesizers.dart';
export 'src/values/values.dart';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
// Author: Max Korbel <[email protected]>

import 'package:rohd/rohd.dart';
import 'package:rohd/src/exceptions/exceptions.dart';

/// An exception that is thrown when a [Conditional] has been constructed in
/// an invalid way.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
// Author: Max Korbel <[email protected]>

import 'package:rohd/rohd.dart';
import 'package:rohd/src/exceptions/rohd_exception.dart';

/// An exception that is thrown when [Combinational.ssa] is attempting to
/// deduce mappings for signals but fails since a signal would be connected
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
// Author: Yao Jing Quek <[email protected]>

import 'package:rohd/rohd.dart';
import 'package:rohd/src/exceptions/rohd_exception.dart';

/// An exception that is thrown when a [Logic] signal is
/// driven multiple times.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
// Author: Max Korbel <[email protected]>

import 'package:rohd/rohd.dart';
import 'package:rohd/src/exceptions/rohd_exception.dart';

/// An exception that is thrown when [Combinational.ssa] detects that an SSA
/// signal is being used before it was initialized.
Expand Down
1 change: 1 addition & 0 deletions lib/src/exceptions/exceptions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ export './logic_value/logic_value_exceptions.dart';
export './module/module_exceptions.dart';
export './name/name_exceptions.dart';
export './sim_compare/sim_compare_exceptions.dart';
export 'illegal_configuration_exception.dart';
export 'rohd_exception.dart';
export 'unsupported_type_exception.dart';
17 changes: 17 additions & 0 deletions lib/src/exceptions/illegal_configuration_exception.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// illegal_configuration_exception.dart
// An exception thrown when something is configured in an illegal way.
//
// 2023 June 13
// Author: Max Korbel <[email protected]>

import 'package:rohd/src/exceptions/exceptions.dart';

/// An exception that is thrown when somethins is configured in an illegal way.
class IllegalConfigurationException extends RohdException {
/// Creates a new [IllegalConfigurationException] with a [message] explaining
/// what was illegal about it.
IllegalConfigurationException(super.message);
}
1 change: 0 additions & 1 deletion lib/src/exceptions/interface/interface_type_exception.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
// Author: Max Korbel <[email protected]>

import 'package:rohd/rohd.dart';
import 'package:rohd/src/exceptions/rohd_exception.dart';

/// An [Exception] thrown when an interface has an issue with its type.
class InterfaceTypeException extends RohdException {
Expand Down
1 change: 0 additions & 1 deletion lib/src/exceptions/logic/invalid_multiplier_exception.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
// Author: Akshay Wankhede <[email protected]>

import 'package:rohd/rohd.dart';
import 'package:rohd/src/exceptions/rohd_exception.dart';

/// An exception that thrown when a [Logic] is replicated with an invalid (<1)
/// multiplier.
Expand Down
1 change: 0 additions & 1 deletion lib/src/exceptions/logic/logic_construction_exception.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
// Author: Max Korbel <[email protected]>

import 'package:rohd/rohd.dart';
import 'package:rohd/src/exceptions/rohd_exception.dart';

/// An exception that thrown when a [Logic] is connecting to itself.
class LogicConstructionException extends RohdException {
Expand Down
1 change: 0 additions & 1 deletion lib/src/exceptions/logic/put_exception.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
// Author: Max Korbel <[email protected]>

import 'package:rohd/rohd.dart';
import 'package:rohd/src/exceptions/rohd_exception.dart';

/// An exception that thrown when a [Logic] signal fails to `put`.
class PutException extends RohdException {
Expand Down
Loading

0 comments on commit f1c647a

Please sign in to comment.