平台:vivado2017.4
仿真:modelsin10.6d
最近在看XILINX的IP仿真时,发现他们做的仿真模型里面使用了很多task和function。这部分类容是在学习verilog期间忽略掉了。
首先来看看官方的解释。
- Task和function说明语句
Task和function说明语句分别用来定义任务和函数。利用任务和函数可以把一个很大的程序模块分解成很多较小的任务和函数便于理解和调试。
- Task和function说明语句的不同点
任务和函数有些不同,主要的不同有以下四点:
- 函数只能与主模块共用同一个仿真时间单位,而任务可以定义自己的仿真时间单位。
- 函数不能启动任务,而任务能启动其它任务和函数。
- 函数至少要有一个输入变量,而任务可以没有或有多个任何类型的变量。
- 函数返回一个值,而任务则不返回值。
函数的目的是同步返回一个值来相应输入信号的值。任务却能支持多种目的。能计算多个结果值,这些结果值只能通过被调用的任务的输出或总线端口送出。Verilog模块使用函数时是把它当作表达式中的操作符,这个操作符的结果值就是这个函数的返回值。
- Task说明语句
如果传给任务的变量值和任务完成后接收结果的变量已经定义,就可以用一条语句启动任务。任务完成以后控制就传回启动过程。如任务内部有定时控制,则启动的时间可以与控制返回的时间不同。任务可以启动其他的任务,其他任务又可以启动别的任务,可以启动的任务数是没有限制的。不管有多少任务启动,只有当所有的任务完成以后,控制才能返回。
Task语法定义:
任务:
task<任务名>;
<端口及数据类型声明语句>
<语句1>
<语句2>
...
<语句n>
endtask
任务的调用以及变量的传递
任务的调用:
<任务名>(端口1,端口2,...端口n);
在一个分频器模块中,我们来使用一个具体的例子看看怎么在模块设计中使用任务。
首先是原verilog模块为一个分频模块。模块的功能是对输入时钟100mhz信号进行分频。输出分频后时钟信号的上升沿。
// *********************************************************************************/
// Project Name :
// Author : i_huyi
// Email : i_huyi@qq.com
// Creat Time : 2022/7/12 16:36:06
// File Name : .v
// Module Name :
// Called By :
// Abstract :此模块的作用是通过寄存器来产生一个频率可调的方波。
//
//
// CopyRight(c) 2020, xxx xxx xxx Co., Ltd..
// All Rights Reserved
//
// *********************************************************************************/
// Modification History:
// 1. initial
// *********************************************************************************/
// *************************
// MODULE DEFINITION
// *************************
`timescale 1 ns / 1 ps
module ad_frequency#(
parameter U_DLY = 1
)
(
//分频系数
input wire[31:0] reg_clk_div_param ,
//产生的波形
output reg clk_cnv ,
//system clk
input wire clk_100mhz ,
input wire rst_n
);
//--------------------------------------
// localparam
//--------------------------------------
//----------------------------------------------------------------
// register define
//----------------------------------------------------------------
reg [31:0] clk_cnt ;
reg [31:0] clk_cnt_h ;
reg [31:0] clk_cnt_l ;
reg clk_cnt_odd ;
reg clk_div_a ;
reg clk_div_b ;
reg clk_div_o ;
reg clk_div_o_dly ;
//----------------------------------------------------------------
// wire define
//----------------------------------------------------------------
//----------------------------------------------------------------
// assignment
//----------------------------------------------------------------
//assign ad_start = clk_div_a || clk_div_b;
//----------------------------------------------------------------
//----------------------------------------------------------------
//奇偶分频计数器
always@(posedge clk_100mhz or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
clk_cnt_odd <= #U_DLY 1'b0;
clk_cnt_h <= #U_DLY 32'h0;
clk_cnt_l <= #U_DLY 32'h0;
end
else
begin
clk_cnt_odd <= #U_DLY reg_clk_div_param[0];
clk_cnt_h <= #U_DLY (reg_clk_div_param >>1);
clk_cnt_l <= #U_DLY clk_cnt_h + clk_cnt_odd;
end
end
//----------------------------------------------------------------
//----------------------------------------------------------------
//clk_a
always@(posedge clk_100mhz or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
clk_cnt <= #U_DLY 32'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 32'h1;
clk_div_a <= #U_DLY 1'b0;
end
else if(clk_cnt >= clk_cnt_l)//奇数个个数
begin
clk_cnt <= #U_DLY 32'h1;
clk_div_a <= #U_DLY 1'b1;
end
else
begin
clk_cnt <= #U_DLY clk_cnt + 32'h1;
clk_div_a <= #U_DLY clk_div_a;
end
end
//----------------------------------------------------------------
//----------------------------------------------------------------
//clk_b
always@(negedge clk_100mhz or 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_100mhz or negedge rst_n)
begin
if(rst_n == 1'b0)
clk_div_o <= #U_DLY 1'b0;
else
clk_div_o <= clk_div_a || clk_div_b;
end
always@(posedge clk_100mhz or negedge rst_n)
begin
if(rst_n == 1'b0)
clk_div_o_dly <= #U_DLY 1'b0;
else
clk_div_o_dly <= clk_div_o;
end
always@(posedge clk_100mhz or negedge rst_n)
begin
if(rst_n == 1'b0)
clk_cnv <= #U_DLY 1'b0;
else if(clk_div_o == 1'b1 && clk_div_o_dly == 1'b0)
clk_cnv <= #U_DLY 1'b1;
else
clk_cnv <= #U_DLY 1'b0;
end
//----------------------------------------------------------------
//----------------------------------------------------------------
endmodule
下面是他的仿真模块。主要是看task任务语法的定义和使用。
模拟复位信号
模拟产生分频系数
创建两个任务作用分别为对输入的分频值加100,和延迟输入值那么多的时钟周期。
具体的代码如下。
`timescale 1ns / 1ps
// Company:
// Engineer:
//
// Create Date: 14:36:04 07/28/2022
// Design Name: ad_frequency
// Module Name: E:/code1/xiyao/PXI_9801_FPGA_H0/9801_seq_writes/PXI_9801_seq/sources/sim/vtf_ad_frequency.v
// Project Name: PXI_9801
// Target Device:
// Tool versions:
// Description:
//
// Verilog Test Fixture created by ISE for module: ad_frequency
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
module vtf_ad_frequency;
// Inputs
reg [31:0] reg_clk_div_param;
reg clk_100mhz;
reg rst_n;
// Outputs
wire clk_cnv;
// Instantiate the Unit Under Test (UUT)
ad_frequency uut (
.reg_clk_div_param (reg_clk_div_param ),
.clk_cnv (clk_cnv ),
.clk_100mhz (clk_100mhz ),
.rst_n (rst_n )
);
//------------------------------------------------------
//复位参数
//------------------------------------------------------
integer i;
//设置复位参数
initial
begin
$display("[%t] : reset begin...", $realtime);
rst_n = 0;
for( i=0 ; i<100 ; i=i+1)
begin
@(posedge clk_100mhz );
end
$display("[%t] : reset stop...", $realtime);
rst_n = 1;
end
//------------------------------------------------------
//分频数值
//------------------------------------------------------
parameter clk_10mhz = 10;
parameter clk_1mhz = 100;
parameter clk_100khz = 1000;
parameter delay_cnt = 10000;
reg [31:0] div_value;
reg delay_over;
initial
begin
clk_100mhz = 0;
wait(rst_n == 1);
$display("[%t] : div start...", $realtime);
//------------------------------------------------------
//调任务
clk_div(div_value,clk_10mhz);//调用分频值加任务
reg_clk_div_param = div_value;//赋值
$display("[%t] : clk_10mhz begin...", $realtime);
delay(delay_over,delay_cnt);//调用等待任务
$display("[%t] : clk_10mhz end...", $realtime);
//------------------------------------------------------
//调任务
clk_div(div_value,clk_1mhz);//调用分频值加任务
reg_clk_div_param = div_value;//赋值
$display("[%t] : clk_1mhz begin...", $realtime);
delay(delay_over,delay_cnt);//调用等待任务
$display("[%t] : clk_1mhz end...", $realtime);
//------------------------------------------------------
//调任务
clk_div(div_value,clk_100khz);//调用分频值加任务
reg_clk_div_param = div_value;//赋值
$display("[%t] : clk_100khz begin...", $realtime);
delay(delay_over,delay_cnt);//调用等待任务
$display("[%t] : clk_100khz end...", $realtime);
$finish(2);
end
//------------------------------------------------------
//任务,输入的分频数值加100后输出
//------------------------------------------------------
task clk_div;
output [31:0] div_out;
input [31:0] div_in;
begin
repeat(10)@(posedge clk_100mhz);
div_out = div_in + 100;
end
endtask
//------------------------------------------------------
//任务,延时模块
//------------------------------------------------------
task delay;
output delay_over;
input [31:0] delay_in;
begin
repeat(delay_in)@(posedge clk_100mhz);
delay_over = 1;
end
endtask
always #5 clk_100mhz = ~clk_100mhz;
endmodule
仿真波形
可以看到我们定义了三个初始值,为10,100,1000调用任务模块后给到分频系数的值为110,200,1100。产生输出的cnv也不同。
另外,Verilog中function函数的使用说明,见下一篇。(15条消息) Verilog中function函数的使用说明_hy_520520的博客-CSDN博客