Verilog学习之TASK的使用

开发工具:ISE Design Suite 14.7

仿真工具:Modelsim SE-64 10.4

在初学FPGA时设计工程较小仿真比较简单,未考虑使用task任务来写TestBench,随着不断的学习和对官方IP的使用逐渐发现仿真程序会越写越多,导致仿真程序结构不好规划,看到大多数官方的仿真都使用了Task使仿真更加清晰,通过自己的理解对task做了简单总结。

一、task说明

task说明语句用来定义任务。利用任务可以把一个很大的程序模块分解成许多较小的任务便于理解和调试。输入、输出和总线信号的值可以传入、传出任务。任务往往还是大的程序模块中在不同地点多次用到的相同的程序段。使用task语句可以简化程序的结构,使程序明白易懂。

在任务的使用过程中容易和函数产生混淆,在Verilog中函数的说明语句是function。

1.1、task与function区别

task和function的区别如下:

  1. 函数只能与主模块共用同一个仿真时间单位,而任务可以定义自己的仿真时间单位。

  2. 函数不能启动任务,而任务能启动其它任务和函数。

  3. 函数至少要有一个输入变量,而任务可以没有或有多个任何类型的变量。

  4. 函数返回一个值,而任务则不返回值。

函数的目的是通过返回一个值来响应输入信号的值。任务却能支持多种目的,能计算多个结果值,这些结果值只能通过被调用的任务的输出或总线端口送出。Verilog HDL模块使用函数时是把它当作表达式中的操作符,这个操作的结果值就是这个函数的返回值。

1.2、Task语法

如果传给任务的变量值和任务完成后接收结果的变量已定义,就可以用一条语句启动任务。任务完成 以后控制就传回启动过程。如任务内部有定时控制,则启动的时间可以与控制返回的时间不同。任务可以启动其它的任务,其它任务又可以启动别的任务,可以启动的任务数是没有限制的。不管有多少任务启动,只有当所有的启动任务完成以后,控制才能返回。

1.2.1 任务的定义

定义任务的语法如下:

task <任务名>;

<端口及数据类型声明语句>

<语句1>

<语句2>

.....

<语句n>

endtask

这些声明语句的语法与模块定义中的对应声明语句的语法是一致的。 下面通过一个任意(整数)分频器对task进行举例。

二、task使用

下面是任意分频器工程模块

