0、前言
一般情况下,C语言中的每一行代码都要参加编译。但有时候出于对程序代码优化的考虑,希望只对其中一部分内容进行编译,此时就需要在程序中加上条件,让编译器只对满足条件的代码进行编译,将不满足条件的代码舍弃,这就是条件编译(conditional compile)。条件编译允许只编译源文件中满足条件的程序段,使生成的目标程序较短,从而减少了内存的开销,并提高程序的效率,可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。这对于程序的移植和调试是很有用的。
Verilog的编译和C语言的编译二者自然不可同日而语,具体到FPGA的开发,其条件编译可以通俗的理解为,根据条件选择性地将指定部分综合为电路,而未被指定部分则不综合成电路,这可以有效地减少电路面积和提高代码的复用性和灵活性。
1、一个小例子
现在老板让我开发一个模块,功能是要能实现两个4bit输入的与、或、异或运算,但这三种运算不需要同时满足,也就是说在该模块的某次使用中或被调用中可能是使用或运算,也可能是使用与运算,具体是什么说不清楚,得看老板心情。
这还不简单?针对与、或、异或运算写三条语句,再给它加个选择器就完事了,就像这样:
module op_test(
input [3:0] in1,
input [3:0] in2,
input [1:0] sel,
output reg [3:0] out
);
wire [3:0] out_and;
wire [3:0] out_or;
wire [3:0] out_xor;
assign out_and = in1 & in2;
assign out_or = in1 | in2;
assign out_xor = in1 ^ in2;
always@(*)begin
case(sel)
2'd0: out = out_and;
2'd1: out = out_or;
2'd2: out = out_xor;
default:out = out_and;
endcase
end
endmodule
用vivado分析出来的电路是这样的:
嗯,完美满足老板要求。嘻嘻。
不料老板看了代码后,对着我劈头盖脸就是一顿臭骂:说了一次只会做一种运算,你给我弄3个运算模块干嘛?做或运算的时候,异或运算和与运算模块就站着看戏?节约资源懂不懂伐?性价比懂不懂伐?钞票懂不懂伐?
虽然我内心立马条件反射般地对老板竖了个中指,但是仔细想想老板说的话也确实有道理,所以只好再找找其他办法。
找了半天还真给我找着了(不愧是我),这就是Verilog语法中的条件编译指令`ifdef, `ifndef,`else, `elsif, `endif。
2、条件编译指令
条件编译指令可以根据指定条件来生成对应的电路,这可以减少电路面积并提高代码的复用性。
2.1、 `ifdef 的使用
`ifdef 需要搭配 `endif 使用,其使用方法为:
`ifdef <define_name>
<statements>;
`endif
比如上面的例子(采用与运算时):
`define OP_AND //指定条件为与运算,标识符建议使用大写
//`define OP_OR //指定条件为或运算,标识符建议使用大写
//`define OP_XOR //指定条件为异或运算,标识符建议使用大写
module op_test(
input [3:0] in1,
input [3:0] in2,
output [3:0] out
);
`ifdef OP_AND
assign out = in1 & in2;
`endif
`ifdef OP_OR
assign out = in1 | in2;
`endif
`ifdef OP_XOR
assign out = in1 ^ in2;
`endif
endmodule
用vivado分析出来的电路是这样的:
可以看到,在希望进行与运算时,综合出来的电路已经只有与运算模块了,异或运算模块、或预算模块和选择器模块都没有被综合出来,这有效地减少了电路面积。
下一次,如果需要进行或运算,则只需要把 `define OP_AND 这一句注释掉,再把这一句 `define OP_OR 给有效化,此时就会综合出这样的电路(仅有或运算模块):
异或运算模块的使用则不赘述。
2.2、 `else 与 `elsif 的使用
在2.1节为了实现3个模块的条件编译,使用了三个`ifdef···`endif 块,这使得代码看起来很臃肿。就像你通常会使用 else if 和 else 来搭配 if 语句使用一样,你也可以使用 `else 与 `elsif 来搭配 `ifdef 使用。
`else 与 `elsif 的使用方法:
`ifdef <define_name>
<statements>;
`elsif <define_name>
<statements>;
`else
<statements>;
`endif
所以,上面的例子可以改成这样的形式(采用与运算时):
`define OP_AND //指定条件为与运算,标识符建议使用大写
//`define OP_OR //指定条件为或运算,标识符建议使用大写
//`define OP_XOR //指定条件为异或运算,标识符建议使用大写
module op_test(
input [3:0] in1,
input [3:0] in2,
output [3:0] out
);
`ifdef OP_AND
assign out = in1 & in2;
`elsif OP_OR
assign out = in1 | in2;
`else
assign out = in1 ^ in2;
`endif
endmodule
2.3、 `ifndef 的使用
`ifndef 的作用与 `ifdef 是相反的----当其后的标识符未被定义时,则编译后续的代码段,Verilog语法:
`ifndef <define_name>
<statements>;
`endif
举个例子,通过标识符 OP_OR 实现功能:如果OP_OR 未被定义,则实现两个输入的或运算;如果 OP_OR被定义,则实现两个输入的与运算。
不实现或运算(即实现与预算)情况的代码如下:
`define OP_OR //指定条件为或运算,标识符建议使用大写
module op_test(
input [3:0] in1,
input [3:0] in2,
output [3:0] out
);
`ifndef OP_OR
assign out = in1 | in2;
`else
assign out = in1 & in2;
`endif
endmodule
此时,定义了 OP_OR ,所以会执行第二句:assign out = in1 & in2;
此时综合出来的电路:
稍微修改一下,实现或运算情况的代码如下(仅注释掉了 `define OP_OR):
//`define OP_OR //指定条件为或运算,标识符建议使用大写
module op_test(
input [3:0] in1,
input [3:0] in2,
output [3:0] out
);
`ifndef OP_OR
assign out = in1 | in2;
`else
assign out = in1 & in2;
`endif
endmodule
此时,未定义 OP_OR ,所以会执行第一句:assign out = in1 | in2;
综合出来的电路: