From 79c1bf90dc6743fe9cca7956e9957307fc4b8b22 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Wed, 27 Jul 2022 13:07:44 -0700 Subject: [PATCH 1/6] experimenting with making the traffic light fsm better --- test/fsm_test.dart | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/test/fsm_test.dart b/test/fsm_test.dart index 5a103056f..fc8191c8b 100644 --- a/test/fsm_test.dart +++ b/test/fsm_test.dart @@ -45,6 +45,11 @@ class Direction extends Const { Direction.northTraffic() : this._(bin('01')); Direction.eastTraffic() : this._(bin('10')); Direction.both() : this._(bin('11')); + + static Logic isEastActive(Logic dir) => + dir.eq(Direction.eastTraffic()) | dir.eq(Direction.both()); + static Logic isNorthActive(Logic dir) => + dir.eq(Direction.northTraffic()) | dir.eq(Direction.both()); } class LightColor extends Const { @@ -61,39 +66,35 @@ class TrafficTestModule extends Module { var eastLight = addOutput('eastLight', width: traffic.width); var clk = SimpleClockGenerator(10).clk; reset = addInput('reset', reset); + + // var eastActive = traffic[1]; + // traffic.eq(Direction.eastTraffic()) | traffic.eq(Direction.both()); + // var northActive = traffic[0]; + // traffic.eq(Direction.northTraffic()) | traffic.eq(Direction.both()); + 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, + ~Direction.isEastActive(traffic): LightStates.northFlowing, + Direction.isEastActive(traffic): 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, + Const(1): 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, + ~Direction.isNorthActive(traffic): LightStates.eastFlowing, + Direction.isNorthActive(traffic): 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, + Const(1): LightStates.northFlowing, }, actions: [ northLight < LightColor.red(), eastLight < LightColor.yellow(), From ce9750ec99df990e3fa60300d923eb1193288451 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 19 Sep 2023 00:14:39 -0700 Subject: [PATCH 2/6] Updates for FSM abstraction, fix #409 --- .../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 | 4 +- 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 + .../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 - lib/src/interfaces/pair_interface.dart | 1 - lib/src/module.dart | 2 - lib/src/modules/conditional.dart | 1 - lib/src/modules/gates.dart | 1 - lib/src/signals/port.dart | 1 - lib/src/signals/signals.dart | 1 - lib/src/state_machine.dart | 198 ------------------ 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 | 38 +++- 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 - 48 files changed, 62 insertions(+), 263 deletions(-) delete mode 100644 lib/src/state_machine.dart 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..60e0b0aa3 100644 --- a/doc/user_guide/_docs/A15-fsm.md +++ b/doc/user_guide/_docs/A15-fsm.md @@ -6,7 +6,7 @@ last_modified_at: 2022-12-06 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 FSMs in a simple & refactorable way. The below example shows a 2 way Traffic light FSM. Note that [`FiniteStateMachine`](https://intel.github.io/rohd/rohd/FiniteStateMachine-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/FiniteStateMachine/generateDiagram.html). ```dart class TrafficTestModule extends Module { @@ -54,7 +54,7 @@ class TrafficTestModule extends Module { eastLight < LightColor.yellow(), ]), ]; - StateMachine(clk, reset, LightStates.northFlowing, states); + FiniteStateMachine(clk, reset, LightStates.northFlowing, states); } } ``` 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/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/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..3d8f15710 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'; 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/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/state_machine.dart b/lib/src/state_machine.dart deleted file mode 100644 index fd8834fff..000000000 --- a/lib/src/state_machine.dart +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (C) 2022-2023 Intel Corporation -// SPDX-License-Identifier: BSD-3-Clause -// -// state_machine.dart -// fsm generators -// -// 2022 April 22 -// Author: Shubham Kumar - -import 'dart:io'; -import 'dart:math'; - -import 'package:collection/collection.dart'; -import 'package:rohd/rohd.dart'; - -/// Simple class for FSM [StateMachine]. -/// -/// Abstraction for representing Finite state machines (FSM). -/// Contains the logic for performing the state transitions. -class StateMachine { - /// List containig objects of class [State]. - List> get states => UnmodifiableListView(_states); - final List> _states; - - /// A map to store the state identifier as the key - /// and the object as the value. - final Map> _stateLookup = {}; - - /// A map to store the state object as the key and the index of the state in - /// _states as the value. - final Map, int> _stateValueLookup = {}; - - /// The clock signal to the FSM. - final Logic clk; - - /// The reset signal to the FSM. - final Logic reset; - - /// The reset state of the FSM to default to when the reset signal is high. - final StateIdentifier resetState; - - /// The current state of the FSM. - final Logic currentState; - - /// The next state of the FSM. - final Logic nextState; - - 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) - : _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; - - for (final state in _states) { - _stateLookup[state.identifier] = state; - _stateValueLookup[state] = stateCounter++; - } - - Combinational([ - Case( - currentState, - _states - .map((state) => CaseItem( - Const(_stateValueLookup[state], width: _stateWidth), [ - ...state.actions, - Case( - Const(1), - state.events.entries - .map((entry) => CaseItem(entry.key, [ - nextState < - _stateValueLookup[ - _stateLookup[entry.value]] - ])) - .toList(growable: false), - conditionalType: ConditionalType.unique, - defaultItem: [nextState < currentState]) - ])) - .toList(growable: false), - conditionalType: ConditionalType.unique, - defaultItem: [ - nextState < currentState, - - // zero out all other receivers from state actions... - // even though out-of-state is unreachable, - // we don't want any inferred latches - ..._states - .map((state) => state.actions) - .flattened - .map((conditional) => conditional.receivers) - .flattened - .toSet() - .map((receiver) => receiver < 0) - ]) - ]); - - Sequential(clk, [ - If( - reset, - then: [currentState < _stateValueLookup[_stateLookup[resetState]]], - orElse: [currentState < nextState], - ) - ]); - } - - /// 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. - /// - /// Output to mermaid diagram at [outputPath]. - void generateDiagram({String outputPath = 'diagram_fsm.md'}) { - final figure = _MermaidStateDiagram(outputPath: outputPath) - ..addStartState(resetState.toString()); - - for (final state in _states) { - for (final entry in state.events.entries) { - figure.addTransitions(state.identifier.toString(), - entry.value.toString(), entry.key.name); - } - } - figure.writeToFile(); - } -} - -/// Simple class to initialize each state of the FSM. -class State { - /// Identifier or name of the state. - final StateIdentifier identifier; - - /// 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. - final Map events; - - /// Actions to perform while the FSM is in this state. - final List actions; - - /// 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}); -} - -/// A state diagram generator for FSM. -/// -/// Outputs to vcd format at [outputPath]. -class _MermaidStateDiagram { - /// The diagram to be return as String. - late StringBuffer _diagram; - - /// The output filepath of the generated state diagram. - final String outputPath; - - /// The file to write dumped output waveform to. - final File _outputFile; - - // An empty spaces indentation for state. - final _indentation = ' ' * 4; - - /// Generate a [_MermaidStateDiagram] that initialized the diagram of - /// mermaid as `stateDiagram`. - /// - /// Passed output path to save in custom directory. - _MermaidStateDiagram({this.outputPath = 'diagram_fsm.md'}) - : _outputFile = File(outputPath) { - _diagram = StringBuffer('stateDiagram-v2'); - } - - /// Register a new transition [event] that point the - /// current state [currentState] to next state [nextState]. - void addTransitions(String currentState, String nextState, String event) => - _diagram.write('\n$_indentation$currentState --> $nextState: $event'); - - /// Register a start state [startState]. - void addStartState(String startState) => - _diagram.write('\n$_indentation[*] --> $startState'); - - /// Write the object content to [_outputFile] by enclose it with - /// mermaid identifier. - void writeToFile() { - final outputDiagram = StringBuffer(''' -```mermaid -$_diagram -``` -'''); - _outputFile.writeAsStringSync(outputDiagram.toString()); - } -} 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..604775efa 100644 --- a/test/fsm_test.dart +++ b/test/fsm_test.dart @@ -41,7 +41,7 @@ class TestModule extends Module { ]), ]; - StateMachine(clk, reset, MyStates.state1, states) + FiniteStateMachine(clk, reset, MyStates.state1, states) .generateDiagram(outputPath: _simpleFSMPath); } } @@ -109,7 +109,8 @@ class TrafficTestModule extends Module { ]), ]; - StateMachine(clk, reset, LightStates.northFlowing, states) + FiniteStateMachine( + clk, reset, LightStates.northFlowing, states) .generateDiagram(outputPath: _trafficFSMPath); } } @@ -130,6 +131,39 @@ void main() { expect(sv, contains("b = 1'h0;")); }); + 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()); 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 { From c38ada677c1f87f4bc7d51bfe7be2e376dee0cff Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 19 Sep 2023 00:15:06 -0700 Subject: [PATCH 3/6] add files i forgot --- .../illegal_configuration_exception.dart | 17 ++ lib/src/finite_state_machine.dart | 235 ++++++++++++++++++ 2 files changed, 252 insertions(+) create mode 100644 lib/src/exceptions/illegal_configuration_exception.dart create mode 100644 lib/src/finite_state_machine.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/finite_state_machine.dart b/lib/src/finite_state_machine.dart new file mode 100644 index 000000000..e5a42ecda --- /dev/null +++ b/lib/src/finite_state_machine.dart @@ -0,0 +1,235 @@ +// Copyright (C) 2022-2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// finite_state_machine.dart +// Finite state machine generators +// +// 2022 April 22 +// Author: Shubham Kumar + +import 'dart:io'; +import 'dart:math'; + +import 'package:collection/collection.dart'; +import 'package:rohd/rohd.dart'; + +/// 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 FiniteStateMachine { + /// List of all the [State]s in this machine. + List> get states => UnmodifiableListView(_states); + final List> _states; + + /// A map to store the state identifier as the key + /// and the object as the value. + final Map> _stateLookup = {}; + + /// A map to store the state object as the key and the index of the state in + /// _states as the value. + final Map, int> _stateValueLookup = {}; + + /// 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. + final Logic clk; + + /// The reset signal to the FSM. + final Logic reset; + + /// The reset state of the FSM to default to when the reset signal is high. + 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. + FiniteStateMachine(this.clk, 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(); + + for (final state in _states) { + _stateLookup[state.identifier] = state; + _stateValueLookup[state] = stateCounter++; + } + + Combinational([ + Case( + currentState, + _states + .map((state) => CaseItem( + Const(_stateValueLookup[state], width: _stateWidth), [ + ...state.actions, + Case( + Const(1), + state.events.entries + .map((entry) => CaseItem(entry.key, [ + nextState < + _stateValueLookup[ + _stateLookup[entry.value]] + ])) + .toList(growable: false), + conditionalType: ConditionalType.unique, + defaultItem: [nextState < currentState]) + ])) + .toList(growable: false), + conditionalType: ConditionalType.unique, + defaultItem: [ + nextState < currentState, + + // zero out all other receivers from state actions... + // even though out-of-state is unreachable, + // we don't want any inferred latches + ..._states + .map((state) => state.actions) + .flattened + .map((conditional) => conditional.receivers) + .flattened + .toSet() + .map((receiver) => receiver < 0) + ]) + ]); + + Sequential(clk, 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. + /// + /// Output to mermaid diagram at [outputPath]. + void generateDiagram({String outputPath = 'diagram_fsm.md'}) { + final figure = _MermaidStateDiagram(outputPath: outputPath) + ..addStartState(resetState.toString()); + + for (final state in _states) { + for (final entry in state.events.entries) { + figure.addTransitions(state.identifier.toString(), + entry.value.toString(), entry.key.name); + } + } + figure.writeToFile(); + } +} + +/// Simple class to initialize each state of the FSM. +class State { + /// Identifier or name of the state. + final StateIdentifier identifier; + + /// 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. + final Map events; + + /// Actions to perform while the FSM is in this state. + final List actions; + + /// 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}); +} + +/// A state diagram generator for FSM. +/// +/// Outputs to vcd format at [outputPath]. +class _MermaidStateDiagram { + /// The diagram to be return as String. + late StringBuffer _diagram; + + /// The output filepath of the generated state diagram. + final String outputPath; + + /// The file to write dumped output waveform to. + final File _outputFile; + + // An empty spaces indentation for state. + final _indentation = ' ' * 4; + + /// Generate a [_MermaidStateDiagram] that initialized the diagram of + /// mermaid as `stateDiagram`. + /// + /// Passed output path to save in custom directory. + _MermaidStateDiagram({this.outputPath = 'diagram_fsm.md'}) + : _outputFile = File(outputPath) { + _diagram = StringBuffer('stateDiagram-v2'); + } + + /// Register a new transition [event] that point the + /// current state [currentState] to next state [nextState]. + void addTransitions(String currentState, String nextState, String event) => + _diagram.write('\n$_indentation$currentState --> $nextState: $event'); + + /// Register a start state [startState]. + void addStartState(String startState) => + _diagram.write('\n$_indentation[*] --> $startState'); + + /// Write the object content to [_outputFile] by enclose it with + /// mermaid identifier. + void writeToFile() { + final outputDiagram = StringBuffer(''' +```mermaid +$_diagram +``` +'''); + _outputFile.writeAsStringSync(outputDiagram.toString()); + } +} From 129ece162ab964c9ebb5dbebd0ad08e4eb898ebe Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 19 Sep 2023 03:43:53 -0700 Subject: [PATCH 4/6] Support FSM priority type and state index --- doc/user_guide/_docs/A15-fsm.md | 3 + lib/src/finite_state_machine.dart | 55 +++++++++--- test/fsm_test.dart | 135 ++++++++++++++++++------------ 3 files changed, 126 insertions(+), 67 deletions(-) diff --git a/doc/user_guide/_docs/A15-fsm.md b/doc/user_guide/_docs/A15-fsm.md index 60e0b0aa3..c2def3433 100644 --- a/doc/user_guide/_docs/A15-fsm.md +++ b/doc/user_guide/_docs/A15-fsm.md @@ -6,6 +6,9 @@ last_modified_at: 2022-12-06 toc: true --- +# TODO: need to update all this doc! + + 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 [`FiniteStateMachine`](https://intel.github.io/rohd/rohd/FiniteStateMachine-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/FiniteStateMachine/generateDiagram.html). ```dart diff --git a/lib/src/finite_state_machine.dart b/lib/src/finite_state_machine.dart index e5a42ecda..1582cbe04 100644 --- a/lib/src/finite_state_machine.dart +++ b/lib/src/finite_state_machine.dart @@ -7,6 +7,12 @@ // 2022 April 22 // Author: Shubham Kumar +//TODO: PR tickets: +// https://github.com/intel/rohd/issues/409 +// https://github.com/intel/rohd/issues/372 +// https://github.com/intel/rohd/issues/406 +// https://github.com/intel/rohd/issues/400 + import 'dart:io'; import 'dart:math'; @@ -73,22 +79,20 @@ class FiniteStateMachine { /// 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. + //TODO: async reset (#406) + + /// 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(this.clk, 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++; @@ -110,8 +114,10 @@ class FiniteStateMachine { _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, @@ -178,19 +184,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/test/fsm_test.dart b/test/fsm_test.dart index cb9b28f4e..3439cd59c 100644 --- a/test/fsm_test.dart +++ b/test/fsm_test.dart @@ -33,9 +33,12 @@ 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, ]), @@ -48,24 +51,30 @@ class TestModule extends Module { 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 Direction { + noTraffic(0), + northTraffic(1), + eastTraffic(2), + both(3); + + final int value; + + const Direction(this.value); static Logic isEastActive(Logic dir) => - dir.eq(Direction.eastTraffic()) | dir.eq(Direction.both()); + dir.eq(Direction.eastTraffic.value) | dir.eq(Direction.both.value); static Logic isNorthActive(Logic dir) => - dir.eq(Direction.northTraffic()) | dir.eq(Direction.both()); + dir.eq(Direction.northTraffic.value) | dir.eq(Direction.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 { @@ -76,38 +85,45 @@ class TrafficTestModule extends Module { final clk = SimpleClockGenerator(10).clk; reset = addInput('reset', reset); - // var eastActive = traffic[1]; - // traffic.eq(Direction.eastTraffic()) | traffic.eq(Direction.both()); - // var northActive = traffic[0]; - // traffic.eq(Direction.northTraffic()) | traffic.eq(Direction.both()); - - var states = [ - State(LightStates.northFlowing, events: { - ~Direction.isEastActive(traffic): LightStates.northFlowing, - Direction.isEastActive(traffic): LightStates.northSlowing, - }, actions: [ - northLight < LightColor.green(), - eastLight < LightColor.red(), - ]), - State(LightStates.northSlowing, events: { - Const(1): LightStates.eastFlowing, - }, actions: [ - northLight < LightColor.yellow(), - eastLight < LightColor.red(), - ]), - State(LightStates.eastFlowing, events: { - ~Direction.isNorthActive(traffic): LightStates.eastFlowing, - Direction.isNorthActive(traffic): LightStates.eastSlowing, - }, actions: [ - northLight < LightColor.red(), - eastLight < LightColor.green(), - ]), - State(LightStates.eastSlowing, events: { - Const(1): LightStates.northFlowing, - }, actions: [ - northLight < LightColor.red(), - eastLight < LightColor.yellow(), - ]), + final states = >[ + State( + LightStates.northFlowing, + events: { + Direction.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: { + Direction.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, + ], + ), ]; FiniteStateMachine( @@ -132,6 +148,15 @@ 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( @@ -194,18 +219,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': Direction.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); From 33f249c11ce4a0f35dbf14b6a488eab5418150d8 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 19 Sep 2023 04:32:29 -0700 Subject: [PATCH 5/6] Add multi-trigger support to abstractions for async reset, fix #406 --- lib/src/finite_state_machine.dart | 28 +++++++++++++---- lib/src/modules/conditional.dart | 4 +++ lib/src/modules/pipeline.dart | 50 +++++++++++++++++++++++++++---- 3 files changed, 70 insertions(+), 12 deletions(-) diff --git a/lib/src/finite_state_machine.dart b/lib/src/finite_state_machine.dart index 1582cbe04..32ead8261 100644 --- a/lib/src/finite_state_machine.dart +++ b/lib/src/finite_state_machine.dart @@ -52,8 +52,15 @@ class FiniteStateMachine { return _stateValueLookup[_stateLookup[id]]; } - /// The clock signal to the FSM. - final Logic clk; + /// 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; @@ -79,12 +86,21 @@ class FiniteStateMachine { /// Width of the state. final int _stateWidth; - //TODO: async reset (#406) - /// 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(this.clk, this.reset, this.resetState, this._states) + 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)), @@ -137,7 +153,7 @@ class FiniteStateMachine { ]) ]); - Sequential(clk, reset: reset, resetValues: { + Sequential.multi(_clks, reset: reset, resetValues: { currentState: _stateValueLookup[_stateLookup[resetState]] }, [ currentState < nextState, diff --git a/lib/src/modules/conditional.dart b/lib/src/modules/conditional.dart index 3d8f15710..b11b3487a 100644 --- a/lib/src/modules/conditional.dart +++ b/lib/src/modules/conditional.dart @@ -405,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/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( From 168902b8ff999659a660d5f45328ca8da584df2a Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Tue, 19 Sep 2023 05:06:50 -0700 Subject: [PATCH 6/6] Update documentation for FSMs, fix #400 --- doc/user_guide/_docs/A15-fsm.md | 157 ++++++++++++++++++++---------- lib/src/finite_state_machine.dart | 6 -- test/fsm_test.dart | 41 ++++---- 3 files changed, 129 insertions(+), 75 deletions(-) diff --git a/doc/user_guide/_docs/A15-fsm.md b/doc/user_guide/_docs/A15-fsm.md index c2def3433..ecba06b84 100644 --- a/doc/user_guide/_docs/A15-fsm.md +++ b/doc/user_guide/_docs/A15-fsm.md @@ -2,62 +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 --- -# TODO: need to update all this doc! +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 -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 [`FiniteStateMachine`](https://intel.github.io/rohd/rohd/FiniteStateMachine-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/FiniteStateMachine/generateDiagram.html). +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 -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(), - ]), - ]; - FiniteStateMachine(clk, reset, LightStates.northFlowing, states); - } +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 +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/lib/src/finite_state_machine.dart b/lib/src/finite_state_machine.dart index 32ead8261..c090e1477 100644 --- a/lib/src/finite_state_machine.dart +++ b/lib/src/finite_state_machine.dart @@ -7,12 +7,6 @@ // 2022 April 22 // Author: Shubham Kumar -//TODO: PR tickets: -// https://github.com/intel/rohd/issues/409 -// https://github.com/intel/rohd/issues/372 -// https://github.com/intel/rohd/issues/406 -// https://github.com/intel/rohd/issues/400 - import 'dart:io'; import 'dart:math'; diff --git a/test/fsm_test.dart b/test/fsm_test.dart index 3439cd59c..cac200323 100644 --- a/test/fsm_test.dart +++ b/test/fsm_test.dart @@ -51,7 +51,7 @@ class TestModule extends Module { enum LightStates { northFlowing, northSlowing, eastFlowing, eastSlowing } -enum Direction { +enum TrafficPresence { noTraffic(0), northTraffic(1), eastTraffic(2), @@ -59,12 +59,14 @@ enum Direction { final int value; - const Direction(this.value); + const TrafficPresence(this.value); static Logic isEastActive(Logic dir) => - dir.eq(Direction.eastTraffic.value) | dir.eq(Direction.both.value); + dir.eq(TrafficPresence.eastTraffic.value) | + dir.eq(TrafficPresence.both.value); static Logic isNorthActive(Logic dir) => - dir.eq(Direction.northTraffic.value) | dir.eq(Direction.both.value); + dir.eq(TrafficPresence.northTraffic.value) | + dir.eq(TrafficPresence.both.value); } enum LightColor { @@ -80,22 +82,20 @@ enum LightColor { 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: { - Direction.isEastActive(traffic): LightStates.northSlowing, - }, - actions: [ - northLight < LightColor.green.value, - eastLight < LightColor.red.value, - ], - ), + State(LightStates.northFlowing, events: { + TrafficPresence.isEastActive(traffic): LightStates.northSlowing, + }, actions: [ + northLight < LightColor.green.value, + eastLight < LightColor.red.value, + ]), State( LightStates.northSlowing, events: {}, @@ -108,7 +108,7 @@ class TrafficTestModule extends Module { State( LightStates.eastFlowing, events: { - Direction.isNorthActive(traffic): LightStates.eastSlowing, + TrafficPresence.isNorthActive(traffic): LightStates.eastSlowing, }, actions: [ northLight < LightColor.red.value, @@ -127,8 +127,11 @@ class TrafficTestModule extends Module { ]; FiniteStateMachine( - clk, reset, LightStates.northFlowing, states) - .generateDiagram(outputPath: _trafficFSMPath); + clk, + reset, + LightStates.northFlowing, + states, + ).generateDiagram(outputPath: _trafficFSMPath); } } @@ -223,7 +226,7 @@ void main() { 'eastLight': LightColor.red.value }), Vector({}, {}), - Vector({'traffic': Direction.eastTraffic.value}, {}), + Vector({'traffic': TrafficPresence.eastTraffic.value}, {}), Vector({}, { 'northLight': LightColor.yellow.value, 'eastLight': LightColor.red.value