bash 复制代码
`timescale 1 ns / 1 ps
module clk_arb_odd#(
parameter           U_DLY = 1
                                        )
                                        (
output  wire        uart_baud_en        ,   // flag for uart data baud
output  wire        clk_c               ,   

input   wire[11:0]  uart_baud_param     ,   // uart baud parameter
input   wire        rst_n               ,   // reset, active low
input   wire        clk                     
                                        );

//----------------------------------------------------------------
// register define 
//----------------------------------------------------------------
reg         [11:0]  clk_cnt             ;   //clk cycle count//
reg         [11:0]  clk_cnt_h           ;   //Hign level count//
reg         [11:0]  clk_cnt_l           ;   //Low  level count//
reg                 clk_cnt_odd         ;   //If input div_para odd, for adjustment//用于奇数分频
reg                 clk_div_a           ;
reg                 clk_div_b           ;
reg                 clk_div_o           ;
reg                 clk_div_o_dly       ;
reg                 uart_baud_flag      ;


//----------------------------------------------------------------
// assignment 
//----------------------------------------------------------------
assign  uart_baud_en    = (uart_baud_param == 12'h001) ? clk : uart_baud_flag;
assign  clk_c = (clk_div_a || clk_div_b) ? 1'b1 : 1'b0;
//----------------------------------------------------------------
//----------------------------------------------------------------
always @(posedge clk,negedge rst_n)
begin
    if(rst_n == 1'b0)
        begin 
            clk_cnt_odd <= #U_DLY  1'b0;
            
            clk_cnt_h   <= #U_DLY 12'h0;
            clk_cnt_l   <= #U_DLY 12'h0; 
        end
    else
        begin
            clk_cnt_odd <= #U_DLY uart_baud_param[0];     //if odd Num., low cnt_l + 1//
            clk_cnt_h   <= #U_DLY (uart_baud_param >>1);
            clk_cnt_l   <= #U_DLY clk_cnt_h + clk_cnt_odd;
        end
            
end
//----------------------------------------------------------------
//----------------------------------------------------------------
always @(posedge clk,negedge rst_n)
begin
    if(rst_n == 1'b0)
        begin
            clk_cnt     <= #U_DLY 12'h0;
            clk_div_a   <= #U_DLY  1'b0;
        end
    else if((clk_div_a == 1'b1) && (clk_cnt >= clk_cnt_h))
        begin
            clk_cnt     <= #U_DLY 12'h1;
            clk_div_a   <= #U_DLY  1'b0;
        end
    else if(clk_cnt >= clk_cnt_l)
        begin
            clk_cnt     <= #U_DLY 12'h1;
            clk_div_a   <= #U_DLY  1'b1;
        end
    else
        begin
            clk_cnt     <= #U_DLY clk_cnt + 12'h1;
            clk_div_a   <= #U_DLY clk_div_a;
        end
end
//----------------------------------------------------------------
//----------------------------------------------------------------
always @(negedge clk,negedge rst_n)
begin
    if (rst_n == 1'b0)
        clk_div_b   <= #U_DLY 1'b0;
    else if ((clk_div_a == 1'b1) && (clk_cnt_odd == 1'b1))  
        clk_div_b   <= #U_DLY 1'b1;
    else
        clk_div_b   <= #U_DLY 1'b0;
end

//----------------------------------------------------------------
//----------------------------------------------------------------
always @(posedge clk,negedge rst_n)
begin
    if(rst_n == 1'b0)
        begin
            clk_div_o       <= #U_DLY 1'b0;
            clk_div_o_dly   <= #U_DLY 1'b0;
            uart_baud_flag  <= #U_DLY 1'b0;
        end
    else
        begin
            clk_div_o_dly   <= #U_DLY clk_div_o;
            uart_baud_flag  <= #U_DLY (clk_div_o_dly == 1'b0 && clk_div_o == 1'b1);
            if (uart_baud_param == 12'h000)
                clk_div_o   <= #U_DLY 1'b0;
            else if (uart_baud_param == 12'h001)
                clk_div_o   <= #U_DLY clk;
            else
                clk_div_o   <= #U_DLY clk_div_a || clk_div_b;      //cnt_L > cnt_H, use"|", else "&"//
        end
end
//----------------------------------------------------------------
//----------------------------------------------------------------
endmodule

下面是仿真模块,该工程比较小,可以不用task任务进行调试更方便,但是这儿为了对task进行巩固还是选择用task。下面仿真分别在任务中设置了3、10和100分频。

bash 复制代码
module vtf_clk_arb_odd;

	// Inputs
	reg [11:0] uart_baud_param;
	reg rst_n;
	reg clk;

	// Outputs
	wire uart_baud_en;
    wire clk_c;

	// Instantiate the Unit Under Test (UUT)
	clk_arb_odd uut (
		.uart_baud_en(uart_baud_en), 
		.uart_baud_param(uart_baud_param), 
        .clk_c(clk_c),
		.rst_n(rst_n), 
		.clk(clk)
	);
//-----------------------------------
//分频参数设置
//-----------------------------------
parameter   clk_div3    = 32'd3;
parameter   clk_div10   = 32'd10;
parameter   clk_div100  = 32'd100;
//-----------------------------------
//复位初始化
//-----------------------------------
initial
begin
        rst_n = 0;
        clk = 0;
        #100
        $display("reset start...");
        rst_n = 1;
        $display("reset end...");
        
end
//-----------------------------------
//分频任务设置
//-----------------------------------
    task freq_param;
            input       [31:0]  freq_in;
            output  reg [31:0]  freq_out;
            begin
                    repeat(200)
                    @(posedge clk)
                        freq_out = freq_in;
            end
    endtask
//-----------------------------------
//分频任务使用
//-----------------------------------
reg [31:0]  clk_param;

initial 
begin

        uart_baud_param = 0;
        clk_param = 0;

        wait(rst_n == 1);

        $display("frequency start...");

        $display("3 frequency");
        freq_param(clk_div3,clk_param);//使用任务设置3分频
        uart_baud_param = clk_param;
        $finish(1);

        $display("10 frequency");
        freq_param(clk_div10,clk_param);//使用任务设置10分频参数
        uart_baud_param = clk_param;
        $finish(2);

        $display("100 frequency");
        freq_param(clk_div100,clk_param);//使用任务设置100分频
        uart_baud_param = clk_param;
        $finish(3);
		// Add stimulus here

end

//-----------------------------------
//主时钟
//-----------------------------------
   always #5 clk = ~clk;   

endmodule

仿真工程写完后使用modelsim进行仿真查看;下图还是仿真波形,分别对应了3分频、10分频和100分频;

下面是3分频和10分频放大图;

关于task的简单理解也就差不多了,欢迎大家交流讨论。

最后可以看到在Transcript中打印了一系列的信息,这就和设置的display和finish有关系了,在Verilog中display就相当于C语言中printf作用,finish就相当于打断点的作用。

相关推荐
cycf7 小时前
CRC校验
fpga开发
landyjzlai9 小时前
AMBA总线(15)关于AXI-stream(sg模式)
arm开发·fpga开发·amba
白狐_7989 小时前
Quartus Prime 新手完全使用指南
fpga开发
Aaron158820 小时前
三种主流接收机架构(超外差、零中频、射频直采)对比及发展趋势浅析
c语言·人工智能·算法·fpga开发·架构·硬件架构·信号处理
博览鸿蒙20 小时前
一颗数字系统是如何在 FPGA 上“跑起来”的?
fpga开发
雨洛lhw1 天前
FPGA JTAG接口设计全解析
fpga开发·jtag
minglie11 天前
iverilog 配合 Makefile 搭建 Verilog 仿真工程
fpga开发
芒果树技术1 天前
MangoTree案例分享:基于AtomRIO FPGA平台,客户实现自适应主动减振
测试工具·fpga开发·模块测试
雨洛lhw1 天前
按键电路设计的细节
fpga开发
minglie12 天前
vio_uart的浏览器版上位机
fpga开发