博文目錄
-
- 寫在前面
- 正文
- 文法
- 什麼是敏感清單?
- always塊是用來幹什麼的?
- 如果沒有敏感清單怎麼辦?
- 時序邏輯執行個體
- 組合邏輯執行個體
- 注意事項
由于剛畢業,又發生了很多事,在進入工作的準備期,就這樣一停更很多日,接下來幾天也将如此,直到生活穩定下來。
這個簡單的教程還沒有完成,繼續吧。
Verilog中的always塊是Verilog中最常用的一個文法點,可以這麼說,你稍微進行一個正常的設計都會用到always塊,時序邏輯一定會用到,組合邏輯也很可能會用到。
參考網際網路資料,我們從如下幾個方面講解always塊文法。
- always塊是用來做什麼的?
- 如果沒有敏感清單會怎樣?
- 例子
- 時序邏輯設計執行個體
- 組合邏輯設計執行個體
文法很簡單,如何按照結構劃分可以分為:
always @ (event)
[statement]
always @ (event) begin
[multiple statements]
end
第一種是塊内隻有一條語句,不需要使用begin end;第二種是有多條文法,需要使用begin end包裹起來。
這其實都是廢話。我們推薦全部都用begin end包裹起來,這樣形式比較固定,比較方面閱讀以及形成固定風格。
我們按照下面方式劃分,時序邏輯群組合邏輯差別劃分:
劃分組合邏輯以及時序邏輯的依據是敏感清單裡面的内容有沒有邊沿事件?
- 組合邏輯文法:
always @ (level event) begin
[multiple statements]
end
括号内部的敏感清單僅僅為電平事件,例如:
always@(a,b,c,d) begin
out = a&b&c&d;
end
這樣寫的缺點在于有的時候,敏感清單過多,一個一個加入太麻煩,容易忘掉,為了解決這個問題,verilog 2001标準說可以使用*替換敏感清單,表示預設,編譯器會根據always塊内部的内容自動識别敏感變量。
如:
always@(*) begin
out = a&b&c&d;
end
這樣就友善很多。
- 時序邏輯文法:
時序邏輯的always塊将内部敏感清單包括了邊沿事件,一般是時鐘邊沿。
always @ (edge event) begin
[multiple statements]
end
例如我們描述一個同步複位的D觸發器,可以這樣描述:
always@(posedge i_clk) begin
if(i_rst) begin
q <= 0;
end
else begin
q <= d;
end
end
這表示當檢測到時鐘上升沿時,判斷是否複位有效,如果有效對輸出複位,否則采樣輸入d值。
當然時序邏輯敏感清單中的邊沿事件不一定隻是時鐘,也可以是其他邊沿,例如描述一個異步複位的D觸發器:
always@(posedge i_clk or negedge i_rst) begin
if(i_rst) begin
q <= 0;
end
else begin
q <= d;
end
end
這表示當檢測到時鐘上升沿或者複位的上升沿時,都會觸發always塊内部的語句。
但是明顯看出,複位的優先級更高,也就是說,當檢測到複位的上升沿時,無論時鐘邊沿是否檢測到都執行複位操作,否則,當檢測到時鐘上升沿時,采樣輸入值d。
這裡提一嘴吧,敏感清單就是觸發always塊内部語句的條件。
在下面的代碼中,每當信号a或b的值發生變化時,always塊中的所有語句都會被執行。
// Execute always block whenever value of "a" or "b" change
always @ (a or b) begin
[statements]
end
always塊是Verilog中用來描述組合邏輯以及時序邏輯的文法。
在這上面的文法小節中也說過了。
需要補充的是一個設計中可以有多個always塊,或者說一定有很多個always塊。
這些硬體塊都是互相獨立同時工作的。每個塊之間的連接配接是決定資料流的原因。為了模拟這種行為,一個always塊被做成一個連續的過程(硬體不可能斷斷續續工作),當敏感清單中的一個信号變化時,它就會被觸發并執行一些動作(always塊内的語句)。
這個話題比較有意思,你可能說怎麼可能沒有敏感清單?其實,還真的可以沒有敏感清單,這是仿真中的用法。我們經常使用沒有敏感清單的always來表示不斷的觸發,用此特性來生成時鐘。
例如:
always #10 clk = ~clk;
其實,always塊内的敏感清單就是為了控制内部語句什麼時候觸發的。那麼可以了解為一種定時,如果沒有了敏感清單,則為零延遲,那麼就會不斷的觸發,如下:
// always block is started at time 0 units
// But when is it supposed to be repeated ?
// There is no time control, and hence it will stay and
// be repeated at 0 time units only. This continues
// in a loop and simulation will hang !
always clk = ~clk;
上面顯示的示例是一個always塊,它試圖反轉信号clk的值。該語句每0個時間機關執行一次。是以,由于語句中沒有延遲,是以它将永遠執行。
這是沒有意義的,我們正确的做法應該給一個定時或延遲:
即使敏感度清單為空,也應該有其他形式的時間延遲。always如下所示,通過構造中的延遲語句來提前仿真時間。現在,每10個時間機關完成一次時鐘反轉。
always #10 clk = ~clk;
當然,這種沒有敏感清單的顯示延遲,是不能綜合的,隻能用于仿真。
下面顯示的代碼定義了一個名為tff的子產品,該子產品接受資料輸入,時鐘和低電平有效複位。每當在時鐘的上升沿發現d為1 時,輸出就會反相。此處,該always塊在clk的上升沿或rstn的下降沿觸發。
module tff (input d,
clk,
rstn,
output reg q);
always @ (posedge clk or negedge rstn) begin
if (!rstn)
q <= 0;
else begin
if (d)
q <= ~q;
else
q <= q;
end
end
endmodule
開始的文法講解中其實也舉了一些組合邏輯以及時序邏輯的例子,這裡在給出使用always塊設計組合邏輯的一個例子:
如下圖:
使用always塊描述這個邏輯:
module combo ( input a,
input b,
input c,
input d,
output reg o);
always @ (a or b or c or d) begin
o <= ~((a & b) | (c^d));
end
endmodule
- always塊内被指派的變量(左值)都應該為reg類型。
- 個人微信公衆号: FPGA LAB