最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

驗(yàn)證基礎(chǔ)-線程的控制和同步

2022-05-14 16:43 作者:不吃蔥的酸菜魚  | 我要投稿

????????按照軟件的思維理解硬件仿真,仿真中的各個(gè)模塊首先是獨(dú)立運(yùn)行的線程(thread)。

????????模塊(線程)在仿真一開(kāi)始便并行執(zhí)行,除了每個(gè)線程會(huì)依照自身內(nèi)部產(chǎn)生的事件來(lái)觸發(fā)過(guò)程語(yǔ)句塊之外,也同時(shí)依靠相鄰模塊間的信號(hào)變化來(lái)完成模塊之間的線程同步。??

????????線程,其實(shí)就是獨(dú)立運(yùn)行的程序。在module中的initial和always,都可以看做獨(dú)立的線程,它們會(huì)在仿真0時(shí)刻開(kāi)始,而選擇結(jié)束或者不結(jié)束。? ? ? ??

????????有關(guān)線程,我們需要先知道幾個(gè)概念:任何的線程都應(yīng)該有父線程,父線程可以開(kāi)辟若干個(gè)子線程,父線程可以暫停或者終止子線程,子線程終止時(shí)父線程可以繼續(xù)執(zhí)行,但父線程終止時(shí),其所開(kāi)辟的所有子線程都應(yīng)當(dāng)會(huì)終止。

????????多線程的同時(shí)工作,顯然會(huì)對(duì)仿真資源造成一定的消耗,那么如何降低仿真時(shí)的內(nèi)存消耗呢?

????????1.可以降低模塊之間的信號(hào)跳變頻率(減少該信號(hào)的跳變頻率,可以減少該事件觸發(fā)的邏輯,從而降低資源消耗)、2.只在必要的時(shí)候創(chuàng)建軟件對(duì)象、3.在不需要時(shí)鐘的時(shí)候關(guān)閉時(shí)鐘、4.在數(shù)據(jù)帶寬需求低的時(shí)候降低時(shí)鐘頻率。

多線程函數(shù) fork...join

????????軟件環(huán)境中的initial塊對(duì)語(yǔ)句有兩種分組方式,使用begin......end 或者 fork......join。其中begin.......end中的語(yǔ)句以順序方式執(zhí)行,而fork......join中的語(yǔ)句則以并發(fā)方式執(zhí)行,其中fork......join又有多種類型比如fork......join_any、fork......join、fork_join_none三種。

????????對(duì)于fork......join,開(kāi)辟的子線程,必須全部執(zhí)行完,fork語(yǔ)句才結(jié)束;fork......join_any是fork語(yǔ)句塊中,有一個(gè)線程執(zhí)行完了,fork語(yǔ)句塊就會(huì)退出并執(zhí)行后面的語(yǔ)句,但是fork....join_any中還沒(méi)執(zhí)行完的線程還會(huì)繼續(xù)執(zhí)行下去;fork......join_none只要fork一起頭,那么就會(huì)離開(kāi)fork....join_none語(yǔ)句塊執(zhí)行后面的語(yǔ)句,fork_join_none中的線程也會(huì)執(zhí)行下去,fork......join_none就像點(diǎn)火一樣,點(diǎn)個(gè)火就走。

????????但是上述沒(méi)執(zhí)行完的語(yǔ)句還會(huì)繼續(xù)執(zhí)行的說(shuō)法有個(gè)例外,就是在 fork-join_any 如果完成了 fork 內(nèi)部的一條語(yǔ)句后會(huì)執(zhí)行 fork-join_any 后的語(yǔ)句我們是知道的,但是如果 fork-join_any 后面沒(méi)有語(yǔ)句,直接是initial語(yǔ)句塊的end,那么因?yàn)橐呀?jīng)執(zhí)行到end了,所以哪怕fork-join_any里面語(yǔ)句還沒(méi)執(zhí)行完,那也結(jié)束了,直接被截?cái)啵槐粓?zhí)行了。

