欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 产业 > Verilog学习之TASK的使用

Verilog学习之TASK的使用

2025/3/20 15:53:40 来源:https://blog.csdn.net/weixin_50835177/article/details/146374104  浏览:    关键词: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使用

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

`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)
beginif(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; endelsebeginclk_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;endend
//----------------------------------------------------------------
//----------------------------------------------------------------
always @(posedge clk,negedge rst_n)
beginif(rst_n == 1'b0)beginclk_cnt     <= #U_DLY 12'h0;clk_div_a   <= #U_DLY  1'b0;endelse if((clk_div_a == 1'b1) && (clk_cnt >= clk_cnt_h))beginclk_cnt     <= #U_DLY 12'h1;clk_div_a   <= #U_DLY  1'b0;endelse if(clk_cnt >= clk_cnt_l)beginclk_cnt     <= #U_DLY 12'h1;clk_div_a   <= #U_DLY  1'b1;endelsebeginclk_cnt     <= #U_DLY clk_cnt + 12'h1;clk_div_a   <= #U_DLY clk_div_a;end
end
//----------------------------------------------------------------
//----------------------------------------------------------------
always @(negedge clk,negedge rst_n)
beginif (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;elseclk_div_b   <= #U_DLY 1'b0;
end//----------------------------------------------------------------
//----------------------------------------------------------------
always @(posedge clk,negedge rst_n)
beginif(rst_n == 1'b0)beginclk_div_o       <= #U_DLY 1'b0;clk_div_o_dly   <= #U_DLY 1'b0;uart_baud_flag  <= #U_DLY 1'b0;endelsebeginclk_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;elseclk_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分频。

module vtf_clk_arb_odd;// Inputsreg [11:0] uart_baud_param;reg rst_n;reg clk;// Outputswire 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
beginrst_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;beginrepeat(200)@(posedge clk)freq_out = freq_in;endendtask
//-----------------------------------
//分频任务使用
//-----------------------------------
reg [31:0]  clk_param;initial 
beginuart_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 hereend//-----------------------------------
//主时钟
//-----------------------------------always #5 clk = ~clk;   endmodule

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

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

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

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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词