本文首發於微信公眾號「花螞蟻」,想要學習FPGA及Verilog的同學可以關注一下。

task和function說明語句分別用來定義任務和函數。

利用任務和函數可以把一個很大的程序模塊分解成許多較小的任務和函數便於理解和調試。

輸入、輸出和匯流排信號的值可以傳入、傳出任務和函數。任務和函數往往還是大的程序模塊中在不同地點多次用到的相同的程序段。

學會使用task和function語句可以簡化程序的結構,使程序明白易懂,是編寫較大型模塊的基本功。

一. task和function說明語句的不同點

任務和函數有些不同,主要的不同有以下四點:

  • 1) 函數只能與主模塊共用同一個模擬時間單位,而任務可以定義自己的模擬時間單位。
  • 2) 函數不能啟動任務,而任務能啟動其它任務和函數。
  • 3) 函數至少要有一個輸入變數,而任務可以沒有或有多個任何類型的變數。
  • 4) 函數返回一個值,而任務則不返回值。

函數的目的是通過返回一個值來響應輸入信號的值。任務卻能支持多種目的,能計算多個結果值,這些結果值只能通過被調用的任務的輸出或匯流排埠送出。Verilog HDL模塊使用函數時是把它當作表達式中的操作符,這個操作的結果值就是這個函數的返回值。下面讓我們用例子來說明:

例如,定義一任務或函數對一個16位的字進行操作讓高位元組與低位元組互換,把它變為另一個字(假定這個任務或函數名為: switch_bytes)。

任務返回的新字是通過輸出埠的變數,因此16位字位元組互換任務的調用源碼是這樣的:

switch_bytes(old_word,new_word);

任務switch_bytes把輸入old_word的字的高、低位元組互換放入new_word埠輸出。

而函數返回的新字是通過函數本身的返回值,因此16位字位元組互換函數的調用源碼是這樣的:

new_word = switch_bytes(old_word);

下面分兩節分別介紹任務和函數語句的要點。

二. task說明語句

如果傳給任務的變數值和任務完成後接收結果的變數已定義,就可以用一條語句啟動任務。任務完成以後控制就傳回啟動過程。如任務內部有定時控制,則啟動的時間可以與控制返回的時間不同。任務可以啟動其它的任務,其它任務又可以啟動別的任務,可以啟動的任務數是沒有限制的。不管有多少任務啟動,只有當所有的啟動任務完成以後,控制才能返回。

1) 任務的定義

定義任務的語法如下:

任務:

task <任務名>;
<埠及數據類型聲明語句>
<語句1>
<語句2>
.....
<語句n>
endtask

這些聲明語句的語法與模塊定義中的對應聲明語句的語法是一致的。

2) 任務的調用及變數的傳遞

啟動任務並傳遞輸入輸出變數的聲明語句的語法如下:

任務的調用:

<任務名>(1,埠2...,埠n);

下面的例子說明怎樣定義任務和調用任務:

任務定義:

task my_task;
input a, b;
inout c;
output d, e;

<語句> //執行任務工作相應的語句

c = foo1; //賦初始值
d = foo2; //對任務的輸出變數賦值t
e = foo3;
endtask

任務調用:

my_task(v,w,x,y,z);

任務調用變數(v,w,x,y,z)和任務定義的I/O變數(a,b,c,d,e)之間是一一對應的。當任務啟動時,由v,w,和x.傳入的變數賦給了a,b,和c,而當任務完成後的輸出又通過c,d和e賦給了x,y和z。下面是一個具體的例子用來說明怎樣在模塊的設計中使用任務,使程序容易讀懂:

module traffic_lights;
reg clock, red, amber, green;
parameter on=1, off=0, red_tics=350,
amber_tics=30,green_tics=200;
//交通燈初始化
initial red=off;
initial amber=off;
initial green=off;
//交通燈控制時序
always
begin
red=on; //開紅燈
light(red,red_tics); //調用等待任務
green=on; //開綠燈
light(green,green_tics); //等待
amber=on; //開黃燈
light(amber,amber_tics); //等待
end

//定義交通燈開啟時間的任務
task lightcolor,tics);
output color;
input[31:0] tics;
begin
repeat(tics) @(posedge clock);//等待tics個時鐘的上升沿
color=off;//關燈
end
endtask
//產生時鐘脈衝的always塊
always
begin
#100 clock=0;
#100 clock=1;
end
endmodule

這個例子描述了一個簡單的交通燈的時序控制,並且該交通燈有它自己的時鐘產生器。

任務定義時需注意以下事項:

(1)在第一行「task」語句中不能列出埠名列表。

(2)任務中可以有延時語句、敏感事件控制語句等事件控制語句。

