一.四段式状态机
第一段:同步时序的 always 模块,格式化描述次态迁移到现态寄存器。
verilog
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
变量释义
state_c:current state,现态(当前状态寄存器)
state_n:next state,次态(即将跳转的目标状态)
IDLE:空闲初始状态,复位默认进入该待命状态
第二段:组合逻辑的 always 模块,描述状态转移条件,只规定方向不进行电平信号条件判断。
verilog
always@(*)begin
case(state_c)
IDLE:begin
if(idle2s1_start)begin
state_n = S1;
end
else begin
state_n = state_c;
end
end
S1:begin
if(s12s2_start)begin
state_n = S2;
end
else begin
state_n = state_c;
end
end
S2:begin
if(s22id1_start)begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
end
default:begin
state_n = IDLE;
end
endcase
end
功能说明
状态机第二段(次态判断组合逻辑)
敏感列表 @(*) 属于组合逻辑,输入为当前状态 state_c 和跳转触发信号;
根据现态 state_c + 跳转条件,计算出下一个状态 state_n;
跳转逻辑:
第三段:定义状态转移条件。
verilog
assign idle2s1_start = state_c==IDLE && ;
assign s12s2_start = state_c==S1 && ;
assign s22idl_start = state_c==S2 && ;
语法:当前状态 + 外部触发信号共同构成跳转使能。
示例补全参考:可以加括号更容易理清不易出错
verilog
assign idle2s1_start = (state_c == IDLE) && trigger_en;
不加括号一样,二者等价
verilog
assign idle2s1_start = state_c == IDLE && trigger_en;
第四段:设计输出。
规范要求一个 always 设计一个信号,因此有多少个输出信号,就有多少个 always。
verilog
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
out1 <=1'b0;
end
else if(state_c==S1)begin
out1 <=1'b1;
end
else begin
out1 <=1'b0;
end
end
一个 always 块只赋值唯一一个寄存器信号(本例只操作 out1),多个输出就要分开多个独立 always,防止多驱动、逻辑混乱。
逻辑功能:复位时 out1 拉低;状态机处于 S1 时输出高电平,其余状态输出低电平。
规范化
状态机第二段,要求所有判断条件都用信号,严禁同时做判断。
至于状态转移条件,应该在第三段来定义。
状态机的划分依据
一个好的状态机应包含所有状态
功能单一原则:每一段只干一件事,各司其职;
组合与时序分离:寄存器动作、判断运算、条件生成、输出互相拆开;
便于调试维护:改跳转、改输出、改初始状态互不干扰;
规避代码隐患:防止锁存器、多驱动、复杂优先级歧义;
符合团队编码规范,可读性强,别人接手容易看懂。
状态对齐对象
状态机对齐din信号,然后产生dout信号



