前言

本文主要记录一下systemverilog中`define宏定义的使用,主要介绍带参数的宏传递方法。


0 为什么要用宏定义

用宏定义主要是可以简化代码。
例如有如下for循环代码,经过宏定义后,就可以简化。
在这里插入图片描述
在这里插入图片描述

1 标准中关于`define宏的介绍

在《SystemVerilog_3.1a》标准中,关于`define宏的描述如下所示:
在这里插入图片描述
可以看到,在systemverilog中,define宏包含三个关键的特殊符号。

1.1 特殊符号(顿号引号)

`"    //这个符号表示一个特殊的引号,引号内部的变量也会被替换

在这里插入图片描述

1.2 特殊符号(顿号顿号)

``    //分隔词,标记而不引入空白,允许从参数构造标识符,在宏定义的变量前后都加上这个符号,就可以实现变量的整体替换。

在这里插入图片描述

1.3 特殊符号(顿号反斜杠顿号引号)

`\`"  //表示展开时包含转义序列,转义出来就是\"符号

在这里插入图片描述

1.4 宏定义传参

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2 带参数的宏`define

2.1 带参数宏的使用方法及其存在的问题

下面这个文章介绍了,如何使用带参数的宏define。
[SV]SystemVerilog中`define传参 — 带参数的宏函数(Macro function)

文章中提到,宏定义在传参的时候有一个缺陷,不能接收传进来的变量,不然会将变量的名字传进define中,而不是变量所代表的值传进去,造成这个现象的本质原因是,宏的参数传递的本质是替换,具体描述如下。

引用传进来的参数时要在参数前和后加``,不接收传进来的变量
比如
generate
for(genvar jj=1;jj<`CC_NUM;jj++)begin
`A_SRAM_RW(jj,0)
end
endgenerate
上面这种写法是不行的,相当于只是把jj传进了define中,并不会把jj所代表的值传进去。

2.2 解决宏定义变量传参的方法

考虑到宏定义传参的本质是替换,那么解决宏定义变量传参的方法,就是将宏作为参数。

下面将展示,将宏作为参数传递所带来的妙用。

有如下场景:要检查寄存器的值和对应的端口连线是否正确。

常规的思路,先检查默认值,再检查翻转值。
检查默认值:针对每个寄存器,先读出寄存器的默认值,再取到端口上的值,进行比对。
检查翻转值:将读到的默认值取反,写到寄存器中,再读出来,重新取端口上的值,进行比对。

按照上面的思路,如果挨个寄存器进行操作,代码量将非常大,而且基本上都是重复的代码,后期维护起来也不方便。

那么能不能将上述重复的代码放到一个task里边呢,每次只需要将必要的参数传进去就好了。

在传参的过程中,存在一个问题:端口名字如何作为一个参数传进去呢?
由于取端口上的值的时候,路径都是一样的,只是端口名字不一样,所以自然的想到用带参数的宏定义,那么在task中调用这个宏的时候,端口名字如何作为一个参数怎么传进去呢?

那么解决方案就是用宏作为参数传进去,那么在外层调用的时候,就只需要改变这个宏就好了。代码如下所示:

`define PMU_TOP(x) CHIPTOP.A_PMU.``x``
`define PORT pow_15    //这里定义端口名字的宏
task reg_connect_check(input bit[31:0] addr, input int index);
    ...
    read_data(addr, data)
    if(data[index] == `PMU_TOP(`PORT))//这里将端口名字的宏作为参数传进去
    ...
endtask : reg_connect_check
// 外层的调用
addr = 32'h10;
index = 0;
`define PORT pow_15  //这里通过改变宏,从而将端口名字传进去task
reg_connect_check(addr, index); 

addr = 32'h14;
index = 1;
`define PORT pow_11  //这里通过改变宏,从而将端口名字传进去task
reg_connect_check(addr, index); 

可以看到,通过上述的改变,原本读寄存器、取端口值、数据比对等,这些重复的代码,在task里边得到了很好的复用,外层则只需要改变参数,调用task就可以了,简洁明了,维护起来也方便。

特别注意:
reg_connect_check这个task和外层调用最好放到同一个类中。如果reg_connect_check这个task放到基类中,然后外层调用的时候在子类中进行调用,就起不到传参的效果,原因是子类中改变宏定义的值无法影响到基类,reg_connect_check这个task始终会用到基类的宏定义。


总结

本文首先记录了在systemverilog中,标准文档对于宏定义的描述,以及几个特殊符号的使用;其次,着重介绍了带参数的宏的使用,解决了如何将变量作为一个参数传递给宏,并通过一个例子,演示了这样做带来的好处。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