任務與函數的區別
和函數一樣,任務(task)可以用來描述共同的代碼段,并在模塊內任意位置被調用,讓代碼更加的直觀易讀。函數一般用于組合邏輯的各種轉換和計算,而任務更像一個過程,不僅能完成函數的功能,還可以包含時序控制邏輯。下面對任務與函數的區別進行概括:
任務
◆任務聲明
任務在模塊中任意位置定義,并在模塊內任意位置引用,作用范圍也局限于此模塊。
模塊內子程序出現下面任意一個條件時,則必須使用任務而不能使用函數。
1)子程序中包含時序控制邏輯,例如延遲,事件控制等
2)沒有輸入變量
3)沒有輸出或輸出端的數量大于 1
Verilog 任務聲明格式如下:
task task_id ;
port_declaration ;
procedural_statement ;
endtask
任務中使用關鍵字 input、output 和 inout 對端口進行聲明。input 、inout 型端口將變量從任務外部傳遞到內部,output、inout 型端口將任務執行完畢時的結果傳回到外部。
進行任務的邏輯設計時,可以把 input 聲明的端口變量看做 wire 型,把 output 聲明的端口變量看做 reg 型。但是不需要用 reg 對 output 端口再次說明。
對 output 信號賦值時也不要用關鍵字 assign。為避免時序錯亂,建議 output 信號采用阻塞賦值。
例如,一個帶延時的異或功能 task 描述如下:
task xor_oper_iner;
input [N-1:0] numa;
input [N-1:0] numb;
output [N-1:0] numco ;
//output reg [N-1:0] numco ; //無需再注明 reg 類型,雖然注明也可能沒錯
#3 numco = numa ^ numb ;
//assign #3 numco = numa ^ numb ; //不用assign,因為輸出默認是reg
endtask
任務在聲明時,也可以在任務名后面加一個括號,將端口聲明包起來。
上述設計可以更改為:
task xor_oper_iner(
input [N-1:0] numa,
input [N-1:0] numb,
output [N-1:0] numco ) ;
#3 numco = numa ^ numb ;
endtask
◆任務調用
任務可單獨作為一條語句出現在 initial 或 always 塊中,調用格式如下:
task_id(input1, input2, …,outpu1, output2, …);
任務調用時,端口必須按順序對應。
輸入端連接的模塊內信號可以是 wire 型,也可以是 reg 型。輸出端連接的模塊內信號要求一定是 reg 型,這點需要注意。
對上述異或功能的 task 進行一個調用,完成對異或結果的緩存。
module xor_oper
#(parameter N = 4)
(
input clk ,
input rstn ,
input [N-1:0] a ,
input [N-1:0] b ,
output [N-1:0] co );
reg [N-1:0] co_t ;
always @(*) begin //任務調用
xor_oper_iner(a, b, co_t);
end
reg [N-1:0] co_r ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
co_r <= 'b0 ;
end
else begin
co_r <= co_t ; //數據緩存
end
end
assign co = co_r ;
/*------------ task -------*/
task xor_oper_iner;
input [N-1:0] numa;
input [N-1:0] numb;
output [N-1:0] numco ;
#3 numco = numa ^ numb ; //阻塞賦值,易于控制時序
endtask
endmodule
激勵部分我們使用簡單的 task 進行描述,看起來會更加的清晰簡潔。
其實,task 最多的應用場景還是在 testbench 中使用。task 在一些編譯器中也不支持綜合。
`timescale 1ns/1ns
module test ;
reg clk, rstn ;
initial begin
rstn = 0 ;
#8 rstn = 1 ;
forever begin
clk = 0 ; # 5;
clk = 1 ; # 5;
end
end
reg [3:0] a, b;
wire [3:0] co ;
initial begin
a = 0 ;
b = 0 ;
sig_input(4'b1111, 4'b1001, a, b);
sig_input(4'b0110, 4'b1001, a, b);
sig_input(4'b1000, 4'b1001, a, b);
end
task sig_input ;
input [3:0] a ;
input [3:0] b ;
output [3:0] ao ;
output [3:0] bo ;
@(posedge clk) ;
ao = a ;
bo = b ;
endtask ; // sig_input
xor_oper u_xor_oper
(
.clk (clk ),
.rstn (rstn ),
.a (a ),
.b (b ),
.co (co ));
initial begin
forever begin
#100;
if ($time >= 1000) $finish ;
end
end
endmodule
◆仿真結果如下。
由圖可知,異或輸出邏輯結果正確,相對于輸入有 3ns 的延遲。
且連接信號 a,b,co_t 與任務內部定義的信號 numa,numb,numco 狀態也保持一致。
任務操作全局變量
因為任務可以看做是過程性賦值,所以任務的 output 端信號返回時間是在任務中所有語句執行完畢之后。
任務內部變量也只有在任務中可見,如果想具體觀察任務中對變量的操作過程,需要將觀察的變量聲明在模塊之內、任務之外,可謂之“全局變量”。
◆例如有以下 2 種嘗試利用 task 產生時鐘的描述方式。
//way1 to decirbe clk generating, not work
task clk_rvs_iner ;
output clk_no_rvs ;
# 5 ; clk_no_rvs = 0 ;
# 5 ; clk_no_rvs = 1 ;
endtask
reg clk_test1 ;
always clk_rvs_iner(clk_test1);
//way2: use task to operate global varialbes to generating clk
reg clk_test2 ;
task clk_rvs_global ;
# 5 ; clk_test2 = 0 ;
# 5 ; clk_test2 = 1 ;
endtask // clk_rvs_iner
always clk_rvs_global;
◆ 仿真結果如下。
第一種描述方式,雖然任務內部變量會有賦值 0 和賦值 1 的過程操作,但中間變化過程并不可見,最后輸出的結果只能是任務內所有語句執行完畢后輸出端信號的最終值。所以信號 clk_test1 值恒為 1,此種方式產生不了時鐘。
第二種描述方式,雖然沒有端口信號,但是直接對“全局變量”進行過程操作,因為該全局變量對模塊是可見的,所以任務內信號翻轉的過程會在信號 clk_test2 中體現出來。
automatic 任務
和函數一樣,Verilog 中任務調用時的局部變量都是靜態的。可以用關鍵字 automatic 來對任務進行聲明,那么任務調用時各存儲空間就可以動態分配,每個調用的任務都各自獨立的對自己獨有的地址空間進行操作,而不影響多個相同任務調用時的并發執行。
如果一任務代碼段被 2 處及以上調用,一定要用關鍵字 automatic 聲明。
◆當沒有使用 automatic 聲明任務時,任務被 2 次調用,可能出現信號間干擾,例如下面代碼描述:
task test_flag ;
input [3:0] cnti ;
input en ;
output [3:0] cnto ;
if (en) cnto = cnti ;
endtask
reg en_cnt ;
reg [3:0] cnt_temp ;
initial begin
en_cnt = 1 ;
cnt_temp = 0 ;
#25 ; en_cnt = 0 ;
end
always #10 cnt_temp = cnt_temp + 1 ;
reg [3:0] cnt1, cnt2 ;
always @(posedge clk) test_flag(2, en_cnt, cnt1); //task(1)
always @(posedge clk) test_flag(cnt_temp, !en_cnt, cnt2);//task(2)
◆ 仿真結果如下。
en_cnt 為高時,任務 (1) 中信號 en 有效, cnt1 能輸出正確的邏輯值;
此時任務 (2) 中信號 en 是不使能的,所以 cnt2 的值被任務 (1) 驅動的共用變量 cnt_temp 覆蓋。
en_cnt 為低時,任務 (2) 中信號 en 有效,所以任務 (2) 中的信號 cnt2 能輸出正確的邏輯值;而此時信號 cnt1 的值在時鐘的驅動下,一次次被任務 (2) 驅動的共用變量 cnt_temp 覆蓋。
可見,任務在兩次并發調用中,共用存儲空間,導致信號間產生了影響。
◆其他描述不變,只在上述 task 聲明時加入關鍵字 automatic,如下所示。
task automatic test_flag ;
◆此時仿真結果如下。
en_cnt 為高時,任務 (1) 中信號 cnt1 能輸出正確的邏輯值,任務 (2) 中信號 cnt2 的值為 X;
en_cnt 為低時,任務 (2) 中信號 cnt2 能輸出正確的邏輯值,任務 (1) 中信號 cnt1 的值為 X;
可見,任務在兩次并發調用中,因為存儲空間相互獨立,信號間并沒有產生影響。
-
Verilog
+關注
關注
28文章
1351瀏覽量
110163 -
時序控制器
+關注
關注
0文章
19瀏覽量
11233 -
CLK
+關注
關注
0文章
127瀏覽量
17185 -
時鐘驅動器
+關注
關注
0文章
33瀏覽量
13862
發布評論請先 登錄
相關推薦
評論