task——Verilog的任务

task——Verilog的任务

之前的文章中记录了使用 function函数等功能:function—— Verilog的函数。这一次,记录使用task任务的功能。

事出有因

设计中需要使用到IIC salve module,那么在testbench中就需要有对应的master module,而且需要一个可以进行读写功能的模型。

如果还使用Verilog进行可综合的编写,未免有些琐碎,还需要严格的控制逻辑。因此,希望有一个功能可以实在进行IIC的读写控制时序操作。

之前的function只有函数功能,函数的操作中均为组合逻辑,无法完成本次需要的时序逻辑的需求,于是就选用了task功能。

Talk is cheap

整个IIC master model的文件如下:

/**
 * Filename		:		iic_master_model.v
 * Date			:		Friday, May 21, 2021 4:50 PM
 *
 * Version		:		V 0.1
 * Author		:		ljacki@163.com
 * Modification history
 *				:		Friday, May 21, 2021 4:50 PM Create the demo
 *				:		Friday, May 28, 2021 2:37 PM Add the task of write pkg.
 */
`timescale 1ns / 100ps
`define		DEBUG_INFO	1'b0
`define		DELAY		2
module iic_master_model (
	// system
	input						clk,
	input						rst_n,
	input 						req,

	input 						rd_valid_i,
	input [7:0]					rd_data_i,

	output reg					start,
	output reg					stop,
	output reg [7:0]			dvice_id,
	output reg 					wr_en_o,
	output reg [7:0]			wr_data_o,

	output reg 					rd_en_o,
	output reg [7:0]			rd_addr_o

	);

parameter DEVICE_ID	= 7'h70;
parameter PKG_LEN	= 8'h10;

//  Declarations
reg [7:0] dout;

// initial

initial begin
	start = 1'b0;
	stop = 1'b0;		// stop信号需要复位,否则master里面req不会assert
	dvice_id = 8'b0;
	wr_en_o = 1'b0;
	wr_data_o = 8'b0;
	rd_en_o = 1'b0;
	rd_addr_o = 8'h0;
	dout = 8'h0;
	if ( `DEBUG_INFO ) begin
		$display("\nINFO: IIC MASTER MODEL INSTANTIATED (%m)");
	end
end

task m_start;

	input wr_rd;	//1'b0: write, 1'b1:read;

	begin
		// @(posedge clk);
		#`DELAY
		start = 1'b1;
		dvice_id = {DEVICE_ID, wr_rd};

		@(posedge clk);
		#`DELAY
		start = 1'b0;
		dvice_id = 8'h0;

		// if ( wr_rd == 1'b0 ) begin
		// 	$display("status: %t master START WRITE,\tDEVICE ID : %X", $time, DEVICE_ID);
		// end else begin
		// 	$display("status: %t master START READ, \tDEVICE ID : %X", $time, DEVICE_ID);
		// end
	end
endtask


task m_wr_data;
	input [7:0] wr_addr;
	input [7:0] wr_data;
	input stop_flag;

	begin
		while(~req) @(posedge clk);
		#`DELAY
		wr_en_o = 1'b1;
		wr_data_o = wr_addr;

		@(posedge clk);
		#`DELAY
		wr_en_o = 1'b0;
		wr_data_o = 8'b0;

		if ( stop_flag) begin
			while(~req) @(posedge clk);
			#`DELAY
			stop = 1'b1;
			wr_en_o = 1'b1;
			wr_data_o = wr_data;

			@(posedge clk);
			#`DELAY
			stop = 1'b0;
			wr_en_o = 1'b0;
			wr_data_o = 8'b0;
		end else begin
			while(~req) @(posedge clk);
			#`DELAY
			stop = 1'b0;
			wr_en_o = 1'b1;
			wr_data_o = wr_data;

			@(posedge clk);
			#`DELAY
			stop = 1'b0;
			wr_en_o = 1'b0;
			wr_data_o = 8'b0;
		end

		if ( `DEBUG_INFO) begin
			$display("status: %t master WRITE ADDR:%X,\tDATA:%X\n", $time, wr_addr, wr_data);
		end
	end
endtask

task m_write;

	input [7:0] waddr;
	input [7:0] wdata;

	begin

		m_start(1'b0);

		m_wr_data(waddr, wdata, 1'b1);

		repeat(50) @(posedge clk);

		if ( `DEBUG_INFO) begin
			$display("status: %t master WRITE ADDR:%X,\tDATA:%X\n", $time, waddr, wdata);
		end
	end

