宏除了可以进行简单的文本替换,还可以像函数和任务一样传递指定多个参数分别对文本进行对应的替换.
示例1:
`define Disp(pa,pb,pc) \
initial \
begin \
#1200; \
$display("%d \n",(pa+pb+pc)); \
$display(`" data_ ``pa+ data_ ``pb + data_ ``pc =%d`",(pa+pb+pc)); \
end
module cdc_single_tb;
reg src_clk;
reg src_in;
reg dest_clk;
wire dest_out;
initial
begin
src_clk=0;
src_in=0;
dest_clk=0;
#100 src_in=1;
#50 src_in=0;
#50 src_in=1;
#10000 src_in=0;
#100 src_in=1;
#100 src_in=0;
#100000 $stop;
end
`Disp(1,2,3)
always #5 src_clk=~src_clk;
always #3 dest_clk=~dest_clk;
xpm_cdc_single #(
// Module parameters
4,0, 0,1, 0
) inst_single (
// Module ports
. src_clk(src_clk ) ,
. src_in(src_in),
. dest_clk(dest_clk),
. dest_out(dest_out)
);
endmodule
仿真结果如下:
# 6
#
# data_ pa+ data_ pb + data_ pc = 6
示例表示,当宏定义中宏文本内容比较多时,写在一行阅读起来比较麻烦。此时可以使用“\”进行换行。其次,宏名可以带有任意多个“形参”,各个参数之间通过“,”或者若干个空格进行分割(此处空格不会作为被替换的文本进行替换)。这里还需要注意宏替换时存在优先匹配的情况,示例中虽然宏文本中“d3=”为一个连续的字符串,但是替换时首先对匹配的“d3”进行替换,而不是“d3=”.
既然宏定义时可以指定参数,那么这些参数是不是也可以指定默认值呢?在实际的应用过程中,也可以为宏定义指定的参数指定默认值,但是在使用时需要特别注意,不能随意使用(当然任何语法都不能随意使用)。
示例二:
`define disp_str(s1,s2="Default",s3) \
initial begin \
$display("Start",s1,s2,s3,"End!"); \
end
module cdc_single_tb;
reg src_clk;
reg src_in;
reg dest_clk;
wire dest_out;
initial
begin
src_clk=0;
src_in=0;
dest_clk=0;
#100 src_in=1;
#50 src_in=0;
#50 src_in=1;
#10000 src_in=0;
#100 src_in=1;
#100 src_in=0;
#100000 $stop;
end
`disp_str("Before",,"After")
`disp_str("Before","Haha","After")
`disp_str(,,"After")
`disp_str("Before",,"After")
`disp_str(,,)
//`disp_str(,,,)
//`disp_str(,)
always #5 src_clk=~src_clk;
always #3 dest_clk=~dest_clk;
xpm_cdc_single #(
// Module parameters
4,0, 0,1, 0
) inst_single (
// Module ports
. src_clk(src_clk ) ,
. src_in(src_in),
. dest_clk(dest_clk),
. dest_out(dest_out)
);
endmodule
仿真结果如下:
# StartBeforeDefaultAfterEnd!
# StartBeforeHahaAfterEnd!
# Start DefaultAfterEnd!
# StartBeforeDefaultAfterEnd!
# Start Default End!
从仿真结果可以观测到:
`disp_str("Before",,"After"),宏的第二个参数并没有给出,此时替换时将采用的是宏定义时指定的默认参数,并且各个字符串之间并没有空格出现;
`disp_str("Before","Haha","After"),宏的第二个参数显式给出,此时替换时将采用显式给出的参数进行替换而不是宏定义时指定的默认参数;
`disp_str(,,"After"),宏的第一个参数和第二个参数都没有显式给出,但是通过都好保留了参数的位置,其中第一个参数没有指定默认参数,所以进行替换后仅用一个空格空出了该参数的位置,第二个参数因为有指定的默认值,所以在实参没有指定时,宏定义将使用指定的默认参数进行替换,如果第二个参数没有指定默认值,那么此时第二个参数的位置也会有一个空格空出该参数的位置;
`disp_str(" Before",,"After" ),双引号中第一个参数的首字母前增加一个空格,那么这个空格将会成为第一个参数的一部分替换到宏文本中.最后一个参数双引号外增加了多个空格,因为空格出现在字符串以外,所以不属于替换字符串的一部分,不会替换到宏文本中.
`disp_str(,,),三个参数均没有进行设置,因为第二个参数存在默认值,所以第二个参数将会使用默认值替换宏文本,宏文本被对应参数替换后,第一个和第三个参数所在位置将会有一个空格空出该参数的位置;
`disp_str(,,,),虽然没有指定参数,但是实际留下参数的位置个数与宏定义中指定参数个数不同,多于宏定义指定的三个参数,编译错误;
`disp_str(,)与`disp_str(,,,) 错误类似,这里只是参数个数少于指定的参数个数(这里因为三个参数仅指定了一个默认参数,如果三个参数都有默认值编译不会错误,后文将示例).所以,参数可以不指定,但是空余的位置还是需要与定义时匹配;
宏除了像函数和任务那样存在参数列表外,还可以像函数和任务那样进行相互调用。
`define FI first
`define TW "`FI two"
`define S1(x) "first two x"
`define S2(x) `"first two x`"
`define ADD(a,b) a+b
module tb;
integer sum;
initial
begin
$display("`FI");
$display(`TW);
$display(`S1(third));
$display(`S2(third));
$display(`S2(`S2(forth))); //illegal
sum=`ADD(`ADD(1,2),`ADD(3,4));
$display("the sum id %h ",sum);
end
endmodule
仿真结果如下:
第10行字符串中的宏定义不会被替换掉;
第11行虽然宏文本中有宏`FI调用,但是处于字符串中的宏调用不会被调用;
第12行字符串中的内容不会被宏名后的参数替换;
第13行“`”对原字符串进行了处理,所以宏名后的参数可以替换文本中的内容;
第14行字符串中的宏调用不能进行,其调用实现过程如下:
综上所述,可以得到以下几点关于宏的使用的通用规律:
Ø 如果对应的宏参数指定了默认值,那么该参数在宏调用时可以不指定实参.如果所有的参数都指定了默认参数,那么在宏调用时可以仅保留括号不传递任何参数;
Ø 如果存在部分宏参数没有指定默认值,那么在调用宏时不能省略所有的实参,但是可以使用“,”空留出各个参数的位置;
Ø 如果宏参数中最后一个参数指定了默认值,那么如果期望使用该参数默认值时,在调用宏时可以不保留该参数的位置,即不使用逗号专门空留对应位置;
Ø 宏定义的宏名不能与编译命令名字相同,例如define的宏名不能是define等;
Ø 宏名可以作为一般的信号名,与宏名不冲突,例如定义的宏名为“VAR_V”,那么可以在使用该宏的代码中定义“reg VAR_V”变量,该变量与宏“VAR_V”不冲突,其实主要是使用宏时,在宏名前指定了“`”,将宏名与其他信号变量进行了区分;
Ø 可以重复定义宏,但是仅有最后一次定义的宏有效,也就是说前几次其实都被最后一次定义覆盖了;
Ø 如果宏调用出现在字符串文本中,那么该宏调用将不能进行;
在进行一些文本打印时,有时文本中部分内容需要包含双引号字符,但是文本本身就是以双引号作为限定的,即字符串会将第一个"到下一个"之间的内容作为字符串文本,如果在文本字符串中本身包含",那么会认为当前字符文本结束,问题就来了,这种情况如何实现文本中的双引号的输出呢?
【示例】
【仿真结果】
示例中可以看到,通过在需要插入引号的地方使用转义符“\”,既可以实现字符串中引号的嵌入.那么如果试图将宏文本中插入的双引号中的内容进行宏参数替换,可以按照如下示例方法进行.
【示例】
【仿真结果】
其中首尾的`"确保了双引号中的内容也可以被宏参数进行文本替换,`\`"确保了其中的双引号可以被有效输出,同时该双引号中的内容也可以被宏参数替换.