????????在上面代碼的fork-join語(yǔ)句塊中,有四條子線程,display、#50、#10、begin...end,四條線程并行執(zhí)行,耗時(shí)最長(zhǎng)的是#50線程,在fork-join語(yǔ)句塊前還延時(shí)了10個(gè)時(shí)間單位,所以fork-join語(yǔ)句塊會(huì)在第60個(gè)時(shí)間單位的時(shí)候完成運(yùn)行。

運(yùn)行結(jié)果如上。

????????在SV中,當(dāng)程序的 initial 塊全部執(zhí)行完畢,仿真器就退出了。如果我們希望 fork 塊中的所有線程都執(zhí)行完畢再退出結(jié)束 initial 塊,我們可以使用 wait fork 語(yǔ)句來(lái)等待所有子線程結(jié)束。

????????相反,如果fork-join_any或fork-join_none后想終止目前還未執(zhí)行的完畢的線程,可以使用disable來(lái)指定需要停止的線程。

????????給一個(gè)fork塊命名,想要終止該fork塊的所有線程的時(shí)候,使用disable + 塊名就能終止線程。但是如果你給某一任務(wù)或者線程指定了名字,那么當(dāng)這個(gè)線程被調(diào)用多次以后,如果通過(guò)disable去禁止這個(gè)線程名,那么所有衍生的同名線程都將被禁止。

線程中的通信

????????測(cè)試平臺(tái)中的所有線程都需要同步交換數(shù)據(jù)。一個(gè)線程可能需要等待另一個(gè)線程執(zhí)行完畢,多個(gè)線程可能同時(shí)訪問(wèn)同一個(gè)資源,線程之間可能需要交換數(shù)據(jù)。所有這些數(shù)據(jù)交換和同步稱之為線程間的通信(IPC,Interprocess Communication)。

event 事件

????????Verilog 中,一個(gè)線程總是要等待一個(gè)帶@操作符的事件。這個(gè)操作符是邊沿敏感的,所以它總是阻塞著、等待事件變化。

????????其它線程可以通過(guò)->操作符來(lái)觸發(fā)事件,結(jié)束對(duì)第一個(gè)線程的阻塞。

仿真結(jié)果:

第一個(gè)初始化塊啟動(dòng),觸發(fā)e1事件,然后阻塞在e2上。

第二個(gè)初始化塊啟動(dòng),觸發(fā)e2事件,然后阻塞在e1上。

????????e1和e2在同一被觸發(fā),但是實(shí)際上仿真有一個(gè)delta cycle的時(shí)間差,使得第二個(gè)initial語(yǔ)句塊無(wú)法等到e1,但是第一個(gè)initial語(yǔ)句塊可以等到e2。

????????所以為了解決上面的問(wèn)題,可以使用event自帶的函數(shù)triggered(),這個(gè)函數(shù)是電平敏感的,@e1是邊沿敏感的。如果事件在當(dāng)前時(shí)間點(diǎn)已經(jīng)被觸發(fā)過(guò)了,那么就不會(huì)引起阻塞。

這樣修改后,仿真結(jié)果為:

????????語(yǔ)句$display("@%0t: 2: after trigger",$time);不會(huì)被阻塞,因?yàn)?triggered() 是電平敏感的函數(shù)語(yǔ)句,看名字也可以看的出來(lái),“被觸發(fā)”,可以看出該事件是否被觸發(fā)過(guò)。這種使用triggered函數(shù)的方法,比起使用@ 而言,可以保證同一時(shí)刻只要 event 被觸發(fā)過(guò),就不會(huì)引起阻塞。而@是邊沿敏感的,可以被多次觸發(fā),所以我們需要根據(jù)情況來(lái)選擇是使用 @ 還是 wait(e.triggered()) 來(lái)等待事件。

????????我們可以先使用wait(A.triggered()),等到event A被觸發(fā),或者在某一時(shí)刻不分time step的先后有wait和A觸發(fā),那么哪怕wait的時(shí)刻比觸發(fā)時(shí)刻晚,wait函數(shù)也會(huì)被滿足。但是如果event A 已經(jīng)觸發(fā),再使用wait(A.triggered()),且不是在一個(gè)時(shí)刻,而是有時(shí)間長(zhǎng)度的先后關(guān)系,則wait會(huì)被阻塞。

