FPGA学习笔记-三段式状态机
类似多分支语句,更自由一些可以自定义多个分支的跳转条件和每个分支下的行为,每一个分支叫做一个状态。
状态机四要素:现态,次态,条件,动作。
优:方便纠错,逻辑清晰。 缺:费时序逻辑资源。
通用格式
由三个always语句构成,一般第一段和第三段为时序逻辑,第二段为组合逻辑。
第一段为定义当前状态到下一个状态的转换。(现态与次态的状态切换)
第二段描述状态转换的条件。(根据现态和条件确定次态)
第三段表现当前状态下的行为。(根据现态和条件确定每个状态的动作)
状态转换图
写状态机之前最好提前决定好需要多少个状态,每个状态之间切换的条件是什么理清思路再写。

例如让4个led灯每隔1s切换一个亮,就需要一个空闲态加上4个led灯分别亮的4个状态一共5个状态。(并不是一定要按照顺序切换状态也不是一定要有空闲态,根据实际情况来)
状态编码
二进制编码:节省位宽。(十进制的0,1,2,3)
例如:2'd0 2'd1 2'd2 2'd3
独热码:简单,译码快。(每个状态一个高电平)
例如:4'b0001 4'b0010 4'b0100 4'b1000
格雷码:减少竞争冒险的概率。(相邻状态之间仅有一位变化)
例如:3'b000 3'b001 3'b011 3'b111 3'b110 3'b100 3'b101
竞争冒险
竞争:在同一个逻辑门中,有两路相反的信号到达目标门的时间有先有后导致在时间上产生的差异现象。
冒险:由竞争产生的尖峰脉冲的现象。
解决办法:并联滤波电容(通交流,阻直流) 接入选通脉冲(使能信号) 修改逻辑设计(增加冗余项)三种办法。

//状态机实现流水灯
`timescale 1ns / 1ps
module biji_1(
input sysclk,
input rst_n,
output reg [3:0]led
);
localparam idle=3'd0;//状态编码
localparam s1=3'd1; //状态编码
localparam s2=3'd2; //状态编码
localparam s3=3'd3; //状态编码
localparam s4=3'd4; //状态编码
reg [3:0] cur_state;//当前状态
reg [3:0] next_state;//下一个状态
localparam delay=50_000_000;//1s
reg [31:0] cnt;//计时器
always@(posedge sysclk)//1s计时器
if (!rst_n)
cnt<=0;
else if (cnt == delay-1)
cnt<=0;
else
cnt<=cnt+1;
always@(posedge sysclk)//一段
if (!rst_n)
cur_state<=idle;
else
cur_state<=next_state;
always@(*)//二段
if (!rst_n)
next_state=idle;
else
case(cur_state)
idle:begin
if (cnt == delay-1)//条件
next_state=s1;
else
next_state=idle;
end
s1:begin
if (cnt == delay-1)
next_state=s2;
else
next_state=s1;
end
s2:begin
if (cnt == delay-1)
next_state=s3;
else
next_state=s2;
end
s3:begin
if (cnt == delay-1)
next_state=s4;
else
next_state=s3;
end
s4:begin
if (cnt == delay-1)
next_state=s1;
else
next_state=s4;
end
default next_state=idle;
endcase
always@(posedge sysclk)
if (!rst_n)
led<=4'b0000;
else
case(cur_state)
idle:led<=4'b0000;
s1:led<=4'b1000;
s2:led<=4'b0100;
s3:led<=4'b0010;
s4:led<=4'b0001;
default led<=4'b0000;
endcase
endmodule