Verilog语法之条件编译指令`ifdef, `ifndef,`else, `elsif, `endif

时间:2022-12-11 16:05:42

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分析出来的电路是这样的:

Verilog语法之条件编译指令`ifdef, `ifndef,`else, `elsif, `endif

        嗯,完美满足老板要求。嘻嘻。

        不料老板看了代码后,对着我劈头盖脸就是一顿臭骂:说了一次只会做一种运算,你给我弄3个运算模块干嘛?做或运算的时候,异或运算和与运算模块就站着看戏?节约资源懂不懂伐?性价比懂不懂伐?钞票懂不懂伐? 

        虽然我内心立马条件反射般地对老板竖了个中指,但是仔细想想老板说的话也确实有道理,所以只好再找找其他办法。

Verilog语法之条件编译指令`ifdef, `ifndef,`else, `elsif, `endif

        找了半天还真给我找着了(不愧是我),这就是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分析出来的电路是这样的: 

Verilog语法之条件编译指令`ifdef, `ifndef,`else, `elsif, `endif

        可以看到,在希望进行与运算时,综合出来的电路已经只有与运算模块了,异或运算模块、或预算模块和选择器模块都没有被综合出来,这有效地减少了电路面积。

        下一次,如果需要进行或运算,则只需要把  `define    OP_AND    这一句注释掉,再把这一句  `define    OP_OR  给有效化,此时就会综合出这样的电路(仅有或运算模块):

Verilog语法之条件编译指令`ifdef, `ifndef,`else, `elsif, `endif

        异或运算模块的使用则不赘述。


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;   

        此时综合出来的电路:

Verilog语法之条件编译指令`ifdef, `ifndef,`else, `elsif, `endif

        稍微修改一下,实现或运算情况的代码如下(仅注释掉了 `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;   

        综合出来的电路:

Verilog语法之条件编译指令`ifdef, `ifndef,`else, `elsif, `endif