Verilog中延時和時序檢查
- 1. 延時模型
- 1.1 分布延遲
- 1.2 集總延遲
- 1.3 路徑延遲
- 2. specify 語法
- 2.1 指定路徑延時
- 基本路徑延時
- 邊沿敏感路徑延時
- 狀態依賴路徑延時
- 2.2 時序檢查
- $setup, $hold, $setuphold
- $recovery, $removal, $recrem
- $width, $period
- notifier
1. 延時模型
真實的邏輯元器件和它們之間的互連線上都會有延時的存在。雖然 Verilog 設計主要考慮的是邏輯功能的正確性,但是 Verilog 語法是支持定義延時的。
Verilog 中延時模型有三種:分布延遲、集總延遲(lumped) 和路徑延遲。
1.1 分布延遲
分布延遲指的是給電路中每個獨立的元件進行延遲定義,不同的路徑有不同的延時,如下圖所示。
對應的 verilog 描述為:
// 例化邏輯門單元的時候指定延時
module and4(output out,input a, b, c, d);wire an1, an2 ;and #1 (an1, a, b);and #2 (an2, c, d);and #1.5 (out, an1, an2);
endmodule// assign 語句中指定延時
module and4(output out,input a, b, c, d);wire an1, an2 ;assign #1 an1 = a & b ;assign #2 an2 = c & d ;assign #1.5 out = an1 & an2 ;
endmodule
1.2 集總延遲
集總延遲是將全部路徑累計的延時集中到最后一個門單元上。
到最后一個門單元上的延遲會因路徑的不同而不同,此時取最大延時作為最后一個門單元的延時。
將上述分布延遲圖轉化為集總延遲圖,如下所示。
對應的 verilog 描述如下:
module and4(output out,input a, b, c, d);wire an1, an2 ;and (an1, a, b);and (an2, c, d);and #3.5 (out, an1, an2); //set the max delay at the last gate
endmodule
1.3 路徑延遲
路徑延遲是對每個輸入引腳到每個輸出引腳的所有路徑指定延遲時間。
路徑延遲模型需要使用 specify
關鍵字來定義,上圖對應的 verilog 描述如下所示:
module and4(output out,input a, b, c, d);specify(a => out) = 2.5 ;(b => out) = 2.5 ;(c => out) = 3.5 ;(d => out) = 3.5 ;endspecifywire an1, an2 ;and (an1, a, b);and (an2, c, d);and (out, an1, an2);
endmodule
2. specify 語法
Verilog 中的路徑延遲使用 specify 塊語句來描述,從 specify
為開始,到 endspecify
結束。
specify
是 module 中獨立的一部分,不能出現在其他語句塊(initial, always 等)中。
specify
塊語句的主要功能是:指定所有路徑中引腳到引腳的延遲、在電路中設置時序檢查。
2.1 指定路徑延時
基本路徑延時
specify
塊語句有兩種基本的語法來定義延時:
方法一: 并行連接,每條路徑都有一個源引腳和目的引腳,將這些路徑的延遲依次用 specify 語句描述出來。
基本語法為:
(<source_io> => <destination_io>) = <delay_value>;
一個帶有路徑延時的 4 輸入與門的 verilog 描述如下:
module and4(output out,input a, b, c, d);specify(a => out) = 2.5 ;(b => out) = 2.5 ;(c => out) = 3.5 ;(d => out) = 3.5 ;endspecifywire an1, an2 ;and (an1, a, b);and (an2, c, d);and (out, an1, an2);
endmodule
在使用 specify
定義路徑延時的時候,也可以定義參數,如下所示:
specifyspecparam ab_2_out = 2.5 ;specparam cd_2_out = 3.5 ;(a => out) = ab_2_out ;(b => out) = ab_2_out ;(c => out) = cd_2_out ;(d => out) = cd_2_out ;
endspecify
需要注意的是,specparam
只能在 specify
內部聲明及使用,而 parameter
只能在 specify
語句塊的外部聲明及使用。
在并行連接中,源引腳和目的引腳是一一對應的。并行連接也支持多位寬信號間的路徑延遲描述,但是位寬必須保持一致。
module paral_conn(input [3:0] d,output [3:0] q);specify(d => q) = 3 ;endspecifyassign q = d & 0101 ;
endmodule
上例的路徑延時定義等價于:
specify(d[0] => q[0]) = 3 ;(d[1] => q[1]) = 3 ;(d[2] => q[2]) = 3 ;(d[3] => q[3]) = 3 ;
endspecify
方法二: 全連接,源引腳中的每一位與目標引腳的每一位相連接。源引腳和目的引腳的連接是組合遍歷的,且不要求位寬對應。
基本語法為:
(<multiple_source_io> *> <multiple_destination_io>) = <delay_value> ;
如下所示,4 輸入的與邏輯模塊的路徑延時可以為:
module and4(output out,input a, b, c, d);specify(a,b *> out) = 2.5 ;(c,d *> out) = 3.5 ;endspecifywire an1, an2 ;and (an1, a, b);and (an2, c, d);and (out, an1, an2);
endmodule
邊沿敏感路徑延時
邊沿敏感路徑延時用于對時序電路的輸入到輸出延遲進行建模,需要使用邊緣標識符指明觸發條件。如果沒有指明的話,任何變化都會觸發源引腳到目的引腳的延遲值的變化。
示例1:
(posedge clk => (out +: in)) = (1,2);
在 clk 的上升沿,對于從 clk 到 out 的路徑,其上升延時是 1,下降延時是 2。+:
的意思是 in 到 out 的數據路徑是同向傳輸,即 out = in。
示例2:
(negedge clk => (out -: in)) = (1,2);
在 clk 的下降沿,對于從 clk 到 out 的路徑,其上升延時是 1,下降延時是 2。-:
的意思是 in 到 out 的數據路徑是反向傳輸,即 out = ~in。
示例3:
(negedge clk => (out : in)) = (1,2);
clk 的任何變化,從clk到out的模塊路徑,其上升延時是1,下降延時是2,從in到out的數據路徑的傳輸是不可預知的,同向或者反向或者不變。
狀態依賴路徑延時
Verilog 也允許模型中根據信號值的不同,有條件的給路徑延遲進行不同的賦值。
一個簡單的示例如下所示:
specifyif (a) (a => out) = 2.5 ;if (~a) (a => out) = 1.5 ;if (b & c) (b => out) = 2.5 ;if (!(b & c)) (b => out) = 1.5 ;if ({c, d} == 2'b01)(c,d *> out) = 3.5 ;ifnone (c,d *> out) = 3 ;
endspecify
需要注意的是:
if
語句的操作數可以是標量,也可以是向量,條件表達式也可以包含任意操作符;- 所有輸入狀態都應該說明,否則沒有說明的路徑使用分布延時,如果也沒有聲明分布延時的話,那么使用零延時(zero delay)。如果路徑延時和分布延時同時聲明的話,則選擇最大的延時作為路徑延時;
- 可以使用
ifnone
語句,在其它所有條件都不滿足的情況下,說明一個缺省的狀態依賴路徑延時。
2.2 時序檢查
使用 specify
指定路徑延遲之后,可以讓仿真的時序更加接近實際數字電路的時序。
除此之外,specify
還可以定義一些系統任務,用來進行時序檢查。
Verilog 中常用的用于時序檢查的系統任務包括:$setup, $hold, $recovery, $removal, $widt, $period
,這些系統任務只能在 specify
塊中調用。
$setup, $hold, $setuphold
$setup
用來進行建立時間檢查,$hold
用來進行保持時間檢查,基本語法格式如下:
$setup(data_event, reference_event, limit, notifier);
data_event:被檢查的信號,判斷它是否違反約束
reference_event:用于檢查的參考信號,一般為時鐘信號的跳變沿
limit:最小建立時間
當 reference_event time - limit < data_event time < reference_event time 的時候,仿真的時候會打印出 Timing Violation 的報告。
$hold (reference_event, data_event, limit, notifier);
當 reference_event time < data_event time < reference_event time + limit 的時候,仿真的時候會報告 Hold Timing Violation。
這里需要注意的是,$setup
和 $hold
中輸入端口的位置是不一樣的。setup 檢查中,數據要先到,hold 檢查中,數據要晚走,所以可以按照事件的事件順序來記憶。
此外,Verilog 還提供了同時檢查setup 和 hold 的系統任務:
$setuphold (reference_event, data_event, setup_limit, hold_limit, notifier)
該系統函數等價于:
$setup(data_event, reference_event, setup_limit, notifier);
$hold (reference_event, data_event, hold_limit, notifier);
當 reference_event time - setup_limit < data_event time < reference_event time + hold_limit 的時候,仿真的時候會報時序違例。
$recovery, $removal, $recrem
對于異步復位的觸發器來說,異步復位信號也需要滿足 recovery time(恢復時間)和 removal time(去除時間),才能有效的復位和釋放復位,防止出現亞穩態。
$recovery, $removal, $recrem
的基于語法如下:
$recovery (reference_event, data_event, limit, notifier);
reference_event:用于檢查的參考信號,一般為清零或復位信號跳變沿;
data_event:被檢查的信號,一般為時鐘信號跳變沿。
limit:設置的最小 removal time。
當data_event time - limit(clk) < reference_event time(async rst) < data_event time(clk) 時,就會報告recovery time violations。
$removal (reference_event, data_event, limit, notifier);
當 data_event time < reference_event time < data_event time + limit時,就會報告removal time violations。
$recrem (reference_event, data_event, recovery_limit, removal_limit, notifier)
$width, $period
有些數字設計,例如 flash 存儲器,還需要對脈沖寬度或周期進行檢查,為此 Verilog 分別提供了系統任務 $width
和 $period
。用法如下:
$width (ref_event, time_limit, notifier);$period(ref_event, time_limit, notifier);
ref_event:邊沿觸發事件
time_limit:脈沖的最小寬度
這里data_event是隱含的,它等于reference_event的相反邊沿。
$width
用于檢查邊沿觸發事件 ref_event 到下一個反向跳變沿之間的時間,常用于脈沖寬度的檢查。如果兩次相反跳邊沿之間的時間小于 time_limit,則會報告 violation。
$period
用于檢查邊沿觸發事件 ref_event 到下一個同向跳變沿之間的時間,常用于時鐘周期的檢查。如果兩次同向跳邊沿之間的時間小于 time_limit,則報告中會打印 violation。
notifier
任意一條 timing check 語句檢測到timing violation發生時,對應的 timing check 語句就會把 notifier 的值做一次 toggle。
notifier 的初始默認值是 x,第1次 timing violation 時,notifier 的值會從x變為0或1。后續每發生一次 timing violation,notifier 的值也會被做一次toggle。如果舊值為0,則新值為1。如果舊值為1,則新值為0。
notifier 的 toggle,會導致寄存器的Q端變為 x。
如下圖所示,notifier 是 dff 的一個輸入端口,當前發生任意跳變的時候,dff 的輸出都將變為 x 態。