README.adoc
May 18, 2022 · View on GitHub
= Recipe
A BSV library providing a way to easily build state machines, similarly to the Stmt BSV sub-language.
== An example
Here is a BSV code sample showing how to define Recipe:
[source,bsv]
import Recipe :: *; #include "RecipeMacros.h"
import FIFO :: *;
module top ();
PulseWire done <- mkPulseWire; FIFO#(Bit#(0)) delay <- mkFIFO1;
Recipe r = FastSeq time), time), action time); delay.enq(?); endaction, time), time), action time); delay.deq; endaction, time), time), done.send End;
RecipeFSM m <- mkRecipeFSM(r);
// Start runing the recipe rule run; time); $display("------------------------------------------"); m.trigger; endrule
// On the recipe's last cyle, terminate simulation rule endSim (done); display("finishing at time %0t", finish(0); endrule
endmodule
The expected output of the simulator generated when building this code is:
[source,shell]
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
== Getting started
The library sources are contained in https://github.com/CTSRD-CHERI/Recipe/tree/master/Recipe.bsv[Recipe.bsv]. On top of the standard BSV libraries, Recipe relies on the https://github.com/CTSRD-CHERI/BlueBasics.git[BlueBasics] library, which is a git submodule of this git repo. It must be checked out before building Recipe, by running the following:
[source,shell]
$ git submodule update --init --recursive
Some Recipe examples are provided in the https://github.com/CTSRD-CHERI/Recipe/tree/master/examples/[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. Some additional more advanced Recipe constructors can be easily created, such as rOneMatch or rMutExGroup...
== Library overview
The Recipe type is defined as a BSV union tagged:
[source,bsv]
typedef union tagged { // union tagged constructors... } Recipe;
The BSV type constructors can be found in https://github.com/CTSRD-CHERI/Recipe/tree/master/Recipe.bsv[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 <
To build a state machine from a Recipe, the library defines a mkRecipeFSM module with the following type signature:
[source,bsv]
module [Module] mkRecipeFSM#(Recipe r) (RecipeFSM);
A Recipe can be compiled using the mkRecipeFSM module to get a RecipeFSM interface as follows:
[source,bsv]
Recipe r; // create Recipe ... RecipeFSM m <- mkRecipeFSM(r);
The RecipeFSM interface is defined as follows:
[source,bsv]
interface RecipeFSM; method Bool canTrigger; method Action trigger; endinterface
As their names suggest,
- the
canTriggermethod returnsTrueif the machine can be started - the
triggermethod starts the machine
Additionally, the library provides the mkRecipeFSMRules module with a type signature as follows:
[source,bsv]
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.
== A readable Recipe
To help define Recipes in a concise and readable way, macros are provided that simplify the calls to
common recipe constructors:
[source,bsv]
Seq display("B"), $display("C") End
is equivalent to
[source,bsv]
rSeq(rBlock( display("B"), $display("C") ))
Similarly, macros are defined for Par, FastSeq, Pipe, If and Else, When and While.
Those are defined in https://github.com/CTSRD-CHERI/Recipe/tree/master/RecipeMacros.h[RecipeMacros.h], which must be included in your BSV sources
as follows:
[source,bsv]
#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 https://github.com/CTSRD-CHERI/Recipe/tree/master/RecipeMacros.h[RecipeMacros.h].
Alternatively, the https://github.com/CTSRD-CHERI/Recipe/tree/master/RecipeMacros.inc[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.
== Recipe constructors
- The
rActrecipe constructor simply wraps anAction
[source,bsv]
function Recipe rAct(Action a);
- The
rActVrecipe constructor simply wraps anActionValue#(Bool)
[source,bsv]
function Recipe rActV(ActionValue#(Bool) a);
- The
rSeqrecipe constructor creates a sequence ofRecipes executed in order, with one cycle of latency between each of them. The constructor expects aList#(Recipe)and should be used to wrap a call torBlockwhich allows for arbitrary manyRecipeto be placed in a list.
[source,bsv]
function Recipe rSeq(List#(Recipe) rs);
- The
rParrecipe constructor creates a group ofRecipes executed in parallel. The constructor expects aList#(Recipe)and should be used to wrap a call torBlockwhich allows for arbitrary manyRecipeto be placed in a list.
[source,bsv]
function Recipe rPar(List#(Recipe) rs);
- The
rPiperecipe constructor is similar to therSeqrecipe constructor, but allows for pipelined execution of the different elements of the sequence ofRecipe, whererSeqblocks until the sequence is fully traversed. The constructor expects aList#(Recipe)and should be used to wrap a call torBlockwhich allows for arbitrary manyRecipeto be placed in a list.
[source,bsv]
function Recipe rPipe(List#(Recipe) rs);
- The
rFastSeqrecipe constructor creates a sequence ofRecipes executed in order with no latency when possible. The constructor expects aList#(Recipe)and should be used to wrap a call torBlockwhich allows for arbitrary manyRecipeto be placed in a list.
[source,bsv]
function Recipe rFastSeq(List#(Recipe) rs);
- The
rIfElserecipe constructor takes aBoolcondition and twoRecipes. It executes the firstRecipeif the condition isTrueand the secondRecipeif the condition isFalse.
[source,bsv]
function Recipe rIfElse(Bool c, Recipe r0, Recipe r1);
- The
rWhenrecipe constructor takes aBoolcondition and aRecipes. It executes theRecipeif the condition isTrue.
[source,bsv]
function Recipe rWhen(Bool c, Recipe r);
- The
rWhilerecipe constructor takes aBoolcondition together with aRecipe. It executes theRecipeas long as the condition isTrue.
[source,bsv]
function Recipe rWhile(Bool c, Recipe r);
- The
rMutExGrouprecipe constructor takes a mutual exclusion group name as aStringand aRecipeto be added to that mutual exclusion group. When compiling,Recipes belonging to a mutual exclusion group generate rules that will be scheduled as mutually exclusive to those belonging to a different mutual exclusion group.Recipes that are not tagged with any mutual exclusion group are scheduled normally.
[source,bsv]
function Recipe rMutExGroup(String n, Recipe r);
- The
rAllGuardrecipe constructor takes a list of guardsList#(Bool), and a list of recipesList#(Recipe). It executes all recipes with a True guard in parallel.
[source,bsv]
function Recipe rAllGuard(List#(Bool) gs, List#(Recipe) rs)
- The
rOneMatchrecipe 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.