**分类:**摩尔型(Moore)有限状态机,输出只由当前状态确定。米利型(Mealy)有限状态机,输出不止于当前状态有关还与其输入有关。
独热码用的寄存器多,但译码简单;格雷码用于异步多时钟多bit的转换;二进制码用的寄存器少,但译码复杂。一般在CPLD中,由于能提供较多的逻辑资源推荐使用独热码,而在FPGA中提供较多的时序逻辑而推荐使用二进制码。
**一段式:**整个状态机写在一个always模块里面,在该模块中既描述状态转移,又描述状态的输入和输出。
**两段式:**用两个always模块来描述状态机,其中一个always模块采用同步时序描述状态转移,另一个模块采用组合逻辑判断状态转移条件,描述状态转移规律及其输出。
**三段式:**在两个always模块描述方法基础上,使用三个always模块,一个always模块采用同步时序描述状态转移,一个always采用组合逻辑判断状态转移条件,描述状态转移规律,另一个always模块描述状态输出(可以用组合电路输出,也可以用时序电路输出)。
**注意事项:**①二段式状态机要在后面加一个触发器消除组合逻辑对输出产生的毛刺的影响。②编写状态机需要穷举所有状态,或者使用default,避免产生不必要的锁存器。③在设计中要有异步或者同步复位来确保状态机始终有个初始态。
需要先设置好vivado与modelsim的联调,具体参考其他博客,这段代码在打开modelsim后,再进行仿真时,需要删掉,否则会出现error。
实现字符串“hello”的检测。
代码在一个.v文件中编写,可分段调试。
`timescale 1ns / 1ns
module fsm_hello(
input sys_clk,
input reset_n,
input [7:0]data_in,
input data_in_valid,
output reg check_ok
);
localparam
CHECK_h = 5'b00_001,
CHECK_e = 5'b00_010,
CHECK_l1 = 5'b00_100,
CHECK_l2 = 5'b01_000,
CHECK_o = 5'b10_000;
reg [4:0]state;//一段式状态机
reg [4:0]pre_state;//两段式和三段式状态机
reg [4:0]next_state;
always@(posedge sys_clk or negedge reset_n)
if(!reset_n)begin
check_ok <= 1'b0;
state <= CHECK_h;
end
else begin
case(state)
CHECK_h:begin
check_ok <= 1'b0;
if(data_in_valid && data_in == "h")
state <= CHECK_e;
else
state <= CHECK_h;
end
CHECK_e:begin
check_ok <= 1'b0;
if(data_in_valid && data_in == "e")
state <= CHECK_l1;
else if(data_in_valid && data_in == "h")
state <= CHECK_e;
else if(data_in_valid)
state <= CHECK_h;
else
state <= CHECK_e;
end
CHECK_l1:begin
check_ok <= 1'b0;
if(data_in_valid && data_in == "l1")
state <= CHECK_l2;
else if(data_in_valid && data_in == "h")
state <= CHECK_e;
else if(data_in_valid)
state <= CHECK_h;
else
state <= CHECK_l1;
end
CHECK_l2:begin
check_ok <= 1'b0;
if(data_in_valid && data_in == "l2")
state <= CHECK_o;
else if(data_in_valid && data_in == "h")
state <= CHECK_e;
else if(data_in_valid)
state <= CHECK_h;
else
state <= CHECK_l2;
end
CHECK_o:
if(data_in_valid && data_in == "h")
state <= CHECK_e;
else if(data_in_valid)begin
state <= CHECK_h;
if(data_in == "o")
check_ok <= 1'b1;
else
check_ok <= 1'b0;
end
else
state <= CHECK_o;
default:state <= CHECK_h;
endcase
end
//第一个过程,同步时序always块,描述状态转移方程
always@(posedge sys_clk)
if(!reset_n)
pre_state <= CHECK_h;
else
pre_state <= next_state;
//第二个过程,组合逻辑always块,描述激励方程以及输出方程
always@(pre_state or reset_n)begin //电平触发,现存状态为敏感信号
case(pre_state)
CHECK_h:
if(!reset_n)begin
check_ok <= 1'b0;
next_state <= CHECK_h;
end
else if(data_in_valid && data_in == "h")
next_state <= CHECK_e;
else
next_state <= CHECK_h;
CHECK_e:
if(!reset_n)begin
check_ok <= 1'b0;
next_state <= CHECK_h;
end
else if(data_in_valid && data_in == "e")
next_state <= CHECK_l1;
else if(data_in_valid && data_in == "h")
next_state <= CHECK_e;
else if(data_in_valid)
next_state <= CHECK_h;
else
next_state <= CHECK_e;
CHECK_l1:
if(!reset_n)begin
check_ok <= 1'b0;
next_state <= CHECK_h;
end
else if(data_in_valid && data_in == "l1")
next_state <= CHECK_l2;
else if(data_in_valid && data_in == "h")
next_state <= CHECK_e;
else if(data_in_valid)
next_state <= CHECK_h;
else
next_state <= CHECK_l1;
CHECK_l2:
if(!reset_n)begin
check_ok <= 1'b0;
next_state <= CHECK_h;
end
else if(data_in_valid && data_in == "l2")
next_state <= CHECK_o;
else if(data_in_valid && data_in == "h")
next_state <= CHECK_e;
else if(data_in_valid)
next_state <= CHECK_h;
else
next_state <= CHECK_l2;
CHECK_o:
if(!reset_n)begin
check_ok <= 1'b0;
next_state <= CHECK_h;
end
else if(data_in_valid && data_in == "h")
next_state <= CHECK_e;
else if(data_in_valid)begin
next_state <= CHECK_h;
if(data_in == "o")
check_ok <= 1'b1;
else
check_ok <= 1'b0;
end
else
next_state <= CHECK_o;
default:next_state <= CHECK_h;
endcase
end
//第一个过程,同步时序always块,描述状态转移方程
always@(posedge sys_clk)
if(!reset_n)
pre_state <= CHECK_h;
else
pre_state <= next_state;
//第二个过程,组合逻辑always块,描述激励方程
always@(pre_state or reset_n)begin //电平触发,现存状态为敏感信号
case(pre_state)
CHECK_h:
if(!reset_n)
next_state <= CHECK_h;
else if(data_in_valid && data_in == "h")
next_state <= CHECK_e;
else
next_state <= CHECK_h;
CHECK_e:
if(!reset_n)
next_state <= CHECK_h;
else if(data_in_valid && data_in == "e")
next_state <= CHECK_l1;
else if(data_in_valid && data_in == "h")
next_state <= CHECK_e;
else if(data_in_valid)
next_state <= CHECK_h;
else
next_state <= CHECK_e;
CHECK_l1:
if(!reset_n)
next_state <= CHECK_h;
else if(data_in_valid && data_in == "l1")
next_state <= CHECK_l2;
else if(data_in_valid && data_in == "h")
next_state <= CHECK_e;
else if(data_in_valid)
next_state <= CHECK_h;
else
next_state <= CHECK_l1;
CHECK_l2:
if(!reset_n)
next_state <= CHECK_h;
else if(data_in_valid && data_in == "l2")
next_state <= CHECK_o;
else if(data_in_valid && data_in == "h")
next_state <= CHECK_e;
else if(data_in_valid)
next_state <= CHECK_h;
else
next_state <= CHECK_l2;
CHECK_o:
if(!reset_n)
next_state <= CHECK_h;
else if(data_in_valid && data_in == "h")
next_state <= CHECK_e;
else if(data_in_valid)
next_state <= CHECK_h;
else
next_state <= CHECK_o;
default:next_state <= CHECK_h;
endcase
end
//第三个always块,描述输出方程
always@(pre_state or reset_n)begin //电平触发,现存状态为敏感信号
case(pre_state)
CHECK_h:
if(!reset_n)
check_ok <= 1'b0;
CHECK_e:
if(!reset_n)
check_ok <= 1'b0;
CHECK_l1:
if(!reset_n)
check_ok <= 1'b0;
CHECK_l2:
if(!reset_n)
check_ok <= 1'b0;
CHECK_o:
if(!reset_n)
check_ok <= 1'b0;
else if(data_in_valid && data_in == "h")
;
else if(data_in_valid)begin
if(data_in == "o")
check_ok <= 1'b1;
else
check_ok <= 1'b0;
end
else
;
default:;
endcase
end
本篇主要参考小梅哥《小梅哥Xilinx FPGA自学教程V1.3》以及夏老的《Verilog数字系统设计教程(第3版)》,小梅哥的基础教程非常适合FPGA小白入门,夏老的教程更是学习Verilog必读的一本书,本人能力有限,博客主要粘贴自己平时做的笔记,难免有错误,希望大家一起讨论学习,谢谢!