最近見識了大佬在代碼中用"@"出了“一劍”瓜饥,百思不得其解,甚至對“劍”本身都產(chǎn)生了懷疑······所幸在前輩的劍譜中找到答案戴质,一起來見識一下吧。
欲練此功踢匣,必先xx理解基本功告匠。
1.電路的兩種延時
- 傳輸延時(Transport Delay)
電路的輸入需要經(jīng)過一段時間以后才能在輸出端得到響應。
與此最相近的電路就是傳輸導線了离唬,假如從線上A點到B點需要5ns后专,那么A點處信號可以隨意變化且每次變化維持時間沒有限制,在經(jīng)過5ns以后這些變化可以完全的傳遞到B點输莺。 - 慣性延時(Inertial Delay)
維持時間小于門電路自身傳播延時的輸入信號是不能引起電路的輸出響應的戚哎。
對于組合電路其慣性延時的長度就等于其傳播延時長度,而對于時序電路由于其具有“記憶功能”嫂用,所以傳播延時被中間節(jié)點切開為若干段型凳,此時每一小段組合電路的慣性延時長度就等于各自小段傳播延時的長度,故而整體來看尸折,時序電路的輸入的慣性延時會比輸入到輸出的傳播延時短啰脚。
對于組合電路我們可以先拿反相器來來理解,輸入Vin使反相器的電容充放電才導致輸出Vout的電位發(fā)生變化实夹,電容充放電需要時間橄浓,這個時間也就是器件的傳播延時,也即Vin需要維持至少傳播延時的時間才能使電容充放電到位亮航,才能在輸出端得到一個穩(wěn)定的Vout荸实,若維持時間不夠,電容充放電沒完成則電路處于一個不穩(wěn)定狀態(tài)則會再逆向的充放電回到原來的穩(wěn)態(tài)缴淋,也即輸出不會發(fā)生變化准给;
對于更復雜的電路也是同理泄朴,可以把每一級電路等效為反相器,一級一級電容的充放電都需要時間露氮,所以輸入Vin需要維持至少傳播延時的時間才能在輸出端得到一個穩(wěn)定的Vout祖灰;
時序電路具體就是鎖存器和寄存器(由觸發(fā)器組成),它們都可分為采樣和維持兩個階段畔规,只不過寄存器只在CLK邊沿才采樣局扶,并對輸入D有setup/hold要求;
而鎖存器在整個CLK為高(或低)電平期間一直將輸入采樣到輸出(此時對輸入的持續(xù)時間也是要滿足慣性延時的叁扫,只不過由于采用傳輸門電路結(jié)構(gòu)這一段電路的慣性延時非常小三妈,所以很容易滿足),最后在CLK下降沿處進行最后一次采樣(相當于寄存器的采樣)莫绣,并對最后一次的輸入D有setup/hold要求畴蒲,以保證采到的數(shù)據(jù)在關(guān)閉之前可以正確的傳遞到Q端。
2.setup/hold time
2.1 鎖存器
鎖存器在CLK控制鎖存器導通期間即采樣階段对室,從輸入到輸出是完全透明的模燥,輸出會完全跟隨輸入變化(經(jīng)過傳播延時以后),也即所謂的(輸入)電平敏感软驰,此時輸入D->Q的門電路延時就是D的慣性延時涧窒,也是傳播延時,由于采用的是傳輸門結(jié)構(gòu)所以它非常小锭亏,很容易滿足;當CLK將要控制鎖存器關(guān)閉時硬鞍,由于關(guān)閉后需要在輸出端維持最后一次采樣的輸入D慧瘤,所以在這個CLK的邊沿,就對D有了setup/hold要求固该,從輸入D到中間節(jié)點X的tD->X(D->X的門延時一般比D->Q的門延時長)就是該鎖存器的建立時間tsu锅减,若CLK控制關(guān)斷之后D還能影響X則D還需再保持一段時間,這段時間就是thold伐坏。
2.2 寄存器
setup/hold time是由寄存器本身的結(jié)構(gòu)決定的怔匣,如下圖,一個寄存器的工作分為兩個階段:
<div align="center">
<img src="https://gitee.com/lumafilter/Image-Hosting/raw/master/img/tc1.jpg" width="500" height="500" alt="關(guān)注我吧"></img>
</div>
- 1.將數(shù)據(jù)D采樣到中間節(jié)點X桦沉,記從D->X的門延時為tD->X每瞒,這一采樣過程一般是在CLK上升沿之前,但也有的電路結(jié)構(gòu)是在上升沿之后纯露;
- 2.將數(shù)據(jù)從中間節(jié)點X傳遞到輸出端Q剿骨,記從X->Q的門延時為tX->Q,有點電路結(jié)構(gòu)會有兩個中間節(jié)點(多個中間節(jié)點也可以等效為2個)埠褪,分別記為X1和X2浓利,此時采樣過程是D->X1挤庇,傳遞過程又分為兩部分:X1->X2、X2->Q贷掖;
- 3嫡秕、因為這兩個過程都是受到CLK控制的,所以再引入一個從上升沿到輸出端口的延時tC->Q苹威,C表示上升沿的正中間的時刻昆咽;
根據(jù)這兩個階段就可以劃分setup/hold time了,分兩種情況:
- 采樣過程在CLK上升沿之前
這樣C時刻在X點拿到的就是tD->X之前的時刻的D的數(shù)據(jù)(門電路傳播延時)屠升,并且要求D在tD->X這段時間里維持不變(慣性延時)潮改,此時tD->X稱為建立時間tsu;
C時刻以后即上升沿之后的高電平階段腹暖,進入傳輸階段汇在,也分兩種情況:- 若此時D還能影響到節(jié)點X一段時間(稱為電路透明時間),這段時間的結(jié)束以D到X的通路關(guān)閉(如脈沖寄存器)或者在有多個中間節(jié)點的情況下已經(jīng)從第一個中間節(jié)點X1傳輸?shù)较乱粋€中間節(jié)點(此時D已經(jīng)通過X1正確的往后傳了脏答,如單相邊沿觸發(fā)寄存器)為結(jié)束時刻糕殉,那么上升沿以后的這段時間稱為保持時間thold,即數(shù)據(jù)D在上升沿之后仍需保持不變的時間殖告;這是由電路的結(jié)構(gòu)決定的阿蝶,如果在這段透明期間D發(fā)生變化,即數(shù)據(jù)D在上升沿之后仍需保持不變的時間小于thold黄绩,那么在經(jīng)歷固定的tD->X(傳輸延時)以后中間節(jié)點X保持不變的時間也小于thold(慣性延時)羡洁,從而導致X處的值不能被后面電路采到,也就不能正確的傳輸?shù)絈爽丹;
- 若C時刻以后D就不能影響到節(jié)點X筑煮,那thold自然等于0;
- 采樣過程在CLK上升沿之后
此時由于D->X和X->Q都在CLK上升沿之后粤蝎,所以可以省去中間節(jié)點X真仲,直接記D->X + X->Q 為tD->Q,即從輸入到輸出的門延時初澎;
這時秸应,由于電路結(jié)構(gòu),此時數(shù)據(jù)D能直接影響Q一段時間(類似上面能影響中間節(jié)點X一樣)碑宴,且這段時間必然大于等于tD->Q软啼,否則數(shù)據(jù)還沒傳到Q傳輸?shù)耐肪捅魂P(guān)閉了;- 若這段透明時間剛好等于tD->Q墓懂,那數(shù)據(jù)D就要在C時刻準備好焰宣,然后保持tD->Q時間后剛好抵達Q,此時tsu=0捕仔,thold=tD->Q匕积;
- 若這段透明時間大于tD->Q盈罐,那么D在C時刻甚至C時刻以后才準備好數(shù)據(jù)都是可以在透明時間結(jié)束前經(jīng)歷tD->Q傳到Q的,上面定義從D準備好數(shù)據(jù)到C時刻為tsu闪唆,那這里tsu >= t透明時間-tD->Q盅粪,一般取極限值即tsu = t透明時間-tD->Q,因為tsu越小時鐘周期T就可以越小悄蕾,時鐘頻率就可以越大票顾;
綜上,我們可以對setup/hold time有一個更直觀的理解:
由于寄存器的電路結(jié)構(gòu)帆调,使D->X和X->Q以CLK的上升沿C時刻為界分別有一段時間是導通的奠骄,根據(jù)電路的慣性延時D需保持tD->X才能傳到X,這段時間就是建立時間tsu番刊;而X在傳到下一級節(jié)點(下一個中間節(jié)點或者直接是Q)時含鳞,根據(jù)慣性延時也需要保持一段時間tX->才能傳過去,如果此時D還能影響X那tX->就會要求D再保持tX->時間芹务,這個時間對于D而言就是thold蝉绷,否則X自己會滿足tX->延時,并不會要求D繼續(xù)保持枣抱,即對于輸入 thold=0熔吗;
如果D->X和X->Q都在CLK上升沿的同一邊,那么考慮慣性延時和傳播延時時D->Q就是一個整體佳晶,不必引入中間節(jié)點X桅狠,此時tD->Q與電路結(jié)構(gòu)的從D->Q的透明時間就劃分了tsu和thold,此時tsu和thold二者之一就有可能取負值了轿秧,但不會同時為負垂攘;
3. Verilog 仿真原理
首先Verilog所描述的硬件電路是并行的、輸入驅(qū)動的淤刃,所以仿真器為了模擬這些硬件行為就有了仿真時間、事件和進程等概念吱型。
-
仿真時間
同一個時刻會有很多事件需要執(zhí)行逸贾,其優(yōu)先級是由分層事件隊列決定的;
仿真時間是根據(jù)時序模型與延時向前推進的津滞; -
事件和進程
(更新)事件是指模型中數(shù)值發(fā)生了變化铝侵,這是模擬的電路的輸入發(fā)生了變化;
更新事件會驅(qū)動進程的運算触徐,進程的運算又會產(chǎn)生新的更新事件咪鲜,更新事件和計算事件的執(zhí)行順序就是仿真器的調(diào)度;
進程也是一種事件撞鹉,是計算事件疟丙,可以認為模擬的是電路的其中一級電路颖侄,進程包括原語、模塊享郊、Initial和always過程塊览祖、連續(xù)賦值語句、異步任務以及過程賦值語句(過程塊中的assign和force語句)等炊琉;
進程在碰到時序控制(@展蒂、#、wait)時會被掛起苔咪,直到滿足條件才被再次激活锰悼,這是模擬的電路的延時或者說是事件驅(qū)動;
進程被掛起的話就會忽略掛起期間所有敏感信號的變化团赏; -
分層事件隊列
仿真器先根據(jù)時間對時間排序箕般,分為當前當前仿真時間事件和將來仿真時間事件;
然后同一時刻的事件再進行優(yōu)先級排序馆里,整個分層事件隊列的優(yōu)先級由高到低為:活躍事件隘世、非活躍事件、非租塞賦值更新事件鸠踪、監(jiān)控事件和將來事件如下圖:
<div align="center">
<img src="https://gitee.com/lumafilter/Image-Hosting/raw/master/img/tc2.png" width="500" height="500" alt="關(guān)注我吧"></img>
</div>
-
仿真時序模型
時序模型是仿真器的時間推進模型丙者,分為門級時序模型和過程時序模型兩種- 門級時序模型
適用于分析連續(xù)賦值語句、過程連續(xù)賦值語句和原語等营密;
它對輸入敏感械媒,只要輸入有變化就會重新計算該電路的輸出,模擬的是組合邏輯電路评汰;
新的更新事件來的時候如果還有之前的事件已被調(diào)度但還未執(zhí)行纷捞,則會撤銷舊事件只執(zhí)行新事件,這個模擬的就是組合電路的慣性延時被去,輸入變化太快則得不到輸出響應主儡; - 過程時序模型
適用于分析只對部分變量敏感的initial和always過程塊;
它只對部分輸入變量敏感惨缆,并不會對所有的變化都敏感橄镜,模擬的是時序邏輯電路早龟,如對于鎖存器在導通期間輸出會跟隨輸入變化概耻,即電平敏感权逗;對于寄存器它只在時鐘邊沿采樣輸入;
在一個更新事件被調(diào)度但沒完成捣染,再次調(diào)度該寄存器的另一個更新事件骄瓣,即使在同一個時刻,前一個更新事件也不會被取消耍攘,這模擬的是時序電路榕栏,因為其具有記憶性畔勤。
對于寄存器,它的工作可分為兩個階段采樣和維持臼膏,采樣由D->X硼被,再有X->Q,也即從上升沿到Q是有延時tC->Q的渗磅,在這期間數(shù)據(jù)D或者clk都有可能變化的嚷硫,所以仿真器就模擬寄存器也將這一過程分為2步,第一步采樣D到X(計算右值)始鱼,接著再把X更新到Q(更新)仔掸,并將這兩步放進兩個不同的隊列,也即允許這兩步中間插入別的事件医清,比如D再次發(fā)生變化(違反hold條件)或者clk上升沿再次來臨(違反setup條件)起暮,因為實際電路中是有可能違反setup/hold的所以仿真器這種模型也是可以允許違反出現(xiàn)的。
對于鎖存器会烙,在它導通期間由D到Q是透明的负懦,Q完全跟隨D變化也即只要輸入D(電平敏感信號)變化,輸出Q就要重新計算一遍柏腻,也即它的結(jié)果更新事件也應該是可以被打斷的纸厉,所以它的計算和更新也應該分開,并且放進兩個個不同的隊列五嫂。所以對于寄存器和鎖存器最好用非阻塞賦值颗品,因為它的右值計算和左值更新就是分兩步且可被打斷的,而用阻塞賦值這兩步連在一起不可打斷就模擬不了鎖存器輸入快速變化以及寄存器違反setup/hold條件的情形沃缘。
- 門級時序模型
關(guān)于上述兩種仿真模型躯枢,這里照搬參數(shù)書上的例子來幫助理解:
- 門級時序模型的慣性延時:
assign #8ms Ck_delay = Ck;//Ck是周期為488ns的時鐘信號
上述代碼想將一個周期為488ns的時鐘信號延時8ms后傳給Ck_delay,門級時序模型的寫法永遠也傳不過去槐臀,因為Ck每隔244ns就變化一次锄蹂,即每隔244ns assign語句就會產(chǎn)生一個新的更新事件,并撤銷未完成的上一個更新事件(因為它延時8ms水慨,現(xiàn)在才過去244ns自然還未完成)败匹,這樣Ck永遠也沒機會更新到Ck_delay;
這樣就實現(xiàn)了過濾掉維持時間小于慣性延時8ms的輸入讥巡;
- 過程時序模型:
錯誤寫法:
always@(Ck)
begin
Ck_delay = #8ms Ck;
end
這種寫法由于使用阻塞賦值所以計算右值Ck以及8ms后更新左值要連在一起,所以這8ms里會對Ck的新變化不敏感舔哪,換言之欢顷,這8ms里Ck每次的新變化都不會重新進入這個always塊,更得到輸出響應捉蚤,所以這種寫法不是過程時序模型抬驴;綜合前的RTL代碼會錯過Ck的變化炼七,而綜合后的電路是一根導線,所以這種寫法并沒有使仿真器模擬出我們想要的電路(一根帶8ms延時的傳輸導線)布持,所以這種寫法是錯誤的豌拙;
正確寫法:
always@(Ck)
begin
Ck_delay <= #8ms Ck;
end
這種使用了阻塞賦值所以每次計算完右值就又可以對輸入Ck敏感了,所以Ck的每一次變化都可以在8ms后傳遞到Ck_delay上题暖;這種對應的電路就相當于一個鎖存器后面接了一段傳輸延時為8ms的傳輸線按傅,這里#8ms就完美的模擬了傳輸線的傳輸延時;
- 阻塞賦值與非阻塞賦值
在過程塊中的過程賦值胧卤,操作符是=唯绍,就是阻塞賦值;操作符是<=枝誊,就是非阻塞賦值况芒;(操作符=對線網(wǎng)的賦值稱為連續(xù)賦值,不是阻塞賦值)
根據(jù)上面對過程時序模型的分析叶撒,我們可以看出在寫時序邏輯(寄存器和鎖存器)時最好使用非阻塞賦值绝骚,在使用always塊寫組合邏輯時最好用阻塞賦值。
這是由仿真器原理決定的祠够,<=的設(shè)計就是過程時序模型压汪,它分將右值計算和左值分兩步并可被打斷的設(shè)計模擬的就是寄存器和鎖存器的工作原理,配合邊沿觸發(fā)或者部分敏感的always塊實現(xiàn)時序邏輯是最合理的哪审;
組合邏輯使用assign連續(xù)賦值和全敏感的always塊結(jié)合阻塞賦值兩種方式來實現(xiàn)蛾魄,且兩種方式是等價的,只不過放always塊里可對reg型變量操作湿滓,位選可以更方便滴须,這兩種方式都體現(xiàn)出組合電路的并行和輸入驅(qū)動的特點。
這只是根據(jù)仿真原理得到的描述電路的組合方式叽奥,至于其他的組合也有可能綜合出正確的電路扔水,但有可能前仿真和后仿真會不一致。并且代碼看起來很沒有硬件思維朝氓,還很容易仿真出錯魔市,如:
always@(posedge clk or negedge rstn)
begin
if(~rstn)
y1 = 'h0;
else
y1 = y2;
end
always@(posedge clk or negedge rstn)
begin
if(~rstn)
y2 = 'h1;
else
y2 = y1;
end
上述代碼兩個always塊是并行的,誰先誰后仿真順序隨機赵哲,但是由于使用了阻塞賦值(賦值期間不允許被打斷)待德,所以兩個always之間就存在競爭,而若使用非阻塞賦值就無此問題枫夺。
always@(posedge clk or negedge rstn)
begin
if(~rstn)
max = 'h0;
else
begin
tmp1 = a > b ? a : b;
tmp2 = c > d ? c : d;
max = tmp1 > tmp2 ? tmp1 : tmp2;
end
end
always@(*)
begin
tmp1 = a > b ? a : b;
tmp2 = c > d ? c : d;
end
always@(posedge clk or negedge rstn)
begin
if(~rstn)
max <= 'h0;
else
max <= tmp1 > tmp2 ? tmp1 : tmp2;
end
再比如上面兩端代碼實現(xiàn)的是同樣的邏輯将宪,但是第二段代碼明顯可以看出電路的思維:先3個組合電路比較器(tmp1 > tmp2 也算)輸出線接到寄存器的輸入,在時鐘上升沿對其進行采樣。
雖然也有將邏輯電路嵌入到寄存器的電路結(jié)構(gòu)较坛,但除非特殊需求印蔗,我們一般只描述邏輯,具體的電路由工具根據(jù)約束自動選擇丑勤。而且主要第二種寫法更能體現(xiàn)硬件思維华嘹。
- 時序控制
時序控制包括延時語句(#)、事件語句(@)和等待語句(wait)法竞,時序控制總是伴隨著進程的掛起和激活耙厚。
執(zhí)行一個進程時如果碰到時序控制語句(#、@爪喘、wait)颜曾,該進程就會被掛起,直到該事件發(fā)生秉剑、已經(jīng)過延時中的時間單位數(shù)或者等待語句的表達式變?yōu)檎娣汉溃撨M程才會被再次激活。- 延時語句
延時語句有正規(guī)延時和內(nèi)定延時兩種:
正規(guī)延時是立刻掛起進程等延時時間過了再拿那個時刻的輸入信號做運算侦鹏;
而內(nèi)定延時是先拿當前時刻的信號做完運算诡曙,然后將結(jié)果存起來再將進程掛起,等過完延時后再更新結(jié)果略水。
延時語句與阻塞/非阻塞賦值語句是兩個工具价卤,組合在一起用來模擬組合電路中的慣性延時和電路中傳輸導線的傳輸延時,但是只有兩種組合能正確的模擬出實際電路的的慣性延時和傳輸延時: - 慣性延時 :正規(guī)延時和阻塞賦值渊涝;
- 延時語句
always@(a or b)
begin
#5 c = a + b;//過濾掉維持小于慣性延時5ms的所有輸入
end
- 傳輸延時 :內(nèi)定延時和非阻塞賦值慎璧;
always@(posedge clk)
begin
Ck_delay <= #8ms Ck;//模擬8ms的傳輸線延時
end
而其他組合雖不能用來模擬真實的電路延時,但根據(jù)語法也是可以存在的跨释,一般可以在TestBench中巧妙運用胸私,如:
always@(clk)
begin
#50 clk <= ~ clk;
end
always@(clk)
begin
#50;
clk <= ~ clk;
end
上述兩條用來生成時鐘鳖谈,且是等價的因為從語法上遇到正規(guī)延時都會將進程掛起岁疼,而這些組合若使用不好也會達不到預期的功能,如:
always@(clk)
begin
#50 clk = ~ clk;
end
原因其實根據(jù)仿真器原理和語法顯而易見--使用了阻塞賦值缆娃。進程會先因為#50而掛起捷绒,然后執(zhí)行阻塞賦值,一直到左值更新完才恢復進程對輸入clk的敏感贯要,但此時clk已經(jīng)更新完了暖侨,所以震蕩不起來的。
注意到語法上內(nèi)定延時也會掛起進程的崇渗,如:
always@(clk or a)
begin
clk <= #50 ~ clk;
a <= a + 1;
end
這段代碼仿真邏輯如下它碎,首先不管因為clk變化還是a變化而進來函荣,都先計算~clk
,存下結(jié)果后掛起扳肛,50個時間單位后執(zhí)行非阻塞,此時右值算計算完成乘碑,丟一個更新事件到隊列挖息,這樣begin塊的第一條語句執(zhí)行完畢,到第二條兽肤,計算右值a+1
后丟一個更新事件到隊列執(zhí)行完畢套腹,這樣兩條語句執(zhí)行完以后再等同一時刻活躍隊列其他事件都執(zhí)行完畢后開始更新非阻塞賦值,這樣~clk
和a+1會在同一時刻更新到左值去资铡,并立即再次出發(fā)進程并這樣一直重復电禀。
延時語句只有在仿真階段其作用,在綜合時會被忽略笤休。
- 事件語句
@()里面可以放電平尖飞、邊沿和命名的event。
@(···)和延時#的使用語法非常類似店雅,只是把掛起結(jié)束的條件從延時改為事件發(fā)生政基,如@()也有外放和內(nèi)放兩種用法:
always@(b)
begin
@(posedge clk)a <= b + 1;//同樣等價于 @(posedge clk);a <= b + 1;
end
每當b變化進來闹啦,然后遇到@直接掛起沮明,直到clk上升沿才執(zhí)行非阻塞賦值,計算b+1窍奋,丟更新事件到隊列荐健,等下一次b變化。
always@(b)
begin
a <= @(posedge clk)b + 1;
end
每當b變化進來琳袄,計算b+1并保存起來江场,然后遇到@掛起,直到clk上升沿才算完成右值計算挚歧,丟更新事件到隊列扛稽,等下一次b變化。
上述兩段乍一看貌似和下面代碼實現(xiàn)的功能一樣滑负,但在b不變化的時候還是有區(qū)別的在张,且對于intra模式,它拿到是b剛發(fā)生變化時刻的b的值矮慕,與下面在上升沿處才拿b的值也有區(qū)別帮匾。
always@(posedge clk)
begin
a <= b + 1;
end
設(shè)計@這種事件操作是為了模擬鎖存器和寄存器的觸發(fā)條件,至于其他的用法我們只要根據(jù)語法分析就好了痴鳄,像上面延時那樣不一定就有對應的實際功能的電路的瘟斜。如:
always@(posedge clk)
begin
@(posedge clk)a <= b + 1;
end
第一個上升沿進來,遇到@直接掛起,直到再來一個clk上升沿才執(zhí)行非阻塞賦值螺句,計算b+1虽惭,丟更新事件到隊列,等下一次上升沿到來蛇尚,這樣這個進程總共需要兩個clk上升沿才能執(zhí)行完芽唇。
@還可以和repeat結(jié)合,在TB里很好用取劫,如repeat (5)@(posedge clk)a <= b + 1;
表示在第5個上升沿處進行操作匆笤。
好了,我們最后來分析大佬使出的那一招劍法吧:
always@(a or b)
begin
b[7:0] <= a;
@(posedge clk);
b[8 +:8] <= b[7:0];
b[16 +:8] <= a;
end
a或b變化進來谱邪,計算右值a并更新到b[7:0]炮捧,丟一個更新事件到隊列,begin塊第一條語句執(zhí)行完畢惦银,向下遇到@掛起咆课,此時等該時刻活躍隊列事件執(zhí)行完畢后更新事件執(zhí)行,
于是b發(fā)生變化再次觸發(fā)該進程璧函,于是還是在該時刻第二個進程2計算右值a并更新到b[7:0]傀蚌,丟一個更新事件到隊列,begin塊第一條語句執(zhí)行完畢蘸吓,向下遇到@掛起善炫,此時等該時刻活躍隊列事件執(zhí)行完畢后更新事件執(zhí)行,但是這次b沒變化所以不會觸發(fā)新進程库继;
等到第一個上升沿來臨時執(zhí)行進程1下面兩條阻塞賦值箩艺,由于b發(fā)生變化于是各自分別再觸發(fā)新的進程3和4,并都遇到@掛起宪萄;
等到再來一個上升沿來臨時執(zhí)行進程1下面兩條阻塞賦值艺谆,由于b發(fā)生變化于是各自分別再觸發(fā)新的進程5和6,并都遇到@掛起拜英;
···
這樣一直重復就相當于與時鐘上升沿同步起來了静汤,再也不會因為a變化而觸發(fā)進程進來了。
若將其中的非阻塞賦值換為阻塞賦值居凶,則達不到這樣的效果虫给。
always@(a or b)
begin
b[7:0] = a;
@(posedge clk);
b[8 +:8] = b[7:0];
b[16 +:8]= a;
end
a或b變化進來,計算右值a并更新到b[7:0](由于是阻塞賦值所以b更新完以后才恢復敏感侠碧,故不會二次觸發(fā))抹估,begin塊第一條語句執(zhí)行完畢,向下遇到@掛起弄兜,等上升沿來臨時執(zhí)行下面兩條阻塞賦值药蜻,且都不會二次觸發(fā)進程瓷式,所以等待下一次a或b變化再進來。
參考文獻:
1.《設(shè)計與驗證--Verilog HDL》
2.《Verilog 編程藝術(shù)》
3.《IEEE Standard for Verilog HDL》
歡迎關(guān)注公眾號 :)
可在公眾號內(nèi)回復關(guān)鍵字“verilog”獲取上述資料语泽,僅供學習交流使用贸典。
<div align="center">
<img src="https://gitee.com/lumafilter/Image-Hosting/raw/master/img/2.jpg" width="300" height="300" alt="我是縮小后并居中的圖"></img>
</div>