With Bluespec, a state machine can be written by a series of
independent rules, which the compiler will properly order and
synthesize control logic. Alternately, the transitions can be combined
into a function and that function instantiated in one rule, which
highlights the independent and synchronous nature of the rules.
FSM with Multiple Rules
... typedef enum S0, S1, S2, S3 StateType deriving (Eq, Bits); ... Reg#(StateType) curr_state <- mkReg (S0) ; (* fire_when_enabled, no_implicit_conditions *) rule state_S0 (curr_state == S0); curr_state <= S1; endrule (* fire_when_enabled, no_implicit_conditions *) rule state_S1 (curr_state == S1); curr_state <= S2; endrule (* fire_when_enabled, no_implicit_conditions *) rule state_S2 (curr_state == S2); curr_state <= S3; endrule (* fire_when_enabled, no_implicit_conditions *) rule state_S3 (curr_state == S3); curr_state <= S0; endrule ...
Each rule corresponds to one or more state transitions, where the
rule condition is the current state and input condition for transition
to occur. Each rule is also annotated with a fire_when_enabled
attribute. This attribute enables compile time checks to insure that
other conditions (rules or method will not block the rule execution --
basically a check for independence between rules.) The no_implicit_condition
attribute, is a compile-time assertion which insures that there are no
implicit conditions (such as full or empty FIFOs) which would prevent
this rule from firing. Together, these attribute check the designer's
intentions and understanding of the Bluespec model.
FSM with One Rule
Alternately, the FSM may be described in one rule, which gives the full FSM behavior in a case statement.
Reg#(StateType) curr_state <- mkReg (S0) ;
(* fire_when_enabled *) rule rule1_StateChange (True); StateType next_state; case (curr_state) S0 : next_state = S1; S1 : next_state = S2; S2 : next_state = S3; S3 : next_state = S0; default : next_state = S0; endcase curr_state <= next_state; endrule: rule1_StateChange
Another One Rule FSM Example
This FSM example is a minor variation of the above, where a function
describes the state transitions and the rule instantiates the function.
This style has the advantage that the rule bodies, which update state,
and call actions are separate from the function, thus allowing easier
maintenance and reuse.
typedef enum S0, S1, S2, S3 StateType deriving (Eq, Bits); function StateType next_state (StateType curr_state); let myLocalVar; case (curr_state) S0 : myLocalVar = S1; S1 : myLocalVar = S2; S2 : myLocalVar = S3; S3 : myLocalVar = S0; default : myLocalVar = S0; endcase return myLocalVar; endfunction ... rule rule1_StateChange (True); curr_state <= next_state(curr_state); endrule: rule1_StateChange
Discussion
The implementation style for an FSM strongly depends on scope of the
FSM within the design, where the following guidelines should be used:
- If the FSM implements only the control structure, that is, it
provides the next state logic, but does not detail the actions which
occur within a given state, then the FSM should be implemented in one
rule. This is similar to common Verilog styles, and does not take
advantage of Bluespec features.
- If the FSM design includes the actions within each state, then
the recommended style is one rule per transition or state arc. That is,
given two transition, state A to B and state A to C, there should be a
two rules, one per transition. This style has the advantage that
Bluespec can automatically handle implicit conditions of the actions,
thus shortening design, implementation and verification time. For
example, if some actions in the transition from A to B are not ready,
then the rule does not fire, and the state transition will not occur.
Bluespec collects and analyzes these action implicit condition and then
builds logic for correct behavior.
The use of multiple rules per FSM is also recommended for multiple
interacting FSMs, since each rule describes correct behavior allowing
Bluespec to analyze and build the correct concurrent operations of the
machines.
|