FPGA研發之道(24)-控制(下)

 首先依次回答上篇提出的幾個問題:

      第一個問題:如何避免狀態機產生lacth 示例如下,通過在always*)語句塊中,添加默認賦值,ns_state = cs_state;

always@(*)

      ns_state = cs_state;

      case(cs_state)     

         idle :

                  if(start)

                  ns_state =  op1_state;

         op0_state :

                if(op0_over)

                  ns_state =  op1_state;

         op1_state :

                if(op1_over)

                  ns_state =  op2_state;

         op2_state :

                if(op2_over)

                  ns_state =  op3_state;

         op3_state :

                if(op3_over)

                  ns_state =  op4_state;

         op4_state :

                if(op4_over)

                  ns_state =  op4_state;

         default ns_state = idle;

       endcase

這樣,分支沒有賦值的語句全部會賦值爲ns_state = cs_state ;IDLE狀態爲例,當前cs_stateidle。因此實際上 ns_state=idle。這條語句的作用,即在沒有分支賦值的情況下,默認賦值當前狀態。

第二個問題:更直觀的獨熱碼的狀態機實現方式。示例如下

//狀態定義

parameter   idle == 0,

             op0_state == 1,

             op1_state == 2,

             op2_state == 3,

             op3_state == 4,

             op4_state == 5;

//1)當前狀態

always@(posedge sys_clk or negedge rst_n)

       if(!rst_n)

         cs_state <=  6'b000001;

       else

         cs_state <= ns_state;

//2)下一狀態的賦值

always@(*)

        ns_state = 0;

      case(1)     

         cs_state[idle] :

                  if(start)

                    ns_state[op0_state] =  1'b1;

                  else

                    ns_state[idle] = 1'b1; 

          cs_state[op0_state] :

                  if(op0_over)

                    ns_state[op1_state] = 1'b1;

                  else

                    ns_state[op0_state] = 1'b1; 

          cs_state[op1_state] :

                  if(op1_over)

                    ns_state[op2_state] = 1'b1;

                  else

                    ns_state[op1_state] = 1'b1; 

         cs_state[op2_state] :

                  if(op2_over)

                    ns_state[op3_state] = 1'b1;

                  else

                    ns_state[op2_state] = 1'b1; 

         cs_state[op3_state]:

                  if(op3_over)

                    ns_state[op4_state] = 1'b1;

                  else

                    ns_state[op3_state] = 1'b1; 

         cs_state[op4_state] :

                  if(op4_over)

                    ns_state[idle]= 1'b1;

                  else

                    ns_state[op4_state] = 1'b1; 

         default    ns_state[idle]= 1'b1;

       endcase

//3)輸出狀態  

assign out1 = cs_state [op1_state];

always@(posedge sys_clk or negedge rst_n)

       if(!rst_n)

         out2_reg <= 1'b0;

       else if (cs_state[op2_state])

         out2_reg <= 1'b1;

       else

         out2_reg <= 1'b0;

        

上例中,定義狀態機是,同樣定義爲0,1,2,3,4,5,6的值而不是獨熱碼,只不過使用時,這些值用於賦值的爲狀態機的某一bit。值得注意的是,在ns_state 通過組合邏輯賦值時,首先需要將ns_state賦值爲零,也就數說,除了需要賦值爲1的狀態,其他都需要賦值爲0。但此種編碼方式下,就需要謹慎對待分支賦值不全的情況,因此此時,ns_state會賦值爲0。產生非想要的後果。

   通過第三段的輸出賦值可以看出,其輸出分別是cs_state [op1_state]的直接輸出,cs_state[op2_state]的寄存後一拍再輸出。其產生的效果與前文(控制-上)中介紹的產生的效果是一致的。因此可根據習慣,選擇一種的實現即可。

最後一個問題:狀態機使用可以直觀的通過定義的狀態來控制各個信號的輸出和控制,獨熱碼本質上還是將狀態機轉變成一組某一時刻只有一個起效的寄存器,換個角度可以看做加強版的移位寄存器。其他需要注意問題有,

1)如果狀態機定義而沒有使用,綜合工具將綜合掉此狀態,因此綜合後的狀態會和工程師所定義的狀態不同。通過檢查綜合文件,就能得知其對應關係,避免通過嵌入式邏輯分析儀抓信號時,信號與實際不一致現象。

2)如通過單週期信號啓動狀態機,要注意,如單週期信號起效時,狀態機未跳轉回有效狀態,會導致出錯,應該將單週期信號轉換成電平信號,等啓動有效後再將電平信號拉低。

3)狀態如出現未定義的狀態(如獨熱碼出現全零狀態),latch是其中一個主要的原因,上電後未有效復位也會導致此種可能。

4)狀態機結合移位寄存器可以有效減少狀態的數目。例如某個狀態中,每個週期要進行多個操作,不需要再分解成多個小狀態,通過移位寄存器來控制這些狀態的操作能夠簡化設計。

總之:狀態機是FPGA設計的一項基本設計,而“獨熱碼”和“三段式的設計能夠使設計達到事半功倍的效果。也是事實上行業內的FPGA設計的標準寫法。而標準化能使難以理解的FPGA的代碼能夠有更好的移植和IP化的基礎。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章