手把手教你如何使用SV宏 |
您所在的位置:网站首页 › 函数名宏定义怎么写的 › 手把手教你如何使用SV宏 |
前 言 本文主要给大家介绍SV宏,谈到SV宏大家应该不会感到陌生,因为大家在做前端设计或验证的时候会用到`define定义宏,进行条件编译,或者使用宏来定义参数等,做前端验证的工程师会使用`define来定义一些信号路径等。这些SV宏的用法都是比较常规的用法,很少有验证工程师使用SV宏来处理一些其他的事情。猜其原因主要是大家对SV宏的语法及应用场景不了解,很少有相关资料进行说明,即使作为验证验证工程师人手必备的绿皮书也只是在数据结构章节简略提到了使用`define定义参数。验证工程师在搭建UVM验证平台时经常会使用SV宏,比如`uvm_info、`uvm_error、`uvm_fatal, 亦或者是`uvm_do等, 但是大多数工程师只是知道如何调用这些宏,并不知道这些宏的内部结构。这些宏都是UVM源码中使用时SV宏进行定义的。SV宏作为Systemverilog中最强大的功能之一,如果验证工程师可以透彻的了解,正确地应用于验证平台中,可以节省大量的时间,提高代码的可读性和高效性。 那么为什么建议大家要学会和正确使用SV宏呢? 对于任何设计验证项目,遵循最佳的编码实践可以让你的队友的工作更加轻松。另一方面,糟糕的编码风格会在代码重复使用或当代码移交给另一个所有者以在未来进行加强时导致很多的问题。有时,这会导致代码中的大量返工和补丁,并使代码在项目的后期或需要重复使用的未来项目中的维护变得非常困难。 通常,在进行设计验证项目时,需要将较大的代码段分成较小的块,以使代码更易于阅读和调试(以及出于可重用性的目的)。此类较小的代码段可在设计验证环境中的不同位置用于多个组件、模块等。 验证工程师提问:我们为什么不能使用函数或者任务达到同样的目的呢? 任务和函数可用于将大型、复杂的代码分解为更小、更简单的代码段,易于阅读和理解。函数和任务主要用于在验证环境中的多个位置执行相同功能,但是它们的用法仅限于定义它们的模块或类访问边界。对于在不同且完全隔离的模块中常见且经常使用的某些代码,由于其访问边界限制,该函数/任务无法直接重用。 举一个简单的例子,如果任务/函数具有用于两个不同monitor和两个不同接口的相同代码,则验证工程师通常会在两个monitor中添加重复的代码。在许多其他情况下,我们会看到代码重复。"SV宏"是解决此类重复的众多解决方案之一。 如果在SV环境中正确使用宏,它将非常有效,并且可以帮助节省大量时间。为了可以让大家透彻了解SV宏,本文重点讨论了SV宏及语法,并提供一些示例来说明在何处使用它可以节省验证时间。 什么是宏(macro)? “宏”是指一行或几行文本代码的替换,即使用`define编译器指令创建的代码段。一旦宏定义了,可以在需要时在编译单元范围内的任何位置使用它。宏本质上有三部分组成,即名称,一些文本和可选参数。可以通过(`)字符后跟宏名称来调用它。一个宏可以使用参数定义。参数对于自定义要广泛使用的宏很有用,例如一个函数/任务。此类宏参数可以使用默认值定义,这样如果设计验证工程师不传递任何特定值,则宏将替换为默认值。 宏定义 单行宏: 如上面的示例代码所示,宏“val”和“addition”是单行宏。 多行宏: 上面的代码是多行宏的示例。如上所示,对于多行宏,新行前面带有反斜杠“ \ ”。如果一行中没有反斜杠,则将其视为宏的最后一行。在使用宏和替换实际宏代码的实际代码中,反斜杠不会出现。 注意:在行尾的反斜杠“ \ ”之后不能有空格或字符,否则编译器会报错。 我们为什么要使用宏呢? 在编写测试平台和测试代码时,我发现自己在多个位置打印出字节数组。在我的代码的各个地方,我会有这样的片段: 相反,我可以像下面这样定义一个宏,将5行重复的代码替换为1行,节省一些输入并使代码更具可读性。此外,如果我需要改变打印样式,那么我只需要在一个地方改变它! 我特别喜欢将宏用于特殊的打印功能,而对于这些宏来说,这`uvm_info还不够。如果您的所有团队成员都使用此宏,那么您将获得一致的打印消息,这将使每个人都更容易阅读仿真log。 建议使用宏的一种可能方法: 你和你的团队成员建立一个宏库。 为该库中的宏使用命名约定,例如 _utils(print_byte_utils,等)。 将其放在一个名为macro_utils.sv的文件中,并将其include到base package中。 在适当的情况下使用这些宏,而不是重复代码,成为您的验证方法论的一部分。 宏的语法 宏名称 宏名称的唯一规则是您可以使用除编译器指令以外的任何名称,即,`define, `ifdef, `endif, `else, `elseif, `include等关键字不能使用。如果您确实错误地使用了编译器指令,则会收到诸如此类的错误。同样,宏的命名空间是Global。因此,是否在类内或类外定义内容无关紧要,编译的顺序至关重要。一旦编译器编译了宏,便可以在任何地方使用它。如果您重新定义宏,则会收到这样的警告。在log中注意这些。 宏定义符号名称 一般定义宏都是基于下面三个特殊字符(反引号)和参数,其替换的实际代码具有不同的含义。所有可能的宏可以使用以下三个符号形成: ` ” (Backtick and Quotes)反引号和引号 如果宏文本被括在双引号(")中,它本质上就变成了一个字符串。双引号内的参数不会被替换,如果宏文本内嵌了其他宏,则不会展开它们。符号`"重写了"的意思,并指示宏展开应该: 包含双引号; 双引号中的参数应该被替换; 任何嵌入的宏都应该被扩展; 让我们通过下面的例子加深理解: `append_front_bad-接受一个参数MOD,期望宏将导致两个字符串MOD和".master"的连接。但是由于使用双引号,输出结果是字符串“MOD.master”,参数没有被替换。 `append_front_good-这是`append_front_bad的正确版本。通过在双引号前使用反勾号,我们告诉编译器在宏被展开时必须替换参数MOD。 `` (Double Backtick)双反引号 ``本质上是分隔符标记,它帮助编译器清楚地区分宏文本中的参数和字符串的其余部分。先通过一个简单的例子解释这个符号的作用,然后再通过几个例子进一步说明。 宏定义: 宏用法: 宏替换的实际代码: 我们可以看到ARG1 = 3用于形成变量的名称。(即m_mst_3和mst_3_lcl)。 接下来通过几个例子综合理解` ”和``的作用。 `append_front_2a and 2b-空格字符(' ')和句点字符('.')是自然标记定界符,即,编译器可以清楚地标识宏文本中的参数。 `append_front_2c_bad-如果参数附加了非空格或非句点字符,例如`"MOD_master`"中的下划线'_',则编译器将无法确定参数字符串的MOD结尾。 在`append_front_2c_good中``创建一个在MOD和之间的定界符_master,这就是为什么MOD要正确标识和替换的原因。 `append_middle还有`append_end另外两个示例展示了这一点。 `append_front_3-可以``在自然标记定界符(即空格或句点)之前或之后放置一个,但这没有效果。 `\`" (Slashes, Ticks and Quotes)反斜杠,反引号和双引号 如果您需要在扩展的宏文本中使用双引号,请使用此选项。 其他: 您可以在宏中调用宏。 允许在宏中使用注释,这没有什么不同。 ifdef也允许在宏定义中使用,对此也没有什么不同。 传递Args 有关宏参数的几点注意事项: 参数可以具有默认值。 您可以通过将该位置保留为空来跳过参数,即使该位置没有默认值也是如此。请参阅`test2(,,)示例2中显示的内容。 查看`debug1并`debug2。如果您的参数是一个字符串,则是否需要将参数括在双引号中的决定取决于该参数在宏文本中的替换位置。在中`debug1,MODNAME在宏文本中的引号内,因此program-block在调用宏时我没有将字符串括在引号中。而`debug2传递的参数是"program-block"因为MODNAME在宏文本中出现在引号之外。 宏的风格指南 在团队中的宏遵循一致的命名风格非常有用。由于UVM现在被广泛用作验证方法,因此我们可以从他们的剧本中借用一个页面,并使用与UVM宏相同的编码样式。如果查看UVM库源代码,则将看到以下内容: 如果使用宏定义a function或task使用大写宏名称和小写参数名称。 如果使用宏定义class,代码片段等等-使用小写宏名和大写参数名称。 宏名称中的单词用下划线分隔。 在下一节中,我从UVM库中选择了一些宏,这些宏显示了上述命名约定。 参考实例 有时候你所需要的只是一堆例子来刷新你对如何使用宏的记忆。下面是我从UVM库源代码中挑选的一些代码。您应该能够从你上面所学的理解所有这些。 语法总结 整个团队遵循编码风格一致性。 定义函数和任务的宏使用大写宏名称和小写宏参数。 使用带下划线的单词。 使用小写宏名称和大写其他所有参数定义例如类,代码段等宏。 在使用宏时要谨慎,过度使用可能会使代码不可读。 知道`", ``和 `\ 符号的作用。 宏定义是全局的。在类中定义宏并不意味着它仅对该类可见。 在编译log中当心宏重新定义警告。 编写宏时,请使用本文中的示例作为参考。 宏的应用场景 下面给出一些验证中经常会使用到宏的一些场景。 宏对于覆盖点(Coverpoint)的用法 如果设计验证工程师想要覆盖具有相同宽度的多个变量的walk0 / walk1库(bins),则他/她可以为需要的walk1 / walk0覆盖库(bins)中的所有此类信号创建并使用宏。 例如: 宏定义: 宏用法: 宏对于覆盖组(Covergroup)的用法 在验证项目中,很多时候需要在不同的地方编写相同的覆盖范围,例如,在主(master)组件和从属(slave)组件中编写相同的代码。我们可以为覆盖组(Covergroup)定义可以在所有此类组件中使用的通用宏。 例如: 宏定义: 宏用法: "STRING"是"bus_cg_macro"宏的参数。无论在何处使用宏,都应考虑"STRING"参数来替换覆盖组(Covergroup)、覆盖点(Coverpoints)及其库(bins)。 宏在sv断言(Assertion)中的用法 就像覆盖一样,很多时候在设计验证项目中,我们都有一些共同的断言,可以在多个地方和不同的组件中使用。 例如,需要检查在主(master)和从(slave)中,如果不存在复位,则信号值在每个时钟周期都保持变化。我们可以定义一个宏并将其同时用于主(master)和从(slave)。 例如: 宏定义: 宏用法: 宏在测试案例中的用法 在自检寄存器的写/读测试中,每次读取后,都会根据预期的读取数据检查读取值。考虑到设计的复杂性,我们可能有多个模块,并且对于每个模块,我们可能都有相应的寄存器测试。对于每个此类测试,我们都可以使用一个通用的宏进行自我检查。 自检宏: 宏用法: 宏在程序块中的用法 用于覆盖多个地方相同的程序块代码的宏。 程序块的宏: 宏用法: 这里介绍了一些示例宏,但是验证工程师可以根据其项目需求和可重用性来创建和使用相似的宏。 结论 通过使用本文中介绍的正确语法使用SV宏,设计验证工程师可以将较大的复杂代码分解为较小的块,并可以在许多地方重复使用。一个宏可以在被定义后编译单元中的任何地方使用。工程师可以通过输入参数使用宏来形成标识符名称。 SV宏是system verilog最强大的功能之一,如果在透彻了解的情况下正确使用并明智地应用于设计验证项目中,它可以节省大量时间,并使代码更具可读性和高效性。 END 参考文章:System Verilog Macro: A Powerful Feature for Design Verification Projects 参考链接:https://www.systemverilog.io/macros 关注我 发现更多精彩 回复 "分享群" 即可加入"IC验证分享圈"微信群。 往期精彩内容: 手把手教你学条件编译ifdef、ifndef uvm_root是什么? Easier UVM编码指南之 Part1:词法准则和命名约定 Easier UVM编码指南之 Part2:通用代码结构 Easier UVM编码指南之 Part3:时钟、时序、同步和接口 Easier UVM编码指南之 Part4:Transactions Easier UVM编码指南之 Part5:Sequences |
今日新闻 |
点击排行 |
|
推荐新闻 |
图片新闻 |
|
专题文章 |
CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭 |