FPGA三段式描述狀態(tài)機是否完全正確?FPGA三段式描述有沒有思維陷阱呢,?讓我們一起來解讀一下FPGA三段式狀態(tài)機的思維邏輯,。
用三段式描述狀態(tài)機的好處,,國內外各位大牛都已經說的很多了,,大致可歸為以下三點:
1.將組合邏輯和時序邏輯分開,,利于綜合器分析優(yōu)化和程序維護;
2.更符合設計的思維習慣;
3.代碼少,,比一段式狀態(tài)機更簡潔,。
對于第一點,我非常認可,,后兩點在Clifford E. Cummings著的(Synthesizable Finite State Machine Design Techniques Using theNew SystemVerilog 3.0 Enhancements和The Fundamentals ofEfficient Synthesizable Finite State Machine Design using NC-Verilog andBuildGates)中多次提到,,我并不完全贊同,下面談談我的一些看法,。
先談談第二點關于思維習慣,。我發(fā)現(xiàn)有些人會有這樣一種習慣,先用一段式狀態(tài)機實現(xiàn)功能,,仿真ok后,,再將其轉成三段式,他們對這種開發(fā)方式的解釋是一段式更直觀,,可以更便捷的構建功能框架,,但是大家都說三段式性能會更好,所以最后又在搭好的邏輯框架下,,將一段式轉化為了三段式,。這從一個側面說明了,對一部人來說一段式更符合他們的思維習慣,,但當已經習慣了一段式的思維方式后,,再要換用三段式時,可就不這么容易了,,一段式和三段式的寫法之間存在著思維陷阱,,特權同學也曾經不小心在此失足過,。
舉一個例子,初始狀態(tài)是wr_st,,q和p輸出都為0,,當計數(shù)器count計數(shù)到指定的數(shù)值10后,輸出結束信號end,,狀態(tài)機接收到end有效后跳轉為 rd_st,同時輸出q變?yōu)?,在rd_st狀態(tài),,如jump有效則跳轉到erase_st,,如end有效p輸出0否則輸出1,用一段式實現(xiàn)起來很簡單,,如下所示,。
assign end = (count == 10);
always @(posedge clk)
begin
case(state)
wr_st: if(end) begin
q <= 1;
state<= rd_st;
end
else begin
q <= 0;
state<= wr_st;
end
rd_st: if(jump) begin
p <=1;
state<= erase_st;
end
elseif(end) begin
p <= 0;
state<= rd_st;
end
else begin
p <= 1;
state<= rd_st;
end
...
endcase
end
狀態(tài)機一
輸出波形如下所示。
圖一
換用三段式描述時,,有些人會寫成這樣,。
always @(posedge clk or negedge rst)
begin
if(!rst) state <= wr_st;
else state <=nextstate;
end
always @(*)
begin
case(state)
wr_st: if(end) nextstate = rd_st;
else nextstate = wr_st;
rd_st: if(jump) nextstate = erase_st
else nextstate = rd_st;
...
end
endcase
always @(posedge clk)
begin
case(nextstate)
wr_st: if(end) q <= 1;
else q<= 0;
rd_st: if(end) p <= 0;
else p <= 1;
...
endcase
end
狀態(tài)機二
看似代碼好像沒有什么問題,但從輸出波形可以看出,,q沒有正確輸出,。
圖二
上圖中狀態(tài)轉移正確,但是q輸出錯誤,,nextstate由組合邏輯輸出,,當end有效后,nextstate立刻變?yōu)閞d_st,導致A時刻q沒有變化,,在將一段式改為三段式的過程中,,我們仍保留了一段式的思維習慣,想當然的利用了end信號去控制狀態(tài)跳轉,,同時又控制了q的輸出,,這種思維誤區(qū)由以下兩點對三段式狀態(tài)機的認知缺陷構成。
1.書本網上大部分狀態(tài)機例程的第三段都是基于nextstate輸出的,,很少看到有基于state輸出的,,這就形成了一種思維定勢,認為三段式的第三段只能基于nextstate描述,。
2.當三段式狀態(tài)機的輸出基于nextstate描述時,,無法用同一個輸入信號即觸發(fā)當前狀態(tài)跳轉,又控制當前狀態(tài)輸出正確邏輯,,上述例子中A時刻q的錯誤輸出印證了這一點,,end可以觸發(fā)狀態(tài)從wr_st跳轉到rd_st,但無法同時讓q輸出1,。
有兩種解決辦法,。
第一種解決辦法是增加狀態(tài),,將wr_st拆分為wr_st0和wr_st1兩個狀態(tài),end信號只控制狀態(tài)跳轉,,q的輸出跟隨wr_st0和wr_st1變化,,第一段不變,如下所示
always @(*)
begin
case(state)
wr_st0: if(end) nextstate = wr_st1;
else nextstate = wr_st0;
wr_st1: nextstate= rd_st;
rd_st: if(jump) nextstate = erase_st;
else nextstate = rd_st;
...
end
endcase
always @(posedge clk)
begin
case(nextstate)
wr_st0: q<= 0;
wr_st1: q<= 1;
rd_st: if(end) p <= 0;
else p <= 1;
...
endcase
end
狀態(tài)機三
更改后波形輸出正確,,如圖一所示,。有些人會覺得這種方式沒有一段式直觀,數(shù)據(jù)手冊標明的協(xié)議只有寫和讀兩個狀態(tài),,為什么用三段式狀態(tài)機描述時還要增加一個狀態(tài)呢,?反而有一種拼湊時序的感覺;另一些人會覺得這種思維方式很自然,,協(xié)議里只有兩個狀態(tài),,但是每個狀態(tài)里又會有不同的輸出,根據(jù)輸入和輸出的不同可以將一個狀態(tài)解剖為多個細分狀態(tài),,狀態(tài)分的越細,,越利于綜合工具分析優(yōu)化,但是狀態(tài)太多了也不利于人員的查看維護,。將這個問題延展開,,目前網站書籍中講解狀態(tài)機的例子都以"狀態(tài)多,輸出少"為主,,這種類型的狀態(tài)機,,不用太多考慮狀態(tài)劃分問題,直接用moor型就ok了,,不過,,現(xiàn)實工作中我們還會遇到很多"狀態(tài)少,輸出多"的情況,,那該如何劃分狀態(tài)呢,?
一幫人會覺得狀態(tài)少更直觀,使用盡量少的狀態(tài),,把所有跟當前狀態(tài)相關的輸出都寫在同一個狀態(tài)里,,這種習慣會傾向于寫成一段式或者mealy型;
一幫人覺得如果一個狀態(tài)里的輸出太多了不利于理解,,會使用盡量多的狀態(tài),,每一個狀態(tài)只對應一種輸出,這種習慣會將狀態(tài)機傾向寫成moor型,。
如換用上文的例程,,主張狀態(tài)少的幫派會寫成一段式的狀態(tài)機一,或寫成錯誤的狀態(tài)機二,主張多狀態(tài)的幫派會寫成狀態(tài)機三,,從性能方面考慮,,后者將狀態(tài)細分的更清楚,綜合工具會更容易優(yōu)化分析,,獲得更好的性能,,但是綜合工具altera和xilinx每年都在更新,分析能力也越來越強,,越來越聰明,,減少開發(fā)者的經驗門檻,按這種趨勢,,前者和后者的性能差異也會逐年縮小,。從維護升級的方面考慮,前者和后者的輸出都一樣,,但是前者的狀態(tài)少,代碼會更少些,,更利于查看,,對代碼理解上面,本來就存在兩種不同的思維習慣,,只能智者見智了,。
回到本例中,第二種解決辦法是,,僅將狀態(tài)機二的第三段的nextstate換成state,,其他兩段不變,如下所示,。
always @(posedge clk)
begin
case(state)
wr_st: if(end) q <= 1;
else q<= 0;
rd_st: if(end) p <= 0;
else p<= 1;
...
endcase
end
輸出波形和一段式相同,,如圖一所示,三段式狀態(tài)機的第三段并沒有規(guī)定一定要基于nextstate輸出,,只是主流資料在介紹三段式狀態(tài)機時,,多用moor型為例,moor型的特點是輸出僅由狀態(tài)決定,,當狀態(tài)變化時,,輸出立刻變化,如要實現(xiàn)輸出緊跟著狀態(tài)變化,,第三段中就必須要基于nextstate輸出才可以,,對比圖一和圖二B時刻,使用state時,,當前狀態(tài)已經變?yōu)閞d_st,,輸出p滯后了一個時鐘才輸出,而使用nextstate時,當前狀態(tài)變?yōu)?rd_st的同時輸出p就變化了,,再比較圖一和圖二的C時刻,,在同一個狀態(tài)下,end有效后,,兩者的p輸出都一樣,,所以可得出,第三段使用nextstate和state的區(qū)別在于,,當狀態(tài)跳轉時,,基于nextstate的輸出是立刻變化的,而基于state輸出會延遲一個周期,,其他情況都一樣,,應該根據(jù)自己的時序要求,選擇用nextstate還是state,。
這里提到的三段式的思維陷阱,,特權同學曾經也不小心犯過,所著的《深入淺出玩轉FPGA》的p40,,漫談狀態(tài)機設計一節(jié)中舉了sram的例子比較一段式和三段式的區(qū)別,,一段式是可以按照程序正常運行的,但是三段式的輸出在讀取的狀態(tài)下會和一段式略有不同,,當cstate進入RD_S1時,,如果此時wr_req有效,cmd不會輸出3`b101,,而是3`b111,,問題在于使用了 wr_req同時控制了RD_S1的跳轉和cmd輸出,RD_S2也存在同樣的問題,,如下所示,。
case(cstate)
...
RD_S1: if(wr_req) nstate <=WR_S2;
else nstate <= RD_S2;
RD_S2: if(wr_req) nstate <=WR_S1;
else nstate <= IDLE;
...
endcase
...
case(nstate)
...
WR_S2: cmd <= 3'b111;
RD_S1: if(wr_req) cmd <=3'b101;
else cmd <= 3'b110;
RD_S2: if(wr_req) cmd <=3'b011;
else cmd <= 3'b111;
...
endcase
再回到開篇,談談第三點關于代碼量,,Clifford E. Cummings在文中提到一段式狀態(tài)機會比三段式狀態(tài)機會多20%到80%的代碼量,,并舉例證明。但是舉得例子都有一個特點"狀態(tài)多,,輸出少",,比如有 10種狀態(tài),但是輸出種類只有5種,,很多的狀態(tài)都是相同的輸出,,這在第三段式描述時就可以利用case的簡寫語法減少代碼量,如下所示
case (next)
S0, S2, S4, S5 : ; // defaultoutputs
S7 : y3<= 1'b1;
S1 : y2<= 1'b1;
S3 :begin
y1<= 1'b1;
y2<= 1'b1;
end
S8 :begin
y2<= 1'b1;
y3<= 1'b1;
end
S6,S9 : begin
y1<= 1'b1;
y2<= 1'b1;
y3<= 1'b1;
end
endcase
但是實際的狀態(tài)機中,,還有很多是“狀態(tài)少,,輸出多”的情況,,每種狀態(tài)都會有不同的輸出,這就無法利用上述的case的簡寫語法了,,再試著比較一下,,一段式和三段式的輸出邏輯代碼量幾乎一樣,狀態(tài)轉移部分也差不多,,但是在輸入判斷代碼量方面,,一段式只用判斷一次可完成狀態(tài)轉移和輸出,對于mealy型,,第二段和第三段都要判斷,,這就肯定比一段式多了,對于moor型,,第二段需要判斷一次,,第三段雖然不用判斷但是為了實現(xiàn)相同的功能肯定要將狀態(tài)擴展,相比一段式增加了不少的代碼量,。所以不能一概而論一段式的代碼量就一定比三段式多,,要視具體情況而定。
更多信息可以來這里獲取==>>電子技術應用-AET<<