數(shù)字電路中,信號(hào)傳輸與狀態(tài)變換時(shí)都會(huì)有一定的延時(shí)。
例如,對(duì)于給定邏輯 ?F = A & A'
?,電路如左下圖所示。
由于反相器電路的存在,信號(hào) ?A'
? 傳遞到與門(mén)輸入端的時(shí)間相對(duì)于信號(hào) ?A
? 會(huì)滯后,這就可能導(dǎo)致與門(mén)最后的輸出結(jié)果 ?F
? 會(huì)出現(xiàn)干擾脈沖。如下圖所示。
其實(shí)實(shí)際硬件電路中,只要門(mén)電路各個(gè)輸入端延時(shí)不同,就有可能產(chǎn)生競(jìng)爭(zhēng)與冒險(xiǎn)。
例如一個(gè)簡(jiǎn)單的與門(mén),輸入信號(hào)源不一定是同一個(gè)信號(hào)變換所來(lái),由于硬件工藝、其他延遲電路的存在,也可能產(chǎn)生競(jìng)爭(zhēng)與冒險(xiǎn),如下圖所示。
在邏輯表達(dá)式,保持一個(gè)變量固定不動(dòng),將剩余其他變量用 0 或 1 代替,如果最后邏輯表達(dá)式能化簡(jiǎn)成?Y = A + A'
?或?Y = A · A'
?的形式,則可判定此邏輯存在競(jìng)爭(zhēng)與冒險(xiǎn)。
例如邏輯表達(dá)式 ?Y = AB + A'C
?,在 ?B=C=1
? 的情況下,可化簡(jiǎn)為 ?Y = A + A'
?。顯然,?A
? 狀態(tài)的改變,勢(shì)必會(huì)造成電路存在競(jìng)爭(zhēng)冒險(xiǎn)。
有兩個(gè)相切的卡諾圈,并且相切處沒(méi)有其他卡諾圈包圍,可能會(huì)出現(xiàn)競(jìng)爭(zhēng)與冒險(xiǎn)現(xiàn)象。
例如左下圖所存在競(jìng)爭(zhēng)與冒險(xiǎn),右下圖則沒(méi)有。
其實(shí),卡諾圖本質(zhì)上還是對(duì)邏輯表達(dá)式的一個(gè)分析,只是可以進(jìn)行直觀(guān)的判斷。
例如,左上圖邏輯表達(dá)式可以簡(jiǎn)化為 ?Y = A'B' + AC
?,當(dāng) ?B=0
? 且 ?C=1
? 時(shí),此邏輯表達(dá)式又可以表示為 ?Y = A' + A
?。所以肯定會(huì)存在競(jìng)爭(zhēng)與冒險(xiǎn)。
右上圖邏輯表達(dá)式可以簡(jiǎn)化為 ?Y = A'B' + AB
?,顯然 B 無(wú)論等于 1 還是 0,此式都不會(huì)化簡(jiǎn)成 ?Y = A' + A
?。所以此邏輯不存在競(jìng)爭(zhēng)與冒險(xiǎn)。
需要注意的是,卡諾圖是首尾相臨的。如下圖所示,雖然看起來(lái)兩個(gè)卡諾圈并沒(méi)有相切,但實(shí)際上,m6 與 m4 也是相鄰的,所以下面卡諾圖所代表的數(shù)字邏輯也會(huì)產(chǎn)生競(jìng)爭(zhēng)與冒險(xiǎn)。
其他較為復(fù)雜的情況,可能需要采用 "計(jì)算機(jī)輔助分析 + 實(shí)驗(yàn)" 的方法。
對(duì)數(shù)字電路來(lái)說(shuō),常見(jiàn)的避免競(jìng)爭(zhēng)與冒險(xiǎn)的方法主要有 4 種。
此種方法需要在輸出端并聯(lián)一個(gè)小電容,將尖峰脈沖的幅度削弱至門(mén)電路閾值以下。
此方法雖然簡(jiǎn)單,但是會(huì)增加輸出電壓的翻轉(zhuǎn)時(shí)間,易破壞波形。
利用卡諾圖,在兩個(gè)相切的圓之間,增加一個(gè)卡諾圈,并加在邏輯表達(dá)式之中。
如下圖所示,對(duì)數(shù)字邏輯 ?Y = A'B' + AC
? 增加冗余項(xiàng) ?B'C
?,則此電路邏輯可以表示為 ?Y = A'B' + AC + B'C
?。此時(shí)電路就不會(huì)再存在競(jìng)爭(zhēng)與冒險(xiǎn)。
同步電路信號(hào)的變化都發(fā)生在時(shí)鐘邊沿。對(duì)于觸發(fā)器的 D 輸入端,只要毛刺不出現(xiàn)在時(shí)鐘的上升沿并且不滿(mǎn)足數(shù)據(jù)的建立和保持時(shí)間,就不會(huì)對(duì)系統(tǒng)造成危害,因此可認(rèn)為 D 觸發(fā)器的 D 輸入端對(duì)毛刺不敏感。 利用此特性,在時(shí)鐘邊沿驅(qū)動(dòng)下,對(duì)一個(gè)組合邏輯信號(hào)進(jìn)行延遲打拍,可消除競(jìng)爭(zhēng)冒險(xiǎn)。
延遲一拍時(shí)鐘時(shí),會(huì)一定概率的減少競(jìng)爭(zhēng)冒險(xiǎn)的出現(xiàn)。實(shí)驗(yàn)表明,最安全的打拍延遲周期是 3 拍,可有效減少競(jìng)爭(zhēng)冒險(xiǎn)的出現(xiàn)。
當(dāng)然,最終還是需要根據(jù)自己的設(shè)計(jì)需求,對(duì)信號(hào)進(jìn)行合理的打拍延遲。
為說(shuō)明對(duì)信號(hào)進(jìn)行打拍延遲可以消除競(jìng)爭(zhēng)冒險(xiǎn),我們建立下面的代碼模型。
module competition_hazard
(
input clk ,
input rstn ,
input en ,
input din_rvs ,
output reg flag
);
wire condition = din_rvs & en ; //combination logic
always @(posedge clk or negedge !rstn) begin
if (!rstn) begin
flag <= 1'b0 ;
end
else begin
flag <= condition ;
end
end
endmodule
testbench 描述如下:
`timescale 1ns/1ns
module test ;
reg clk, rstn ;
reg en ;
reg din_rvs ;
wire flag_safe, flag_dgs ;
//clock and rstn generating
initial begin
rstn = 1'b0 ;
clk = 1'b0 ;
#5 rstn = 1'b1 ;
forever begin
#5 clk = ~clk ;
end
end
initial begin
en = 1'b0 ;
din_rvs = 1'b1 ;
#19 ; en = 1'b1 ;
#1 ; din_rvs = 1'b0 ;
end
competition_hazard u_dgs
(
.clk (clk ),
.rstn (rstn ),
.en (en ),
.din_rvs (din_rvs ),
.flag (flag_dgs ));
initial begin
forever begin
#100;
if ($time >= 1000) $finish ;
end
end
endmodule // test
仿真結(jié)果如下:
由圖可知,信號(hào) ?condition
?出現(xiàn)了一個(gè)尖峰脈沖,這是由于信號(hào) ?din_rvs
?與信號(hào) ?en
?相對(duì)于模塊內(nèi)部時(shí)鐘都是異步的,所以到達(dá)內(nèi)部門(mén)電路時(shí)的延時(shí)是不同的,就有可能造成競(jìng)爭(zhēng)冒險(xiǎn)。
雖然最后的仿真結(jié)果 ?flag
?一直為 0,似乎是我們想要的結(jié)果。但是實(shí)際電路中,這個(gè)尖峰脈沖在時(shí)間上非??拷鼤r(shí)鐘邊沿,就有可能被時(shí)鐘采集到而產(chǎn)生錯(cuò)誤結(jié)果。
下面我們對(duì)模型進(jìn)行改進(jìn),增加打拍延時(shí)的邏輯,如下:
module clap_delay
(
input clk ,
input rstn ,
input en ,
input din_rvs ,
output reg flag
);
reg din_rvs_r ;
reg en_r ;
always @(posedge clk or !rstn) begin
if (!rstn) begin
din_rvs_r <= 1'b0 ;
en_r <= 1'b0 ;
end
else begin
din_rvs_r <= din_rvs ;
en_r <= en ;
end
end
wire condition = din_rvs_r & en_r ;
always @(posedge clk or negedge !rstn) begin
if (!rstn) begin
flag <= 1'b0 ;
end
else begin
flag <= condition ;
end
end // always @ (posedge clk or negedge !rstn)
endmodule
將此模塊例化到上述 testbench 中,得到如下仿真結(jié)果。
由圖可知,信號(hào) ?condition
?沒(méi)有尖峰脈沖的干擾了,仿真結(jié)果中 ?flag
?為 0 也如預(yù)期。
其實(shí),輸入信號(hào)與時(shí)鐘邊沿非常接近的情況下,時(shí)鐘對(duì)輸入信號(hào)的采樣也存在不確定性,但是不會(huì)出現(xiàn)尖峰脈沖的現(xiàn)象。對(duì)輸入信號(hào)多打 2 拍,是更好的處理方式,對(duì)競(jìng)爭(zhēng)與冒險(xiǎn)有更好的抑制作用。
遞加的多 bit 位計(jì)數(shù)器,計(jì)數(shù)值有時(shí)候會(huì)發(fā)生多個(gè) bit 位的跳變。
例如計(jì)數(shù)器變量 counter 從 5 計(jì)數(shù)到 6 時(shí), 對(duì)應(yīng)二進(jìn)制數(shù)字為 4'b101 到 4'b110 的轉(zhuǎn)換。因?yàn)楦?nbsp;bit 數(shù)據(jù)位的延時(shí),counter 的變換過(guò)程可能是: ?4'b101 -> 4'b111 -> 4'b110
?。如果有以下邏輯描述,則信號(hào) cout 可能出現(xiàn)短暫的尖峰脈沖,這顯然是與設(shè)計(jì)相悖的。
cout = counter[3:0] == 4'd7 ;
而格雷碼計(jì)數(shù)器,計(jì)數(shù)時(shí)相鄰的數(shù)之間只有一個(gè)數(shù)據(jù) bit 發(fā)生了變化,所以能有效的避免競(jìng)爭(zhēng)冒險(xiǎn)。
好在 Verilog 設(shè)計(jì)時(shí),計(jì)數(shù)器大多都是同步設(shè)計(jì)。即便計(jì)數(shù)時(shí)存在多個(gè) bit 同時(shí)翻轉(zhuǎn)的可能性,但在時(shí)鐘驅(qū)動(dòng)的觸發(fā)器作用下,只要信號(hào)間滿(mǎn)足時(shí)序要求,就能消除掉 100% 的競(jìng)爭(zhēng)與冒險(xiǎn)。
一般來(lái)說(shuō),為消除競(jìng)爭(zhēng)冒險(xiǎn),增加濾波電容和邏輯冗余,都不是 Verilog 設(shè)計(jì)所考慮的。
計(jì)數(shù)采用格雷碼計(jì)數(shù)器,大多數(shù)也是應(yīng)用在高速時(shí)鐘下減少信號(hào)翻轉(zhuǎn)率來(lái)降低功耗的場(chǎng)合。
利用觸發(fā)器在時(shí)鐘同步電路下對(duì)異步信號(hào)進(jìn)行打拍延時(shí),是 Verilog 設(shè)計(jì)中經(jīng)常用到的方法。
除此之外,為消除競(jìng)爭(zhēng)冒險(xiǎn),Verilog 編碼時(shí)還需要注意一些問(wèn)題,詳見(jiàn)下一小節(jié)。
在編程時(shí)多注意以下幾點(diǎn),也可以避免大多數(shù)的競(jìng)爭(zhēng)與冒險(xiǎn)問(wèn)題。
下面,對(duì)以上注意事項(xiàng)逐條分析。
前面講述非阻塞賦值時(shí)就陳述過(guò),時(shí)序電路中非阻塞賦值可以消除競(jìng)爭(zhēng)冒險(xiǎn)。
例如下面代碼描述,由于無(wú)法確定 a 與 b 阻塞賦值的操作順序,就有可能帶來(lái)競(jìng)爭(zhēng)冒險(xiǎn)。
always @(posedge clk) begin
a = b ;
b = a ;
end
而使用非阻塞賦值時(shí),賦值操作是同時(shí)進(jìn)行的,所以就不會(huì)帶來(lái)競(jìng)爭(zhēng)冒險(xiǎn),如以下代碼描述。
always @(posedge clk) begin
a <= b ;
b <= a ;
end
例如,我們想實(shí)現(xiàn) C = A&B, F=C&D 的組合邏輯功能,用非阻塞賦值語(yǔ)句如下。
兩條賦值語(yǔ)句同時(shí)賦值,F(xiàn) <= C & D 中使用的是信號(hào) C 的舊值,所以導(dǎo)致此時(shí)的邏輯是錯(cuò)誤的,F(xiàn) 的邏輯值不等于 A&B&D。
而且,此時(shí)要求信號(hào) C 具有存儲(chǔ)功能,但不是時(shí)鐘驅(qū)動(dòng),所以 C 可能會(huì)被綜合成鎖存器(latch),導(dǎo)致競(jìng)爭(zhēng)冒險(xiǎn)。
always @(*) begin
C <= A & B ;
F <= C & D ;
end
對(duì)代碼進(jìn)行如下修改,F(xiàn) = C & D 的操作一定是在 C = A & B 之后,此時(shí) F 的邏輯值等于 A&B&D,符合設(shè)計(jì)。
always @(*) begin
C = A & B ;
F = C & D ;
end
雖然時(shí)序電路中可能涉及組合邏輯,但是如果賦值操作使用非阻塞賦值,仍然會(huì)導(dǎo)致如規(guī)范 1 中所涉及的類(lèi)似問(wèn)題。
例如在時(shí)鐘驅(qū)動(dòng)下完成一個(gè)與門(mén)的邏輯功能,代碼參考如下。
always @(posedge clk or negedge rst_n)
if (!rst_n) begin
q <= 1'b0;
end
else begin
q <= a & b; //即便有組合邏輯,也不要寫(xiě)成:q = a & b
end
end
always 涉及的組合邏輯中,既有阻塞賦值又有非阻塞賦值時(shí),會(huì)導(dǎo)致意外的結(jié)果,例如下面代碼描述。
此時(shí)信號(hào) C 阻塞賦值完畢以后,信號(hào) F 才會(huì)被非阻塞賦值,仿真結(jié)果可能正確。
但如果 F 信號(hào)有其他的負(fù)載,F(xiàn) 的最新值并不能馬上傳遞出去,數(shù)據(jù)有效時(shí)間還是在下一個(gè)觸發(fā)時(shí)刻。此時(shí)要求 F 具有存儲(chǔ)功能,可能會(huì)被綜合成 latch,導(dǎo)致競(jìng)爭(zhēng)冒險(xiǎn)。
always @(*) begin
C = A & B ;
F <= C & D ;
end
如下代碼描述,仿真角度看,信號(hào) C 被非阻塞賦值,下一個(gè)觸發(fā)時(shí)刻才會(huì)有效。而 F = C & D 雖然是阻塞賦值,但是信號(hào) C 不是阻塞賦值,所以 F 邏輯中使用的還是 C 的舊值。
always @(*) begin
C <= A & B ;
F = C & D ;
end
下面分析假如在時(shí)序電路里既有阻塞賦值,又有非阻塞賦值會(huì)怎樣,代碼如下。
假如復(fù)位端與時(shí)鐘同步,那么由于復(fù)位導(dǎo)致的信號(hào) q 為 0,是在下一個(gè)時(shí)鐘周期才有效。
而如果是信號(hào) a 或 b 導(dǎo)致的 q 為 0,則在當(dāng)期時(shí)鐘周期內(nèi)有效。
如果 q 還有其他負(fù)載,就會(huì)導(dǎo)致 q 的時(shí)序特別混亂,顯然不符合設(shè)計(jì)需求。
always @(posedge clk or negedge rst_n)
if (!rst_n) begin //假設(shè)復(fù)位與時(shí)鐘同步
q <= 1'b0;
end
else begin
q = a & b;
end
end
需要說(shuō)明的是,很多編譯器都支持這么寫(xiě),上述的分析也都是建立在仿真角度上。實(shí)際中如果阻塞賦值和非阻塞賦值混合編寫(xiě),綜合后的電路時(shí)序?qū)⑹清e(cuò)亂的,不利于分析調(diào)試。
與 C 語(yǔ)言有所不同,Verilog 中不允許在多個(gè) always 塊中為同一個(gè)變量賦值。此時(shí)信號(hào)擁有多驅(qū)動(dòng)端(Multiple Driver),是禁止的。當(dāng)然,也不允許 assign 語(yǔ)句為同一個(gè)變量進(jìn)行多次連線(xiàn)賦值。 從信號(hào)角度來(lái)講,多驅(qū)動(dòng)時(shí),同一個(gè)信號(hào)變量在很短的時(shí)間內(nèi)進(jìn)行多次不同的賦值結(jié)果,就有可能產(chǎn)生競(jìng)爭(zhēng)冒險(xiǎn)。
從語(yǔ)法來(lái)講,很多編譯器檢測(cè)到多驅(qū)動(dòng)時(shí),也會(huì)報(bào) Error。
具體分析見(jiàn)下一章:《避免 Latch》。
點(diǎn)擊這里下載源碼
更多建議: