RTL建模中的函数和任务讨论

描述

RTL建模中的函数和任务(Function 和 task)

SystemVerilog的函数和任务可以将复杂的功能划分为更小的、可重用的代码块。函数对于RTL建模非常有用,本文将对此进行研究。

任务虽然有自己的优势,但在RTL模型中几乎没有价值。使用void函数(将在本节后面讨论)是比使用任务更好的RTL编码方式。因此,本文仅简要讨论任务。

函数和任务可以在使用它们的模块或接口中定义。定义可以出现在调用函数或任务的语句之前或之后完成,函数和任务也可以在包中定义,然后导入到模块或接口中,包导入语句必须出现在调用函数或任务之前。

函数

调用时,函数执行其编程语句并返回值。对函数的调用可以在任何可以使用表达式(如网络或变量)的地方使用。这里展示了一个函数定义示例和对该函数的调用。本节后面将展示更实用的可综合示例。

RTL

SystemVerilog语法要求函数在零仿真时间内执行。可综合函数不能包含时钟周期或传播延迟。

静态和自动(Static and automatic)函数。函数(和任务)可以声明为静态或自动。如果两者都未指定,则模块、接口或包中定义的函数的默认值为静态。

静态函数保留从一个调用到下一个调用的任何内部变量或存储的状态。函数名和函数输入是隐式内部变量,在函数退出时将保留它们的值。

这种静态存储的效果是-对函数的新调用可以记住以前调用的值。这种“记忆性”在验证代码中很有用,但这种行为并不能准确地仿真综合编译器从函数实现的门级行为,这可能导致RTL模型仿真与ASIC或FPGA的实际功能不匹配。

自动函数在每次调用该函数时都会分配新的存储。递归函数调用(如上文所示的 阶乘f(factorial_f) 函数示例)需要自动存储(两个不同过程同时调用同一任务的可重入任务调用也需要自动存储)

最佳实践指南6-8
将RTL模型中使用的函数声明为自动函数。

静态存储的默认设置不适用于硬件行为的RTL建模。此外,综合编译器要求包或接口中声明的函数必须声明为自动函数

函数默认为静态存储是有历史原因的。在Verilog仿真的最初几年,当时计算机内存有限且处理器速度慢得多,静态存储有助于提高仿真运行时性能,与使用现代仿真器和计算服务器的自动存储相比,静态存储没有性能优势。SystemVerilog标准保留了静态函数的原始语言默认值,以便与遗留的验证代码保持向后兼容,这些代码可能是为了利用函数的静态存储而编写的。

函数返回

函数的返回数据类型定义在函数名之前。在上面的 阶乘f(factorial_f) 示例中,该函数返回一个N位宽的向量,其类型为logic(4-state)。如果未指定返回类型,则默认情况下,函数返回为1位logic(4-state)类型。

SystemVerilog提供了两种指定函数返回值的方法。一种方法是使用return关键字,如上面的factorial_f示例所示。return关键字后面是函数要返回的值。或者,可以将此返回值括在括号中。

第二种指定返回值的方法是为函数名赋值。函数名是与返回值数据类型相同的隐式变量类型,当函数计算返回值时,此隐式变量可用于临时存储。分配给函数名的最后一个值将成为函数返回值。本节开头显示的 阶乘f(factorial_f) 函数可以重新编码,以使用函数名作为隐式内部变量来计算返回值。

RTL

Void函数

函数返回类型可以声明为void。Void函数不返回值,不能像其他函数一样用作表达式。void函数被称为语句,而不是表达式。

RTL

最佳实践指南6-9
使用void函数代替任务进行RTL建模。仅在验证代码中使用任务。

void函数和任务之间的唯一区别是函数必须在零时间内执行。大多数综合编译器不支持任务中任何形式的时钟延迟。使用void函数代替任务使得这种综合限制成为语法要求,并且可以防止编写可以仿真但不可以综合的RTL模型。

函数参数。函数定义中的参数称为形式参数(formal arguments)。函数调用中的参数称为实际参数(actual arguments)。形式参数可以是input、output或inout,并使用与模块端口相同的语法声明,默认方向(如果未定义)为input,上面fill_packet示例中的形式参数是32-bit 4-state输入,用户定义的packet_t类型的输出形式参数。

