任務(wù)與函數(shù)的區(qū)別
和函數(shù)一樣,任務(wù)(task)可以用來描述共同的代碼段,并在模塊內(nèi)任意位置被調(diào)用,讓代碼更加的直觀易讀。函數(shù)一般用于組合邏輯的各種轉(zhuǎn)換和計(jì)算,而任務(wù)更像一個(gè)過程,不僅能完成函數(shù)的功能,還可以包含時(shí)序控制邏輯。下面對(duì)任務(wù)與函數(shù)的區(qū)別進(jìn)行概括:
任務(wù)
◆任務(wù)聲明
任務(wù)在模塊中任意位置定義,并在模塊內(nèi)任意位置引用,作用范圍也局限于此模塊。
模塊內(nèi)子程序出現(xiàn)下面任意一個(gè)條件時(shí),則必須使用任務(wù)而不能使用函數(shù)。
1)子程序中包含時(shí)序控制邏輯,例如延遲,事件控制等
2)沒有輸入變量
3)沒有輸出或輸出端的數(shù)量大于 1
Verilog 任務(wù)聲明格式如下:
task task_id ;
port_declaration ;
procedural_statement ;
endtask
任務(wù)中使用關(guān)鍵字 input、output 和 inout 對(duì)端口進(jìn)行聲明。input 、inout 型端口將變量從任務(wù)外部傳遞到內(nèi)部,output、inout 型端口將任務(wù)執(zhí)行完畢時(shí)的結(jié)果傳回到外部。
進(jìn)行任務(wù)的邏輯設(shè)計(jì)時(shí),可以把 input 聲明的端口變量看做 wire 型,把 output 聲明的端口變量看做 reg 型。但是不需要用 reg 對(duì) output 端口再次說明。
對(duì) output 信號(hào)賦值時(shí)也不要用關(guān)鍵字 assign。為避免時(shí)序錯(cuò)亂,建議 output 信號(hào)采用阻塞賦值。
例如,一個(gè)帶延時(shí)的異或功能 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 類型,雖然注明也可能沒錯(cuò)
#3 numco = numa ^ numb ;
//assign #3 numco = numa ^ numb ; //不用assign,因?yàn)檩敵瞿J(rèn)是reg
endtask
任務(wù)在聲明時(shí),也可以在任務(wù)名后面加一個(gè)括號(hào),將端口聲明包起來。
上述設(shè)計(jì)可以更改為:
task xor_oper_iner(
input [N-1:0] numa,
input [N-1:0] numb,
output [N-1:0] numco ) ;
#3 numco = numa ^ numb ;
endtask
◆任務(wù)調(diào)用
任務(wù)可單獨(dú)作為一條語句出現(xiàn)在 initial 或 always 塊中,調(diào)用格式如下:
task_id(input1, input2, …,outpu1, output2, …);
任務(wù)調(diào)用時(shí),端口必須按順序?qū)?yīng)。
輸入端連接的模塊內(nèi)信號(hào)可以是 wire 型,也可以是 reg 型。輸出端連接的模塊內(nèi)信號(hào)要求一定是 reg 型,這點(diǎn)需要注意。
對(duì)上述異或功能的 task 進(jìn)行一個(gè)調(diào)用,完成對(duì)異或結(jié)果的緩存。
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 //任務(wù)調(diào)用
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 ; //數(shù)據(jù)緩存
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 ; //阻塞賦值,易于控制時(shí)序
endtask
endmodule
◆對(duì)上述設(shè)計(jì)進(jìn)行簡單仿真,testbench 描述如下。
激勵(lì)部分我們使用簡單的 task 進(jìn)行描述,看起來會(huì)更加的清晰簡潔。
其實(shí),task 最多的應(yīng)用場景還是在 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
◆仿真結(jié)果如下。
由圖可知,異或輸出邏輯結(jié)果正確,相對(duì)于輸入有 3ns 的延遲。
且連接信號(hào) a,b,co_t 與任務(wù)內(nèi)部定義的信號(hào) numa,numb,numco 狀態(tài)也保持一致。
任務(wù)操作全局變量
因?yàn)槿蝿?wù)可以看做是過程性賦值,所以任務(wù)的 output 端信號(hào)返回時(shí)間是在任務(wù)中所有語句執(zhí)行完畢之后。
任務(wù)內(nèi)部變量也只有在任務(wù)中可見,如果想具體觀察任務(wù)中對(duì)變量的操作過程,需要將觀察的變量聲明在模塊之內(nèi)、任務(wù)之外,可謂之“全局變量”。
◆例如有以下 2 種嘗試?yán)?task 產(chǎn)生時(shí)鐘的描述方式。
//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;
◆ 仿真結(jié)果如下。
第一種描述方式,雖然任務(wù)內(nèi)部變量會(huì)有賦值 0 和賦值 1 的過程操作,但中間變化過程并不可見,最后輸出的結(jié)果只能是任務(wù)內(nèi)所有語句執(zhí)行完畢后輸出端信號(hào)的最終值。所以信號(hào) clk_test1 值恒為 1,此種方式產(chǎn)生不了時(shí)鐘。
第二種描述方式,雖然沒有端口信號(hào),但是直接對(duì)“全局變量”進(jìn)行過程操作,因?yàn)樵撊肿兞繉?duì)模塊是可見的,所以任務(wù)內(nèi)信號(hào)翻轉(zhuǎn)的過程會(huì)在信號(hào) clk_test2 中體現(xiàn)出來。
automatic 任務(wù)
和函數(shù)一樣,Verilog 中任務(wù)調(diào)用時(shí)的局部變量都是靜態(tài)的??梢杂藐P(guān)鍵字 automatic 來對(duì)任務(wù)進(jìn)行聲明,那么任務(wù)調(diào)用時(shí)各存儲(chǔ)空間就可以動(dòng)態(tài)分配,每個(gè)調(diào)用的任務(wù)都各自獨(dú)立的對(duì)自己獨(dú)有的地址空間進(jìn)行操作,而不影響多個(gè)相同任務(wù)調(diào)用時(shí)的并發(fā)執(zhí)行。
如果一任務(wù)代碼段被 2 處及以上調(diào)用,一定要用關(guān)鍵字 automatic 聲明。
◆當(dāng)沒有使用 automatic 聲明任務(wù)時(shí),任務(wù)被 2 次調(diào)用,可能出現(xiàn)信號(hào)間干擾,例如下面代碼描述:
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)
◆ 仿真結(jié)果如下。
en_cnt 為高時(shí),任務(wù) (1) 中信號(hào) en 有效, cnt1 能輸出正確的邏輯值;
此時(shí)任務(wù) (2) 中信號(hào) en 是不使能的,所以 cnt2 的值被任務(wù) (1) 驅(qū)動(dòng)的共用變量 cnt_temp 覆蓋。
en_cnt 為低時(shí),任務(wù) (2) 中信號(hào) en 有效,所以任務(wù) (2) 中的信號(hào) cnt2 能輸出正確的邏輯值;而此時(shí)信號(hào) cnt1 的值在時(shí)鐘的驅(qū)動(dòng)下,一次次被任務(wù) (2) 驅(qū)動(dòng)的共用變量 cnt_temp 覆蓋。
可見,任務(wù)在兩次并發(fā)調(diào)用中,共用存儲(chǔ)空間,導(dǎo)致信號(hào)間產(chǎn)生了影響。
◆其他描述不變,只在上述 task 聲明時(shí)加入關(guān)鍵字 automatic,如下所示。
task automatic test_flag ;
◆此時(shí)仿真結(jié)果如下。
en_cnt 為高時(shí),任務(wù) (1) 中信號(hào) cnt1 能輸出正確的邏輯值,任務(wù) (2) 中信號(hào) cnt2 的值為 X;
en_cnt 為低時(shí),任務(wù) (2) 中信號(hào) cnt2 能輸出正確的邏輯值,任務(wù) (1) 中信號(hào) cnt1 的值為 X;
可見,任務(wù)在兩次并發(fā)調(diào)用中,因?yàn)榇鎯?chǔ)空間相互獨(dú)立,信號(hào)間并沒有產(chǎn)生影響。
-
Verilog
+關(guān)注
關(guān)注
28文章
1351瀏覽量
110397 -
時(shí)序控制器
+關(guān)注
關(guān)注
0文章
19瀏覽量
11244 -
CLK
+關(guān)注
關(guān)注
0文章
127瀏覽量
17236 -
時(shí)鐘驅(qū)動(dòng)器
+關(guān)注
關(guān)注
0文章
34瀏覽量
13885
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論