三段式代码多,但是有时钟同步,延时少,组合逻辑跟时序逻辑分开并行出错少。
(1)同步状态转移 (2)当前状态判断接下来的状态 (3)动作输出
如果程序复杂可以不止三个always 。always 后常接case case必须有default ,对于FPGA常用 状态数较少,独热码编码 ,或者格雷码
//独热码编码
parameter NO_KEY_PRESSED = 6'b000_001; // 没有按键按下
parameter SCAN_row0 = 6'b000_010; // 扫描第0行
parameter SCAN_row1 = 6'b000_100; // 扫描第1行
parameter SCAN_row2 = 6'b001_000; // 扫描第2行
parameter SCAN_row3 = 6'b010_000; // 扫描第3行
parameter KEY_PRESSED = 6'b100_000; // 有按键按下
reg [5:0] current_state, next_state; // 现态、次态
(1)第一个always模块,格式化描述次态寄存器迁移到现态寄存器
always @ (posedge key_clk, negedge rst_n) //异步复位
if (!rst_n)
current_state <= NO_KEY_PRESSED;//初始化按键没按下 //默认状态
else
current_state <= next_state; //注意,使用的是非阻塞赋值
(2)第二个进程,组合逻辑always模块,描述状态转移条件判断用current_state
always @ (current_state) //电平触发 或者always @ *
begin
next_state = x; //要初始化,使得系统复位后能进入正确的状态
case(current_state)
S1: if(...)
next_state = S2; //阻塞赋值
...
endcase
end
(3)第三个进程,同步时序always模块,描述次态寄存器输出
always @ (posedge clk or negedge rst_n)
...//初始化
case(next_state)
S1:
out1 <= 1'b1; //注意是非阻塞逻辑
S2:
out2 <= 1'b1;
default:... //default的作用是免除综合工具综合出锁存器。
endcase
end
三段式并不是一定要写为3个always块,如果状态机更复杂,就不止3段了。
1. 三段always模块中,第一个和第三个always模块是同步时序always模块,用非阻塞赋值(“ <= ”);第二个always模块是组合逻辑always模块,用阻塞赋值(“ = ”)。
2. 第二部分为组合逻辑always模块,为了抑制warning信息,对于always的敏感列表建议采用always@(*)的方式。
3. 第二部分,组合逻辑always模块,里面判断条件一定要包含所有情况!可以用else保证包含完全。
4. 第二部分,组合逻辑电平要维持超过一个clock,仿真时注意。
5. 需要注意:第二部分case中的条件应该为当前态(current_state)。
6. 第三部分case中的条件应该为次态(next_state)。
7. 编码原则,binary和gray-code适用于触发器资源较少,组合电路资源丰富的情况(CPLD),对于FPGA,适用one-hot code。这样不但充分利用FPGA丰富的触发器资源,还因为只需比较一个bit,速度快,组合电路简单。
//------------------------------------------------------------------------------------------------------------------------------------------------------------------
module finite_fsm(
z_o,
clk,
Rst_n,
w_i
);
//输出端口
output z_o;
//输入端口
input clk;
input Rst_n;
input w_i;
//输出端口类型声明
reg z_o;
//参数声明
parameter IDLE = 2'b00;
parameter S0 = 2'b01;
parameter S1 = 2'b10;
//内部信号声明
reg[1:0] current_state;
reg[1:0] next_state;
//状态寄存器
always @ (posedge clk or negedge Rst_n) begin
if(!Rst_n)
current_state <= IDLE;
else
current_state <= next_state;
end
//次态的组合逻辑
always @ (w_i or current_state) begin
case(current_state)
IDLE:begin
if(w_i) next_state = S0;
else next_state = IDLE;
end
S0: begin
if(w_i) next_state = S1;
else
next_state = IDLE;
end
S1: begin
if(w_i) next_state = S1;
else next_state = IDLE;
end
default : next_state = 2'bxx;
endcase
end
//输出逻辑
always @ (*)
beign
case(current)
IDLE: z_o = 1'b0;
S0: z_o = 1'b0;
S1: z_o = 1'b1;
default: z_0 = 1'b0;
endcase
end
endmodule
/*---------------------
关于三段式状态机的一点总结
1确定输入输出信号,及其类型(是wire还是reg);
2声明内部信号,一般需要定义current_state和next_state;
3用3个always语句描述状态机;第一个用来次态和现态的转换,第二个always用于现态在输入情况下转换为次态的组合逻辑;第三个语句用于现态到输出的组合逻辑输出。
*/