? ? ? ? 綜上:使用wait(A.triggered())的方式,可以避免在相同時(shí)刻觸發(fā)event而帶來(lái)的競(jìng)爭(zhēng)問(wèn)題,但同樣無(wú)法捕捉已經(jīng)被觸發(fā),但后續(xù)才等待的事件。


semaphore旗語(yǔ)

????????semaphore可以實(shí)現(xiàn)對(duì)同一個(gè)資源的訪問(wèn)控制。對(duì)于初學(xué)者來(lái)說(shuō),無(wú)論線程之間在共享什么資源,都應(yīng)該使用semaphore等資源訪問(wèn)控制的手段,來(lái)避免多線程同時(shí)訪問(wèn)同一塊資源。????

????????semaphore 有三種基本操作。 new() 方法可以創(chuàng)建一個(gè)帶單個(gè)或者多個(gè)鑰匙的semaphore,使用 get() 可以獲得一個(gè)或者多個(gè)鑰匙,而 put() 可以返回一個(gè)或者多個(gè)鑰匙。如果你試圖獲取一個(gè)semaphore而希望不被阻塞,可以使用?try_get()?函數(shù),它返回1表示有足夠多的鑰匙,而返回0則表示鑰匙不夠。

????????可以看到上面的代碼,在fork-join中,同時(shí)使用了兩個(gè)總線線程sequencer。而sequencer里面又調(diào)用了sendTrans任務(wù),會(huì)對(duì)總線進(jìn)行同時(shí)訪問(wèn)對(duì)總線進(jìn)行操作(bus.cb.addr <= t.addr),這就產(chǎn)生了沖突,所以我們利用旗語(yǔ)來(lái)解決這個(gè)沖突,我們要保證一次只有一個(gè)線程對(duì)總線進(jìn)行操作。利用旗語(yǔ),只有一把鑰匙,那么一定有一個(gè)線程先拿到鑰匙,拿到鑰匙的線程就能對(duì)總線進(jìn)行操作,沒(méi)拿到鑰匙的線程就只能等上一個(gè)線程歸還鑰匙后,才能取得鑰匙再對(duì)總線進(jìn)行操作。

????????使用旗語(yǔ)時(shí)要注意,你返回的鑰匙可以比你取出來(lái)的鑰匙多,因?yàn)槠煺Z(yǔ)有一個(gè)bug就是你手里沒(méi)有鑰匙的時(shí)候你也可以歸還鑰匙,這就很滑稽,所以你可能會(huì)突然間有兩把鑰匙而實(shí)際上只有一輛汽車,。當(dāng)你的測(cè)試程序需要獲取和返回多個(gè)鑰匙時(shí),務(wù)必謹(jǐn)慎,假設(shè)你剩下一把鑰匙,有一個(gè)線程請(qǐng)求兩把鑰匙而被阻塞,這時(shí)第二個(gè)線程出現(xiàn),它只請(qǐng)求一把,那么這個(gè)get(1)因?yàn)?strong>滿足要求而悄悄排到get(2)前面而獲得一把鑰匙,但是事實(shí)上這并不符合我們先進(jìn)先出的規(guī)則,所以務(wù)必謹(jǐn)慎。

如果要解決旗語(yǔ)的這個(gè)問(wèn)題,可以參考如下代碼:

上面定義了兩個(gè)類,用來(lái)解決旗語(yǔ)沖突與設(shè)置旗語(yǔ),下面就是正式使用的模塊。

mailbox 信箱

????????線程之間如果傳遞信息,可以使用mailbox。mailbox和隊(duì)列queue有相似之處,mailbox是一種對(duì)象,因此也需要使用new()來(lái)例化,例化時(shí)有一個(gè)可選的參數(shù)size來(lái)限定其存儲(chǔ)的最大數(shù)量。如果size是0或者沒(méi)有指定,則信箱是無(wú)限大的,可以容納任意多的條目。

