FPGA設(shè)計(jì)藝術(shù)(14)使用函數(shù)和任務(wù)提升邏輯的可重用性(fpga的設(shè)計(jì)方法)

      網(wǎng)友投稿 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 (input ); // Declaration of local variables begin // function code end endfunction :

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      // Second function declaration style - arguments in body function ; (input ); // Declaration of local variables begin // function code end endfunction

      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ù)的輸入。

      我們使用字段來聲明函數(shù)返回哪種Verilog數(shù)據(jù)類型。如果我們排除了函數(shù)聲明的這一部分,則該函數(shù)將默認(rèn)返回一個1位值。

      當(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 (); // Code which implements the task endtask

      1

      2

      3

      4

      5

      6

      7

      // Task syntax with IO declared in the task body task ; begin // Code which implements the task end endtask

      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í)例。

      FPGA的設(shè)計(jì)藝術(shù)(14)使用函數(shù)和任務(wù)提升邏輯的可重用性(fpga的設(shè)計(jì)方法)

      相反,無論何時調(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)容。

      上一篇:網(wǎng)絡(luò)虛擬化(網(wǎng)絡(luò)虛擬化技術(shù)有哪些)
      下一篇:無監(jiān)督訓(xùn)練用堆疊自編碼器是否落伍?ML博士對比了8個自編碼器
      相關(guān)文章
      亚洲国产精品国产自在在线| 亚洲人成图片网站| 亚洲va在线va天堂va手机| 亚洲成AV人片一区二区| 亚洲色欲久久久综合网东京热| 亚洲国产av无码精品| avtt亚洲天堂| 老牛精品亚洲成av人片| 亚洲成a∨人片在无码2023| 亚洲老熟女五十路老熟女bbw | 亚洲午夜精品第一区二区8050| 国产成人高清亚洲一区91| 色偷偷亚洲第一综合| 亚洲欧美国产精品专区久久| 亚洲乱色伦图片区小说| 亚洲日韩精品国产3区| 久久久久久亚洲精品无码| 国产亚洲综合视频| 亚洲综合色成在线播放| 亚洲中文字幕无码爆乳AV| 日韩亚洲人成在线综合日本| 亚洲成a人片在线观看中文动漫| 亚洲AV综合色区无码一区爱AV | 亚洲成AV人片高潮喷水| 亚洲人成网站18禁止| 激情婷婷成人亚洲综合| 国产日产亚洲系列最新| 久久亚洲精品视频| 亚洲综合日韩中文字幕v在线| 亚洲精品91在线| 亚洲日本视频在线观看| 亚洲熟妇自偷自拍另欧美| 婷婷亚洲综合五月天小说在线| 亚洲国产精品一区二区第四页| 国产亚洲精品自在线观看| 亚洲AV综合色区无码一区| 91精品国产亚洲爽啪在线影院 | 久久久久亚洲AV无码专区网站 | 亚洲av无码兔费综合| 亚洲片国产一区一级在线观看| 亚洲色无码一区二区三区|