演示文稿主題怎么設(shè)置啊(怎么將演示文稿主題設(shè)置)
847
2022-05-30
前言
提到函數(shù)與任務(wù),很多已從業(yè)的邏輯設(shè)計(jì)人員甚至都會對此陌生,聽過是聽過,但是很少用過。
與大多數(shù)編程語言一樣,我們應(yīng)該嘗試使盡可能多的Verilog代碼可重用。這使我們能夠減少將來項(xiàng)目的開發(fā)時間,因?yàn)槲覀兛梢愿p松地將代碼從一種設(shè)計(jì)移植到另一種設(shè)計(jì)。
人是懶惰的,覺得麻煩且使用場合局限,就不去重視,甚至不予接觸,我寧愿使用其他語法代替。這樣的話,就失去了這些語法寶貴的用途,Verilog中的函數(shù)與任務(wù)有時候使用起來會給工作帶來很多便利,且有的時候這是一種競爭力,作為工作的一份子,難道你接觸的都是自己的代碼嗎?不可能吧,甚至可以說,你接觸的大多數(shù)都是別人的代碼,你能保證別人不使用函數(shù)和任務(wù)嗎?所以,這一關(guān)始終是難以繞過的。
使用Verilog中的函數(shù)和任務(wù),可以編寫出很多精煉的代碼,讓代碼可讀性提高。例如仿真中,某個功能模塊我們需要重復(fù)利用,那么就可以使用函數(shù)或者任務(wù)的一種,讓其成為我們仿真平臺的一部分,逼格提升了不說,效率也提高了不少。
本文首發(fā):https://www.ebaina.com/articles/140000010072
舉個例子,此時你可以不用懂函數(shù)以及任務(wù)的語法,就看它們帶來的方便即可:
定義一個求和函數(shù):
function [7:0] sum; input [7:0] a, b; begin sum = a + b; end endfunction
1
2
3
4
5
6
7
仿真時調(diào)用這個函數(shù):
reg [7:0] result; reg [7:0] a, b; initial begin a = 4; b = 5; #10 result = sum (a, b); end
1
2
3
4
5
6
7
8
9
有人會覺得好傻,我直接加不就可以了。
當(dāng)然,不會為此抬杠,這只是一個例子,如果是其他復(fù)雜的復(fù)用結(jié)構(gòu),函數(shù)會是一個利器。
任務(wù)也是如此:
task sum; input [7:0] a, b; output [7:0] c; begin c = a + b; end endtask
1
2
3
4
5
6
7
8
initial begin reg [7:0] x, y , z; sum (x, y, z); end
1
2
3
4
這里只是說明函數(shù)和任務(wù)可以干什么?下面探討如何在Verilog中使用任務(wù)和函數(shù)。這些被統(tǒng)稱為子程序,它們使我們能夠編寫可重用的Verilog代碼。在后續(xù)的進(jìn)階中,你可能會閱讀到很多進(jìn)階的設(shè)計(jì),用到了函數(shù)以及任務(wù),你會越來越熟悉它們。
函數(shù)與任務(wù)
功能和任務(wù)之間有兩個主要區(qū)別。
當(dāng)我們編寫一個verilog函數(shù)時,它將執(zhí)行計(jì)算并返回一個值。
相反,verilog任務(wù)執(zhí)行許多時序語句,但不返回值。相反,任務(wù)可以有無限數(shù)量的輸出。
除此之外,verilog函數(shù)可立即執(zhí)行,并且不能包含耗時的構(gòu)造,例如延遲,posege宏或wait語句
一個Verilog的任務(wù),在另一方面,可以包含耗時結(jié)構(gòu)。
接下來,我們將深入討論這兩種構(gòu)造。這包括提供一些示例,說明我們?nèi)绾卧赩erilog中編寫和調(diào)用函數(shù)和任務(wù)。
Verilog 函數(shù)
在Verilog中,函數(shù)是一個子程序,它接受一個或多個輸入值,執(zhí)行一些計(jì)算并返回輸出值。
我們使用函數(shù)來實(shí)現(xiàn)一小部分代碼,這些代碼要在設(shè)計(jì)中的多個地方使用。
通過使用函數(shù)而不是在多個位置重復(fù)相同的代碼,我們使我們的代碼更具可維護(hù)性。
我們在verilog模塊中編寫函數(shù)代碼,以調(diào)用該函數(shù)。
下面的代碼段顯示了Verilog中函數(shù)的一般語法。
// First function declaration style - inline arguments function
1
2
3
4
5
6
7
8
9
10
11
12
13
// Second function declaration style - arguments in body function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
我們必須給每個函數(shù)起一個名字,如上例中的字段所示。
我們可以在函數(shù)聲明中內(nèi)聯(lián)聲明輸入,也可以將其聲明為函數(shù)主體的一部分。我們用來聲明輸入?yún)?shù)的方法對函數(shù)的性能沒有影響。
但是,當(dāng)我們使用內(nèi)聯(lián)聲明時,如果需要,我們也可以省略begin和end關(guān)鍵字。
在上面的示例中,我們使用字段聲明函數(shù)的輸入。
我們使用
當(dāng)我們返回一個值時,我們通過為函數(shù)名稱分配一個值來實(shí)現(xiàn)。下面的代碼片段顯示了我們?nèi)绾魏唵蔚貙⑤斎敕祷亟o函數(shù)。
function integer easy_example (input integer a); easy_example = a; endfunction : easy_example
1
2
3
4
5
盡管函數(shù)通常很簡單,但是編寫Verilog函數(shù)時必須遵循一些基本規(guī)則。
函數(shù)最重要的規(guī)則之一是它們不能包含任何耗時的構(gòu)造,例如延遲,posege宏或wait語句。
當(dāng)我們想編寫一個消耗時間的子程序時,我們應(yīng)該改用verilog任務(wù)。
結(jié)果,我們也無法從函數(shù)中調(diào)用任務(wù)。相反,我們可以從函數(shù)體內(nèi)調(diào)用另一個函數(shù)。
當(dāng)函數(shù)立即執(zhí)行時,我們只能在Verilog函數(shù)中使用阻塞分配。
當(dāng)我們在verilog中編寫函數(shù)時,我們可以聲明和使用局部變量。這意味著我們可以在函數(shù)中聲明無法在聲明其的函數(shù)之外訪問的變量。
除此之外,我們還可以訪問Verilog函數(shù)中的所有全局變量。
例如,如果我們在模塊塊中聲明一個函數(shù),則該函數(shù)中可以訪問和修改該模塊中聲明的所有變量。
下面總結(jié)在Verilog中使用函數(shù)的規(guī)則。
在Verilog中使用函數(shù)的規(guī)則
Verilog函數(shù)可以具有一個或多個輸入?yún)?shù)
函數(shù)只能返回一個值
函數(shù)不能使用耗時的構(gòu)造,例如posege,wait或delays(#)
我們不能從函數(shù)中調(diào)用任務(wù)
我們可以從一個函數(shù)中調(diào)用其他函數(shù)
非阻塞分配不能在函數(shù)中使用
局部變量可以在函數(shù)內(nèi)部聲明和使用
我們可以從verilog函數(shù)內(nèi)部訪問和修改全局變量
如果不指定返回類型,則該函數(shù)將返回單個位
為了更好地演示如何使用Verilog函數(shù),讓我們考慮一個基本示例。
對于此示例,我們將編寫一個函數(shù),該函數(shù)接受2個輸入?yún)?shù)并返回它們的總和。
我們使用verilog整數(shù)類型作為輸入?yún)?shù)和返回類型。
我們還必須使用verilog加法運(yùn)算符來計(jì)算輸入的總和。
下面的代碼段顯示了此示例函數(shù)在verilog中的實(shí)現(xiàn)。
正如我們之前討論的,可以使用兩種方法來聲明verilog函數(shù),這兩種方法都顯示在下面的代碼中。
// Using inline declaration of the inputs function integer addition (input integer in_a, in_b); // Return the sum of the two inputs addition = in_a + in_b; endfunction : addition
1
2
3
4
5
6
7
8
9
// Declaring the inputs in the function body function integer addition; input integer in_a; input integer in_b; begin // Return the sum of the two inputs addition = in_a + in_b; end endfunction : addition
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
當(dāng)我們想在Verilog設(shè)計(jì)的另一部分中使用函數(shù)時,必須調(diào)用它。我們用于執(zhí)行此操作的方法類似于其他編程語言。
當(dāng)我們調(diào)用一個函數(shù)時,我們以與聲明它們相同的順序?qū)?shù)傳遞給該函數(shù)。這稱為位置關(guān)聯(lián),這意味著我們聲明參數(shù)的順序非常重要。
下面的代碼段顯示了如何使用位置關(guān)聯(lián)來調(diào)用附加示例函數(shù)。
在下面的示例中,in_a將映射到a參數(shù),而in_b將映射到b。
// Calling a verilog function using positional association func_out = addition(a, b);
1
2
3
我們還可以使用verilog automatic關(guān)鍵字將函數(shù)聲明為reentrant。
但是,自動關(guān)鍵字是在verilog 2001標(biāo)準(zhǔn)中引入的,這意味著在使用verilog 1995標(biāo)準(zhǔn)時我們無法編寫可重入函數(shù)。
當(dāng)我們將一個函數(shù)聲明為可重入函數(shù)時,該函數(shù)內(nèi)的變量和參數(shù)將動態(tài)分配。相反,普通函數(shù)對內(nèi)部變量和參數(shù)使用靜態(tài)分配。
當(dāng)我們編寫一個普通函數(shù)時,用于執(zhí)行該函數(shù)處理的所有內(nèi)存僅分配一次。這個過程在計(jì)算機(jī)科學(xué)中被稱為靜態(tài)內(nèi)存分配。
因此,我們的仿真軟件必須完全執(zhí)行該功能,然后才能再次使用該功能。
這也意味著該函數(shù)使用的內(nèi)存永遠(yuǎn)不會被釋放。結(jié)果,存儲在該存儲器中的所有值將在調(diào)用該函數(shù)之間保持其值。
相反,每當(dāng)調(diào)用函數(shù)時,使用自動關(guān)鍵字的函數(shù)都會分配內(nèi)存。函數(shù)完成后,便會釋放內(nèi)存。
在計(jì)算機(jī)科學(xué)中,此過程稱為自動或動態(tài)內(nèi)存分配。
結(jié)果,我們的仿真軟件可以執(zhí)行自動功能的多個實(shí)例。
我們可以使用自動關(guān)鍵字在verilog中編寫遞歸函數(shù)。這意味著我們可以創(chuàng)建調(diào)用自身以執(zhí)行計(jì)算的函數(shù)。
例如,遞歸函數(shù)的一個常見用例是計(jì)算給定數(shù)字的階乘。
下面的代碼段顯示了如何使用自動關(guān)鍵字在verilog中編寫遞歸函數(shù)。
function automatic integer factorial (input integer a); begin if (a > 1) begin factorial = a * factorial(a - 1); end else begin factorial = 1; end end endfunction : factorial
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Verilog任務(wù)
就像函數(shù)一樣,我們使用任務(wù)來實(shí)現(xiàn)一小段代碼,這些代碼可以在整個設(shè)計(jì)中重復(fù)使用。
在Verilog中,任務(wù)可以具有任意數(shù)量的輸入,也可以生成任意數(shù)量的輸出。這與只能返回單個值的函數(shù)相反。
與函數(shù)不同,我們還可以在任務(wù)中使用耗時的構(gòu)造,例如等待,姿勢或延遲(#)。
結(jié)果,我們可以在verilog任務(wù)中同時使用阻塞分配和非阻塞分配。
這些功能意味著任務(wù)最適合用來實(shí)現(xiàn)簡單的代碼,這些代碼在我們的設(shè)計(jì)中重復(fù)了幾次。一個很好的例子是在已知接口(例如SPI或I2C)上驅(qū)動引腳。
我們在verilog模塊中編寫任務(wù)代碼,該代碼將用于調(diào)用任務(wù)。
我們還可以創(chuàng)建全局任務(wù),這些任務(wù)由給定文件中的所有模塊共享。為此,我們只需在文件中的模塊聲明之外編寫任務(wù)的代碼即可。
下面的代碼段顯示了Verilog中任務(wù)的一般語法。
與函數(shù)一樣,有兩種方法可以聲明任務(wù),但是兩種方法的性能相同。
// Task syntax using inline IO task
1
2
3
4
5
6
7
// Task syntax with IO declared in the task body task
1
2
3
4
5
6
7
8
9
10
11
12
13
我們必須給每個任務(wù)起一個名字,如上面的字段所示。
當(dāng)我們編寫在任務(wù)主體中聲明輸入和輸出的任務(wù)時,還必須使用begin和end關(guān)鍵字。
但是,當(dāng)我們將內(nèi)聯(lián)聲明用于輸入和輸出時,可以省略begin和end關(guān)鍵字。
當(dāng)我們在verilog中編寫任務(wù)時,我們可以聲明和使用局部變量。這意味著我們可以在任務(wù)中創(chuàng)建不能在聲明它的任務(wù)之外訪問的變量。
除此之外,我們還可以訪問Verilog任務(wù)中的所有全局變量。
與verilog函數(shù)不同,我們可以從一個任務(wù)中調(diào)用另一個任務(wù)。我們還可以從任務(wù)中調(diào)用函數(shù)。
讓我們考慮一個簡單的示例,以更好地演示如何編寫Verilog任務(wù)。
對于此示例,我們將編寫一個可用于生成脈沖的基本任務(wù)。當(dāng)我們在設(shè)計(jì)中調(diào)用任務(wù)時,可以指定脈沖的長度。
為此,我們需要一個輸入(用于確定脈沖多長時間)和一個用于生成脈沖的輸出。
下面的verilog代碼使用兩種不同的任務(wù)樣式顯示了此示例的實(shí)現(xiàn)。
// Task implementation using inline declaration of IO task pulse_generate(input time pulse_length, output pulse); pulse = 1'b1 #pulse_time pulse = 1'b0; endtask
1
2
3
4
5
6
7
8
9
10
11
// Task implementation with IO declared in body task pulse_generate; input time pulse_length; output pulse; begin pulse = 1'b1; #pulse_time pulse = 1'b0; end endtask
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
盡管此示例非常簡單,但是我們可以在此處看到如何在任務(wù)中使用Verilog延遲運(yùn)算符(#)。如果嘗試在函數(shù)中編寫此代碼,則在嘗試對其進(jìn)行編譯時將導(dǎo)致錯誤。
從這個例子中我們還可以看出,返回值的方式與使用函數(shù)的方式不同。
相反,我們必須聲明在任務(wù)聲明中使用的所有輸出。
在verilog中編寫任務(wù)時,我們可以包含并驅(qū)動任意數(shù)量的輸出。
與函數(shù)一樣,當(dāng)我們要在Verilog設(shè)計(jì)的另一部分中使用任務(wù)時,必須調(diào)用它。
我們用于執(zhí)行此操作的方法類似于用于調(diào)用函數(shù)的方法。
但是,在verilog中調(diào)用任務(wù)和函數(shù)之間存在一個重要區(qū)別。
當(dāng)我們在Verilog中調(diào)用任務(wù)時,不能像使用函數(shù)一樣將其用作表達(dá)式的一部分。
相反,我們應(yīng)該將任務(wù)調(diào)用視為在設(shè)計(jì)中包含代碼塊的簡便方法。
與函數(shù)一樣,當(dāng)我們調(diào)用任務(wù)時,我們使用位置關(guān)聯(lián)將參數(shù)傳遞給任務(wù)。
這只是意味著我們以編寫任務(wù)代碼時所聲明的順序?qū)?shù)傳遞給任務(wù)。
下面的代碼片段顯示了我們將如何使用位置關(guān)聯(lián)來調(diào)用先前考慮過的pulse_generate任務(wù)。
在這種情況下,pulse_length輸入被映射到pulse_time變量,而脈沖輸出被映射到pulse_out變量。
// Calling a task using positional association generate_pulse(pulse_time, pulse_out);
1
2
3
我們還可以將自動關(guān)鍵字與verilog任務(wù)一起使用,以使其可重入。同樣,此關(guān)鍵字是在verilog 2001標(biāo)準(zhǔn)中引入的,這意味著它不能與verilog 1995兼容代碼一起使用。
如前所述,使用關(guān)鍵字auto意味著我們的仿真工具使用了動態(tài)內(nèi)存分配。
與函數(shù)一樣,任務(wù)默認(rèn)情況下使用靜態(tài)內(nèi)存分配,這意味著仿真軟件只能運(yùn)行任務(wù)的一個實(shí)例。
相反,無論何時調(diào)用任務(wù),使用自動關(guān)鍵字的任務(wù)都會分配內(nèi)存。任務(wù)完成后,然后釋放內(nèi)存。
讓我們考慮一個基本示例,以顯示自動任務(wù)的使用以及它們與正常任務(wù)的區(qū)別。
對于此示例,我們將使用一個簡單的任務(wù),該任務(wù)將局部變量的值增加給定的數(shù)量。
然后,我們可以在仿真工具中多次運(yùn)行此命令,以查看局部變量在使用自動任務(wù)和正常任務(wù)時的行為。
下面的代碼顯示了我們?nèi)绾尉帉戩o態(tài)任務(wù)來實(shí)現(xiàn)此示例。
// Task which performs the increment task increment(input integer incr); integer i = 1; i = i + incr; $display("Result of increment = %0d", i); endtask
1
2
3
4
5
6
7
8
9
10
11
// Run the task three times initial begin increment(1); increment(2); increment(3); end
1
2
3
4
5
6
7
8
9
10
11
在icarus verilog仿真工具中運(yùn)行此代碼將得到以下輸出:
Result of increment = 2 Result of increment = 4 Result of increment = 7
1
2
3
4
5
從中可以看出,局部變量i的值是靜態(tài)的,并存儲在單個存儲位置中。
結(jié)果,i的值是持久的,并且在任務(wù)調(diào)用之間保持其值。
當(dāng)我們調(diào)用任務(wù)時,我們將增加已經(jīng)存儲在給定存儲位置中的值。
下面的代碼段顯示了相同的任務(wù),除了這次我們使用了自動關(guān)鍵字。
// Automatic task which performs the increment task automatic increment(input integer incr); integer i = 1; i = i + incr; $display("Result of increment = %0d", i); endtask
1
2
3
4
5
6
7
8
9
10
11
// Run the task three times initial begin increment(1); increment(2); increment(3); end
1
2
3
4
5
6
7
8
9
10
11
在icarus verilog仿真工具中運(yùn)行此代碼將得到以下輸出:
Result of increment = 2 Result of increment = 3 Result of increment = 4
1
2
3
4
5
由此,我們現(xiàn)在可以看到局部變量i是動態(tài)的,并且在調(diào)用任務(wù)時會如何創(chuàng)建它。創(chuàng)建它之后,然后為其分配值1。
任務(wù)完成運(yùn)行后,將釋放動態(tài)分配的內(nèi)存,并且本地變量不再存在。
FPGA
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。