转自EDACN,部分已修改
前几天在论坛上跟大家讨论了一下关于状态机风格写法的问题,应要求我今天在这里做一个简单的总结。
现在大家比较统一的观点是,状态机的写法应该是用三段式写法,即第一部分说明初始状态,current_state<=next_state,第二部分是状态机的状态转化的描述,第三部分是每一步状态的组合逻辑的描述。这样写调理更加清晰,也更加利于综合器综合。而且,大家一致认同的是onehot 编码是状态机编码的一种非常优秀的方式,虽然占的位数可能稍多一点,但是省去了状态变量的译码电路,因此总体上是比较划算的,而且采用onehot编码还可以比较有效地避免竞争冒险现象。现在讨论的焦点在于状态机状态变量的索引(index)和非索引写法。现在我就给出两个简单的程序,分别是用index 写法和非index写法。
非index写法,这个大家都应该很熟悉了:
module test2(clk,reset,ena,out1,out2,out3);
input clk,reset,ena;
output[3:0] out1,out2,out3;
parameter s1=3'b001;
parameter s2=3'b010;
parameter s3=3'b100;
reg[2:0] current_state,next_state;
reg[3:0] out1,out2,out3;
always@(posedge clk or negedge reset)
begin
if(reset==0)
begin
current_state<=s1;
end
else
current_state<=next_state;
end
always@(current_state or ena)
begin
next_state = current_state;//***************************sign1
case(current_state)
s1:
begin
if(ena==0)
next_state=s1;
else
next_state=s2;
end
s2:
next_state=s3;
s3:
next_state=s1;
endcase
end
always@(posedge clk or negedge reset)
begin
if(!reset)
begin
out1<=4'b0000;
out2<=4'b0000;
out3<=4'b0000;
end
else if(next_state==s1) //个人觉得此处用CURRENT_STATE来替代,虽然输出延时了一个节拍,但输出毛刺能够更好的消除,因为截断了关键路径
out1<=4'd5;
else if(next_state==s2)
out2<=4'd6;
else if(next_state==s3)
out3<=4'd2;
end
endmodule
在程序当中sign1这一句,论坛上已经讨论过了,到底加不加,至少我现在做出来的结果是不管加不加都不影响最后的结果。我想可能它的作用类似于后面那个程序sign2处那句话的作用吧,因此说有了它才能够综合成状态机不是没有道理的。但是事实上,从我做的结果来看都无关紧要了。上面的程序是可以被 Synplify和Quartus综合成状态机的,而且由于上面程序的状态非常的少,用Synplify综合的时候会自动的改变编码方式,将onehot 编码改成binary编码。当状态比较多的时候,状态机编码会被综合成onehot编码。
现在我们重点来看看所谓的index编码写法:
module test(clk,reset,ena,out1,out2,out3,state_out);
input clk,reset,ena;
output[3:0] out1,out2,out3;
output[2:0] state_out;
parameter s1=0;
parameter s2=1;
parameter s3=2;
reg[2:0] current_state,next_state;
reg[3:0] out1,out2,out3;
always@(posedge clk or negedge reset)
begin
if(reset==0)
current_state<=3'b001;
else
current_state<=next_state;
end
always@(current_state or ena)
begin
next_state=3'b000;//synthesis parallel_case
case(1'b1)
current_state[s1]:
begin
if(ena==0)
next_state[s1]=1'b1;
else
next_state[s2]=1'b1;
end
current_state[s2]:
next_state[s3]=1'b1;
current_state[s3]:
next_state[s1]=1'b1;
default:
_state[s1]=1'b1;
endcase
end
always@(posedge clk or negedge reset)
begin
if(!reset)
begin
out1<=4'b0000;
out2<=4'b0000;
out3<=4'b0000;
end
else if(next_state[s1]==1)
out1<=4'd5;
else if(next_state[s2]==1)
out2<=4'd6;
else if(next_state[s3]==1)
out3<=4'd2;
end
assign state_out=current_state;
endmodule
这种写法,小弟最开始是在《基于Verilog HDL的数字系统应用设计》(王钿, 卓兴旺编著,国防工业出版社,2006)上面看到的。然后又在论坛上面看到有人发帖,道出了这种写法的原始出处,来自于一个叫做cliff的外国佬的paper,据说这个人是经常跟mentor等作培训的人。要了解更多东西大家可以登录这个网站:http://www.sunburst-design.com/。下面重点讨论这种写法。
有人说,传统的非index写法并没有体现出onehot的优势,onehot的优势在于状态每一次变化的时候都只有一位在变,但是实际上我们可以看到,在综合的时候很多时候即使你写成noehot编码,综合器也会跟你综合成binary码,而这种index编码是彻底的进行位操作,从而从根本上体现了 onehot的优势。接下来,关键在于这种写法是否被综合成了状态机。
根据我和几位论坛上朋友的实验结果在Synplify当中这种index码是被综合成了状态机的,而且在综合报告当中可以看到状态机报告:由于状态太少,编码方式被综合成binary码。然而这种写法在Quartus当中却没有综合成状态机!准确的说是没有在状态机报告当中体现出来。在Quartus当中仿真,选择functional模式,仿真波形毫无问题,非常正确,但是一旦选择成timing模式就出问题了。状态机状态current_state出现了下面一些非常规的编码:011,101。但是整个电路仿真照常运转,逻辑输出都没有出错,整个状态机非常正常的运转。但是一直让人不解的是,这个状态变量怎么会出现哪些莫名其妙的情况,我们认为分析这个程序的电路的时候,在case的前面有一句:next_state=3'b0,这句实际上是保证了每一次状态首先每一位都是清零的,然后再进行后面的某一位变成一的操作。为了更进一步的看整个状态,我把波形图符在后面,而且用了一句assign state_out=current_state;将状态变量输出。论坛上有人提到过,状态变量是不可以直接作为输出的,但是我想这样做是没有问题的。本来从理论上讲,用assign连接的两个变量的值应该是一样的,但是我们看到波形图,这两个值并不一样!!!state_out的最后一位成功的变成了 0,也即是把state_out看成状态机的状态变量的话,整个状态机是完全没有错的。然而为什么这两个值会不一样,我们可以看到最后综合过后的电路,我从Quartus里面抓了图放在后面,可以看到current_state[0]到state_out[0]中间加了一个反相器,这样就把那些莫名其妙的状态值规整成了合理的状态值。这一点我至今仍旧没有像得很明白。个人觉得,这里本来像是Quartus这个的一个bug,然后被发现了,写Quartus 的人用了这种加反相器的办法来修正这个bug。
回过头来,不管怎样,这种写法虽然在Quartus的报告里面没有将它综合成状态机,但是实际上我们可以看到他仍旧是用状态机的方法来实现的。综合一些网友的观点,不管电路最后是否综合成状态机,只要电路功能等各方面达到要求都是可以的。如果是在做FPGA的话,由于这种写法并不是Quartus推荐的写法,因此他的优越性没有得到体现,或许在做ASIC的时候他在提高频率上的好处才会得到充分的体现。现在可以肯定的是,这种写法本身并没有错,而且据说是 synopsys所推崇的一种写法,但是Quartus却不认同。至于这种写法的好坏,我也不再这里做过多的评价了。
几点补充:
1.上写的那个测试程序状态太少了,这个时候采用binary状态机译码逻辑不大,还更省寄存器,因此会更好一些,这也是综合器自动综合成binary的原因。
2.FPGA是触发器密集型的,一个LE由一个LUT+一个DFF(典型的)组成,用了一个LUT这个LE也就算用完了;而在大状态机条件下,binary编码会导致大的译码逻辑,进而使译码逻辑增大(因为一个状态需要多位表示),因此还不如用one-hot划算。对于ASIC则不同,它的基本单元是由与门、非门、或门、DFF等电路,一个DFF占的面积比与非门等大,因此,用binary编码在面积上会更划算。
3.index和非index的one-hot编码无非是希望让综合工具更好地识别并进行优化,对于一个较好的综合工具来说,这两者是等价的,综合出来的效果也是一样的,因此,这两者的区别主要是哪一种能让综合工具更好地识别,就我个人认识而言,index编码反应了one-hot的本质(非index编码在表面上看有多位参与了译码,这不是one-hot的本质),可以降低对综合器的依靠。就我测试过的情况来说,除quartus外,synplify、dc compile都能很好的识别非index 的编码,综合效果基本上一样的。quartus在状态较为复杂的情况下,似乎并不能很好的识别非index状态机,将所有状态编码都参与了状态译码,综合出来的效果比binary还要差。目前我见过的公司主要使用非index的编码,然后依靠工具对状态机进行识别和优化。
4.状态机在综合后就变成网表了,这时已经只有单元电路的概念,没有了状态机的概念。
文章评论(0条评论)
登录后参与讨论