一、首先明确FSM编码方法
- 二进制编码(Binary code)
- 格雷码(Gray code)
- 独热码(one-hot code)
二进制编码不用介绍,很熟悉,主要介绍格雷码和独热码;
1、独热码(one-hot code)
parameter idle = 4'b1000,
start = 4'b0100,
stop = 4'b0010,
clear = 4'b0001;
每个状态有独立的寄存器位,任意时刻只有1位寄存器为1,即为one hot point。
优点:减少状态寄存器之间的组合逻辑数,提高了运行速度;
缺点:编码位数过多,牺牲寄存器逻辑资源,成本高。
独热码适用于目标器件有较多寄存器资源,寄存器之间组合逻辑较少时。
2、格雷码(Gray code)
相邻性编码,是一种循环码,相邻数只有一位不一样。
三种编码比较:
二进制码、格雷码使用最少的触发器,较多的组合逻辑,独热码反之。
FPGA有更多的触发器资源,多使用独热码;CPLD提供更多的组合逻辑,多使用格雷码;
对于小型设计使用格雷码和二进制码更高效;大型状态机使用独热码更高效。
二、状态机写法
- 一段式(1个always)
- 二段式(2个always)
- 三段式(3个always)
一般一段式和三段式较多使用,主要介绍三段式:
//第一个always,同步时序always模块
always @(posedge clk or negedge rst_n)
if(!rst_n)
current_state <= idle;
else
current_state <= next_state;
//第二个always,组合逻辑模块,描述状态转移条件判断
always @(current_state)
begin
next_state = x;
case(current_state)
s1: if(...)
next_state <= s2;
....
endcase
end
//第三个always,同步时序always模块
always @(posedge clk or negedge rst_n)
....
case(next_state)
s1:
out1 <= 1'b1;
s2:
out2 <= 1'b1;
default:...
endcase
三、几个例子
1、序列检测“10010”:当x输入10010后,y输出1(三段式)
贴代码:
`define simulation
module seqdet (
input clk ,
input rst_n ,
input x ,
output reg y
) ;
`ifdef simulation
parameter IDLE = "IDLE" ;
parameter A = "A" ;
parameter B = "B" ;
parameter C = "C" ;
parameter D = "D" ;
parameter E = "E" ;
parameter state_length = 32 ;
`elsif
parameter IDLE = 3'd0 ;
parameter A = 3'd1 ;
parameter B = 3'd2 ;
parameter C = 3'd3 ;
parameter D = 3'd4 ;
parameter E = 3'd5 ;
parameter state_length = 3 ;
`endif
reg [state_length - 1:0] current_state ;
reg [state_length - 1:0] next_state ;
//状态寄存
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)begin
current_state <= IDLE ;
end
else begin
current_state <= next_state ;
end
end
//组合逻辑
always @ (*)
begin
next_state = IDLE ;
case (current_state)
IDLE :begin
if(x == 1)begin
next_state = A ;
end
else begin
next_state = IDLE ;
end
end
A :begin
if(x == 0)begin
next_state = B ;
end
else begin
next_state = A ;
end
end
B :begin
if(x == 0)begin
next_state = C ;
end
else begin
next_state = A ;
end
end
C :begin
if(x == 1)begin
next_state = D ;
end
else begin
next_state = IDLE ;
end
end
D :begin
if(x == 0)begin
next_state = E ;
end
else begin
next_state = A ;
end
end
E :begin
if(x == 1)begin
next_state = A ;
end
else begin
next_state = C ;
end
end
default :begin
next_state = IDLE;
y = 0 ;
end
endcase
end
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)begin
y <= 0 ;
end
else begin
case (current_state)
D :begin
if(x == 0)begin
y <= 1 ;
end
else begin
y <= 0 ;
end
end
E :begin
y <= 0 ;
end
default :begin
y <= 0 ;
end
endcase
end
end
endmodule
2、自动售货机
`define simulation
module sell(
input clk ,
input rst_n ,
input half_dollar ,
input one_dollar ,
output reg dout , //售出
output reg half_out //找零
);
`ifdef simulation
parameter IDLE = "IDLE" ;
parameter half = "half";
parameter one = "one";
parameter state_length = 32;
`elsif
parameter IDLE = "IDLE" ;
parameter half = "half";
parameter one = "one";
parameter state_length = 2 ;
`endif
reg [state_length-1:0] ns ;
reg [state_length-1:0] cs ;
always @ (posedge clk or negedge rst_n )
begin
if(!rst_n)begin
cs <= IDLE ;
end
else begin
cs <= ns ;
end
end
always @ (*)
begin
ns = IDLE ;
case(cs)
IDLE:begin
if(half_dollar)begin
ns = half ;
end
else begin
ns = one ;
end
end
half:begin
if(half_dollar)begin
ns = one ;
end
else begin
ns = IDLE ;
end
end
one:begin
ns = IDLE ;
end
default:begin
ns = IDLE ;
end
endcase
end
always @ (*)
begin
half_out <= 0 ;
dout <= 0 ;
case(cs)
half:begin
if(half_dollar)begin
dout <= 0 ;
half_out <= 0 ;
end
else begin
dout <= 1 ;
half_out <= 0 ;
end
end
one:begin
if(half_dollar)begin
dout <= 1 ;
half_out <= 0 ;
end
else begin
dout <= 1 ;
half_out <= 1 ;
end
end
endcase
end
endmodule
重点是状态图,状态机的灵魂;