zt system verilog中的宏使用学习笔记 (ok) | 您所在的位置:网站首页 › verilog头文件定义 › zt system verilog中的宏使用学习笔记 (ok) |
前言
宏定义是我们在编码时常用来减少代码量和跨文件定义常量的手段。我的师傅跟我讲如果一段代码你需要重复两次,那么就要考虑封装一下。当然了我在实践中,一般是重复到第三次的时候才会封装代码。 封装代码的方式一般就两种吧,一是封装为task或者function,二是封装为宏。如果条件允许的话,我认为还是尽量选择前者。宏定义虽然能够有效的降低编码量减少重复编码,但是会严重的降低代码可读性和定位效率,而且一旦宏定义重复那么最后的定义会覆盖前面的所有定义且只会报warning极具隐蔽性。 不过既然宏定义是一种极为常见的语法我还是要仔细学习下。 宏的使用方式宏一般使用时可以归结为两种,一种是编译宏一种是替换宏。编译宏一般是作为开关或者隔离选项,比如我们一致在文件前面开头和结尾加的如下所示代码,目的就是避免重复编译。 编译宏还可以加在编译选项中+define XXXX的形式,不过编译宏不是今天学习的重点,我还是先研究下替换宏吧。 替换宏的使用步骤非常简单,先进行定义,在编译时进行替换,示例如下图代码。 //define test field initial begin `define DEF 'h10 int a = `DEF; $display("a value is 'h%0h", a); $display("***************define test field***************"); end这里一定要注意,宏定义的替换是在编译时完成的而不是执行时,也就是说并不会因为某一分支未执行而不进行宏定义。以下代码结果可以看到,后面的宏定义会覆盖之前,且不会和分支是否执行有关。 //define test field initial begin int b; if(1 == 1)begin `define DEF 'h10 ; end if(0 == 1)begin `define DEF 'h20 ; end b = `DEF; $display("b value is 'h%0h", b); $display("***************define test field***************"); end这是典型的编译宏定义方式,一般会跟`ifdef或者`ifndef配合使用;定义会在编译时生效,被宏隔开的代码编译器完全不可见,也就是说里面写什么都无所谓; 注意编译是有先后顺序的,那么一旦上面的代码换下顺序,就完全编译不过了; 编译宏不是今天重点,那就不多说了吧; `define XXX xxx这是典型的替换宏定义方式,之后所有出现`XXX的地方会被替换为xxx,xxx可以是数字、字母、下划线和$(记忆中是没有其他的了,如果有疏漏请补充); 如下图代码,注意宏定义的地方是不需要;号的,下图里之所以有是因为;本身是替换的一部分。 initial begin `define PRINT $display("this is print define"); `PRINT $display("***************define test field***************"); end由于宏本身的替换属性,宏文本也可以是一个宏,比如下面的代码: `define AAA 15 `define BBB `AAA int a = `BBB; $display("value is %d", a); $display("***************define test field***************");或者你写成下面的样子也不是不可以,会打印同样的效果,原因在于宏是一个编译时替换的过程,编译到int a赋值那句时BBB->AAA->15的替换关系是确定的; `define BBB `AAA `define AAA 15 int a = `BBB; $display("value is %d", a); $display("***************define test field***************");不过再调一下顺序,写成下面这样就不行了,会告诉你宏AAA还没有被定义; `define BBB `AAA int a = `BBB; `define AAA 15 $display("value is %d", a); $display("***************define test field***************");当宏定义中遇到非【数字、字母、下划线和$】的字符时,会视为宏名称与文本的分割点,比如下面的代码(不管缩进的问题了。。。),虽然没有空格但是仍然把`DEF替换为了'h20,注意这里的'虽然作为分隔符但是本身不会消失,仍然是替换的一部分; initial begin int b; `define DEF'h20 b = `DEF; $display("b value is 'h%0h", b); $display("***************define test field***************"); end因此`define XXX-xxx那么之后`XXX会被替换为-xxx,注意下就好; `define XXX(a) xxx_``a这种定义方式主要用于字符的拼接,定义后所有`XXX(a)都会被替换为xxx_a,比如下面这个例子; `define XXX(a) xxx_``a int xxx_1 = 10; int xxx_2 = 15; int yyy, zzz; yyy = `XXX(1); zzz = `XXX(2); $display("yyy = %0d, zzz = %0d", yyy, zzz); $display("***************define test field***************");通过``来连接字符并不一定要接在最后,放前面也行,放中间也行; `define XXX(a) xxx_``a``_sss int xxx_1_sss = 10; int xxx_2_sss = 15; int yyy, zzz; yyy = `XXX(1); zzz = `XXX(2); $display("yyy = %0d, zzz = %0d", yyy, zzz); 当一行装不下我们膨胀的宏定义时,就涉及到了跨行定义,下面的代码是跨行定义的示例: `define PRINT $display("***************line 1***************"); \ $display("***************line 2***************"); \ \ $display("***************line 3***************"); `PRINT $display("***************define test field***************");跨行定义大体上有这么几个原则: a.除了最后一行,其他行都必须要在结尾处加上\,换句话说,编译时第一个不带\的行会被识别为宏定义的最后一行; b.\的左边可以有空格,右侧不可以有,右侧有空格的话会报编译错还是你看不懂啥意思的那种编译错; c.完全空的一行也要有\; 总之定义的时候千万小心就对了; 带参数的宏定义带参数的宏定义和function/task高度接近,不过还是要牢记宏是在编译时候进行替换而不是“调用”,看下下面的代码; `define like_func(aa, bb) \ aa = aa + bb; int a = 10; int b = 5; `like_func(a, b) $display("a = %0d", a); $display("***************define test field***************");还是要千万注意,这里是直接替换,也就是说上面的代码在编译完成后实际就是下面这样,不存在"调用"的这层关系; int a = 10; int b = 5; a = a + b; //`like_func(a, b) $display("a = %0d", a); $display("***************define test field***************");对了,传参时可以传实参也可以传表达式,实参也可以是宏的形式; 宏的打印规则宏里面涉及到打印的时候总是很烦人,因为好多时候搞不清是打印的是传参呢还是实参(你看function就没有这个苦恼),再加之VCS的解释和SV的解释有所不同,所以这一块我要仔细学习下。 我们来循序渐进的学习下,先看下第一组代码的打印效果(modelsim下的仿真效果): `define AAA BBB `define CCC "`AAA" `define DDD(x) this is x `define EEE(x) "this is x" $display("`AAA"); $display(`CCC); $display("`DDD(yyy)"); $display(`EEE(yyy)); $display("***************define test field***************");我们可以得到两个结论,如果在“”里面使用宏,那么宏不会被替换;如果宏定义里有" ",那么" "内部的字符也会被完整保留,不会被替换;这与SV标准中的解释是一致的:宏定义时,宏文本中“xxx”中如果包含宏参数,不会进行替换;宏使用时,“xxx”中使用的宏也不会进行替换; 如果我们希望被替换的话,要怎么做呢?先看宏定义里的" ",只需要将" "修改为`" `"就可以了,这样引号内部的宏也会被替换,下面就是修改后的结果: `define AAA BBB `define CCC `"`AAA`" `define DDD(x) this is x `define EEE(x) `"this is x`" $display("`AAA"); $display(`CCC); $display("`DDD(yyy)"); $display(`EEE(yyy)); $display("***************define test field***************");那么如果在" "使用宏的时候也想把宏替换掉是不是可以呢,比如把代码改成下面的样子看下: `define AAA BBB `define CCC `"`AAA`" `define DDD(x) this is x `define EEE(x) `"this is x`" $display(`"`AAA`"); $display(`CCC); $display(`"`DDD(yyy)`"); $display(`EEE(yyy)); $display("***************define test field***************");发现第一个打印的还是`AAA,并没有被替换成BBB,这一点在SV中貌似没有进行支持; 不过需要注意,上述行为时SV标准,modelsim应该是严格遵守了这个标准,但是VCS中对其行为作了修改, 修改在两点: 1.宏定义时,VCS不区分"和`",所以不用担心,宏定义时候引号里面也都给你替换掉(如果我不想替换怎么办??); 2.使用宏时候, `" `AAA `"会被打印为BBB,也就是刚刚说的SV不支持的那个行为; 其他使用规则 宏不可以拆分数字/字符串/变量/操作符/注释宏的替换过程也不是一个完全无脑的替换,什么意思呢,就是说你不可以定义了 `define A "this is `define B a bad test" 然后想用`A`B来表示"this is a bad test",这是绝对不行的,字符串"xxxx"是一个整体,不能被宏拆分; 宏一般情况下只能整体性的替换掉一个东西,不能拆分和拼凑; 当然了如果你就是想简单的做下字符串内部的构造和拼接还是很简单的,之前的语法就够了; `define PA tc_test `define PB _tb_test `define PC `"`PA```PB`" $display("PC = %s", `PC); $display("***************define test field***************");宏的传参可以是自己这点很好理解,比如下面这种代码形式,实际上展开为3+1+1的代码了: `define ADD(x) x+1 $display("value = %0d", `ADD(`ADD(3))); $display("***************define test field***************");不要递归也很好理解,宏是一种描述性的语句不是执行语句,他本身又不会真正的“执行”起来,你递归就是为难编译工具,当然编译工具也会毫不犹豫的打死你; 换句话说,使用时传参可以时自己,但是定义时不能包括自己。 `define ADD(x) `ADD(x)+1 $display("value = %0d", `ADD(`ADD(3))); $display("***************define test field***************");
|
CopyRight 2018-2019 实验室设备网 版权所有 |