状态机的设计直接体现了工程师的逻辑功底 !
本人学习状态机经历了几个阶段,现在还在体会,摸索之中 。
1、《verilog 那些事儿》中一种编程思想,“仿顺序操作”。例子如下:
module top(clk,rst_n,datain_en,datain,dataout_en,dataout);
input clk;
input rst_n;
input datain_en;
input datain;
output reg dataout_en;
output reg dataout;
reg [3:0] cnt; //计数
reg [2:0] i; // 状态/步骤
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
i<=3'd0;
dataout_en<=1'b0;
dataout<=1'b0;
cnt<=4'd0;
end
else case(i)
3'd0: if((datain_en)&(cnt<=4'd5)) // 无视前6个数据
begin i<=3'd0; cnt<=cnt+4'd1; end
else
begin
i<=i+3'd1;
dataout_en<=1'b1; //输出有效
dataout<=datain; // 中间5个数据输出
cnt<=4'd0;
end
3'd1: if((datain_en)&(cnt<=4'd3))
begin
i<=3'd1;
dataout<=datain; // 中间5个数据输出
cnt<=cnt+4'd1;
end
else
begin
i<=i+3'd1;
dataout<=~datain; //后边的数据,输出反相
end
3'd2: if(datain_en)
begin
i<=3'd2;
dataout<=~datain;
end
else
begin
i<=i+3'd1;
dataout_en<=1'b0;
end
3'd3: i<=3'd3;
default: i<=3'd0;
endcase
endmodule
“仿顺序操作”用代码很好实现,但是,不能综合成状态机。理由:The state transition logic does not use arithmetic operators. For example, this condition is not met if the state transition logic uses the expression next_state <= state + 1;, which contains the arithmetic operator。从各种程序测试结果来看,状态变量(state和next_state)除了可以用于'case (state)'语句以及条件判别'(state==s1)'和状态参数赋值'next_state=s1'以外,不能用于其它的操作,这就是为什么常常 不能综合成状态机的原因。我们可以这样来判断:将状态变量(state和next_state)和状态参数(s1,s2...)看成一些特殊的变量或者标 识符,可以用状态参数或者状态变量对另外的状态变量赋值或者判别它们是否相等,但是它们不是'数',一旦将它们看成'数',例如state=s1+1或者 state[s1]=1'b1(等数学操作)就不能综合成状态机。
2、稍做改动,把上边的状态用常数表示,就能综合成状态机
module top(clk,rst_n,datain_en,datain,dataout_en,dataout);
input clk;
input rst_n;
input datain_en;
input datain;
output reg dataout_en;
output reg dataout;
reg [3:0] cnt; //计数
reg [2:0] i; // 状态/步骤
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
i<=3'd0;
dataout_en<=1'b0;
dataout<=1'b0;
cnt<=4'd0;
end
else case(i)
3'd0: if((datain_en)&(cnt<=4'd5)) // 无视前6个数据
begin i<=3'd0; cnt<=cnt+4'd1; end
else
begin
i<=3'd1;
dataout_en<=1'b1; //输出有效
dataout<=datain; // 中间5个数据输出
cnt<=4'd0;
end
3'd1: if((datain_en)&(cnt<=4'd3))
begin
i<=3'd1;
dataout<=datain; // 中间5个数据输出
cnt<=cnt+4'd1;
end
else
begin
i<=3'd2;
dataout<=~datain; //后边的数据,输出反相
end
3'd2: if(datain_en)
begin
i<=3'd2;
dataout<=~datain;
end
else
begin
i<=3'd3;
dataout_en<=1'b0;
end
3'd3: i<=3'd3;
default: i<=3'd0;
endcase
endmodule
这样,便会综合成状态机,(黄色方框代表状态机)
这就是“一段式状态机”,和下边的完全等效
module top(clk,rst_n,datain_en,datain,dataout_en,dataout);
input clk;
input rst_n;
input datain_en;
input datain;
output reg dataout_en;
output reg dataout;
parameter state0=3'd0,state1=3'd1,state2=3'd2,state3=3'd3;
reg [3:0] cnt; //计数
reg [2:0] state; // 状态
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
state<=state0;
dataout_en<=1'b0;
dataout<=1'b0;
cnt<=4'd0;
end
else case(state)
state0: if((datain_en)&(cnt<=4'd5)) // 无视前6个数据
begin state<=state0; cnt<=cnt+4'd1; end
else
begin
state<=state1;
dataout_en<=1'b1; //输出有效
dataout<=datain; // 中间5个数据输出
cnt<=4'd0;
end
state1: if((datain_en)&(cnt<=4'd3))
begin
state<=state1;
dataout<=datain; // 中间5个数据输出
cnt<=cnt+4'd1;
end
else
begin
state<=state2;
dataout<=~datain; //后边的数据,输出反相
end
state2: if(datain_en)
begin
state<=state2;
dataout<=~datain;
end
else
begin
state<=state3;
dataout_en<=1'b0;
end
state3: state<=state3;
default: state<=state0;
endcase
endmodule
这种“一段式状态机”,当把整个状态机卸载一个always模块中,并且这个模块既包含状态转移,又含有组合逻辑输入/输出时,称为一段式状态机。说到底,还是一种“仿顺序”的思想,很难看出状态机的模型,变换思维方式,就要写成“两段式状态机”或“三段式状态机”,即把同步时序状态转移和组合逻辑判断状态转移的条件分开 ! 这完全是一种思维方式的改变 !如下:
3、三段式
module top(clk,rst_n,datain_en,datain,dataout_en,dataout);
input clk;
input rst_n;
input datain_en;
input datain;
output reg dataout_en;
output reg dataout;
parameter state0=3'd0,state1=3'd1,state2=3'd2,state3=3'd3;
reg [3:0] cnt; //计数
reg [2:0] state; // 状态
reg [2:0] next_state;
//状态跳转程序设计
always@(posedge clk or negedge rst_n)
if(!rst_n)
state<=state0;
else
state<=next_state;
//状态跳转逻辑
always@(state or datain_en or cnt)
case(state)
state0: if((datain_en)&(cnt<=4'd5)) // 无视前6个数据
next_state=state0;
else next_state=state1;
state1: if((datain_en)&(cnt<=4'd3))
next_state=state1;
else next_state=state2;
state2: if(datain_en)
next_state=state2;
else next_state=state3;
state3: next_state=state3;
default: next_state=state0;
endcase
//输出
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin dataout_en<=1'b0;dataout<=1'b0;cnt<=4'd0; end
else case(state)
state0: if((datain_en)&(cnt<=4'd5))
cnt<=cnt+4'd1;
else begin cnt<=4'd0;dataout<=datain;dataout_en<=1'b1;end
state1: if((datain_en)&(cnt<=4'd3))
begin dataout<=datain;cnt<=cnt+4'd1; end
else dataout<=~datain;
state2: if(datain_en)
dataout<=~datain;
else dataout_en<=1'b0;
default: dataout<=1'b0;
endcase
endmodule
Quartus II中对状态机的设计有一些特别的规定,如果不了解这些规定在设计的时候常常使人产生困惑。
对于Verilog HDL这些规定是:
1.The Always Construct that describes the state machine is clocked.
2.All assignments to the signal or variable that represents the state machine are within the Always Construct.
3.The state machine has more than two states.
4.The state register is not used as an output of the module.
5.The state transition logic does not use arithmetic operators. For example, this condition is not met if the state transition logic uses the expression next_state <= state + 1;, which contains the arithmetic operator
Quartus II中状态机的编码风格是可选择的,通过状态机处理逻辑选项,可以选择四种方式:
1.自动格式:编译程序自动选择最好的状态机编码格式(缺省格式);
2.最少位格式:用最少位数的状态机编码格式;
3.one-hot格式:one-hot一般译成一位热码格式,传统的格式是每种状态中只有一位是'1'其它位是'0',而Quartus II中与此不同,Quartus II的reset状态是全'0',其它状态有两位是'1'(其中最低位是'1'),其它位是'0',这样的效果与传统的格式是一样的,每一位可以定一种状 态,好处是上电的时候自动设置成reset状态(全'0');
4.用户编码格式:用用户写的状态机编码格式(可能编译程序还会修改其中的一些编码);
Quartus II中还可以选择是否提取Verilog或者VHDL状态机:
1.提取状态机:编译程序用特殊技术优化状态机减小面积和改进性能(缺省方式)。这时综合将报告状态机的状态数和状态机的编码结果,在RTL Viewer中可以看到状态机,这就是我们说的综合成了状态机;
2.不提取状态机:编译程序用通常方式优化状态机。这时综合报告不报告状态机的状态数和编码结果,在RTL Viewer中看不到状态机,但是只要程序是正确的,实际还是综合成了状态机存,运行的结果也是正确的,传统one-hot编码程序风格(位索引方式)最 好选择此选项。
综上所述,Quartus II缺省设置时已经用特殊技术优化状态机减小面积和改进性能,它与具体状态编码格式关系不大,我们在设计状态时应该按照Quartus II的规则,应重点关注状态机的功能设计,编码格选一种能综合成状态机的既可,同时注意将reset状态设置为全'0'。
两段式之所以比一段式编码合理,就在于两段式编码将同步时序和组合逻辑分别放到不同的always程序块中实现。这样做的好处不仅仅是便于阅读、理解、维护,更重要的是利于综合器优化代码,利于用户添加合适的时序约束条件,利于布局布线器实现设计。而一段式FSM描述不利于时序约束、功能更改、调试等,而且不能很好的表示米勒FSM的输出,容易写出Latches,导致逻辑功能错误。
注意: 三段式状态机,并不一定有3个always模块,可以更多,三段式是一种思想 !
文章评论(0条评论)
登录后参与讨论