在verilog中,任務task和函數function之間有明顯的區別,例如任務可以消耗時間而函數不能,函數里面不能帶有諸如#100的時延語句或諸如@(posedge clock)、wait(ready)的阻塞語句,也不能調用任務,還有verilog中的函數必須有返回值,並且返回值必須被使用,例如用到賦值語句中。
關於verilog中任務task和函數function的具體用法和區別參考我寫過的一篇文章;
verilog中的任務task和函數function用法及區別
systemveilog中函數和任務的使用方法如下:
1、一般情況下,不帶參數的子程序在定義或調用時不需要帶空括號();
2、begin...end為可選的,因為task...endtask和function...endfunction關鍵詞足以定義這些程序的邊界, 但是在verilog-1995中,除了單行以外的子程序是必須的;
3、可以采用簡明的c語言風格,缺省的類型和方向是logic input;但是在verilog中,對參數要進行兩次聲明,一次是方向聲明,一次是類型聲明;
//systemverilog風格
task test(a, b, output bit [15:0] c, d);
//其中a,b為input logic a,b; c,d為output bit [15:0];
//verilog風格
task test2;
output [31:0] x;
reg [31:0] x;
input y;
endtask
4、可以在參數列表中指定輸入參數(input),輸出參數(output)、輸入輸出參數(inout)或引用參數(ref);只有數組變量可以在形式參數列表中聲明為ref類型,而線網類型不能聲明為ref類型;在使用ref時,有時候為了保護數據對象不被寫入,可以同通過const方式來限定ref聲明的參數
systemverilog中ref的具體用法可以參考我寫的一篇文章:systemverilog中ref的用法
但是在verilog中,對參數的處理方式很簡單,在子程序的開頭把input或inout的值復制給本地變量,在子程序退出時,則復制output或inout的值。除了標量以外,沒有任何把存儲器傳遞給verilog子程序的方法;
5、數組可以作為形式參數傳遞;
6、可以為參數指定一個缺省值,如果在調用時不指明參數,則使用缺省值。
function void print_checksum(ref bit [31:0] a[], input bit [31:0] low, input int high = -1);
bit [31:0] checksum = 0;
if(high = -1 || high >= a.size())
high = a.size() - 1;
for(int i = low, i < high; i++)
checksum += a[i];
$display("The array checksum is %0d", checksum);
endfunction
print_checksum(a); //a[0:size()-1]中所有元素的校驗和---缺省情況
print_checksum(a, 2, 4); //a[2:4]中所有元素的校驗和
print_checksum(a, 1); //a[1;$]中所有元素的校驗和
print_checksum(a,,2) //a[0:2]中所有元素的校驗和
7、采用名字進行參數傳遞;
task many(input int a = 1, b = 2; c = 3, d = 4);
$display("%0d %0d %0d %0d", a, b, c, d);
endtask
initial begin
many(6, 7, 8, 9); //a b c d的值分別為 6 7 8 9
many(); //a b c d全部使用默認值
many(.c(8)); //c的值為8,其他全部用默認值
many(,6, , d(8)); // 1 6 3 8混合指定方式
end
8、需要注意的其他問題:
task sticky(ref int array[10], int a, b);
上述a和b參數類型會采用與前一個參數一致的ref類型,對簡單的int變量使用ref並無必要,但編譯器不會對此做出任何反應,可能連警告都沒有,這是使用了一個錯誤的方向類型。
如果你在子程序中使用了非缺省輸入類型的參數,應該明確指明所有參數的方向,如下所示:
task sticky(ref int array[10], input int a, b);
function的獨特使用規則如下:
1、可以返回數值或不返回數值,如果返回需要使用關鍵詞return,如果不返回,則應該聲明函數為void function;
2、在systemveilog中,允許函數調用任務,但是只能在由fork...join_none語句生成的線程中調用。
void函數如下;
function void print_state();
$display("@%0t: time = %s", $time);
endfunction
3、function中可以使用return得到函數的返回值,不使用return時,與函數名字相同的變量的值就是返回值。
function bit tranmit();
//其他語句
return ~ifc.cb.error;
endfunction
4、從函數返回一個數組有兩種方法:
第一中方法是定義一個數組類型,然后在函數的聲明中使用該類型。
typedef int fixed_array5[5];
fixed_array5 f5;
function fixed_array5 init(int start);
foreach(init[i])
init[i] = i + start;
endfunction
initial begin
f5 = init(5);
foreach(f5[i[)
$display("fd[%0d] = %0d", i, f5[i]);
end
上述代碼存在的問題是,函數init創建了一個數組,給數組的值被復制到數組f5中。如果數組很大,那么可能會引起一個性能上的問題。
第二種方法是通過引用來進行數組參數的傳遞。
function void init(ref int f[5], input int start);
foreach(f[i])
f[i] = i + start;
endfunction
int fa[5];
initial begin
init(fa, 5);
foreach(fa[i]
$display("fa[%0d] = %0d", i, fa[i]);
end
從函數中返回數組的最后一種方式是將數組包裝到一個類中,然后返回對象的句柄。
task的獨特使用規則如下:
1、task無法通過return返回結果,因此只能通過output, inout或者ref參數來返回;
2、任務中可以使用return,提前返回;
task load_array(int len, ref int array[]);
if(len < 0) begin
$display("bad len");
return;
//其他代碼
endtask
3、task可以內置耗時語句,而function不能。常見的耗時語句包括@event, wait event, #delay等。
task和function的選擇建議:
1、在不耗時定義為function,在內置耗時語句時使用task;function一般用於純粹的數字或邏輯運算,task可能被運用於需要耗時的信號采樣或者驅動場景;
2、function可以被function和task調用,內置有耗時語句的task只能由task來調用;
關於task和function使用的例題:
typedef struct {
bit [1:0] cmd;
bit [7:0] addr;
bit [31:0] data;
} trans;
function automatic void cp_copy(trans s, trans t);
t = s;
endfunction
initial beign
trans s, t;
s.cmd = 'h1;
s.addr = 'h10;
s.data = 'h100;
op_copy(t, s);
t.cmd = 'h2;
end
//result
t.cmd = 'h2;
t.addr = 'h0;
t.data = 'h0;
task的調用方法:
logic red;
parameter red_tics = 100;
always begin //控制燈光
red = on; //打開紅燈
light(red,red_tics) ; //等待end
//等待‘tics’時鍾上升沿的任務
//在關掉‘color’燈之前
task light (output color,input [31:0] tics) ;
repeat (tics)@ (posedge clock);
color = off;//關燈
endtask: light