指定路徑延遲,目的是讓仿真的時序更加接近實(shí)際數(shù)字電路的時序。利用時序約束對數(shù)字設(shè)計(jì)進(jìn)行時序仿真,檢查設(shè)計(jì)是否存在違反(violation)時序約束的地方,并加以修改,也是數(shù)字設(shè)計(jì)中不可或缺的過程。
Verilog 提供了一些系統(tǒng)任務(wù),用于時序檢查。這些系統(tǒng)任務(wù)只能在 specify 塊中調(diào)用。下面就介紹 6 種常用的用于時序檢查的系統(tǒng)任務(wù):$setup, $hold, $recovery, $removal, $width 與 $period。
系統(tǒng)任務(wù) $setup 用來檢查設(shè)計(jì)中元件的建立時間約束條件,$hold 用來檢查保持時間約束條件。其用法格式如下:
$setup(data_event, ref_event, setup_limit);
data_event
?: 被檢查的信號,判斷它是否違反約束
ref_event
?: 用于檢查的參考信號,一般為時鐘信號的跳變沿
setup_limit
?: 設(shè)置的最小建立時間如果 ?T( ref_event - data_event) < setup_limit
?, 則會打印存在 ?violation
?的報告。
$hold(ref_event, data_event, hold_limit);
data_event
?: 被檢查的信號,判斷它是否違反約束
ref_event
?: 用于檢查的參考信號,一般為時鐘信號的跳變沿
hold_limit
?: 設(shè)置的最小保持時間如果 ?T( data_event - ref_event ) < hold_limit
?, 則會打印存在 ?violation
?的報告。
注意: ?$setup
? 和 ?$hold
? 輸入端口的位置是不同的。
Verilog 提供了同時檢查建立時間和保持時間的系統(tǒng)任務(wù):
$setuphold (ref_event, data_event, setup_limit, hold_limit);
下面完成一個數(shù)乘以 15 的操作,來說明 ?$setup
? 和 ?$hold
? 的用法。
Verilog 中,一個變量乘以常數(shù)一般用移位相加的方法來完成,例如對變量 num 乘以 15 的操作可以表示為:
num x 15 = (num << 3) + (num << 2) + (num << 1) + num
此操作需要 3 個加法器。下面對加法器進(jìn)行建模,并指定路徑延遲。
全加器功能描述可參考《Verilog 教程》的 《Verilog 連續(xù)賦值》。
//單 bit 全加器,指定路徑延遲
module full_adder1(
input Ai, Bi, Ci,
output So, Co);
assign So = Ai ^ Bi ^ Ci ;
assign Co = (Ai & Bi) | (Ci & (Ai | Bi));
specify
(Ai, Bi, Ci *> So) = 1.1 ;
(Ai, Bi *> Co) = 1.3 ;
(Ci => Co) = 1.2 ;
endspecify
endmodule
//8bit 位寬加法器例化
module full_adder8(
input [7:0] a , //adder1
input [7:0] b , //adder2
input c , //input carry bit
output [7:0] so , //adding result
output co //output carry bit
);
wire [7:0] co_temp ;
full_adder1 u_adder0(
.Ai (a[0]),
.Bi (b[0]),
.Ci (c==1'b1 ? 1'b1 : 1'b0),
.So (so[0]),
.Co (co_temp[0]));
genvar i ;
generate
for(i=1; i<=7; i=i+1) begin: adder_gen
full_adder1 u_adder(
.Ai (a[i]),
.Bi (b[i]),
.Ci (co_temp[i-1]),
.So (so[i]),
.Co (co_temp[i]));
end
endgenerate
assign co = co_temp[7] ;
endmodule
8bit 位寬的觸發(fā)器描述如下。觸發(fā)器中指定路徑延遲,并加入建立時間和保持時間的時序檢查。
建立時間設(shè)置為 2ns,保持時間設(shè)置為 3ns。
module D8(
input [7:0] d ,
input clk ,
output reg [7:0] q);
always @(posedge clk)
q <= d ;
specify
$setup(d, posedge clk, 2);
$hold(posedge clk, d, 3);
(d,clk *> q) = 0.3 ;
endspecify
endmodule
在 testbench 里完成乘以 15 的操作,并在一個周期內(nèi)輸出給下一級寄存器。
`timescale 1ns/1ns
module test ;
reg [3:0] a ;
reg [3:0] b ;
wire [3:0] so ;
wire co ;
parameter CYCLE_10NS = 10ns;
reg clk ;
initial begin
clk = 0 ;
# 111 ;
forever begin
#(CYCLE_10NS/2) clk = ~clk ;
end
end
//需要乘以 15 的數(shù)
reg [7:0] num = 0 ;
always @(posedge clk) begin
num[3:0] <= num[3:0] + 1 ;
end
// num * 8 + num * 4
wire [7:0] adder1 ;
full_adder8 u1_adder8(
.a (num<<2),
.b (num<<3),
.c (1'b0),
.so (adder1),
.co ());
//num * 2 + num
wire [7:0] adder2 ;
full_adder8 u2_adder8(
.a (num<<1),
.b (num),
.c (1'b0),
.so (adder2),
.co ());
//num x 15
wire [7:0] adder3 ;
full_adder8 u3_adder8(
.a (adder1),
.b (adder2),
.c (1'b0),
.so (adder3),
.co ());
//store the result
wire [7:0] res_mul15 ;
D8 data_store(
.d (adder3),
.clk (clk),
.q (res_mul15));
initial begin
forever begin
#100;
if ($time >= 1000) $finish ;
end
end
endmodule // test
仿真報告中則出現(xiàn)了帶有 setup/hold violation 的打印信息,部分截圖如下。
截取出現(xiàn) violation 時間的波形圖,如下所示。
分析如下:
保持時間的時序優(yōu)化,在 RTL 層級描述上一般不好控制,這屬于后端設(shè)計(jì)工程師的工作范疇,這里不做討論。
本次主要簡單探討建立時間不滿足約束條件時的優(yōu)化問題。由上一節(jié)《Verilog 建立時間和保持時間》中可知建立時間約束表達(dá)式為:
Tcq + Tcomb + Tsu <= Tclk + Tskew (1)
Tcq
?: 寄存器 clock 端到 Q 端的延遲;
Tcomb
?: data path 中的組合邏輯延遲;
Tsu
?: 建立時間;
Tclk
?: 時鐘周期;
Tskew
?: 時鐘偏移。優(yōu)化此不等式可從以下幾個方面考慮:
從 RTL 層次進(jìn)行時序優(yōu)化時,只能考慮方法(2)(3)。
例如,將上述仿真中的工作時鐘周期由 10ns 改為 20ns,則不會出現(xiàn) setup violation。
或者,調(diào)整邏輯,一個周期內(nèi)完成 3 次加法運(yùn)算,改為分散到兩個周期內(nèi)完成,中間增加一級寄存器進(jìn)行緩沖,來減少時序上的壓力。同時,變量 num 的變化周期也應(yīng)該變?yōu)樵瓉淼?nbsp;2 倍時長。
testbench 修改如下:
`timescale 1ns/1ns
`define LOGIC_BUF
module test ;
parameter CYCLE_10NS = 10ns;
reg clk ;
initial begin
clk = 0 ;
# 111 ;
forever begin
#(CYCLE_10NS/2) clk = ~clk ;
end
end
reg slow_flag = 0 ;
always @(posedge clk) begin
`ifdef LOGIC_BUF
slow_flag <= ~slow_flag ;
`else
slow_flag <= 1'b1 ;
`endif
end
reg [7:0] num = 0 ;
always @(posedge clk) begin
if(slow_flag)
num[3:0] <= num[3:0] + 1 ;
end
wire [7:0] adder1 ;
full_adder8 u1_adder8(
.a (num<<2),
.b (num<<3),
.c (1'b0),
.so (adder1),
.co ());
wire [7:0] adder2 ;
full_adder8 u2_adder8(
.a (num<<1),
.b (num),
.c (1'b0),
.so (adder2),
.co ());
//====== for better time=========
//adding buffer
wire [7:0] adder1_r, adder2_r ;
D8 adder1_buf(
.d (adder1),
.clk (clk),
.q (adder1_r));
D8 adder2_buf(
.d (adder2),
.clk (clk),
.q (adder2_r));
`ifdef LOGIC_BUF
wire [7:0] adder1_t = adder1_r ;
wire [7:0] adder2_t = adder2_r ;
`else
wire [7:0] adder1_t = adder1 ;
wire [7:0] adder2_t = adder2 ;
`endif
wire [7:0] adder3 ;
full_adder8 u3_adder8(
.a (adder1_t),
.b (adder2_t),
.c (1'b0),
.so (adder3),
.co ());
wire [7:0] res_mul15 ;
D8 data_store(
.d (adder3),
.clk (clk),
.q (res_mul15));
initial begin
forever begin
#100;
if ($time >= 1000) $finish ;
end
end
endmodule // test
此時仿真報告中不再有 violation,仿真截圖如下。
由圖可知,信號提前到達(dá)并保持不變的時間可達(dá) 8.6 ns,完全滿足建立時間的時序要求。
此種方法的根本原理,是將信號多次變化的時序,分散在多個周期內(nèi),來滿足時序約束的要求。此外,流水線設(shè)計(jì),并行設(shè)計(jì)等都可以優(yōu)化時序。
建立時間和保持時間的概念都是出現(xiàn)在同步電路的設(shè)計(jì)中。
對于異步復(fù)位的觸發(fā)器來說,異步復(fù)位信號也需要滿足 recovery time(恢復(fù)時間)和 removal time(去除時間),才能有效的復(fù)位和釋放復(fù)位,防止出現(xiàn)亞穩(wěn)態(tài)。
釋放復(fù)位時,復(fù)位信號在時鐘有效沿來臨之前就需要提前一段時間恢復(fù)到非復(fù)位狀態(tài),這段時間為 recovery time。類似于同步時鐘下觸發(fā)器的 setup time。
復(fù)位時,復(fù)位信號在時鐘有效沿來臨之后,還需要在一段時間內(nèi)保持不變,這段時間為 removal time。類似于同步時鐘下觸發(fā)器的 hold time。
recovery 與 removal time 示意圖如下所示。
系統(tǒng)任務(wù) ?$recovery
? 與 ?$removal
? 分別用于 recovery 和 removal time 的檢查,用法如下:
$recovery (ref_event, data_event, recovery_limit) ;
ref_event
?: 用于檢查的參考信號,一般為清零或復(fù)位信號跳變沿;
data_event
?: 被檢查的信號,一般為時鐘信號跳變沿。
recovery_limit
?:設(shè)置的最小 recovery time。當(dāng) ?ref_event (reset) < data_event (clock)
? 且 ?T(data_event - ref_event) < recovery_limit
? 時,即復(fù)位信號在時鐘信號到來之前如果不滿足 ?recovery time
?,則報告中會打印 ?violation
?
$removal (ref_event, data_event, removal_limit) ;
ref_event
?: 用于檢查的參考信號,一般為清零或復(fù)位信號跳變沿;
data_event
?: 被檢查的信號,一般為時鐘信號跳變沿。
removal_limit
?:設(shè)置的最小 removal time。當(dāng) ?ref_event (reset) > data_event (clock)
? 且 ?T(ref_event - data_event) > removal_limit
? 時,即復(fù)位信號在時鐘信號到來之后如果不滿足 ?removal time
?,則報告中會打印 ?violation
?。
Verilog 提供了同時檢查 revomal 和 recovery 的系統(tǒng)任務(wù):
$recrem (ref_event, data_event, recovery_limit, removal_limit);
有些數(shù)字設(shè)計(jì),例如 flash 存儲器,還需要對脈沖寬度或周期進(jìn)行檢查,為此 Verilog 分別提供了系統(tǒng)任務(wù) $width 和 $period。用法如下:
$width(ref_event, time_limit) ;
ref_event
?: 邊沿觸發(fā)事件
time_limit
?: 脈沖的最小寬度?$width
? 用于檢查邊沿觸發(fā)事件 ?ref_event
?到下一個反向跳變沿之間的時間,常用于脈沖寬度的檢查。如果兩次相反跳邊沿之間的時間小于 ?time_limit
?,則會報告 ?violation
?。
$period(ref_event, time_limit) ;
?$period
? 用于檢查邊沿觸發(fā)事件 ?ref_event
?到下一個同向跳變沿之間的時間,常用于時鐘周期的檢查。如果兩次同向跳邊沿之間的時間小于 ?time_limit
?,則報告中會打印 ?violation
?。
檢查信號 CLK 寬度和周期的 specify 塊描述如下:
specify
$width(posedge CLK, 10);
$period(posedge CLK, 20);
endspecify
點(diǎn)擊這里下載源碼
更多建議: