通过有线状态机(fsm)来编程是一种很方便的编程方法,它适合于多种状态转换问题。常见的fsm有摩尔型和米里型两种,前者次态仅依赖于现态,而后者次态不仅依赖于现态,还依赖于输入,常用的fsm一般都欧式米里型的。
状态编码:
fsm的常用编码有3中,二进制编码,格雷码和one-hot(独热码)。前两种大家比较熟悉,小猪就说一下第三种吧,one-hot顾名思义,整个数中只有一位为1。比如,我们目前有三个状态,则表示状态需要三位,依次为001,010,100.格雷码因为相邻两数字只有一位不同,也比较常用,但是一个状态可能转移到多个状态,因此不适合这种情况。独热码呢,表示比较方便,但是比较耗费寄存器。所以说选择那种编码,要因地制宜。
列一个简单的例子:
在数据传输中,我们需要检测到开始位之后才开始接受数据,这就是序列检测。假设我们要检测到一个10011的开始序列,verilog代码如下:
module serial_detected(
clk,rst,
cin,cout
);
input clk,rst,cin;
output cout;
reg cout;
reg[4:0] state;
//状态编码
parameter idle = 5'b00001,
s1 =
5'b00010,
s2 =
5'b00100,
s3 =
5'b01000,
s4 =
5'b10000;
//状态转移
always @ (posedge clk or posedge rst)
begin
if(rst) begin state<=idle; cout<=1'b0; end
else case(state)
idle:
if(cin==1'b0) begin state<=idle;cout<=1'b0; end
else begin state<=s1;cout<=1'b0; end
s1:
if(cin==1'b1) begin state<=s1;cout<=1'b0; end
else begin state<=s2;cout<=1'b0; end
s2:
if(cin==1'b1) begin state<=s1;cout<=1'b0; end
else begin state<=s3;cout<=1'b0; end
s3:
if(cin==1'b1) begin state<=s4;cout<=1'b0; end
else begin state<=idle;cout<=1'b0; end
s4:
if(cin==1) begin state<=s1;cout<=1'b1; end
else state<=s2;
default: begin state<=idle;cout<=1'b0; end
endcase
end
endmodule
小猪在这里总结了一下用verilog来实现简单状态图的模块(复杂的状态图还是要用两段式或三段式咯~)
module 名称(时钟,复位,输入,输出);
输入端口声明;
输出端口声明;
//状态变量编码
reg[] state;
parameter s0=....,
.......
sn=......;
//状态转移
always @ (posedge clk or posedge rst)
begin
if(rst) 状态初始化;
else case(state)
s0: begin
组合逻辑,控制输出;
状态转移;
end
s1:......
...........
default:默认设置状态
endcase
end
endmodule