(3)任務可以沒有或可以有一個或多個輸入、輸出和雙向埠。

(4)任務可以沒有返回值,也可以通過輸出埠或雙向埠返回一個或多個返回值。

(5)任務可以調用其它的任務或函數,也可以調用該任務本身。

(6)任務定義結構內不允許出現過程塊(initial或always過程塊)。

(7)任務定義結構內可以出現disable終止語句,這條語句的執行將中斷正在執行的任務。在任務被中斷後,程序流程將返回到調用任務的地方繼續向下執行。

二. function說明語句

函數定義是嵌入在關鍵字function和endfunction之間的,其中關鍵詞function標誌著一個函數定義結構的開端,endfunction標誌著一個函數定義結構的結束。

「<函數名>」是給被定義函數取的名稱。這個函數名在函數定義結構內部還代表著一個內部變數,函數調用後的返回值是通過這個函數名變數傳遞給調用語句的。

函數的目的是返回一個用於表達式的值。

定義函數的語法:

function <返回值的類型或範圍> (函數名);
<埠說明語句>
<變數類型說明語句>

begin
<語句>
........
end
endfunction

請注意<返回值的類型或範圍>這一項是可選項,如預設則返回值為一位寄存器類型數據。下面用例子說明:

function [7:0] getbyte;
input [15:0] address;
begin
<說明語句> //從地址字中提取低位元組的程序
getbyte = result_expression; //把結果賦予函數的返回位元組
end
endfunction

從函數返回的值

函數的定義蘊含聲明瞭與函數同名的、函數內部的寄存器。如在函數的聲明語句中<返回值的類型或範圍>為預設,則這個寄存器是一位的,否則是與函數定義中<返回值的類型或範圍>一致的寄存器。

函數的定義把函數返回值所賦值寄存器的名稱初始化為與函數同名的內部變數。下面的例子說明瞭這個概念:getbyte被賦予的值就是函數的返回值。

函數的調用

函數的調用是通過將函數作為表達式中的操作數來實現的。

其調用格式如下:

<函數名> (<表達式><,<表達式>>*)

其中函數名作為確認符。下面的例子中通過對兩次調用函數getbyte的結果值進行位拼接運算來生成一個字。

word = control? {getbyte(msbyte),getbyte(lsbyte)} : 0;

函數的使用規則

與任務相比較函數的使用有較多的約束,下面給出的是函數的使用規則:

  • 1) 函數的定義不能包含有任何的時間控制語句,即任何用#、@、或wait來標識的語句。
  • 2) 函數不能啟動任務。
  • 3) 定義函數時至少要有一個輸入參量。
  • 4) 在函數的定義中必須有一條賦值語句給函數中的一個內部變數賦以函數的結果值,該內部變數具有和函數名相同的名字。

舉例說明

下面的例子中定義了一個可進行階乘運算的名為factorial的函數,該函數返回一個32位的寄存器類型的值,該函數可後向調用自身,並且列印出部分結果值。

例:統計輸入數據中「0」的個數

function[3:0] out0;
input[7:0] x;
reg[3:0] count;
integer i;
begin
count=0;
for(i=0;i<=7;i=i+1)
if(x[i]==1b0) count=count+1;
out0=count;
end
endfunction

在進行函數定義時需要注意以下幾點:

(1)與任務一樣,函數定義結構只能出現在模塊中,而不能出現在過程塊內。

(2)函數至少必須有一個輸入埠。

(3)函數不能有任何類型的輸出埠(output埠)和雙向埠(inout埠)。

(4)在函數定義結構中的行為語句部分內不能出現任何類型的時間控制描述,也不允許使用disable終止語句。

(5)與任務定義一樣,函數定義結構內部不能出現過程塊。

(6)在一個函數內可以對其它函數進行調用,但是函數不能調用其它任務。

(7)在第一行「function」語句中不能出現埠名列表。

(8)在函數聲明的時候,在Verilog HDL的內部隱含地聲明瞭一個名為function_identifier(函數標識符)的寄存器類型變數,函數的輸出結果將通過這個寄存器類型變數被傳遞迴來。

函數調用時要注意以下幾點:

(1)函數的調用不能單獨作為一條語句出現,它只能作為一個操作數出現在調用語句內。

(2)函數的調用既能出現在過程塊中,也能出現在assign連續賦值語句中。

(3)函數定義中聲明的所有局部寄存器都是靜態的,即函數中的局部寄存器在函數的多個調用之間保持它們的值。

最後總結一下任務與函數的區別:

本文首發於微信公眾號「花螞蟻」,想要學習FPGA及Verilog的同學可以關注一下。

推薦閱讀:

相關文章