From f1c647aeae420f8937a60ed2551751489d5abf3c Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Wed, 20 Sep 2023 08:40:33 -0700 Subject: [PATCH] Updates to FSM and Pipeline abstractions and documentation (#414) --- .../chapter_8/02_finite_state_machine.md | 12 +- .../answers/exercise_2_toycapsule_fsm.dart | 6 +- doc/tutorials/chapter_8/oven_fsm.dart | 9 +- doc/user_guide/_docs/A15-fsm.md | 158 ++++++++++----- example/oven_fsm.dart | 13 +- lib/rohd.dart | 3 +- .../invalid_conditional_exception.dart | 1 - ...ped_signal_already_assigned_exception.dart | 1 - .../signal_redriven_exception.dart | 1 - .../uninitialized_signal_exception.dart | 1 - lib/src/exceptions/exceptions.dart | 1 + .../illegal_configuration_exception.dart | 17 ++ .../interface/interface_type_exception.dart | 1 - .../logic/invalid_multiplier_exception.dart | 1 - .../logic/logic_construction_exception.dart | 1 - lib/src/exceptions/logic/put_exception.dart | 1 - .../self_connecting_logic_exception.dart | 1 - .../signal_width_mismatch_exception.dart | 1 - .../invalid_random_logic_value_exception.dart | 1 - .../invalid_truncation_exception.dart | 1 - .../invalid_value_operation_exception.dart | 1 - .../logic_value_construction_exception.dart | 1 - .../value_width_mismatch_exception.dart | 1 - .../module/module_not_built_exception.dart | 1 - .../module/port_width_mismatch_exception.dart | 1 - ...machine.dart => finite_state_machine.dart} | 128 +++++++++--- lib/src/interfaces/pair_interface.dart | 1 - lib/src/module.dart | 2 - lib/src/modules/conditional.dart | 5 +- lib/src/modules/gates.dart | 1 - lib/src/modules/pipeline.dart | 50 ++++- lib/src/signals/port.dart | 1 - lib/src/signals/signals.dart | 1 - lib/src/synthesizers/synth_builder.dart | 1 - lib/src/utilities/simcompare.dart | 1 - lib/src/values/values.dart | 1 - test/bus_test.dart | 1 - test/comb_math_test.dart | 1 - test/comb_mod_test.dart | 1 - test/conditionals_test.dart | 2 - test/definition_name_test.dart | 1 - test/fsm_test.dart | 183 ++++++++++++------ test/gate_test.dart | 1 - test/invalid_latch_test.dart | 1 - test/logic_array_test.dart | 1 - test/logic_name_test.dart | 1 - test/logic_structure_test.dart | 1 - test/logic_value_test.dart | 1 - test/ssa_test.dart | 1 - test/synth_builder_test.dart | 1 - 50 files changed, 424 insertions(+), 201 deletions(-) create mode 100644 lib/src/exceptions/illegal_configuration_exception.dart rename lib/src/{state_machine.dart => finite_state_machine.dart} (58%) diff --git a/doc/tutorials/chapter_8/02_finite_state_machine.md b/doc/tutorials/chapter_8/02_finite_state_machine.md index 7ebeaa443..b3d99e5c5 100644 --- a/doc/tutorials/chapter_8/02_finite_state_machine.md +++ b/doc/tutorials/chapter_8/02_finite_state_machine.md @@ -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 _oven; + late FiniteStateMachine _oven; // A hashmap that represent button value final Map btnVal = { @@ -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 _oven; + late FiniteStateMachine _oven; Logic get led => output('led'); OvenModule(): super(name: 'OvenModule') { @@ -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(clk, reset, OvenState.standby, states); +_oven = FiniteStateMachine(clk, reset, OvenState.standby, states); _oven.generateDiagram(outputPath: 'doc/tutorials/chapter_8/oven_fsm.md'); ``` diff --git a/doc/tutorials/chapter_8/answers/exercise_2_toycapsule_fsm.dart b/doc/tutorials/chapter_8/answers/exercise_2_toycapsule_fsm.dart index 9edb1afb4..6912f1313 100644 --- a/doc/tutorials/chapter_8/answers/exercise_2_toycapsule_fsm.dart +++ b/doc/tutorials/chapter_8/answers/exercise_2_toycapsule_fsm.dart @@ -5,7 +5,7 @@ import 'package:rohd/rohd.dart'; enum ToyCapsuleState { idle, coinInserted, dispensing } class ToyCapsuleFSM extends Module { - late StateMachine _state; + late FiniteStateMachine _state; ToyCapsuleFSM(Logic clk, Logic reset, Logic btnDispense, Logic coin) : super(name: 'toy_capsule_fsm') { @@ -33,10 +33,10 @@ class ToyCapsuleFSM extends Module { ]), ]; - _state = StateMachine(clk, reset, ToyCapsuleState.idle, states); + _state = FiniteStateMachine(clk, reset, ToyCapsuleState.idle, states); } - StateMachine get toyCapsuleStateMachine => _state; + FiniteStateMachine get toyCapsuleStateMachine => _state; Logic get toyCapsule => output('toy_capsule'); } diff --git a/doc/tutorials/chapter_8/oven_fsm.dart b/doc/tutorials/chapter_8/oven_fsm.dart index 939e033c0..172d83006 100644 --- a/doc/tutorials/chapter_8/oven_fsm.dart +++ b/doc/tutorials/chapter_8/oven_fsm.dart @@ -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 `_oven`. + // A private variable with type FiniteStateMachine `_oven`. // // Use `late` to indicate that the value will not be null // and will be assign in the later section. - late StateMachine _oven; + late FiniteStateMachine _oven; // A hashmap that represent button value final Map btnVal = { @@ -157,8 +157,9 @@ class OvenModule extends Module { ]) ]; - // Assign the _oven StateMachine object to private variable declared. - _oven = StateMachine(clk, reset, OvenState.standby, states); + // Assign the _oven FiniteStateMachine object to private variable declared. + _oven = + FiniteStateMachine(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. diff --git a/doc/user_guide/_docs/A15-fsm.md b/doc/user_guide/_docs/A15-fsm.md index 55c0ee92e..ecba06b84 100644 --- a/doc/user_guide/_docs/A15-fsm.md +++ b/doc/user_guide/_docs/A15-fsm.md @@ -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.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.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.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.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(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.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( + 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. diff --git a/example/oven_fsm.dart b/example/oven_fsm.dart index 92c5436d0..27da69737 100644 --- a/example/oven_fsm.dart +++ b/example/oven_fsm.dart @@ -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 `_oven`. + // A private variable with type FiniteStateMachine `_oven`. // // Use `late` to indicate that the value will not be null // and will be assign in the later section. - late StateMachine _oven; + late FiniteStateMachine _oven; // We can expose an LED light output as a getter to retrieve it value. Logic get led => output('led'); @@ -175,12 +175,13 @@ class OvenModule extends Module { ]) ]; - // Assign the _oven StateMachine object to private variable declared. - _oven = StateMachine(clk, reset, OvenState.standby, states); + // Assign the _oven FiniteStateMachine object to private variable declared. + _oven = + FiniteStateMachine(clk, reset, OvenState.standby, states); } - // An ovenStateMachine that represent in getter. - StateMachine get ovenStateMachine => _oven; + // An oven FiniteStateMachine that represent in getter. + FiniteStateMachine get ovenStateMachine => _oven; } Future main({bool noPrint = false}) async { diff --git a/lib/rohd.dart b/lib/rohd.dart index c0fe14235..ad8a7046d 100644 --- a/lib/rohd.dart +++ b/lib/rohd.dart @@ -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'; diff --git a/lib/src/exceptions/conditionals/invalid_conditional_exception.dart b/lib/src/exceptions/conditionals/invalid_conditional_exception.dart index 77bad9052..928748740 100644 --- a/lib/src/exceptions/conditionals/invalid_conditional_exception.dart +++ b/lib/src/exceptions/conditionals/invalid_conditional_exception.dart @@ -8,7 +8,6 @@ // Author: Max Korbel 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. diff --git a/lib/src/exceptions/conditionals/mapped_signal_already_assigned_exception.dart b/lib/src/exceptions/conditionals/mapped_signal_already_assigned_exception.dart index ce8e59c2e..d2dfbc945 100644 --- a/lib/src/exceptions/conditionals/mapped_signal_already_assigned_exception.dart +++ b/lib/src/exceptions/conditionals/mapped_signal_already_assigned_exception.dart @@ -9,7 +9,6 @@ // Author: Max Korbel 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 diff --git a/lib/src/exceptions/conditionals/signal_redriven_exception.dart b/lib/src/exceptions/conditionals/signal_redriven_exception.dart index 884e22f8d..408c35a65 100644 --- a/lib/src/exceptions/conditionals/signal_redriven_exception.dart +++ b/lib/src/exceptions/conditionals/signal_redriven_exception.dart @@ -9,7 +9,6 @@ // Author: Yao Jing Quek 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. diff --git a/lib/src/exceptions/conditionals/uninitialized_signal_exception.dart b/lib/src/exceptions/conditionals/uninitialized_signal_exception.dart index 48011e313..2068486dc 100644 --- a/lib/src/exceptions/conditionals/uninitialized_signal_exception.dart +++ b/lib/src/exceptions/conditionals/uninitialized_signal_exception.dart @@ -8,7 +8,6 @@ // Author: Max Korbel 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. diff --git a/lib/src/exceptions/exceptions.dart b/lib/src/exceptions/exceptions.dart index e71095031..e98d22d1c 100644 --- a/lib/src/exceptions/exceptions.dart +++ b/lib/src/exceptions/exceptions.dart @@ -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'; diff --git a/lib/src/exceptions/illegal_configuration_exception.dart b/lib/src/exceptions/illegal_configuration_exception.dart new file mode 100644 index 000000000..db5d018d4 --- /dev/null +++ b/lib/src/exceptions/illegal_configuration_exception.dart @@ -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 + +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); +} diff --git a/lib/src/exceptions/interface/interface_type_exception.dart b/lib/src/exceptions/interface/interface_type_exception.dart index 510b11786..d509aa4d4 100644 --- a/lib/src/exceptions/interface/interface_type_exception.dart +++ b/lib/src/exceptions/interface/interface_type_exception.dart @@ -8,7 +8,6 @@ // Author: Max Korbel 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 { diff --git a/lib/src/exceptions/logic/invalid_multiplier_exception.dart b/lib/src/exceptions/logic/invalid_multiplier_exception.dart index cc51f1e7d..83c4b000b 100644 --- a/lib/src/exceptions/logic/invalid_multiplier_exception.dart +++ b/lib/src/exceptions/logic/invalid_multiplier_exception.dart @@ -8,7 +8,6 @@ // Author: Akshay Wankhede 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. diff --git a/lib/src/exceptions/logic/logic_construction_exception.dart b/lib/src/exceptions/logic/logic_construction_exception.dart index bb6c2dc1e..62fece6cc 100644 --- a/lib/src/exceptions/logic/logic_construction_exception.dart +++ b/lib/src/exceptions/logic/logic_construction_exception.dart @@ -8,7 +8,6 @@ // Author: Max Korbel 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 { diff --git a/lib/src/exceptions/logic/put_exception.dart b/lib/src/exceptions/logic/put_exception.dart index ab4eebc05..d227a1c95 100644 --- a/lib/src/exceptions/logic/put_exception.dart +++ b/lib/src/exceptions/logic/put_exception.dart @@ -8,7 +8,6 @@ // Author: Max Korbel 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 { diff --git a/lib/src/exceptions/logic/self_connecting_logic_exception.dart b/lib/src/exceptions/logic/self_connecting_logic_exception.dart index e40a404f7..51c953af0 100644 --- a/lib/src/exceptions/logic/self_connecting_logic_exception.dart +++ b/lib/src/exceptions/logic/self_connecting_logic_exception.dart @@ -9,7 +9,6 @@ // Author: Sanchit Kumar 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 SelfConnectingLogicException extends RohdException { diff --git a/lib/src/exceptions/logic/signal_width_mismatch_exception.dart b/lib/src/exceptions/logic/signal_width_mismatch_exception.dart index 304fb398a..b766e8021 100644 --- a/lib/src/exceptions/logic/signal_width_mismatch_exception.dart +++ b/lib/src/exceptions/logic/signal_width_mismatch_exception.dart @@ -8,7 +8,6 @@ // Author: Max Korbel import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/rohd_exception.dart'; /// An [Exception] thrown when a signal has the wrong width. class SignalWidthMismatchException extends RohdException { diff --git a/lib/src/exceptions/logic_value/invalid_random_logic_value_exception.dart b/lib/src/exceptions/logic_value/invalid_random_logic_value_exception.dart index 724ec29b8..2b5344668 100644 --- a/lib/src/exceptions/logic_value/invalid_random_logic_value_exception.dart +++ b/lib/src/exceptions/logic_value/invalid_random_logic_value_exception.dart @@ -10,7 +10,6 @@ import 'dart:math'; import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/rohd_exception.dart'; /// An exception that is thrown when the generation of the random [LogicValue] /// from [Random] results in errors or bugs. diff --git a/lib/src/exceptions/logic_value/invalid_truncation_exception.dart b/lib/src/exceptions/logic_value/invalid_truncation_exception.dart index 43d9867f7..96401303c 100644 --- a/lib/src/exceptions/logic_value/invalid_truncation_exception.dart +++ b/lib/src/exceptions/logic_value/invalid_truncation_exception.dart @@ -8,7 +8,6 @@ // Author: Sanchit Kumar import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/rohd_exception.dart'; /// An exception that is thrown when a [LogicValue] operation /// couldn't be performed due invalid data truncation. diff --git a/lib/src/exceptions/logic_value/invalid_value_operation_exception.dart b/lib/src/exceptions/logic_value/invalid_value_operation_exception.dart index ba94ea654..9b9ce1daa 100644 --- a/lib/src/exceptions/logic_value/invalid_value_operation_exception.dart +++ b/lib/src/exceptions/logic_value/invalid_value_operation_exception.dart @@ -9,7 +9,6 @@ // Author: Sanchit Kumar import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/rohd_exception.dart'; /// An exception that is thrown when a given operation cannot be performed on /// invalid [LogicValue] diff --git a/lib/src/exceptions/logic_value/logic_value_construction_exception.dart b/lib/src/exceptions/logic_value/logic_value_construction_exception.dart index 3ad028d8f..603c518f6 100644 --- a/lib/src/exceptions/logic_value/logic_value_construction_exception.dart +++ b/lib/src/exceptions/logic_value/logic_value_construction_exception.dart @@ -8,7 +8,6 @@ // Author: Max Korbel import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/rohd_exception.dart'; /// An exception that thrown when a [LogicValue] cannot be properly constructed. class LogicValueConstructionException extends RohdException { diff --git a/lib/src/exceptions/logic_value/value_width_mismatch_exception.dart b/lib/src/exceptions/logic_value/value_width_mismatch_exception.dart index 7fd3ac3bf..78765caef 100644 --- a/lib/src/exceptions/logic_value/value_width_mismatch_exception.dart +++ b/lib/src/exceptions/logic_value/value_width_mismatch_exception.dart @@ -8,7 +8,6 @@ // Author: Sanchit Kumar import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/rohd_exception.dart'; /// An exception that is thrown when [LogicValue]s of different width are found. class ValueWidthMismatchException extends RohdException { diff --git a/lib/src/exceptions/module/module_not_built_exception.dart b/lib/src/exceptions/module/module_not_built_exception.dart index 6aff9c0f8..ca2bb8760 100644 --- a/lib/src/exceptions/module/module_not_built_exception.dart +++ b/lib/src/exceptions/module/module_not_built_exception.dart @@ -8,7 +8,6 @@ // Author: Max Korbel import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/rohd_exception.dart'; /// An [Exception] thrown when a [Module] was used in a way that required it /// to be built first, but it was not yet built. diff --git a/lib/src/exceptions/module/port_width_mismatch_exception.dart b/lib/src/exceptions/module/port_width_mismatch_exception.dart index 8ba427e6a..4cebf28ca 100644 --- a/lib/src/exceptions/module/port_width_mismatch_exception.dart +++ b/lib/src/exceptions/module/port_width_mismatch_exception.dart @@ -8,7 +8,6 @@ // Author: Max Korbel import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/rohd_exception.dart'; /// An [Exception] thrown when a port has the wrong width. class PortWidthMismatchException extends RohdException { diff --git a/lib/src/state_machine.dart b/lib/src/finite_state_machine.dart similarity index 58% rename from lib/src/state_machine.dart rename to lib/src/finite_state_machine.dart index fd8834fff..c090e1477 100644 --- a/lib/src/state_machine.dart +++ b/lib/src/finite_state_machine.dart @@ -1,8 +1,8 @@ // Copyright (C) 2022-2023 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // -// state_machine.dart -// fsm generators +// finite_state_machine.dart +// Finite state machine generators // // 2022 April 22 // Author: Shubham Kumar @@ -13,12 +13,16 @@ import 'dart:math'; import 'package:collection/collection.dart'; import 'package:rohd/rohd.dart'; -/// Simple class for FSM [StateMachine]. +/// Deprecated: use [FiniteStateMachine] instead. +@Deprecated('Use FiniteStateMachine instead') +typedef StateMachine = FiniteStateMachine; + +/// Simple class for FSM [FiniteStateMachine]. /// /// Abstraction for representing Finite state machines (FSM). /// Contains the logic for performing the state transitions. -class StateMachine { - /// List containig objects of class [State]. +class FiniteStateMachine { + /// List of all the [State]s in this machine. List> get states => UnmodifiableListView(_states); final List> _states; @@ -30,8 +34,27 @@ class StateMachine { /// _states as the value. final Map, int> _stateValueLookup = {}; - /// The clock signal to the FSM. - final Logic clk; + /// Provides the corresponding index held in state signals such as + /// [nextState] and [currentState] based on the provided [id]. + /// + /// Returns null if the [id] does not have a defined state in the machine. + int? getStateIndex(StateIdentifier id) { + if (!_stateLookup.containsKey(id)) { + return null; + } + + return _stateValueLookup[_stateLookup[id]]; + } + + /// The clock signal to the FSM (when only single-triggered). Otherwise, the + /// first clock. + /// + /// Deprecated: do not reference the clock from [FiniteStateMachine]. + @Deprecated('Do not reference the clock from the `FiniteStateMachine`.') + Logic get clk => _clks.first; + + /// The clock signals to the FSM. + final List _clks; /// The reset signal to the FSM. final Logic reset; @@ -40,30 +63,46 @@ class StateMachine { final StateIdentifier resetState; /// The current state of the FSM. + /// + /// Use [getStateIndex] to map from a [StateIdentifier] to the value on this + /// bus. final Logic currentState; /// The next state of the FSM. + /// + /// Use [getStateIndex] to map from a [StateIdentifier] to the value on this + /// bus. final Logic nextState; + /// Returns a ceiling on the log of [x] base [base]. static int _logBase(num x, num base) => (log(x) / log(base)).ceil(); /// Width of the state. final int _stateWidth; - /// 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. - /// - /// If a [reset] signal is provided the FSM transitions to the [resetState] - /// on the next clock cycle. - StateMachine(this.clk, this.reset, this.resetState, this._states) + /// 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> states, + ) : this.multi([clk], reset, resetState, states); + + /// 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]. + FiniteStateMachine.multi( + this._clks, this.reset, this.resetState, this._states) : _stateWidth = _logBase(_states.length, 2), currentState = Logic(name: 'currentState', width: _logBase(_states.length, 2)), nextState = Logic(name: 'nextState', width: _logBase(_states.length, 2)) { - var stateCounter = 0; + _validate(); + var stateCounter = 0; for (final state in _states) { _stateLookup[state.identifier] = state; _stateValueLookup[state] = stateCounter++; @@ -85,8 +124,10 @@ class StateMachine { _stateLookup[entry.value]] ])) .toList(growable: false), - conditionalType: ConditionalType.unique, - defaultItem: [nextState < currentState]) + conditionalType: state.conditionalType, + defaultItem: [ + nextState < getStateIndex(state.defaultNextState), + ]) ])) .toList(growable: false), conditionalType: ConditionalType.unique, @@ -106,15 +147,27 @@ class StateMachine { ]) ]); - Sequential(clk, [ - If( - reset, - then: [currentState < _stateValueLookup[_stateLookup[resetState]]], - orElse: [currentState < nextState], - ) + Sequential.multi(_clks, reset: reset, resetValues: { + currentState: _stateValueLookup[_stateLookup[resetState]] + }, [ + currentState < nextState, ]); } + /// Validates that the configuration of the [FiniteStateMachine] is legal. + void _validate() { + final identifiers = _states.map((e) => e.identifier); + + if (identifiers.toSet().length != _states.length) { + throw IllegalConfigurationException('State identifiers must be unique.'); + } + + if (!identifiers.contains(resetState)) { + throw IllegalConfigurationException( + 'Reset state $resetState must have a definition.'); + } + } + /// Generate a FSM state diagram [_MermaidStateDiagram]. /// Check on https://mermaid.js.org/intro/ to view the diagram generated. /// If you are using vscode, you can download the mermaid extension. @@ -141,19 +194,44 @@ class State { /// A map of the possible conditions that might be true and the next state /// that the FSM needs to transition to in each of those cases. + /// + /// If no key in [events] matches, then the state of the [FiniteStateMachine] + /// will stay the same. + /// + /// If using [ConditionalType.priority], this should be an ordered [Map]. final Map events; /// Actions to perform while the FSM is in this state. final List actions; + /// The next state to transition to if non of the [events] hit. + final StateIdentifier defaultNextState; + + /// Used to control how different [events] should be prioritized and matched. + /// + /// For example, if [ConditionalType.priority] is selected, then the first + /// matching event in [events] will be executed. If [ConditionalType.unique] + /// is selected, then there will be a guarantee that no two [events] match + /// at the same time. + final ConditionalType conditionalType; + /// Represents a state named [identifier] with a definition of [events] /// and [actions] associated with that state. - State(this.identifier, {required this.events, required this.actions}); + /// + /// If provided, the [defaultNextState] is the default next state if none + /// of the [events] match. + State( + this.identifier, { + required this.events, + required this.actions, + StateIdentifier? defaultNextState, + this.conditionalType = ConditionalType.unique, + }) : defaultNextState = defaultNextState ?? identifier; } /// A state diagram generator for FSM. /// -/// Outputs to vcd format at [outputPath]. +/// Outputs to markdown format at [outputPath]. class _MermaidStateDiagram { /// The diagram to be return as String. late StringBuffer _diagram; diff --git a/lib/src/interfaces/pair_interface.dart b/lib/src/interfaces/pair_interface.dart index 848ae1f78..dfd004ed8 100644 --- a/lib/src/interfaces/pair_interface.dart +++ b/lib/src/interfaces/pair_interface.dart @@ -11,7 +11,6 @@ import 'dart:collection'; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/interface/interface_exceptions.dart'; import 'package:rohd/src/utilities/sanitizer.dart'; /// A direction for signals between a pair of components. diff --git a/lib/src/module.dart b/lib/src/module.dart index 8b6388db3..2bf17d8a6 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -14,8 +14,6 @@ import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/module/module_exceptions.dart'; -import 'package:rohd/src/exceptions/name/name_exceptions.dart'; import 'package:rohd/src/utilities/config.dart'; import 'package:rohd/src/utilities/sanitizer.dart'; import 'package:rohd/src/utilities/timestamper.dart'; diff --git a/lib/src/modules/conditional.dart b/lib/src/modules/conditional.dart index 3c8a8e54b..b11b3487a 100644 --- a/lib/src/modules/conditional.dart +++ b/lib/src/modules/conditional.dart @@ -16,7 +16,6 @@ import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd/src/collections/duplicate_detection_set.dart'; import 'package:rohd/src/collections/traverseable_collection.dart'; -import 'package:rohd/src/exceptions/exceptions.dart'; import 'package:rohd/src/utilities/sanitizer.dart'; import 'package:rohd/src/utilities/synchronous_propagator.dart'; import 'package:rohd/src/utilities/uniquifier.dart'; @@ -406,6 +405,10 @@ class Sequential extends _Always { /// specified reset value. Sequential.multi(List clks, super._conditionals, {super.reset, super.resetValues, super.name = 'sequential'}) { + if (clks.isEmpty) { + throw IllegalConfigurationException('Must provide at least one clock.'); + } + for (var i = 0; i < clks.length; i++) { final clk = clks[i]; if (clk.width > 1) { diff --git a/lib/src/modules/gates.dart b/lib/src/modules/gates.dart index 7b94f39dc..7415e58ff 100644 --- a/lib/src/modules/gates.dart +++ b/lib/src/modules/gates.dart @@ -8,7 +8,6 @@ // Author: Max Korbel import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/exceptions.dart'; /// A gate [Module] that performs bit-wise inversion. class NotGate extends Module with InlineSystemVerilog { diff --git a/lib/src/modules/pipeline.dart b/lib/src/modules/pipeline.dart index 8fff663aa..b6a81c427 100644 --- a/lib/src/modules/pipeline.dart +++ b/lib/src/modules/pipeline.dart @@ -74,8 +74,13 @@ class _PipeStage { /// A simple pipeline, separating arbitrary combinational logic by flop stages. class Pipeline { - /// The clock whose positive edge triggers the flops in this pipeline. - final Logic clk; + /// The clock whose positive edge triggers the flops in this pipeline when + /// single-triggered. Otherwise, the first clock. + @Deprecated('Do not reference the clock from the `Pipeline`.') + Logic get clk => _clks.first; + + /// The clocks whose positive edges trigger the flops in this pipeline. + final List _clks; /// An optional reset signal for all pipelined signals. final Logic? reset; @@ -112,7 +117,21 @@ class Pipeline { /// Each stage can be stalled independently using [stalls], where every index /// of [stalls] corresponds to the index of the stage to be stalled. When /// a stage's stall is asserted, the output of that stage will not change. - Pipeline(this.clk, + Pipeline(Logic clk, + {List Function(PipelineStageInfo p)> stages = const [], + List? stalls, + List signals = const [], + Map resetValues = const {}, + Logic? reset}) + : this.multi([clk], + stages: stages, + stalls: stalls, + signals: signals, + resetValues: resetValues, + reset: reset); + + /// Constructs a [Pipeline] with multiple triggers on any of [_clks]. + Pipeline.multi(this._clks, {List Function(PipelineStageInfo p)> stages = const [], List? stalls, List signals = const [], @@ -225,7 +244,7 @@ class Pipeline { ]) ]; } - Sequential(clk, ffAssignsWithStall, name: 'ff_${newLogic.name}'); + Sequential.multi(_clks, ffAssignsWithStall, name: 'ff_${newLogic.name}'); } /// The stage input for a signal associated with [logic] to [stageIndex]. @@ -304,14 +323,33 @@ class ReadyValidPipeline extends Pipeline { /// If contents are pushed in when the pipeline is not ready, they /// will be dropped. ReadyValidPipeline( - super.clk, + Logic clk, + Logic validPipeIn, + Logic readyPipeOut, { + List Function(PipelineStageInfo p)> stages = const [], + Map resetValues = const {}, + List signals = const [], + Logic? reset, + }) : this.multi( + [clk], + validPipeIn, + readyPipeOut, + stages: stages, + resetValues: resetValues, + signals: signals, + reset: reset, + ); + + /// Creates a [ReadyValidPipeline] with multiple triggers. + ReadyValidPipeline.multi( + super._clks, this.validPipeIn, this.readyPipeOut, { List Function(PipelineStageInfo p)> stages = const [], super.resetValues, List signals = const [], super.reset, - }) : super( + }) : super.multi( stages: stages, signals: [validPipeIn, ...signals], stalls: List.generate( diff --git a/lib/src/signals/port.dart b/lib/src/signals/port.dart index 74b8fc3ee..c7ca6e178 100644 --- a/lib/src/signals/port.dart +++ b/lib/src/signals/port.dart @@ -8,7 +8,6 @@ // Author: Max Korbel import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/name/invalid_portname_exceptions.dart'; import 'package:rohd/src/utilities/sanitizer.dart'; /// An extension of [Logic] which performs some additional validation for diff --git a/lib/src/signals/signals.dart b/lib/src/signals/signals.dart index c385c499f..22de945c4 100644 --- a/lib/src/signals/signals.dart +++ b/lib/src/signals/signals.dart @@ -10,7 +10,6 @@ import 'dart:math'; import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/exceptions.dart'; import 'package:rohd/src/utilities/index_utilities.dart'; import 'package:rohd/src/utilities/sanitizer.dart'; import 'package:rohd/src/utilities/synchronous_propagator.dart'; diff --git a/lib/src/synthesizers/synth_builder.dart b/lib/src/synthesizers/synth_builder.dart index 0e91e12ca..bfa6c229d 100644 --- a/lib/src/synthesizers/synth_builder.dart +++ b/lib/src/synthesizers/synth_builder.dart @@ -9,7 +9,6 @@ import 'package:collection/collection.dart'; import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/module/module_not_built_exception.dart'; import 'package:rohd/src/utilities/uniquifier.dart'; /// A generic class which can convert a module into a generated output using diff --git a/lib/src/utilities/simcompare.dart b/lib/src/utilities/simcompare.dart index 58cb6453c..4a6b073ad 100644 --- a/lib/src/utilities/simcompare.dart +++ b/lib/src/utilities/simcompare.dart @@ -14,7 +14,6 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/exceptions.dart'; import 'package:test/test.dart'; /// Represents a single test case to check in a single clock cycle. diff --git a/lib/src/values/values.dart b/lib/src/values/values.dart index bfb635aab..685ab4b67 100644 --- a/lib/src/values/values.dart +++ b/lib/src/values/values.dart @@ -8,7 +8,6 @@ import 'dart:math' as math; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/exceptions.dart'; import 'package:rohd/src/exceptions/logic_value/invalid_random_logic_value_exception.dart'; import 'package:rohd/src/utilities/index_utilities.dart'; part 'logic_value.dart'; diff --git a/test/bus_test.dart b/test/bus_test.dart index a373415ce..09e96877b 100644 --- a/test/bus_test.dart +++ b/test/bus_test.dart @@ -8,7 +8,6 @@ // Author: Max Korbel import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/exceptions.dart'; import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; diff --git a/test/comb_math_test.dart b/test/comb_math_test.dart index 3719436c2..c58881db7 100644 --- a/test/comb_math_test.dart +++ b/test/comb_math_test.dart @@ -8,7 +8,6 @@ // Author: Max Korbel import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/conditionals/conditional_exceptions.dart'; import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; diff --git a/test/comb_mod_test.dart b/test/comb_mod_test.dart index 4dcc07412..45b3e82d0 100644 --- a/test/comb_mod_test.dart +++ b/test/comb_mod_test.dart @@ -8,7 +8,6 @@ // Author: Max Korbel import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/conditionals/write_after_read_exception.dart'; import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; diff --git a/test/conditionals_test.dart b/test/conditionals_test.dart index 4c87cb3a3..981f73607 100644 --- a/test/conditionals_test.dart +++ b/test/conditionals_test.dart @@ -10,8 +10,6 @@ import 'dart:async'; import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/conditionals/conditional_exceptions.dart'; -import 'package:rohd/src/exceptions/sim_compare/sim_compare_exceptions.dart'; import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; diff --git a/test/definition_name_test.dart b/test/definition_name_test.dart index 479139e06..1f02361d5 100644 --- a/test/definition_name_test.dart +++ b/test/definition_name_test.dart @@ -8,7 +8,6 @@ // Author: Yao Jing Quek import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/name/name_exceptions.dart'; import 'package:test/test.dart'; class DefinitionName { diff --git a/test/fsm_test.dart b/test/fsm_test.dart index cf87a66b1..cac200323 100644 --- a/test/fsm_test.dart +++ b/test/fsm_test.dart @@ -33,84 +33,105 @@ class TestModule extends Module { }, actions: [ b < c, ]), - State(MyStates.state2, events: {}, actions: [ - b < 1, - ]), + State(MyStates.state2, + conditionalType: ConditionalType.priority, + events: {}, + actions: [ + b < 1, + ]), State(MyStates.state3, events: {}, actions: [ b < ~c, ]), ]; - StateMachine(clk, reset, MyStates.state1, states) + FiniteStateMachine(clk, reset, MyStates.state1, states) .generateDiagram(outputPath: _simpleFSMPath); } } enum LightStates { northFlowing, northSlowing, eastFlowing, eastSlowing } -class Direction extends Const { - Direction._(int super.val) : super(width: 2); - Direction.noTraffic() : this._(bin('00')); - Direction.northTraffic() : this._(bin('01')); - Direction.eastTraffic() : this._(bin('10')); - Direction.both() : this._(bin('11')); +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); } -class LightColor extends Const { - LightColor._(int super.val) : super(width: 2); - LightColor.green() : this._(bin('00')); - LightColor.yellow() : this._(bin('01')); - LightColor.red() : this._(bin('10')); +enum LightColor { + green(0), + yellow(1), + red(2); + + final int value; + + const LightColor(this.value); } class TrafficTestModule extends Module { TrafficTestModule(Logic traffic, Logic reset) { traffic = addInput('traffic', traffic, width: traffic.width); + reset = addInput('reset', reset); + final northLight = addOutput('northLight', width: traffic.width); final eastLight = addOutput('eastLight', width: traffic.width); + final clk = SimpleClockGenerator(10).clk; - reset = addInput('reset', reset); - final states = [ - State(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.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.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.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, + + final states = >[ + State(LightStates.northFlowing, events: { + TrafficPresence.isEastActive(traffic): LightStates.northSlowing, }, actions: [ - northLight < LightColor.red(), - eastLight < LightColor.yellow(), + 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, + ], + ), ]; - StateMachine(clk, reset, LightStates.northFlowing, states) - .generateDiagram(outputPath: _trafficFSMPath); + FiniteStateMachine( + clk, + reset, + LightStates.northFlowing, + states, + ).generateDiagram(outputPath: _trafficFSMPath); } } @@ -130,6 +151,48 @@ void main() { expect(sv, contains("b = 1'h0;")); }); + test('conditional type is used', () async { + final pipem = TestModule(Logic(), Logic(), Logic()); + await pipem.build(); + + final sv = pipem.generateSynth(); + + expect(sv, contains('priority case')); + }); + + group('fsm validation', () { + test('duplicate state identifiers throws exception', () { + expect( + () => + FiniteStateMachine(Logic(), Logic(), MyStates.state1, [ + State(MyStates.state1, events: {}, actions: []), + State(MyStates.state2, events: {}, actions: []), + State(MyStates.state2, events: {}, actions: []), + ]), + throwsA(isA())); + }); + + test('missing reset state throws exception', () { + expect( + () => + FiniteStateMachine(Logic(), Logic(), MyStates.state1, [ + State(MyStates.state2, events: {}, actions: []), + ]), + throwsA(isA())); + }); + }); + + test('state index', () { + expect( + FiniteStateMachine(Logic(), Logic(), MyStates.state1, [ + State(MyStates.state4, events: {}, actions: []), + State(MyStates.state1, events: {}, actions: []), + State(MyStates.state3, events: {}, actions: []), + State(MyStates.state2, events: {}, actions: []), + ]).getStateIndex(MyStates.state2), + 3); + }); + group('simcompare', () { test('simple fsm', () async { final pipem = TestModule(Logic(), Logic(), Logic()); @@ -159,18 +222,18 @@ void main() { Vector({ 'reset': 0 }, { - 'northLight': LightColor.green().value, - 'eastLight': LightColor.red().value + 'northLight': LightColor.green.value, + 'eastLight': LightColor.red.value }), Vector({}, {}), - Vector({'traffic': Direction.eastTraffic().value}, {}), + Vector({'traffic': TrafficPresence.eastTraffic.value}, {}), Vector({}, { - 'northLight': LightColor.yellow().value, - 'eastLight': LightColor.red().value + 'northLight': LightColor.yellow.value, + 'eastLight': LightColor.red.value }), Vector({}, { - 'northLight': LightColor.red().value, - 'eastLight': LightColor.green().value + 'northLight': LightColor.red.value, + 'eastLight': LightColor.green.value }) ]; await SimCompare.checkFunctionalVector(pipem, vectors); diff --git a/test/gate_test.dart b/test/gate_test.dart index 5293c4ece..032ecbb62 100644 --- a/test/gate_test.dart +++ b/test/gate_test.dart @@ -8,7 +8,6 @@ // Author: Max Korbel import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/exceptions.dart'; import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; diff --git a/test/invalid_latch_test.dart b/test/invalid_latch_test.dart index bc3bf56fc..1ed6e077a 100644 --- a/test/invalid_latch_test.dart +++ b/test/invalid_latch_test.dart @@ -10,7 +10,6 @@ // - https://github.com/intel/rohd/issues/285 import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/conditionals/conditional_exceptions.dart'; import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; diff --git a/test/logic_array_test.dart b/test/logic_array_test.dart index 46e9a0cc1..2b1670da1 100644 --- a/test/logic_array_test.dart +++ b/test/logic_array_test.dart @@ -11,7 +11,6 @@ import 'dart:math'; import 'package:collection/collection.dart'; import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/exceptions.dart'; import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; diff --git a/test/logic_name_test.dart b/test/logic_name_test.dart index e52eeb87a..5884310c1 100644 --- a/test/logic_name_test.dart +++ b/test/logic_name_test.dart @@ -8,7 +8,6 @@ // Author: Yao Jing Quek import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/name/name_exceptions.dart'; import 'package:rohd/src/utilities/sanitizer.dart'; import 'package:test/test.dart'; diff --git a/test/logic_structure_test.dart b/test/logic_structure_test.dart index bddd4265f..94705f243 100644 --- a/test/logic_structure_test.dart +++ b/test/logic_structure_test.dart @@ -8,7 +8,6 @@ // Author: Max Korbel import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/exceptions.dart'; import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; diff --git a/test/logic_value_test.dart b/test/logic_value_test.dart index 1d5d8103f..c231d32ca 100644 --- a/test/logic_value_test.dart +++ b/test/logic_value_test.dart @@ -10,7 +10,6 @@ import 'dart:math'; import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/exceptions.dart'; import 'package:rohd/src/exceptions/logic_value/invalid_random_logic_value_exception.dart'; import 'package:test/test.dart'; diff --git a/test/ssa_test.dart b/test/ssa_test.dart index 8ac1e4d70..abdebf491 100644 --- a/test/ssa_test.dart +++ b/test/ssa_test.dart @@ -8,7 +8,6 @@ // Author: Max Korbel import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/exceptions.dart'; import 'package:rohd/src/utilities/simcompare.dart'; import 'package:test/test.dart'; diff --git a/test/synth_builder_test.dart b/test/synth_builder_test.dart index bcf74c598..48a23dec0 100644 --- a/test/synth_builder_test.dart +++ b/test/synth_builder_test.dart @@ -8,7 +8,6 @@ // Author: Yao Jing Quek import 'package:rohd/rohd.dart'; -import 'package:rohd/src/exceptions/module/module_not_built_exception.dart'; import 'package:test/test.dart'; class TopModule extends Module {