一、写在前面
本专栏为作者在 【数字IC手撕代码】 【数字IC笔试面经分享】 【数字IC工具解析】 以外开设的第四个独立专栏,旨在学习并提供有关Verilog硬件描述语言中非基础性的高阶语法特性知识,因本身专栏的独特定位,因此作者并不会涉及基础Verilog语言如阻塞式非阻塞赋值,过程块,数据类型等内容;同时受限于作者知识有限,本专栏也不会涉及System Verilog的相关内容,若按照IEEE的相关标准来看,本专栏将会聚焦Verilog-2005,即“IEEE Std 1364™-2005”以及之前的有关内容,提供相关的IC设计领域语法特性。以下为Verilog的进阶框图,有更多学习需求的读者可以检索相关英文标准进行学习。
二、解读task与function
2.1 task与function的区别
简单来说,function有着比task更为严苛的使用条件,严苛在以下几点上
(1) function需要在一个单位仿真时间内完成,而task中可以包含时间控制的命令
因此 “#,@,wait等” 语句都不能出现在function中
(2) function不能调用task但是task可以调用function
(3) function至少需要一个input端口,同时不能出现output和inout端口,task没有这个要求
(4) function需要有返回值,task可以没有返回值
(5) function中不能使用非阻塞赋值
2.2 task的使用细则
2.2.1 task使用格式
task有两种使用格式,但无论是哪一种,task只能存在于module和endmodlue之间。
(1) 以关键字task开始,然后是可选的关键字automatic,然后是使用任务名称和分号,并以关键字endtask结尾.
task [automatic] task_name; {task_item_declaration} begin statement end endtask
(2) 以关键字task开始,然后是可选的关键字automatic,然后是使用任务名称和端口名称,再紧跟分号,接着类比module端口声明,说明端口input/output等属性,并以关键字endtask结尾.
task [automatic] task_name (task_port_list); {block_item_declaration} begin statement end endtask
2.2.2 task使用实例
task常用于仿真中相似行为的建模,也频繁的出现于作者之前的文章中,相较于数字IC设计时,虽说与面向对象的编程相比(SV与UVM)问题和效率颇低,但是与一个端口一个端口的赋值相比,使用task大大增加了代码的复用率,简化了仿真的流程。以下代码摘自作者以往的文章
【数字IC】从零开始的Verilog UART设计
【数字IC】从零开始的Verilog SPI设计
比如说我们可以按照时序要求写出一个复位的task出来,在仿真需要复位的时候直接进行调用即可
task reset; begin sck = 0; rst_n = 1; nss = 1; @(negedge sck); rst_n = 0; @(negedge sck); rst_n = 1; @(negedge sck); @(negedge sck); end endtask
我们也同样可以写出一个读取数据的task,按照时序要求进行数据的发送任务,调用时只需要r_data(8’h1a)即可依次发送十六进制1a数据
task r_data; input [7:0] r_data; begin @(negedge sck); nss = 0; #0.5 mosi = r_data[7]; @(negedge sck); mosi = r_data[6]; @(negedge sck); mosi = r_data[5]; @(negedge sck); mosi = r_data[4]; @(negedge sck); mosi = r_data[3]; @(negedge sck); mosi = r_data[2]; @(negedge sck); mosi = r_data[1]; @(negedge sck); mosi = r_data[0]; end endtask
2.3 disable task
disable语句提供了终止task的能力,它可以在task执行前之前终止它,这种特性对于处理硬件中断和全局重置等异常情况非常有用。
以下的例子说明了这种情况,根据输入的条件,我们可以disable掉continue task块或者break task块
begin : break for (i = 0; i < n; i = i+1) begin : continue @clk if (a == 0) // "continue" loop disable continue; statements statements @clk if (a == b) // "break" from loop disable break; statements statements end end
2.4 function使用细则
function的使用与task相似,以function关键词开始,以endfunction为关键词结尾。我们在计算位宽的系统函数中也提到了fucntion的使用,粘贴如下,有兴趣的读者可以参考以下文章关于Verilog自动计算位宽的系统函数$clog2,这些是你不得不知道的进行学习交流。
function integer clog2( input integer depth ); begin if(depth == 0) clog2 = 1; else if(depth != 0) for(clog2 = 0; depth > 0;clog2 = clog2 + 1) depth = depth >> 1; end endfunction
三、其他《Verilog高级教程》文章
多维数组:灭霸打个响指的功夫,看懂Verilog多维数组
clog2系统函数: 关于Verilog自动计算位宽的系统函数$clog2,这些是你不得不知道的
UDP用户原语:玩转UDP用户原语,这篇文章就够了
$monitor系统函数:放学前的最后几分钟,看懂Verilog中的monitor系统函数
generate语句:一把王者的时间,学会Verilog中的generate语句
parameter常量:玩转parameter与localparameter,这篇文章就够了
inout双向端口:通俗易懂的带你解读inout双向端口
task与function区别:芯片人必会的task与function区别详解