FSM之三--代碼風格

FSM設計之一http://www.cnblogs.com/qiweiwang/archive/2010/11/28/1890244.htmlhtml

  Moore型狀態機與mealy型狀態機相比,因爲其狀態輸出僅與當前狀態有關,而與輸入無關,因此它能夠避免由輸入信號引發的毛刺,所以建議採用Moore型狀態機。可是在實際的應用中,咱們只須要對狀態輸出進行寄存,即在outputs後面加上一級輸出寄存,就能夠有效地避免毛刺的傳播。異步

  Binary、gray-code編碼使用最少的觸發器,較多的組合邏輯。而one-hot 編碼反之。因爲CPLD更多的提供組合邏輯資源,而FPGA更多的提供觸發器資源,因此CPLD 多使用gray-code,而FPGA 多使用one-hot編碼。 另外一方面,對於小型設計使用gray-code和binary編碼更有效,而大型狀態機使用one-hot更高效。 
   看synplicity的文檔,推薦在24個狀態以上會用格雷碼,在5~24個狀態會用獨熱碼,在4個狀態之內用二進制碼,確定獨熱碼比二進制碼在實現FSM部分會佔更多資源,可是譯碼輸出控制簡單,因此若是狀態不是太多,獨熱碼較好。狀態太少譯碼不會太複雜,二進制就能夠了。狀態太多,前面獨熱碼所佔資源太多,綜合考慮就用格雷碼了。this

編碼風格: 
1.  避免生成鎖存器 
  一個完備的狀態機(健壯性強)應該具有初始化(reset)狀態和默認(default)狀態。 當芯片加電或者復位後,狀態機應該可以自動將全部判斷條件復位,並進入初始化狀態。須要注
明的一點是,大多數FPGA有GSR(Global Set/Reset)信號,當FPGA加電後,GSR信號拉高,對全部的寄存器,RAM等單元復位/置位,這時配置於FPGA的邏輯並未生效,因此不能保證正確的進入初始化狀態。因此使用 GSR進入FPGA的初始化狀態,經常會產生種種沒必要必定的麻煩。通常簡單方便的方法是採用異步復位信號,固然也可使用同步復位,可是要注意同步復位的邏輯設計。 
   狀態機也應該有一個默認(default)狀態,當轉移條件不知足,或者狀態發生了突變時,要能保證邏輯不會陷入「死循環」。這是對狀態機健壯性的一個重要要求,也就是常說的要具有「自恢復」功能。對應於編碼就是對 case,if-else 語句要特別注意,要寫完備的條件判斷語句。VHDL 中,當使用CASE語句的時候,要使用「When Others」創建默認狀態。使用「IF...THEN...ELSE」語句的時候,要用在「ELSE」指定默認狀態。 Verilog中, 使用「case」語句的時候要用「default」創建默認狀態, 使用「if...else」語句的注意事項類似。 
     另外有一個技巧:大多數綜合器都支持 Verilog 編碼狀態機的完備狀態屬性--「full case」。編碼

