用Verilog HDL語言設計可綜合的狀態機的指導原則

用Verilog HDL語言設計可綜合的狀態機的指導原則:

因爲大多數FPGA內部的觸發器數目相當多,又加上獨熱碼狀態機( one hot state machine)的譯碼邏輯最爲簡單, 所以在設計採用FPGA實現的狀態機時往往採用獨熱碼狀態機(即每個狀態只有一個寄存器置位的狀態機)。

建議採用case,casex,或casez語句來建立狀態機的模型, 因爲這些語句表達清晰明瞭,可以方便地從當前狀態分支轉向下一個狀態並設置輸出。不要忘記寫上case語句的最後一個分支default,並將狀態變量設爲’bx,這就等於告知綜合器:case語句已經指定了所有的狀態,這樣綜合器就可以刪除不需要
的譯碼電路,使生成的電路簡潔,並與設計要求一致。

如果將缺省狀態設置爲某一確定的狀態(例如:設置default:state = state1)行不行呢?回答是這樣做有一個問題需要注意。因爲儘管綜合器產生的邏輯和設置default:state='bx時相同,但是狀態機的Verilog HDL模型綜合前和綜合後的仿真結果會不一致。爲什麼會是這樣呢?因爲啓動仿真器時,狀態機所有的輸入都不確定,因此立即進入default狀態,這樣的設置便會將狀態變量設爲state1,但是實際硬件電路的狀態機在通電之後,進入的狀態是不確定的,很可能不是state1的狀態,因此還是設置default: state='bx與實際情況相一致。但在有多餘狀態的情況下還是應將缺省狀態設置爲某一確定的有效狀態,因爲這樣做能使狀態機若偶然進入多餘狀態後任能在下一時鐘跳變沿時返回正常工作狀態,否則會引起死鎖。

狀態機應該有一個異步或同步復位端,以便在通電時將硬件電路復位到有效狀態,也可以在操作中將硬件電路復位(大多數FPGA結構都允許使用異步復位端)。

目前大多數綜合器往往不支持在一個always塊中由多個事件觸發的狀態機(即隱含狀態機,implicitstate machines), 爲了能綜合出有效的電路,用Verilog HDL描述的狀態機應明確地由唯一時鐘觸發。 目前大多數綜合器不能綜合採用Verilog HDL描述的異步狀態機。異步狀態機是沒有確定時鐘的狀
態機,它的狀態轉移不是由唯一的時鐘跳變沿所觸發。

千萬不要使用綜合工具來設計異步狀態機。 因爲目前大多數綜合工具在對異步狀態機進行邏輯優化時會胡亂地簡化邏輯,使綜合後的異步狀態機不能正常工作。 如果一定要設計異步狀態機,我們建議採用電路圖輸入的方法,而不要用Verilog HDL輸入的方法。

Verilog HDL中,狀態必須明確賦值,通常使用參數(parameters)或宏定義(define)語句加上賦值語句來實現。使用參數(parameters)語句賦狀態值見下例:
parameter state1 = 2 'h1, state2 = 2 'h2;

current_state = state2; //把current state設置成 2’h2

使用宏定義(define)語句賦狀態值見下例:
` define state1 2 'h1

`define state2 2 'h2

