有限状态机(finite statemachine,FSM)广泛应用于数字系统的控制器设计中.由于设计方法不同,综合出来的电路结构、速度、面积和时延特性都会有很大的差别, 甚至某些臃肿的电路还会产生难以预料的问题.
1.“一段式”
特点:将当前状态向量和输出向量用同一时序always块进行描述。
缺点:代码冗长,不易修改和调试,可维护性较差且占用资源多;通过case语句对输出向量的赋值应是下一个状态的输出,这点容易出错;状态向量和输出向量都是由寄存器逻辑实现,面积较大;不能实现异步mealy有限状态机。
优点:寄存器输出,输出向量不会产生毛刺。
结构:
例程:
moduleFSM(
clk,
rst_n,
data_cap,//
data_low,
out_flow
);
inputclk,rst_n;
input[7:0]data_cap,data_low;//大写,小写字母数据流
outputreg[7:0]out_flow;
reg[7:0]state;
localparam//独热码
CHECK_I=8'b0000_0001,
CHECK_L=8'b0000_0010,
CHECK_o1=8'b0000_0100,
CHECK_v=8'b0000_1000,
CHECK_e=8'b0001_0000,
CHECK_Y=8'b0010_0000,
CHECK_o2=8'b0100_0000,
CHECK_u=8'b1000_0000;
always@(posedgeclkornegedgerst_n)
if(!rst_n)begin
out_flow<=0;
state<=CHECK_I;
end
elsebegin
case(state)
CHECK_I : if(data_cap=="I")begin
state<=CHECK_L;
out_flow<=data_cap;
end
else
state<=CHECK_I;
CHECK_L : if(data_cap=="L")begin
state<=CHECK_o1;
out_flow<=data_cap;
end
else
state<=CHECK_L;
CHECK_o1 : if(data_low=="o")begin
state<=CHECK_v;
out_flow<=data_low;
end
else
state<=CHECK_o1;
CHECK_v : if(data_low=="v")begin
state<=CHECK_e;
out_flow<=data_low;
end
else
state<=CHECK_v;
CHECK_e : if(data_low=="e")begin
state<=CHECK_Y;
out_flow<=data_low;
end
else
state<=CHECK_e;
CHECK_Y : if(data_cap=="Y")begin
state<=CHECK_o2;
out_flow<=data_cap;
end
else
state<=CHECK_Y;
CHECK_o2 : if(data_low=="o")begin
state<=CHECK_u;
out_flow<=data_low;
end
else
state<=CHECK_o2;
CHECK_u : if(data_low=="u")begin
state<=CHECK_I;
out_flow<=data_low;
end
else
state<=CHECK_u;
default : begin
out_flow<=0;
state<=CHECK_I;
end
endcase
end
endmodule
RTL:
资源使用:
仿真效果:
2.“两段式”
特点:(第一段)一个时序always块给当前状态向量赋值,(第二段)一个组合always块给下一个状态向量和输出向量赋值。
缺点:(1)组合逻辑输出会使输出向量产生毛刺;
(2)从速度角度而言,由于这种状态机的输出向量必须由状态向量经译码得到,因此加大了从状态向量到输出向量的延时。
(3)从综合角度而言,组合输出消耗了一部分时钟周期,即增加了由它驱动的下一个模块的输入延时。
结构:
例程:
moduleFSM(
clk,
rst_n,
data_cap,//
data_low,
out_flow
);
inputclk,rst_n;
input[7:0]data_cap,data_low;//大写,小写字母数据流
outputreg[7:0]out_flow;
reg[7:0]state,NS;
localparam//独热码
CHECK_I=8'b0000_0001,
CHECK_L=8'b0000_0010,
CHECK_o1=8'b0000_0100,
CHECK_v=8'b0000_1000,
CHECK_e=8'b0001_0000,
CHECK_Y=8'b0010_0000,
CHECK_o2=8'b0100_0000,
CHECK_u=8'b1000_0000;
always@(posedgeclkornegedgerst_n)
if(!rst_n)begin
state<=CHECK_I;
end
elsebegin
state<=NS;
end
always@(rst_norstateordata_capordata_low)
begin
NS=8'bx;
out_flow<=8'bx;
case(state)
CHECK_I : if(data_cap=="I")begin
NS<=CHECK_L;
out_flow<=data_cap;
end
else
NS<=CHECK_I;
CHECK_L : if(data_cap=="L")begin
NS<=CHECK_o1;
out_flow<=data_cap;
end
else
NS<=CHECK_L;
CHECK_o1 : if(data_low=="o")begin
NS<=CHECK_v;
out_flow<=data_low;
end
else
NS<=CHECK_o1;
CHECK_v : if(data_low=="v")begin
NS<=CHECK_e;
out_flow<=data_low;
end
else
NS<=CHECK_v;
CHECK_e : if(data_low=="e")begin
NS<=CHECK_Y;
out_flow<=data_low;
end
else
NS<=CHECK_e;
CHECK_Y : if(data_cap=="Y")begin
NS<=CHECK_o2;
out_flow<=data_cap;
end
else
NS<=CHECK_Y;
CHECK_o2 : if(data_low=="o")begin
NS<=CHECK_u;
out_flow<=data_low;
end
else
NS<=CHECK_o2;
CHECK_u : if(data_low=="u")begin
NS<=CHECK_I;
out_flow<=data_low;
end
else
NS<=CHECK_u;
default : begin
out_flow<=8'bx;
NS<=CHECK_I;
end
endcase
end
endmodule
RTL:
资源占用:
仿真:
3.“三段式”
特点:两个时序always块,分别产生当前状态向量和输出向量,再用一个组合逻辑always块产生下一个状态向量。
代码主要包含以下三部分:
[1] 状态转移部分(时序逻辑)
[2] 状态转移条件部分(即产生下个状态向量的模块,组合逻辑)
[3] 输出逻辑部分(时序逻辑)
优点:三段式描述方法虽然代码结构复杂了一些,但是换来的优势是:使FSM做到了同步寄存器输出,消除了组合逻辑输出的不稳定与毛刺的隐患,而且更利于时序路径分组,一般来说在FPGA/CPLD等可编程逻辑器件上的综合与布局布线效果更佳。
结构:
例程:
moduleFSM(
clk,
rst_n,
data_cap,//
data_low,
out_flow
);
inputclk,rst_n;
input[7:0]data_cap,data_low;//大写,小写字母数据流
outputreg[7:0]out_flow;
reg[7:0]state,NS;
localparam//独热码
CHECK_I=8'b0000_0001,
CHECK_L=8'b0000_0010,
CHECK_o1=8'b0000_0100,
CHECK_v=8'b0000_1000,
CHECK_e=8'b0001_0000,
CHECK_Y=8'b0010_0000,
CHECK_o2=8'b0100_0000,
CHECK_u=8'b1000_0000;
always@(posedgeclkornegedgerst_n)
if(!rst_n)begin
state<=CHECK_I;
end
elsebegin
state<=NS;
end
always@(rst_norstateordata_capordata_low)
begin
NS=8'bx;
case(state)
CHECK_I : if(data_cap=="I")
NS<=CHECK_L;
else
NS<=CHECK_I;
CHECK_L : if(data_cap=="L")
NS<=CHECK_o1;
else
NS<=CHECK_L;
CHECK_o1: if(data_low=="o")
NS<=CHECK_v;
else
NS<=CHECK_o1;
CHECK_v : if(data_low=="v")
NS<=CHECK_e;
else
NS<=CHECK_v;
CHECK_e : if(data_low=="e")
NS<=CHECK_Y;
else
NS<=CHECK_e;
CHECK_Y : if(data_cap=="Y")
NS<=CHECK_o2;
else
NS<=CHECK_Y;
CHECK_o2: if(data_low=="o")
NS<=CHECK_u;
else
NS<=CHECK_o2;
CHECK_u : if(data_low=="u")
NS<=CHECK_I;
else
NS<=CHECK_u;
default : NS<=CHECK_I;
endcase
end
always@(posedgeclkornegedgerst_n)
if(!rst_n)begin
out_flow<=8'b0;
end
elsebegin
case(NS)
CHECK_I : out_flow<="I";//I
CHECK_L : out_flow<="L";//L
CHECK_o1: out_flow<="o";//o
CHECK_v : out_flow<="v";//v
CHECK_e : out_flow<="e";//e
CHECK_Y : out_flow<="Y";//Y
CHECK_o2: out_flow<="o";//o
CHECK_u : out_flow<="u";//u
endcase
end
endmodule
RTL:
资源占用:
以上的仿真代码:
`timescale1ns/1ns
`defineclock_period20
moduleFSM_tb;
regclk;
regrst_n;
reg[7:0]data_cap,data_low;
wire[7:0]out_flow;
FSMFSM(
.clk(clk),
.rst_n(rst_n),
.data_cap(data_cap),//
.data_low(data_low),
.out_flow(out_flow)
);
always@(posedgeclk)
begin
#(`clock_period)data_cap=65+{$random}%26;
#(`clock_period)data_low=97+{$random}%26;
end
initialclk=1;
always#(`clock_period/2)clk=~clk;
initialbegin
rst_n=0;
//ASCII=0;
#(`clock_period*5);
rst_n=1;
#(`clock_period*500);
//foreverbegin
$stop;
end
endmodule
一般而言,推荐的FSM 描述方法是后两种。这是因为:FSM和其他设计一样,最好使用同步时序方式设计,以提高设计的稳定性,消除毛刺。状态机实现后,一般来说,状态转移部分是同步时序电路而状态的转移条件的判断是组合逻辑。 第二种描述方法同第一种描述方法相比,将同步时序和组合逻辑分别放到不同的always模块中实现,这样做的好处不仅仅是便于阅读、理解、维护,更重要的是利于综合器优化代码,利于用户添加合适的时序约束条件,利于布局布线器实现设计。在第二种方式的描述中,描述当前状态的输出用组合逻辑实现,组合逻辑很容易产生毛刺,而且不利于约束,不利于综合器和布局布线器实现高性能的设计。
为了使FSM 描述清晰简介,易于维护,易于附加时序约束,使综合器和布局布线器更好的优化设计,推荐使用两段式FSM 描述方法。