状态机(State machine)
一、简介(Introduction)
在SpinalHDL中用户可以像在VHDL/Verilog中一样定义状态机, 通过使用计数和Swith/Case语句。但是在SpinalHDL中也可以使用专用的语法。
下方的状态机可以被如下代码实现:
格式A:
import spinal.lib.fsm._
class TopLevel extends Component {
val io = new Bundle {
val result = out Bool()
}
val fsm = new StateMachine {
val counter = Reg(UInt(8 bits)) init (0)
io.result := False
val stateA : State = new State with EntryPoint {
whenIsActive(goto(stateB))
}
val stateB : State = new State {
onEntry(counter := 0)
whenIsActive {
counter := counter + 1
when(counter === 4) {
goto(stateC)
}
}
onExit(io.result := True)
}
val stateC : State = new State {
whenIsActive(goto(stateA))
}
}
}
格式B:
import spinal.lib.fsm._
class TopLevel extends Component {
val io = new Bundle {
val result = out Bool()
}
val fsm = new StateMachine{
val stateA = new State with EntryPoint
val stateB = new State
val stateC = new State
val counter = Reg(UInt(8 bits)) init (0)
io.result := False
stateA
.whenIsActive(goto(stateB))
stateB
.onEntry(counter := 0)
.whenIsActive {
counter := counter + 1
when(counter === 4) {
goto(stateC)
}
}
.onExit(io.result := True)
stateC
.whenIsActive(goto(stateA))
}
}
Verilog:
module MyTopLevel (
input io_cond0,
input io_cond1,
output io_flag,
output [7:0] io_state,
output reg io_result,
input clk,
input reset
);
localparam fsm_enumDef_BOOT = 2'd0;
localparam fsm_enumDef_stateA = 2'd1;
localparam fsm_enumDef_stateB = 2'd2;
localparam fsm_enumDef_stateC = 2'd3;
wire fsm_wantExit;
reg fsm_wantStart;
wire fsm_wantKill;
reg [7:0] fsm_counter;
reg [7:0] counter;
reg [1:0] fsm_stateReg;
reg [1:0] fsm_stateNext;
wire _zz_when_StateMachine_l233;
wire _zz_when_StateMachine_l233_1;
wire when_MyTopLevel_l54;
wire when_StateMachine_l233;
wire when_StateMachine_l249;
`ifndef SYNTHESIS
reg [47:0] fsm_stateReg_string;
reg [47:0] fsm_stateNext_string;
`endif
`ifndef SYNTHESIS
always @(*) begin
case(fsm_stateReg)
fsm_enumDef_BOOT : fsm_stateReg_string = "BOOT ";
fsm_enumDef_stateA : fsm_stateReg_string = "stateA";
fsm_enumDef_stateB : fsm_stateReg_string = "stateB";
fsm_enumDef_stateC : fsm_stateReg_string = "stateC";
default : fsm_stateReg_string = "??????";
endcase
end
always @(*) begin
case(fsm_stateNext)
fsm_enumDef_BOOT : fsm_stateNext_string = "BOOT ";
fsm_enumDef_stateA : fsm_stateNext_string = "stateA";
fsm_enumDef_stateB : fsm_stateNext_string = "stateB";
fsm_enumDef_stateC : fsm_stateNext_string = "stateC";
default : fsm_stateNext_string = "??????";
endcase
end
`endif
assign fsm_wantExit = 1'b0;
always @(*) begin
fsm_wantStart = 1'b0;
case(fsm_stateReg)
fsm_enumDef_stateA : begin
end
fsm_enumDef_stateB : begin
end
fsm_enumDef_stateC : begin
end
default : begin
fsm_wantStart = 1'b1;
end
endcase
end
assign fsm_wantKill = 1'b0;
always @(*) begin
io_result = 1'b0;
if(when_StateMachine_l233) begin
io_result = 1'b1;
end
end
assign _zz_when_StateMachine_l233 = (fsm_stateReg == fsm_enumDef_stateB);
assign _zz_when_StateMachine_l233_1 = (fsm_stateNext == fsm_enumDef_stateB);
always @(*) begin
fsm_stateNext = fsm_stateReg;
case(fsm_stateReg)
fsm_enumDef_stateA : begin
fsm_stateNext = fsm_enumDef_stateB;
end
fsm_enumDef_stateB : begin
if(when_MyTopLevel_l54) begin
fsm_stateNext = fsm_enumDef_stateC;
end
end
fsm_enumDef_stateC : begin
fsm_stateNext = fsm_enumDef_stateA;
end
default : begin
end
endcase
if(fsm_wantStart) begin
fsm_stateNext = fsm_enumDef_stateA;
end
if(fsm_wantKill) begin
fsm_stateNext = fsm_enumDef_BOOT;
end
end
assign when_MyTopLevel_l54 = (fsm_counter == 8'h04);
assign when_StateMachine_l233 = (_zz_when_StateMachine_l233 && (! _zz_when_StateMachine_l233_1));
assign when_StateMachine_l249 = ((! _zz_when_StateMachine_l233) && _zz_when_StateMachine_l233_1);
always @(posedge clk or posedge reset) begin
if(reset) begin
fsm_counter <= 8'h0;
fsm_stateReg <= fsm_enumDef_BOOT;
end else begin
fsm_stateReg <= fsm_stateNext;
case(fsm_stateReg)
fsm_enumDef_stateA : begin
end
fsm_enumDef_stateB : begin
fsm_counter <= (fsm_counter + 8'h01);
end
fsm_enumDef_stateC : begin
end
default : begin
end
endcase
if(when_StateMachine_l249) begin
fsm_counter <= 8'h0; //这会产生有优先级的选择器,该情况优先级更高
end
end
end
endmodule
二、状态机(StateMachine)
StateMachine
是基本类(class), 它管理FSM的逻辑。
val myFsm = new StateMachine {
// 定义状态
}
StateMachine
还提供了一些访问器(accessors):
命名 | 返回类型 | 描述 |
---|---|---|
isActive(state) | Bool | 当状态机处于所给定的状态时返回true |
isEntering(state) | Bool | 当状态机要进入所给定的状态时返回true |
入口点(Entry point)
通过扩展EntryPoint特征, 可以将状态定义为状态机的入口点:
val stateA = new State with EntryPoint
或者通过使用
val stateA = new State with EntryPoint
:val stateA = new State setEntry(stateA)
转换(Transitions)
转换由
goto(nextState)
表示, 它使得状态机在下一个周期转换到nextState
。exit()
将状态机在下一个周期转换为boot状态(boot state)(或者, 在StateFsm
中, 退出当前嵌套状态机)。
三、状态(States)
多种状态可以被使用:
State
(最基本的)StateDelay
StateFsm
StateParallelFsm
它们每个都提供以下函数来定义与它们相关的逻辑:
命名 | 描述 |
---|---|
state.onEntry {yourStatements } |
yourStatements 在状态机未处于state 时应用, 并将在下一个周期处于state |
state.onExit {yourStatements } |
yourStatements 在状态机处于state 时应用, 并将在下一个周期处于其他状态 |
state.whenIsActive {yourStatements } |
yourStatements 在状态机处于state 时应用 |
state.whenIsNext {yourStatements } |
yourStatements 在状态机将在下一个周期处于state 时执行(即使已经是state 状态) |
``state.隐含在一个
new State`块中:
val stateB : State = new State {
onEntry(counter := 0)
whenIsActive {
counter := counter + 1
when(counter === 4) {
goto(stateC)
}
}
onExit(io.result := True)
}
状态延迟(StateDelay)
StateDelay
允许创建一个状态, 该状态在whenCompleted{…}
语句执行之前等待固定数量的周期。首选的使用方法是:val stateG : State = new StateDelay(cyclesCount=40) { whenCompleted { goto(stateH) } }
同样可以用一行代码来写:
val stateG : State = new StateDelay(40) { whenCompleted(goto(stateH)) }
状态中的有限状态机(StateFsm)
StateFsm
允许描述包含嵌套状态机的状态。当嵌套状态机完成(退出)时,whenCompleted{…}
执行。例如:
// 内部的FSM按如下定义 val stateC = new StateFsm(fsm=internalFsm()) { whenCompleted { goto(stateD) } } def internalFsm() = new StateMachine { val counter = Reg(UInt(8 bits)) init (0) val stateA : State = new State with EntryPoint { whenIsActive { goto(stateB) } } val stateB : State = new State { onEntry (counter := 0) whenIsActive { when(counter === 4) { exit() } counter := counter + 1 } } }
在上面的示例中,
exit()
使状态机跳转到boot状态(内部隐藏状态)。这将通知StateFsm
其内部状态机已完成。状态中的并行有限状态机(StateParallelFsm)
StateParallelFsm
允许处理多个嵌套状态机。当所有嵌套状态机完成时,whenCompleted{…}
执行。例如:
val stateD = new StateParallelFsm (internalFsmA(), internalFsmB()) { whenCompleted{ goto(stateE) } }
关于入口状态的注释(Notes about the entry state)
上面定义的进入状态的方式使得在重置复位和第一个时钟采样之间, 状态机处于启动状态。只有在第一次时钟采样之后, 定义的进入状态才有效。这允许正确地进入entry状态(应
onEntry
中的语句), 并允许嵌套状态机。虽然它很有用, 但也可以绕过该特性, 直接让状态机引导(boot)到用户状态。
为此, 使用
makeInstantEntry()
而不是定义一个new State
。这个函数返回boot状态, 在复位后直接激活。注意:该状态的
onEntry
只会在它从另一个状态转换到这个状态时调用, 而不会在启动期间调用。注意:在仿真过程中, boot状态总是命名为
Boot
.例如:
// 状态序列: IDLE, STATE_A, STATE_B, ... val fsm = new StateMachine { // IDLE在仿真中被命名为BOOT val IDLE = makeInstantEntry() val STATE_A, STATE_B, STATE_C = new State IDLE.whenIsActive(goto(STATE_A)) STATE_A.whenIsActive(goto(STATE_B)) STATE_B.whenIsActive(goto(STATE_C)) STATE_C.whenIsActive(goto(STATE_B)) } // 状态序列 : BOOT, IDLE, STATE_A, STATE_B, ... val fsm = new StateMachine { val IDLE, STATE_A, STATE_B, STATE_C = new State setEntry(IDLE) IDLE.whenIsActive(goto(STATE_A)) STATE_A.whenIsActive(goto(STATE_B)) STATE_B.whenIsActive(goto(STATE_C)) STATE_C.whenIsActive(goto(STATE_B)) }