????????使用put()可以把數(shù)據(jù)放入mailbox,使用get()可以從信箱移除數(shù)據(jù)。如果信箱為滿,則put()會(huì)阻塞,如果信箱為空,則get()會(huì)阻塞。peek()可以獲取對(duì)信箱里數(shù)據(jù)的拷貝而不移除它。

????????mailbox在例化時(shí),通過(guò)new(N)的方式可以使其變?yōu)槎ㄩL(zhǎng)(fixed length)容器,這樣負(fù)載到達(dá)N后,無(wú)法再對(duì)其進(jìn)行寫入。如果采用new()的方式,則表示信箱容量不限大小。

????????線程之間的同步方法需要注意,哪些是阻塞方法,哪些是非阻塞方法,即哪些是立即返回的,而哪些可能需要等待時(shí)間。

????????創(chuàng)建了一個(gè)只能存放單條信息的具有最小容量的信箱。此為定容信箱,如果你試圖往信箱里放入多于設(shè)定容量的物品,則put會(huì)阻塞,直到你從郵箱里搬走物品騰出空間。

驗(yàn)證面試高頻題:mailbox 和 隊(duì)列 有什么區(qū)別?

????????malibox和隊(duì)列很像,但是也存在區(qū)別,mailbox必須通過(guò)new()例化,而隊(duì)列只需要聲明即可。mailbox可以將不同的數(shù)據(jù)類型同時(shí)存儲(chǔ),不過(guò)不建議這么做;而隊(duì)列它內(nèi)部存儲(chǔ)的元素類型必須一致

????????mailbox的存取方法put()和get()是阻塞方法,使用它們時(shí)結(jié)果不一定會(huì)立即返回,而隊(duì)列對(duì)應(yīng)的存取方式push_back()和pop_front()是非阻塞的,會(huì)立即返回值。因此在用隊(duì)列取數(shù)時(shí),最好加上一句wait(queue.size > 0) 以免對(duì)空的隊(duì)列進(jìn)行了取數(shù)操作。

????????mailbox只能夠用作FIFO,而queue除了按照FIFO使用,還有其它應(yīng)用的方式例如LIFO(last in First Out)。

???????? 對(duì)于mailbox變量的操作,在傳遞形式參數(shù)時(shí),實(shí)際傳遞并拷貝的是mailbox的指針;如果使用queue時(shí),關(guān)于queue的形式參數(shù)的聲明是ref類型還是默認(rèn)的input類型需要額外考慮,如果是input類型,那么傳遞過(guò)程中發(fā)生的是數(shù)據(jù)的拷貝,以至于方法內(nèi)部對(duì)queue的操作并不會(huì)影響外部的queue本身。如果是ref類型,則相當(dāng)于是傳遞了隊(duì)列的指針,方法內(nèi)部對(duì)queue的修改也會(huì)影響到外部的queue。


event、semaphore、mailbox區(qū)別:

????????event:最小信息量的觸發(fā),即單一的通知和功能。可以用來(lái)做事件的觸發(fā),也可以多個(gè)event組合起來(lái)用來(lái)做線程之間的同步。

????????semaphore:共享資源的安全衛(wèi)士。如果多線程間要對(duì)某一公共資源做訪問(wèn),即可以使用這個(gè)要素。

????????mailbox:精小的SV原生FIFO。在線程之間做數(shù)據(jù)通信或者內(nèi)部數(shù)據(jù)緩存時(shí)可以考慮使用此元素。

















??

驗(yàn)證基礎(chǔ)-線程的控制和同步的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
姜堰市| 潢川县| 阜平县| 乌拉特前旗| 万年县| 米泉市| 灌南县| 星子县| 舒兰市| 滨海县| 安庆市| 盱眙县| 东丽区| 永济市| 克什克腾旗| 时尚| 夏河县| 尚义县| 阜城县| 河北省| 丰宁| 永昌县| 隆德县| 疏附县| 红河县| 玉溪市| 隆安县| 上蔡县| 瑞金市| 航空| 明溪县| 永济市| 临清市| 兖州市| 宁津县| 桦川县| 陕西省| 昌邑市| 湟中县| 固始县| 普宁市|