endtask


task m_write_pkg;

	// input [7:0] len;
	input [8*PKG_LEN-1:0] addr;
	input [8*PKG_LEN-1:0] data;

	begin

		m_start(1'b0);

		m_wr_data(addr[(PKG_LEN-0)*8-1:(PKG_LEN-1)*8], data[(PKG_LEN-0)*8-1:(PKG_LEN-1)*8], 1'b0);
		m_wr_data(addr[(PKG_LEN-1)*8-1:(PKG_LEN-2)*8], data[(PKG_LEN-1)*8-1:(PKG_LEN-2)*8], 1'b0);
		m_wr_data(addr[(PKG_LEN-2)*8-1:(PKG_LEN-3)*8], data[(PKG_LEN-2)*8-1:(PKG_LEN-3)*8], 1'b0);
		m_wr_data(addr[(PKG_LEN-3)*8-1:(PKG_LEN-4)*8], data[(PKG_LEN-3)*8-1:(PKG_LEN-4)*8], 1'b1);

		repeat(50) @(posedge clk);
	end
endtask


task m_read;

	input [7:0] rd_addr;
	output [7:0] rd_dout;

	begin

		@(posedge clk);
		#`DELAY
		m_start(1'b0);

		while(~req) @(posedge clk);
		#`DELAY
		rd_en_o = 1'b1;
		rd_addr_o = rd_addr;

		@(posedge clk);
		#`DELAY
		rd_en_o = 1'b0;
		rd_addr_o = 8'h0;

		while(~req) @(posedge clk);
		#`DELAY
		m_start(1'b1);

		while(~rd_valid_i) @(posedge clk);
		#`DELAY
		rd_dout = rd_data_i;

		repeat(50) @(posedge clk);

		if ( `DEBUG_INFO ) begin
			$display("status: %t master READ ADDR:%X,\tDATA:%X\n", $time, rd_addr, rd_dout);
		end
	end
endtask


task m_cmp;

	input [7:0] addr;
	input [7:0] dexp;	// the expected data

	begin
		m_read(addr, dout);

		if ( dexp != dout ) begin
			$display("\t\t\tData compare error. Received %h, expected %h at time %t", dout, dexp, $time);
		end
	end

endtask

endmodule // the end of iic_master_model

声明:上述时序需要搭配iic master使用;根据文章,只是用来解释task任务的使用方法。

task简介

任务(task)就是一段封装在task-endtask之间的程序。

通过调用来执行,而且只有在调用时才执行,如果定义了任务,但是在整个过程中都没有调用它,则任务就不会被执行。

调用某个任务就以位置可能需要它处理某些数据并返回操作结果,因此Verilog中的task存在输入端和输出端。

定义

task的定义方式如下:

task<任务名>;              // <=  task task_id;           
  <端口及数据类型声明语句> // <=    [declaration]       
  <语句1>                  // <=    procedural_statement
  <语句2>                  // <=    procedural_statement
   .....                   // <=
  <语句n>                  // <=    procedural_statement
endtask                    // <=  endtask 						// <=  endtask

其中,关键词taskendtask将他们之间的内容标志成一个任务定义;

  • task标志着一个任务定义结构的开始;
  • task_id是任务名;
  • 可选项declaration是端口声明语句和变量声明语句,任务接收输入值和返回输出值就是通过此处声明的端口进行的;
  • procedural_statement是一段用来完成这个任务操作的过程语句,如果过程语句多于一条,应将其放在语句块内;
  • endtask为任务定义结构体结束标志。

task可以启动其他的task,其他的task又可以启动别的task,可以启动的task是没有限制的,只有当所有的task都完成之后,控制才能返回。task可以没有或者有多个输入输出类型的变量。

列子

一个task的列子为:

task find_max;      //任务定义结构开头,命名为 find_max
 input  [15:0] x,y; //输入端口说明
 output [15:0] tmp; //输出端口说明

 if(x>y)            //给出任务定义的描述语句
   tmp = x;
 else
   tmp = y;

endtask

注意事项

编写task的注意事项如下:

  • 第一行task语句中不能列出端口名称;
  • 任务的输入、输出端口和双向端口数量不受限制,甚至可以没有输入、输出以及双向端口;
  • 在任务中可以调用其他的任务或函数,也可以调用自身;
  • 在任务定义中可以出现disable中止语句,用于中断这个在执行的任务,但其是不可以综合的。当任务被中断后,程序流程将返回到调用任务的地方继续向下执行。

task的调用

task调用语句的语法形式如下:

task_id(端口1, 端口2, 端口n);

其中,task_id是要调用的任务名,端口1, 端口2,端口n是参数列表。参数列表给出传入任务的数据(进入任务的输入端)和接收返回结果的变量(从任务的输出端接收返回结果)。

在调用task时,需要注意以下几点:

  • 任务调用语句只能出现在过程块内;
  • 任务调用语句和一条普通的行为描述语句的处理方法一致;
  • 当被调用输入、输出户双向端口诗,任务调用语句必须包含端口名列表,且信号端口顺序、类型必须于任务定义结构中的顺序、类型一致;
  • 任务的输出端口必须和寄存器类型的数据变量对应。
  • 可综合的任务只能实现组合逻辑,也就是说调用可综合任务的时间为0。而在面向仿真的任务中可以带有时序控制,如多个时钟周期或时延,一次面向藩镇的任务调用时间不为0。

上述IIC master model可以通过简单的读写任务,对IIC总线添加测试激励并读取总线数据:

	/**
	 * 寄存器全部读一遍
	 */
	for (cnt = 8'h0; cnt <= 8'h57; cnt = cnt + 1'b1) begin
		m0.m_read(cnt, rd_dout);
	end

	/**
	 * 寄存器全部写一遍
	 */
	for (cnt = 8'h0; cnt <= 8'h57; cnt = cnt + 1'b1) begin
		m0.m_write(cnt, cnt);
	end

	/**
	 * 寄存器值进行比较
	 */
	for (cnt = 8'h0; cnt <= 8'h57; cnt = cnt + 1'b1) begin
		m0.m_cmp(cnt, cnt);
	end

	/**
	 * 连续写入,需要修改参数:PKG_LEN;
	 * PKG_LEN: 连续写入寄存器的个数;
	 * addr: 连续写入寄存器地址拼接,MSB 8 bit 优先写入;
	 * data: 连续写入寄存器值拼接,MSB 8 bit 优先写入;
	 * 需要手动修改 m_write_pkg的主体内容;
	 */
	m0.m_write_pkg( addr, data );

	// 短暂复位,寄存器值复位;
	// sys_rst_n = 1'b0;
	// #500
	// sys_rst_n = 1'b1;
	// $display("\nstatus: %t reset again", $time);
	//
	// for (cnt = 8'h0; cnt <= 8'h57; cnt = cnt + 1'b1) begin
	// 	m0.m_read(cnt, rd_dout);
	// end

对于CPU读写时序以及类似的UART/SP/IIC/PC/USB等总线接口时序,早期的验证方法都是提供一些任务来进行总线交互,而现在则有一个专有名称:BFM(Bus Function Model)

任务与函数的区别

任务与函数都是两种常见的执行调用对象,二者区别在于:

差异项任务特点函数特点
1任务可以有input、output和inout,数量不限函数只有input参数,且至少有一个input
2任务可以包含时序控制(如延时等)函数不能包含任何延迟,仿真时间0
3任务可以用disable中断函数不允许disable,wait语句
4任务可以同过I/O端口实现值传递函数名即输出变量名,通过函数返回值
5任务可以调用其他任务和函数函数只能调用其他函数,不能调用任务
6任务可以dinginess自己的仿真时间单位函数只能于主模块公用一个仿真时间单位
7任务能支持多种目的,能计算多个结果值,结果值只能通过被调用的任务输出端口输出或总线端口送出函数通过一个返回值来响应输入信号的值
8函数中不能有wire型变量

参考文章

简谈FPGA verilog中的task用法

Verilog重点解析(3)(task&function):文章中有对静态task和动态task的描述。

2021-6-26.

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShareWow丶

前人栽树,分享知识,传播快乐。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值