形式参数也可以声明为ref(reference的缩写)代替端口方向(direction),ref参数是指向函数调用的实际参数的指针形式,函数必须声明为自动函数(automaticfunction)才能使用ref参数。

最佳实践指南6-10
在RTL模型中使用的函数中只使用输入和输出(input 和 output)形式参数,不要使用inout或ref形式参数。

所有RTL综合编译器都支持输入和输出(input 和 output)函数参数。某些RTL综合编译器不支持inout和ref参数。

调用函数

调用函数时,将实际参数传递给形式参数有两种编码样式:按顺序传递和按名称传递。按顺序传递时,第一个实际参数传递给第一个形式参数,第二个实际参数传递给第二个形式参数,依此类推。按名称传递使用与按名称连接模块相同的语法。形式参数的名称前面有逗号(.),后跟括号中的实际参数。

给定函数定义:

RTL

传递实际参数的两种方式是:

RTL

函数输入默认值

可以为形式参数指定默认值,如下所示:

RTL

具有默认值的参数不需要传递实际参数,如果没有传递实际参数,则使用默认值。例如:

RTL

如果传入实际值,则使用实际值,如下所示:

RTL

笔记
在编写本文时,一些综合编译器不支持默认输入值,工程师应该确保项目中使用的设计流程中的所有工具在RTL模型中使用之前都支持默认参数值。

使用return提前退出函数。

return语句也可以用于在函数中的所有语句都执行之前退出函数,下面的示例可以在3个不同的点退出函数。如果 max 输入为0,则函数在执行for循环之前退出;如果for循环迭代器达到max值,则函数在到达循环末尾之前退出;如果for循环完成,则函数在到达endfunction时退出。

RTL

参数化函数(Parameterized function)。

参数化函数是SystemVerilog中功能强大且广泛使用的功能。可以为模块的每个实例重新定义参数,使模块易于配置和重用。模块级参数可以在函数定义中使用,如前面的sum_to_endpoint_f函数示例所示。使用模块级参数意味着对函数的所有调用将具有相同的向量大小。如果调用函数的每个位置使用不同的向量大小,则无法对函数进行配置。

函数不能像模块那样进行参数化,SystemVerilog不允许函数定义具有内部参数,这些参数可以在调用函数的每个地方重新定义——这限制了编写可重用、可配置函数的能力。但是,对于这个限制,有一个解决方法,即在参数化虚拟类中声明静态函数,可以使用范围解析操作符(无需创建对象)直接调用类定义中的静态函数。

在调用函数的每个地方,都可以重新定义类(class)参数,如下例所示:

RTL

参数化函数可以只创建和维护函数的一个版本,而不必定义具有不同数据类型、向量宽度或其他特征的多个版本。

请注意,在类定义中,static关键字位于function关键字之前,而在模块中,static或automatic关键字位于function关键字之后。有一个重要的语义差异,在类中,静态函数声明类中函数的生存期,并限制函数在类中可以访问的内容,在模块中,静态函数或自动函数指函数中参数和变量的生存期。

笔记
在写这本文的时候,并不是所有的综合编译器都支持参数化虚拟类中的静态函数。在RTL模型中使用静态函数之前,工程师应该确保项目中使用的所有工具都支持参数化虚拟类中的静态函数。

任务-Task

任务是封装一条或多条编程语句的子例程,因此可以从不同的位置调用封装的语句,或在其他项目中重用。与函数不同,任务没有返回值。一个例子是:

RTL

任务被称为编程语句,并使用输出形式参数从任务中传递值。

RTL

语法上;任务与函数非常相似,只是任务没有返回类型。任务和函数之间的一个重要区别是,任务可能包含时钟周期和传播延迟。然而,大多数综合编译器要求任务中的编程语句在零仿真时间内运行。这种综合限制使任务几乎与void函数相同,因为void函数在语法上强制零时间执行,所以最佳编码实践是在RTL模型中需要子例程时使用void函数而不是任务。上面的ReverseBits任务可以重写为void函数,如下所示:

RTL





审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分