文章简介
本系列文章主要针对FPGA初学者编写,包括FPGA的模块书写、基础语法、状态机、RAM、UART、SPI、VGA、以及功能验证等。将每一个知识点作为一个章节进行讲解,旨在更快速的提升初学者在FPGA开发方面的能力,每一个章节中都有针对性的代码书写以及代码的讲解,可作为读者参考。
第十七章:task
task说明语句
task和function说明语句分别用来定义任务和函数。利用任务和函数可以把一个复杂的程序模块分解成若干个较小的任务和函数便于理解和测试。输入、输出和总线信号的值可以传入传出调用的任务和函数。任务和函数往往可以在大的程序模块中进行多次调用,从而简化程序的结构,使得整个程序容易被理解。本章讲解task任务的用法。
二者区别
任务和函数主要的不同点如下:
1、函数只能与主模块共用同一个仿真时间单位,而任务可以定义自己的仿真时间单位;
2、函数不能启动任务,而任务能启动其它任务和函数;
3、函数至少要有一个输入变量,而任务可以没有或有多个任何类型的变量;
4、函数返回一个值,而任务则不返回值。
函数的目的是通过返回一个值来响应输入信号的值。任务却能支持多种目的,能计算多个结果值,这些结果值只能通过被调用的任务的输出或总线端口送出。
task使用语法
定义任务的语法:
task <任务名task_id>;
<端口及数据类型声明语句>
<语句1>
<语句2>
<……>
<语句n>
endtask
其中,关键词 task 和 endtask 将它们之间的内容标志成一个任务定义,task 标志着一个任务定义结构的开始;task_id 是任务名;下来是端口声明语句和变量声明语句,任务接收输入值和返回输出值就是通过此处声明的端口进行的;再下来一段用来完成这个任务操作的过程语句,如果过程语句多于一条,应将其放在语句块内;endtask 为任务定义结构体结束标志。下面给出一个任务定义的实例。
任务定义示例
上述代码示例定义了一个名为“task_use”的任务,求取两个数的最大值。在定义任务时,有下列六点需要注意:
1、在第一行“task”语句中不能列出端口名称;
2、任务的输入、输出端口和双向端口数量不受限制,甚至可以没有输入、输出以及双向端口。
3、在任务定义的描述语句中,可以使用出现不可综合操作符合语句(使用最为频繁的就是延迟控制语句),但这样会造成该任务不可综合。
4、在任务中可以调用其他的任务或函数,也可以调用自身。
5、在任务定义结构内不能出现 initial和 always过程块。
6、在任务定义中可以出现“disable 中止语句” ,将中断正在执行的任务,但其是不可综合的。当任务被中断后,程序流程将返回到调用任务的地方继续向下执行。
任务调用
虽然任务中不能出现 initial 语句和 always 语句语句, 但任务调用语句可以在 initial 语句和 always 语句中使用,其调用语法形式如下:
<任务名>[(端口1, 端口 2, ........, 端口 n)];
其中 <任务名>是要调用的任务名,端口 1、端口 2等是参数列表。参数列表给出传入任务的数据(进入任务的输入端)和接收返回结果的变量(从任务的输出端接收返回结果)。
eg:任务调用:my_task(x,y,z);
任务调用变量(x,y,z)和任务定义的 I/O 变量(输入端口1,输入端口2,输出端口3)之间是一一对应的,当任务启动时,由 x 和 y传入的变量赋给了 端口1 和 端口2,而当任务完成之后的输出又通过 端口3 赋给 z。
任务调用语句中,参数列表的顺序必须与任务定义中的端口声明顺序相同。任务调用语句是过程性语句,所以任务调用中接收返回数据的变量必须是寄存器类型。下面举例说明怎么定义任务与调用任务。
代码示例
测试代码如下:
仿真波形如图所示:
由仿真图可以看:
1、当 start 信号变成高电平的时候,state 状态由 0 跳变到 1 时,此时调用 load 任务,把 data_in 的值寄存在 shift_data ,同时状态机状态跳转到 2。
2、当状态跳转到 2 的时候,调用 shift 任务,把 shift_data 的值往左移一位,同时状态机状态跳转到 3。
3、当状态跳到 3 的时候,调用 out 任务,把 shift_data 的值往 data_out 赋值的同时状态机状态跳转到 0。
4、最终结果:
8‘hA5左移1位,data_out=8‘h4A。
8‘h5A左移1位,data_out=8‘hB4。