Verilog supports a few compiler directives that essentially direct the compiler to treat the code in a certain way. For example, a portion of the code may represent an implementation of a certain feature and there should be some way to not include the code in the design if the feature is not used.

This can be solved with conditional compilation where the designer can wrap the code within compiler directives which tell the compiler to either include or exclude the code for compilation when the given named flag is set.

Syntax

Conditional compilation can be achieved with Verilog `ifdef and `ifndef keywords. These keywords can appear anywhere in the design and can be nested one inside the other.

The keyword `ifdef simply tells the compiler to include the piece of code until the next `else or `endif if the given macro called FLAG is defined using a `define directive.


// Style #1: Only single `ifdef
`ifdef <FLAG>
	// Statements
`endif

// Style #2: `ifdef with `else part
`ifdef <FLAG>
	// Statements
`else
	// Statements
`endif

// Style #3: `ifdef with additional ifdefs
`ifdef <FLAG1>
	// Statements
`elsif <FLAG2>
	// Statements
`elsif <FLAG3>
	// Statements
`else
	// Statements
`endif

The keyword `ifndef simply tells the compiler to include the piece of code until the next `else or `endif if the given macro called FLAG is not defined using a `define directive.

Design Example with `ifdef


module my_design (input clk, d, 
`ifdef INCLUDE_RSTN
                  input rstn,
`endif                  
                  output reg q);
  
  always @ (posedge clk) begin
`ifdef INCLUDE_RSTN
    if (!rstn) begin
      q <= 0;
    end else 
`endif
    begin
      q <= d;
    end
  end
endmodule

Testbench


module tb;
  reg clk, d, rstn;
  wire q;
  reg [3:0] delay;
  
  my_design u0 ( .clk(clk), .d(d),
`ifdef INCLUDE_RSTN
                .rstn(rstn),
`endif
                .q(q));
  
  always #10 clk = ~clk;
  
  initial begin
    integer i;
    
    {d, rstn, clk} <= 0;
    
	#20 rstn <= 1;    
    for (i = 0 ; i < 20; i=i+1) begin
      delay = $random;
      #(delay) d <= $random;
    end
    
    #20 $finish;
  end
endmodule

Note that by default, rstn will not be included during compilation of the design and hence it will not appear in the portlist. However if a macro called INCLUDE_RSTN is either defined in any Verilog file that is part of the compilation list of files or passed through the command line to the compiler, rstn will be included in compilation and the design will have it.

Experiment by adding and removing +define+INCLUDE_RSTN from 'Compile & Run Options' on the left pane to know the difference.

Verilog `ifdef `elsif Example

The following example has two display statements inside separate `ifdef scopes which does not have a default `else part to it. So this means that by default nothing will be displayed. If the macro either MACRO is defined, the corresponding display message is included and will be displayed during simulation


module tb;
  initial begin

`ifdef MACRO1
    $display ("This is MACRO1");
    
`elsif MACRO2
    $display ("This is MACRO2");
    
`endif
  end
endmodule
 Simulation Log
# With no macros defined
ncsim> run
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+MACRO1
ncsim> run
This is MACRO1
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+MACRO2
ncsim> run
This is MACRO2
ncsim: *W,RNQUIE: Simulation is complete.

Verilog `ifndef `elsif Example

The same code can be written with `ifndef and results will be just the opposite.


module tb;
  initial begin

`ifndef MACRO1
    $display ("This is MACRO1");
    
`elsif MACRO2
    $display ("This is MACRO2");
    
`endif
  end
endmodule
 Simulation Log
# With no macros defined
ncsim> run
This is MACRO1
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+MACRO1
ncsim> run
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+MACRO2
ncsim> run
This is MACRO1
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+MACRO1 +define+MACRO2
ncsim> run
This is MACRO2
ncsim: *W,RNQUIE: Simulation is complete.

Verilog Nested `ifdef Example

`ifdef and its flavors can be nested one inside the other to create complex ways of code inclusion and exclusion with defined macros.


module tb;
  initial begin
    `ifdef FLAG
    	$display ("FLAG is defined");
    	`ifdef NEST1_A
    		$display ("FLAG and NEST1_A are defined");
    		`ifdef NEST2
    			$display ("FLAG, NEST1_A and NEST2 are defined");
    		`endif
    	`elsif NEST1_B
    		$display ("FLAG and NEST1_B are defined");
    		`ifndef WHITE
    			$display ("FLAG and NEST1_B are defined, but WHITE is not");
    		`else
    			$display ("FLAG, NEST1_B and WHITE are defined");
    		`endif
    	`else
    		$display ("Only FLAG is defined");
    	`endif
    `else
    	$display ("FLAG is not defined");
    `endif
  end
endmodule
 Simulation Log
# Without defining any macro
ncsim> run
FLAG is not defined
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+FLAG +define+NEST1_B
ncsim> run
FLAG is defined
FLAG and NEST1_B are defined
FLAG and NEST1_B are defined, but WHITE is not
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+FLAG +define+NEST1_B +define+WHITE
ncsim> run
FLAG is defined
FLAG and NEST1_B are defined
FLAG, NEST1_B and WHITE are defined
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+FLAG
ncsim> run
FLAG is defined
Only FLAG is defined
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+WHITE
ncsim> run
FLAG is not defined
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+NEST1_A
ncsim> run
FLAG is not defined
ncsim: *W,RNQUIE: Simulation is complete.

Note that as long as the parent macro is not defined, definition of any other nested macro within it does not get compiled. For example, NEST1_A or WHITE macro definitions without FLAG does not make the compiler pick up the nested code.