Verilog-testbench筆記

並行塊

在測試中經常會用到 fork...join塊。使用並行塊能表示以同一個時間起點算起的多個時間的運行,並行的執行復雜的過程結構,如循環或任務。

eg

module inilne_tb;
reg [7:0] data_bus;
    initial fork
        data_bus=8'b00;
        #10 data_bus=8'h45;
        //以下的兩個repeat開始執行時間不同,但可以並行同時執行
        #20 repeat(10) #10 data_bus=data_bus+1;
        #25 repeat(5) #20 data_bus=data_bus<<1;
        #140 data_bus=8'h0f;
    join
endmodule

  • 0時刻對data_bus賦初始值

  • 10個單位時間之後對data_bus重新賦值

  • 從20單位時間開始,每10個單位時間數據自加

  • 從25單位時間開始,沒20個單位時間數據左移,與上一條指令並行執行

  • 140單位時間再賦值

建立時鐘

雖然有時候在設計中會包含時鐘,但時鐘通常在測試模塊中。可以使用門級和行爲級建立時鐘模型。行爲描述一般使用的人較多。

簡單的對稱方波

reg clk;
always begin
    #peroid/2 clk=0;
    #peroid/2 clk=1;
end

簡單帶延遲的對稱方波時鐘

reg clk;
initial begin
    clk=0;
    #(peroid)
    forever
        #(peroid/2) clk=!clk;
end

不規則形

reg clk;
initial begin
    #(peroid+1) clk=1;
    #(peroid/2-1)
    forever begin
        #(peroid/4) clk=0;
        #(3*peroid/4) clk=1;
    end
end

將會產生一個帶延遲的,佔空比不爲1,同時投脈衝不規則的時鐘。

Verilog高級結構

task

一般用於編寫測試模塊或者行爲描述的模塊。其中可以包含時間控制(如: #delays,@,wait);也可以包含input、output、inout端口定義和參數;同時也可以調用其他任務或函數。


module bus_ctrl_tb;
    reg[7:0] data;
    reg data_valid,data_rd;
    cpu ul(data_valid,data,data_in);
    initial begin
        //在模塊中調用task
        cpu_driver(8'b0000_0000)
        cpu_driver(8'b0000_0000)
        cpu_driver(8'b0000_0000)
    end
    //定義task
    task cpu_driver;
    input [7:0] data_in;
        begin
            #30 data_valid=1;
            wait(data_rd==1);
            #20 data=data_in;
            wait(data_rd==0);
            #20 data=8'hzz;
            #30 data_valid=0;
        end
    endtask
endmodule

在測試模塊中使用任務可以提高程序代碼的效率,可以用任務把多次重複的操作包裝起來。

同時要注意的是,模塊的任務中,用於定時控制的信號,例如clk絕對不能作爲任務的輸入。因爲輸入值只想任務內部傳遞第一次,而定時控制一般來講絕對不止一次傳遞控制。

不要在程序的不同部分同時調用一個任務。這是因爲任務只有一組本地變量,同一時刻調用兩次相同的任務將會導致錯誤。這種情況同時發生在使用定時控制的任務中。


parameter MAX_BITS=8;
reg[MAX_BITS:1] D;

task reverse_bits;
//雙向總線端口被當做寄存器類型
inout [7:0] data;
integer K;
    for(K=0;K<MAX_BITS;K=K+1)
        reverse_bits[MAX_BITS-(K+1)]=data[K];
endtask
always @ (posedge clk)
    reverse_bits(D);

上面的代碼可以看出,在task定義過程中,有直接使用reverse_bits[MAX_BITS-(K+1)]=data[K];,也就是說,在Verilog中與函數一樣,task、function調用都是直接將參數代替函數名直接改變。上面的調用 reverse_bits(D)等價於:

inout [7:0] data;
integer K;
    for(K=0;K<MAX_BITS;K=K+1)
        D[MAX_BITS-(K+1)]=data[K];

任務只含有一個雙向總線(inout)端口和一個內部變量,沒有其他輸入端口、輸出端口和定時控制,沒有引用模塊變量。

在任務調用時候,任務的輸入變量(端口)在任務內部被當做寄存器類型變量處理(D)。


module mult(clk,a,b,out,en_mult);
    input clk,en_mult;
    input [3:0]a,b;
    output [7:0] out;
    reg[15:0] out;
        always @ (posedge clk)
            //任務調用
            multme(a,b,out);
    task multme;
        input [3:0] xme,tome;
        output [7:0] result;
        wait(en_mult)
            result=xme*tome;
    endtask
endmodule

模塊中定義的任務含有輸入,輸出,時間控制和一個內部變量,並且引用了一個本模塊的變量。任務調用時候參數的順序應該與任務定義聲明的變量順序相同。

function

函數不能包含定時控制,但是可以在包含定時控制的過程塊中調用函數。

在模塊中,使用名爲func的函數時,是將它作爲名爲func的寄存器類型變量來處理。


module orand(a,b,c,d,e,out);
    input [&;0] a,b,c,d,e;
    output[7:0] out;
    reg[7:0] out;
    always @(a,b,c,d,e);
        out=func(a,b,c,d,e);
    function [7:0] func;
        input [7:0] a,b,c,d,e;
        if(e==1)
            func=(a|b)&(c|d);
        else
            func=0;
    endfunction
endmodule 
  • 不包含任何定時控制語句

  • 至少一個輸入,不能含有任何輸出和總線口

  • 只返回一個值,值的變量名與函數名同名,數據類型默認爲reg

  • 傳遞給函數的參數順序與函數輸入口聲明的順序相同

  • 函數定義必須在模塊定義內

  • 函數不能調用任務(因爲任務可能包含時間控制),反之可以

  • 雖然函數只能返回一個值,但是他的返回值可以直接賦給一個由多個子信號拼購成的信號變量。{o1,o2,o3,o4}=func(a,b,c,d,e)

在函數定義時,如果在函數名之前定義了位寬,則函數就可以返回多bit構成的矢量。如果定義函數的語句比較多時,可以使用begin...end塊。

函數名前面的位寬代表了返回值(一般就是以函數名爲名的reg)的位寬。

若把函數定義爲整型、實型或時間類型,就可以返回相應類型的數據。可以在任何類型的表達式中調用函數。


module checksub(neg,in_a,in_b);
output neg;
input a,b;
reg neg;
    function integer subtr;
        input [7:0] in_a,in_b;
        subtr=in_a-in_b;
    endfunction
always @(a or b)
    begin if(subtr(a,b)<0)
            neg=1;
    else
        neg=0;
    else
endmodule
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章