2.  參數定義用 parameter 
  狀態的定義用 parameter 定義,不推薦使用`define 宏定義的方式,由於‘define 宏定義在編譯時自動替換整個設計中所定義的宏,而 parameter 僅僅定義模塊內部的參數,定義的參數不會與模塊外的其餘狀態機混淆。設計

3.  必定要使用」<=」非阻塞賦值方式 
  採用非阻塞賦值方式消除不少競爭冒險的隱患。3d

複製代碼
module gray
(
input clk,
input rst_n,
input a,
outputreg y
);
//
parameter s0 =2'b00,
s1 =2'b01,
s2 =2'b10,
s3 =2'b11;
//
reg [1:0] current_state;
reg [1:0] next_state;
//狀態轉移
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
current_state <= s0;
else
current_state <= next_state;
end
//狀態譯碼邏輯
always @ (a or current_state)
begin
case(cs)
s0:
if(a==1'b1)
next_state =s1;
else
next_state=s0;
s1:
next_state = s2;
s2:
next_state = s3;
s3:
next_state = s0;
default:
next_state =s0;
endcase
end
//狀態寄存輸出
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
y <=1'b0;
elseif(next_state==s3)//輸出與next_state有關
y <=!a;
else
y <=1'b0;
end

endmodule
複製代碼

one-hot編碼code

  one-hot顧名思義,指任什麼時候候一個nbit的寄存器組中有且僅有1bit爲1,其餘n-1個寄存器值均爲0。在狀態機編碼的範疇中,它就像是「一個蘿蔔一個坑」,每一個狀態都佔用一個寄存器,該位爲1時表示狀態機進入該狀態,爲0時表示狀態機不在該狀態。所以,每一個狀態只用1bit便可表示出來。在進入一個新狀態時,將原狀態對應的寄存器位置0、新狀態對應的寄存器位置1,以保證「one-hot」。htm

  在電路面積和速度方面,one-hot編碼方式所佔用的DFF資源要比binary編碼多一些,n個狀態要用n個DFF;可是這種編碼方式由在狀態譯碼的時候只須要用1bit參與狀態譯碼,譯碼邏輯較小,生成的組合邏輯相應的較小。整體來講,在FPGA設計中,採用one-hot編碼方式每每消耗的資源要比binary編碼少。blog

  那麼爲何one-hot編碼方式在譯碼的時候只須要有1bit參與譯碼呢?咱們知道,須要多少位參與狀態譯碼和狀態的表示方式頗有關係,也就是說和何種方式去標識一個狀態頗有關係,若是一個狀態用4bit表示,那麼它在譯碼的時候就要用4bit,可是若是隻有一個狀態用1bit表示,則它參與譯碼時只須要1bit便可,因此,one-hot這種「一個蘿蔔一個坑」的結構決定了它在譯碼時每一個狀態只須要用一位參與譯碼。ci

  one-hot編碼方式一般分爲兩類:一種是非index編碼方式;一種是index編碼方式。

複製代碼
module not_index_fsm
(
input clk,
input rst_n,
input [1:0] a,
output y
);
//
parameter U_DLY =1;
parameter s0 =10'b00_0000_0001,
s1 =10'b00_0000_0010,
s2 =10'b00_0000_0100,
s3 =10'b00_0000_1000,
s4 =10'b00_0001_0000,
s5 =10'b00_0010_0000,
s6 =10'b00_0100_0000,
s7 =10'b00_1000_0000,
s8 =10'b01_0000_0000,
s9 =10'b10_0000_0000;
//
reg [9:0] current_state;
reg [9:0] next_state;
reg [5:0] cnt;
//
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
current_state <= s0;
else
current_state <= #U_DLY next_state;
end
//
always @ (a,cnt,current_state)
begin
case(current_state)
s0:
case(a)
2'b00: next_state = s1;
2'b01: next_state = s2;
2'b10: next_state = s3;
2'b11: next_state = s4;
endcase
s1:
next_state = s9;
s2:
next_state = s5;
s3:
next_state = s6;
s4:
next_state = s8;
s5:
next_state = s6;
s6:
next_state = s7;
s7:
begin
if(cnt==6'h2f)
next_state = s9;
else
next_state = s7;
end
s8:
next_state = s9;
s9:
begin
if(cnt ==6'h3f)
next_state = s0;
else
next_state = s9;
end
default:
next_state = s0;
endcase
end
//
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
cnt <=6'b0;
elseif(next_state == s9)
cnt <= #U_DLY cnt+1'b1;
else
cnt <= #U_DLY 6'b0;
end

assign y = current_state[8];

endmodule
複製代碼

 index編碼方式:

複製代碼
module index_fsm
(
input clk,
input rst_n,
input [1:0] a,
output y
);
//
parameter U_DLY =1;
parameter s0 =0,
s1 =1,
s2 =2,
s3 =3,
s4 =4,
s5 =5,
s6 =6,
s7 =7,
s8 =8,
s9 =9;
//
reg [9:0] current_state;
reg [9:0] next_state;
reg [5:0] cnt;
//
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
state <=10'b0;
        state[s0] <= 1'b1;//because the coding of IDLE is 10'b1
else
current_state <= #U_DLY next_state;
end
//
always @ (a,cnt,current_state)
begin
next_state <=10'b0;//給next_state這樣賦默認值綜合後不會出現鎖存器
case(1'b1)//synthesis parallel_case full_case
current_state[s0]:
case(a)
2'b00: next_state[s1] = 1'b1;
2'b01: next_state[s2] = 1'b1;
2'b10: next_state[s3] = 1'b1;
2'b11: next_state[s4] = 1'b1;
endcase
current_state[s1]:
next_state[s9] =1'b1;
current_state[s2]:
next_state[s5] =1'b1;
current_state[s3]:
next_state[s6] =1'b1;
current_state[s4]:
next_state[s8] =1'b1;
current_state[s5]:
next_state[s6] =1'b1;
current_state[s6]:
next_state[s7] =1'b1;
current_state[s7]:
begin
if(cnt==6'h2f)
next_state[s9] =1'b1;
else
next_state[s7] =1'b1;
end
current_state[s8]:
next_state[s9] =1'b1;
current_state[s9]:
begin
if(cnt ==6'h3f)
next_state[s0] =1'b1;
else
next_state[s9] =1'b1;
end
endcase
end
//
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
cnt <=6'b0;
elseif(next_state[s9] ==1'b1)
cnt <= #U_DLY cnt+1'b1;
else
cnt <= #U_DLY 6'b0;
end

assign y = current_state[s8];//在使用one-hot狀態機某些狀況下,能夠將狀態信號直接做爲輸出,從而節約邏輯資源,提供電路速度

endmodule
複製代碼

 由上面的例子分析可知,非index編碼的狀態機在狀態譯碼時使用了10位參與狀態譯碼,index編碼時只用了一位參與狀態譯碼。而由前面的分析知道,one-hot編碼的本質就是一個狀態用一位表示,所以,index編碼才能真正地反映出one-hot的本質。

  非index編碼的狀態機因爲譯碼邏輯過多,在消耗資源級Fmax這兩個指標上都不如index編碼,所以,在使用one-hot編碼時,推薦使用index編碼的方式。

相關文章
相關標籤/搜索