前言 两周前公司任务让我配置一个时序接口,搞了好久怎么搞都出问题,涉及到四块芯片,高速,32个通道同时传输,种种问题加上我能力不足越搞心态越崩,原来自己还是这么的菜,芯片官网给了一个评估代码,其中串转并的部分使用状态机的方法描述,拿起语法书,边搞项目边复习一下基础吧,今天总结下状态机,文中部分内容来自《轻松成为设计高手》和《通信ic》改编总结,最后也给出了我写的几个例子,相信花时间看一遍应该会清楚不少,知识内容我尽量简化说重点,只讲三段式写法。
一.文字概括
1.状态机适合描述有先后顺序有逻辑规律的事情。比如本文最后的红绿灯,饮料机的题目。
2.存储器加上组合电路可以描述任何复杂的电路功能,但经常会有输入相同而历史输入不同导致的输出不同的情况,在数字逻辑中引入fsm,fsm将任意模型简化为:将要输出的结果是当前状态以及当前输入的组合。
3.有限状态机设计4大部分 :状态机编码 状态机复位 状态机条件转换 状态机的输出。
4.采用gary码减少相邻状态瞬变的次数,采用one_hot码可以减少组合逻辑的使用。
5.状态机复位:
同步复位: 指复位要与分频后的时钟信号同步,触发信号仅为分频后的时钟信号。
异步复位: 指复位与分频后的时钟信号都参与触发。
归根结底是触发复位函数的敏感列表中是否把复位键信号作为触发信号。
6.状态机的跳转:状态机核心部分控制状态机在状态间的切换从而决定输出的情况。
7. moore只和当前状态有关,mealy型输出与输入有关(不仅与当前状态有关)。
二.三段式写法举例
三段式:
一:always模块采用同步时序描述状态转移。
二:always模块采用组合逻辑判断状态转移条件,描述状态转移规律。
三:always模块描述每个状态的输出。(这里应该是可以使用同步时序寄存或者说用组合逻辑,使用同步时序的话应该可以减少毛刺)
例1:
module fsm(
input clk,
input rst_n,
input in1,
input in2,
input in3,
output reg out1,
output reg out2,
output reg out3
);
reg [3:0]state;
reg next_state;
parameter state0 = 4'b0001,state1 = 4'b0010,state2 = 4'b0100,state3 = 4'b1000;
//第一段 更新状态寄存器
if(!rst_n)
state <= state0;
else
state <= next_state;
//第二段 组合逻辑电路用于状态转移规律
//根据当前状态和输入确认下一个周期的状态
always@(state or in1 or in2 or in3)
case(state)
state0:if(in1)
next_state <= state1;
else
next_state <= state0;
state1: next_state <= state2;
state2: if(in2)
next_state <= state3;
else
next_state <= state0;
state3: if(in3)
next_state <= state0;
else
next_state <= state3;
default:
next_state <= state0;
endcase
//第三段 利用状态寄存器输出控制结果
always@(state)
begin //先产生默认值 后边再改写 防止寄存器产生
{out1,out2,out3} = 3'b000;
case(state)
state1:{out1,out2,out3} = 3'b100;
state2:{out1,out2,out3} = 3'b110;
state3:{out1,out2,out3} = 3'b111;
endcase
end
endmodule
例2:
图画的潦草了一点…
//功能简介:初始状态s0下输出3'b001,如果收到start信号为1,进入s1;如果start为0,保持。s1输出一个周期的3'b010再到s2状态,s2输出3’b100等待step2为1转移到s3,否则维持。state3和state2类似。
module fsm(
input clk,
input rst_n,
input start,
input step2,
input step3,
output reg [2:0]fsm_out
);
localparam state0 = 2'b00;
localparam state1 = 2'b01;
localparam state2 = 2'b11;
localparam state3 = 2'b10;
//标准三段式 每个周期开始时更新当前状态
reg [1:0] state;
reg [1:0]next_state;
always@(posedge clk or negedge rst_n)
if(!rst_n)
state <= state0;
else
state <= next_state;
//根据当前状态和输入确认下一个周期的状态
always@(state or start or step2 or step3)
begin
case(state)
state0:
begin
if(start)
next_state <= state1;
else
next_state <= state0;
end
state1:
begin
next_state <= state2;
end
state2:
begin
if(step2)
next_state <= state3;
else
next_state <= state2;
end
state3:
begin
if(step3)
next_state <= state0;
else
next_state <= state3;
end
default: next_state <= state0;
endcase
end
//该进程定义fsm的输出
always@(state)
case(state)
state0:fsm_out = 3'b001;
state1:fsm_out = 3'b010;
state2:fsm_out = 3'b100;
state3:fsm_out = 3'b111;
default:fsm_out = 3'b001;//default可以避免锁存器
endcase
endmodule
例3:
//输入信号clk,复位rst_n,共四个状态idel,s1,s2,error
//ns 下一个状态 cs 当前状态
module state(
input clk;
input rst_n;
input i1,i2;
output reg o1,o2,err
);
reg [2:0]ns,cs;
parameter[2:0]//one hot
IDLE = 3'b000,
S1 = 3'b001,
S2 = 3'b010,
ERROR= 3'b100;
always@(posedge clk or negedge rst_n)
if(!rst_n)
cs <= IDLE;
else
cs <= ns;
always@(rst_n or cs or i1 or i2)
begin
ns = 3'bx;
case(cs)
IDLE:
begin
if(!i1) ns <= IDLE;
if(i1&&i2) ns <= S1;
if(i1&&!i2) ns <= ERROR;
end
S1:
begin
if(!i1) ns <= S1;
if(!i1&&i2) ns <= ERROR;
if(i1&&i2) ns <= S2;
end
S2:
begin
if(i2) ns <= S2;
if(!i1&&!i2) ns <= ERROR;
if(i1&&!i2) ns <= IDLE;
end
ERROR:
begin
if(i1) ns <= ERROR;
if(!i1) ns <= IDLE;
end
endcase
end
always@(posedge clk or negedge rst_n)
if(!rst_n)
{o1,o2,err} <= 3'b000;
else
begin
{o1,o2,err} <= 3'b000;
case(ns)
IDLE: {o1,o2,err} <= 3'b000,
S1: {o1,o2,err} <= 3'b100,
S2: {o1,o2,err} <= 3'b010,
ERROR: {o1,o2,err} <= 3'b111;
endcase
end
endmodule
三.实际问题
例1:一个简单的红绿灯问题
module rgy
(
input clk,
input rst_n,
input [2:0]cnt,
output reg LED_R_1,
output reg LED_G_1,
output reg LED_Y_1,
output reg LED_R_2,
output reg LED_G_2,
output reg LED_Y_2
);
parameter IDLE = 0, WE_GO = 1,WE_WAIT = 2,NS_GO = 3,NS_WAIT = 4;
reg[3:0] state;
reg[3:0] next_state;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
state <= IDLE;
else
state <= next_state;
end
always@(state or cnt or rst_n)
begin
case(state)
IDLE:begin
next_state = WE_GO;
end
WE_GO:begin
if(cnt == 3)
next_state = WE_WAIT;
else
next_state = WE_GO;
end
WE_WAIT:begin
if(cnt == 0)
next_state = NS_GO;
else
next_state = WE_WAIT;
end
NS_GO:begin
if(cnt == 3)
next_state = NS_WAIT;
else
next_state = NS_GO;
end
NS_WAIT:begin
if(cnt == 0)
next_state = WE_GO;
else
next_state = NS_WAIT;
end
endcase
end
always@(posedge clk or negedge rst_n) //WE东西方向
begin
if(!rst_n)
begin
LED_R_1 <= 1;
LED_G_1 <= 0;
LED_Y_1 <= 0;
end
else case(next_state)
IDLE:begin
LED_R_1 <= 1;//红
LED_G_1 <= 0;//绿
LED_Y_1 <= 0;//黄
end
WE_GO:begin
LED_R_1 <= 0;
LED_G_1 <= 1;
LED_Y_1 <= 0;
end
WE_WAIT:begin
LED_R_1 <= 0;
LED_G_1 <= 0;
LED_Y_1 <= 1;
end
NS_GO:begin
LED_R_1 <= 1;
LED_G_1 <= 0;
LED_Y_1 <= 0;
end
NS_WAIT:begin
LED_R_1 <= 1;
LED_G_1 <= 0;
LED_Y_1 <= 0;
end
endcase
end
always@(posedge clk or negedge rst_n) //NS
begin
if(!rst_n)
begin
LED_R_2 <= 1;
LED_G_2 <= 0;
LED_Y_2 <= 0;
end
else case(next_state)
IDLE:begin
LED_R_2 <= 1;
LED_G_2 <= 0;
LED_Y_2 <= 0;
end
WE_GO:begin
LED_R_2 <= 1;
LED_G_2 <= 0;
LED_Y_2 <= 0;
end
WE_WAIT:begin
LED_R_2 <= 1;
LED_G_2 <= 0;
LED_Y_2 <= 0;
end
NS_GO:begin
LED_R_2 <= 0;
LED_G_2 <= 1;
LED_Y_2 <= 0;
end
NS_WAIT:begin
LED_R_2 <= 0;
LED_G_2 <= 0;
LED_Y_2 <= 1;
end
endcase
end
endmodule
有点晚了 后边我会把 饮料机的题目重新开一帖子,饮料机内容比较多并且板级验证复习一下按键消抖的模块。