A BSV library providing a way to easily build state machines, similarly to the Stmt
BSV sub-language.
Here is a BSV code sample showing how to define Recipe:
import Recipe :: *;
#include "RecipeMacros.h"
import FIFO :: *;
module top ();
PulseWire done <- mkPulseWire;
FIFO#(Bit#(0)) delay <- mkFIFO1;
Recipe r = FastSeq
$display("%0t -- A", $time),
$display("%0t -- B", $time),
action
$display("%0t -- C (delay.enq)", $time);
delay.enq(?);
endaction,
$display("%0t -- D", $time),
$display("%0t -- E", $time),
action
$display("%0t -- F (delay.deq)", $time);
delay.deq;
endaction,
$display("%0t -- G", $time),
$display("%0t -- H", $time),
done.send
End;
RecipeFSM m <- mkRecipeFSM(r);
// Start runing the recipe
rule run;
$display("starting at time %0t", $time);
$display("------------------------------------------");
m.trigger;
endrule
// On the recipe's last cyle, terminate simulation
rule endSim (done);
$display("------------------------------------------");
$display("finishing at time %0t", $time);
$finish(0);
endrule
endmodule
The expected output of the simulator generated when building this code is:
starting at time 10
------------------------------------------
10 -- A
10 -- B
10 -- C (delay.enq)
10 -- D
10 -- E
20 -- F (delay.deq)
20 -- G
20 -- H
------------------------------------------
finishing at time 20
The library sources are contained in Recipe.bsv. On top of the standard BSV libraries, Recipe relies on the BlueBasics library, which is a git submodule of this git repo. It must be checked out before building Recipe, by running the following:
$ git submodule update --init --recursive
Some Recipe examples are provided in the examples directory. On a system with a working BSV setup, the examples can be built by typing make
. Each example can be executed by running the generated script (simExample-*
) in the output
directory.
Recipe
have the added benefit over the standard BSV Stmt
type that termination can be detected with no latency, and also allow the user to access any desired internal signal in [Recipe.bsv](Recipe.bsv). Some additional more advanced Recipe constructors can be easily created, such as rOneMatch
or rMutExGroup
…
The Recipe
type is defined as a BSV union tagged:
typedef union tagged {
// union tagged constructors...
} Recipe;
The BSV type constructors can be found in Recipe.bsv, but they are not meant to be directly used and are therefore not exported by the package. Instead, a Recipe
is built using a combination of recipe constructors (described in the Recipe constructors section).
To build a state machine from a Recipe
, the library defines a mkRecipeFSM
module with the following type signature:
module [Module] mkRecipeFSM#(Recipe r) (RecipeFSM);
A Recipe
can be compiled using the mkRecipeFSM
module to get a RecipeFSM
interface as follows:
Recipe r;
// create Recipe ...
RecipeFSM m <- mkRecipeFSM(r);
The RecipeFSM
interface is defined as follows:
interface RecipeFSM;
method Bool canTrigger;
method Action trigger;
endinterface
As their names suggest,
-
the
canTrigger
method returnsTrue
if the machine can be started -
the
trigger
method starts the machine
Additionally, the library provides the mkRecipeFSMRules
module with a type signature as follows:
module [Module] mkRecipeFSMRules#(Recipe r) (Tuple2#(Rules, RecipeFSM));
As opposed to the mkRecipeFSM
module, the mkRecipeFSMRules
module will return the compiled Rules
together with the RecipeFSM
interface rather than adding the rules to the current module, giving the
user the ability to schedule the generated rules according to their needs.
The compileMutuallyExclusive
module compiles a list of recipes, adds the rules for each recipe such
that they are mutually exclusive, and returns a list of interfaces to the generated machines.
Mutual exclusivity of generated rule can be further controlled when declaring a Recipe
. See the
rMutExGroup
recipe constructor for more details.
To help define `Recipe`s in a concise and readable way, macros are provided that simplify the calls to common recipe constructors:
Seq
$display("A"),
$display("B"),
$display("C")
End
is equivalent to
rSeq(rBlock(
$display("A"),
$display("B"),
$display("C")
))
Similarly, macros are defined for Par
, FastSeq
, Pipe
, If
and Else
, When
and While
.
Those are defined in RecipeMacros.h, which must be included in your BSV sources
as follows:
#include "RecipeMacros.h"
Additionally, the call to the BSV compiler should have the -cpp
flag together with -Xcpp -Ipath/to/Recipe
where path/to/Recipe
is the path to the folder containing RecipeMacros.h.
Alternatively, the RecipeMacros.inc file defines the `Seq
, `Par
, `Pipe
,
`FastSeq
, `If
, `Else
, `When
and `While
verilog preprocessor macros, and do not
require any additional flag on the BSC compiler invocation.
-
The
rAct
recipe constructor simply wraps anAction
function Recipe rAct(Action a);
-
The
rActV
recipe constructor simply wraps anActionValue#(Bool)
function Recipe rActV(ActionValue#(Bool) a);
-
The
rSeq
recipe constructor creates a sequence ofRecipe`s executed in order, with one cycle of latency between each of them. The constructor expects a `List#(Recipe)
and should be used to wrap a call torBlock
which allows for arbitrary manyRecipe
to be placed in a list.
function Recipe rSeq(List#(Recipe) rs);
-
The
rPar
recipe constructor creates a group ofRecipe`s executed in parallel. The constructor expects a `List#(Recipe)
and should be used to wrap a call torBlock
which allows for arbitrary manyRecipe
to be placed in a list.
function Recipe rPar(List#(Recipe) rs);
-
The
rPipe
recipe constructor is similar to therSeq
recipe constructor, but allows for pipelined execution of the different elements of the sequence ofRecipe
, whererSeq
blocks until the sequence is fully traversed. The constructor expects aList#(Recipe)
and should be used to wrap a call torBlock
which allows for arbitrary manyRecipe
to be placed in a list.
function Recipe rPipe(List#(Recipe) rs);
-
The
rFastSeq
recipe constructor creates a sequence ofRecipe`s executed in order with no latency when possible. The constructor expects a `List#(Recipe)
and should be used to wrap a call torBlock
which allows for arbitrary manyRecipe
to be placed in a list.
function Recipe rFastSeq(List#(Recipe) rs);
-
The
rIfElse
recipe constructor takes aBool
condition and twoRecipe`s. It executes the first `Recipe
if the condition isTrue
and the secondRecipe
if the condition isFalse
.
function Recipe rIfElse(Bool c, Recipe r0, Recipe r1);
-
The
rWhen
recipe constructor takes aBool
condition and aRecipe`s. It executes the `Recipe
if the condition isTrue
.
function Recipe rWhen(Bool c, Recipe r);
-
The
rWhile
recipe constructor takes aBool
condition together with aRecipe
. It executes theRecipe
as long as the condition isTrue
.
function Recipe rWhile(Bool c, Recipe r);
-
The
rMutExGroup
recipe constructor takes a mutual exclusion group name as aString
and aRecipe
to be added to that mutual exclusion group. When compiling, `Recipe`s belonging to a mutual exclusion group generate rules that will be scheduled as mutually exclusive to those belonging to a different mutual exclusion group. `Recipe`s that are not tagged with any mutual exclusion group are scheduled normally.
function Recipe rMutExGroup(String n, Recipe r);
-
The
rAllGuard
recipe constructor takes a list of guardsList#(Bool)
, and a list of recipesList#(Recipe)
. It executes all recipes with a True guard in parallel.
function Recipe rAllGuard(List#(Bool) gs, List#(Recipe) rs)
-
The
rOneMatch
recipe constructor takes a list of guardsList#(Bool)
, a list of recipesList#(Recipe)
, and an extra "fall-through" recipe. The first recipe in the list order with a corresponding guard set to True is executed. If no recipe matches, the "fall-through" recipe is executed.
function Recipe rOneMatch(List#(Bool) gs, List#(Recipe) rs, Recipe r)