current_state = `state2; //把current state設置成 2 'h2

典型的狀態實例:
例1宇宙飛船控制器的狀態機

module statmch1( launch_shuttle, land_shuttle, start_countdown,
				start_trip_meter, clk, all_systems_go,
				just_launched, is_landed, cnt, abort_mission);
	output launch_shuttle, land_shuttle, start_countdown,
			start_trip_meter;
	input clk, just_launched, is_landed, abort_mission,
			all_systems_go;
	input [3:0] cnt;
	reg launch_shuttle, land_shuttle, start_countdown,
		start_trip_meter;
//設置獨熱碼狀態的參數
parameter HOLD=5'h1, SEQUENCE=5'h2, LAUNCH=5'h4;
parameter ON_MISSION=5'h8, LAND=5'h10;
reg [4:0] present_state, next_state;
always @(negedge clk or posedge abort_mission)
begin
/****把輸出設置成某個缺省值,在下面的case語句中
就不必再設置輸出的缺省值*******/
	{launch_shuttle, land_shuttle, start_trip_meter, start_countdown} =4'b0;
/*檢查異步reset的值即abort_mission的值*/
	if(abort_mission)
		next_state=LAND;
	else
		begin // if-else-begin
/*如果reset爲零,把next_state賦值爲present_state*/
		next_state = present_state;
/*根據 present_state 和輸入信號,設置 next_state
和輸出output*/
		case ( present_state )
			HOLD: if(all_systems_go)
					begin
						next_state = SEQUENCE;
						start_countdown = 1;
					end
		SEQUENCE: if(cnt==0)
			next_state = LAUNCH;
		LAUNCH:
			begin
			next_state = ON_MISSION;
			launch_shuttle = 1;
			end
		ON_MISSION:
//取消使命前,一直留在使命狀態
			if(just_launched)
				start_trip_meter = 1;
			LAND: if(is_landed)
				next_state = HOLD;
				else land_shuttle = 1;
/*把缺省狀態設置爲'bx(無關)或某種已知狀態,使其
在做仿真時,在復位前就與實際情況相一致*/
			default: next_state = 'bx;
		endcase
		end // end of if-else
/*把當前狀態變量設置爲下一狀態,待下一有效時鐘沿來到
時當前狀態變量已設置了正確的狀態值*/
		present_state = next_state;
	end //end of always
endmodule

綜合的一般原則:
1) 綜合之前一定要進行仿真,這是因爲仿真會暴露邏輯錯誤,所以建議大家這樣做。如果不做仿真,沒有發現的邏輯錯誤會進入綜合器,使綜合的結果產生同樣的邏輯錯誤。
2) 每一次佈局佈線之後都要進行仿真,在器件編程或流片之前要做最後的仿真。
3) 用Verilog HDL描述的異步狀態機是不能綜合的,因此應該避免用綜合器來設計,如果一定要設計異步狀態機則可用電路圖輸入的方法來設計。
4) 如果要爲電平敏感的鎖存器建模,使用連續賦值語句是最簡單的方法。

語言指導原則
always塊:

  • 每個always塊只能有一個事件控制"@(event-expression)",而且要緊跟在always關鍵字後面。
  • always塊可以表示時序邏輯或者組合邏輯,也可以用always塊既表示電平敏感的透明鎖存器又同時表示組合邏輯。但是不推薦使用這種描述方法,因爲這容易產生錯誤和多餘的電平敏感的透明鎖存器
  • 帶有posedge 或 negedge 關鍵字的事件表達式表示沿觸發的時序邏輯,沒有posedge 或negedge關鍵字的表示組合邏輯或電平敏感的鎖存器,或者兩種都表示。在表示時序和組合邏輯的事件控制表達式中如有多個沿和多個電平,其間必須用關鍵字 “ or ” 連接 。
  • 每個表示時序always塊只能由一個時鐘跳變沿觸發,置位或復位最好也由該時鐘跳變沿觸發。
  • 每個在always塊中賦值的信號都必需定義成reg型或整型。整型變量缺省爲32bit,使用Verilog操作符可對其進行二進制求補的算術運算。綜合器還支持整型量的範圍說明,這樣就允許產生不是32位的整型量。句法結構:integer[:]。
  • always塊中應該避免組合反饋迴路。每次執行always塊時,在生成組合邏輯的always塊中賦值的所有信號必需都有明確的值;否則,需要設計者在設計中加入電平敏感的鎖存器來保持賦值前的最後一個值,只有這樣綜合器才能正常生成電路。如果不這樣做綜合器會發出警告提示設計中插入了鎖存器。如果在設計中存在綜合器認爲不是電平敏感鎖存器的組合迴路時,綜合器會發出錯誤信息(例如設計中有異步狀態機時)。

上面這一段不太好理解,讓我們再解釋一下,這也就是說,用always塊設計純組合邏輯電路時, 在生成組合邏輯的always塊中參與賦值的所有信號都必需有明確的值[即在賦值表達式右端參與賦值的信號都必需在always @(敏感電平列表)中列出],如果在賦值表達式右端引用了敏感電平列表中沒有列出的信號,那麼在綜合時, 將會爲該沒有列出信號隱含地產生一個透明鎖存器,這是因
爲該信號的變化不會立刻引起所賦值的變化,而必須等到敏感電平列表中某一個信號變化時,它的作用才顯現出來,也就是相當於存在着一個透明鎖存器把該信號的變化暫存起來,待敏感電平列表中某一個信號變化時再起作用, 純組合邏輯電路不可能做到這一點。這樣,綜合後所得電路已經不是純組合邏輯電路了,這時綜合器會發出警告提示設計中插入了鎖存器。見下例。

input a,b,c;
	reg e,d;
	always @(a or b or c)
	begin
		e =d & a & b;
/* 因爲d沒有在敏感電平列表中,所以d變化時,
e不能立刻變化,要等到a或b或c變化時才體現出來,
這就是說實際上相當於存在一個電平敏感的透
明鎖存器在起作用, 把d信號的變化鎖存其中 */
		d =e | c;
	end

賦值:

  1. 對一個寄存器型(reg)和整型(integer)變量給定位的賦值只允許在一個always塊內進行,如在另一always塊也對其賦值,這是非法的。
  2. 把某一信號值賦爲’bx,綜合器就把它解釋成無關狀態,因而綜合器爲其生成的硬件電路最簡潔。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章