top of page

7.4. Conditional Compilation:  `ifdef,  `ifndef,  `elsif,  `else,  `endif

What is Conditional Compilation?

Conditional compilation allows you to include or exclude portions of code based on whether certain macros are defined. This is essential for:

​

  • Debug vs. release builds

  • Different feature sets

  • Platform-specific code

  • Simulation vs. synthesis

 Verilog

`ifdef MACRO_NAME

    // Code included if MACRO_NAME is defined

`endif

`ifndef MACRO_NAME

    // Code included if MACRO_NAME is NOT defined

`endif

`ifdef MACRO_A

    // Code if MACRO_A defined

`elsif MACRO_B

    // Code if MACRO_B defined (and MACRO_A not defined)

`else

    // Code if neither defined

`endif

7.4.1. Fundamental Examples

​

Example 1: Debug vs. Release Mode

 Verilog

// Compile with: +define+DEBUG_MODE

`define DEBUG_MODE

 

module processor (

    input wire clk,

    input wire rst_n,

    input wire [31:0] instruction,

    output reg [31:0] result

);

 

    reg [31:0] pc;

    reg [31:0] registers [0:31];

    

    always @(posedge clk) begin

        if (!rst_n) begin

            pc <= 0;

            result <= 0;

        end else begin

            // Execute instruction

            result <= registers[instruction[15:11]] + registers[instruction[20:16]];

            pc <= pc + 4;

            

            `ifdef DEBUG_MODE

                // Debug output - only in debug builds

                $display("[DEBUG] Time=%0t PC=0x%08h Inst=0x%08h Result=0x%08h",

                         $time, pc, instruction, result);

                

                // Additional checking in debug mode

                if (instruction[15:11] > 31) begin

                    $error("Invalid register index!");

                end

            `endif

        end

    end

    

    `ifdef DEBUG_MODE

        // Debug-only register dump

        task dump_registers;

            integer i;

            begin

                $display("=== Register Dump ===");

                for (i = 0; i < 32; i = i + 1) begin

                    $display("R%02d = 0x%08h", i, registers[i]);

                end

            end

        endtask

    `endif

 

endmodule

Example 2: Feature Flags

 Verilog

// Feature configuration

`define ENABLE_CACHE

`define ENABLE_MMU

//`define ENABLE_FPU    // Commented out - feature disabled

 

module cpu_core (

    input wire clk,

    input wire rst_n,

    // Standard interfaces

    input wire [31:0] instruction,

    output reg [31:0] data_out

    

    `ifdef ENABLE_CACHE

        // Cache interface - only if feature enabled

        ,output wire cache_req

        ,output wire [31:0] cache_addr

        ,input wire [31:0] cache_data

        ,input wire cache_ready

    `endif

    

    `ifdef ENABLE_MMU

        // MMU interface

        ,output wire [31:0] virtual_addr

        ,input wire [31:0] physical_addr

        ,input wire page_fault

    `endif

    

    `ifdef ENABLE_FPU

        // FPU interface

        ,output wire [63:0] fp_operand_a

        ,output wire [63:0] fp_operand_b

        ,input wire [63:0] fp_result

    `endif

);

 

    // Core logic always present

    reg [31:0] pc;

    reg [31:0] registers [0:31];

    

    `ifdef ENABLE_CACHE

        // Cache control logic

        reg cache_enable;

        reg [31:0] cache_line [0:15];

        

        assign cache_req = cache_enable && (pc[31:4] != last_cache_line);

        assign cache_addr = {pc[31:4], 4'b0};

        

        always @(posedge clk) begin

            if (cache_ready) begin

                cache_line[pc[3:0]] <= cache_data;

            end

        end

    `endif

    

    `ifdef ENABLE_MMU

        // Virtual memory translation

        wire [31:0] translated_addr;

        assign virtual_addr = pc;

        assign translated_addr = physical_addr;

        

        always @(posedge clk) begin

            if (page_fault) begin

                $display("Page fault at address 0x%08h", pc);

            end

        end

    `endif

    

    `ifdef ENABLE_FPU

        // Floating-point execution unit

        wire fp_operation = (instruction[6:0] == 7'b1010011);

        

        assign fp_operand_a = {registers[instruction[19:15]], 32'b0};

        assign fp_operand_b = {registers[instruction[24:20]], 32'b0};

        

        always @(posedge clk) begin

            if (fp_operation) begin

                registers[instruction[11:7]] <= fp_result[63:32];

            end

        end

    `else

        // FPU disabled - trap on FP instructions

        always @(posedge clk) begin

            if (instruction[6:0] == 7'b1010011) begin

                $error("Floating-point instruction not supported!");

            end

        end

    `endif

    

    // Report configuration at elaboration

    initial begin

        $display("=== CPU Configuration ===");

        $display("Cache:  %s", `ifdef ENABLE_CACHE "ENABLED" `else "DISABLED" `endif);

        $display("MMU:    %s", `ifdef ENABLE_MMU "ENABLED" `else "DISABLED" `endif);

        $display("FPU:    %s", `ifdef ENABLE_FPU "ENABLED" `else "DISABLED" `endif);

    end

 

endmodule

Example 3: Simulation vs. Synthesis

 Verilog

module memory_controller (

    input wire clk,

    input wire rst_n,

    input wire [31:0] addr,

    input wire [31:0] wdata,

    input wire we,

    output reg [31:0] rdata

);

 

    // Memory array

    reg [31:0] memory [0:1023];

    

    `ifndef SYNTHESIS

        // Simulation-only initialization

        integer i;

        initial begin

            // Initialize memory with pattern for simulation

            for (i = 0; i < 1024; i = i + 1) begin

                memory[i] = i * 4;

            end

            $display("Memory initialized for simulation");

        end

        

        // Simulation-only assertions

        always @(posedge clk) begin

            if (we && addr >= 1024) begin

                $error("Write to invalid address 0x%08h", addr);

            end

            if (addr >= 1024) begin

                $warning("Read from invalid address 0x%08h", addr);

            end

        end

        

        // Simulation-only coverage

        covergroup mem_access @(posedge clk);

            address: coverpoint addr {

                bins low = {[0:255]};

                bins mid = {[256:767]};

                bins high = {[768:1023]};

            }

            write_enable: coverpoint we;

        endgroup

        

        mem_access mem_cov = new();

    `endif

    

    // Synthesizable logic

    always @(posedge clk) begin

        if (we) begin

            memory[addr[9:0]] <= wdata;

        end

        rdata <= memory[addr[9:0]];

    end

    

    `ifdef SYNTHESIS

        // Synthesis-only attributes

        // synthesis attribute memory_type of memory is "block"

        // synthesis attribute ram_style of memory is "block"

    `endif

 

endmodule

7.4.2. Advanced Conditional Compilation Patterns

​

7.4.2.1. Pattern 1: Multiple Configuration Options

 Verilog

// Define ONE of these

//`define CONFIG_LOW_POWER

//`define CONFIG_HIGH_PERFORMANCE

`define CONFIG_BALANCED

 

module configurable_processor (

    input wire clk,

    output reg [31:0] result

);

 

    `ifdef CONFIG_LOW_POWER

        // Low power configuration

        localparam PIPELINE_STAGES = 2;

        localparam ENABLE_CLOCK_GATING = 1;

        localparam MAX_FREQ_MHZ = 50;

        localparam CACHE_SIZE = 4096;

        

        $display("Configuration: LOW POWER");

        

    `elsif CONFIG_HIGH_PERFORMANCE

        // High performance configuration

        localparam PIPELINE_STAGES = 8;

        localparam ENABLE_CLOCK_GATING = 0;

        localparam MAX_FREQ_MHZ = 500;

        localparam CACHE_SIZE = 65536;

        

        initial $display("Configuration: HIGH PERFORMANCE");

        

    `elsif CONFIG_BALANCED

        // Balanced configuration

        localparam PIPELINE_STAGES = 4;

        localparam ENABLE_CLOCK_GATING = 1;

        localparam MAX_FREQ_MHZ = 200;

        localparam CACHE_SIZE = 16384;

        

        initial $display("Configuration: BALANCED");

        

    `else

        // Error - no configuration specified

        initial $error("No configuration defined! Define CONFIG_LOW_POWER, CONFIG_HIGH_PERFORMANCE, or CONFIG_BALANCED");

    `endif

    

    // Use configuration parameters

    reg [31:0] pipeline_regs [0:PIPELINE_STAGES-1];

    reg [31:0] cache [0:CACHE_SIZE/4-1];

 

endmodule

7.4.2.2. Pattern 2: Platform-Specific Code

 Verilog

// Platform selection

//`define PLATFORM_XILINX

//`define PLATFORM_ALTERA

`define PLATFORM_ASIC

 

module platform_specific_memory (

    input wire clk,

    input wire [9:0] addr,

    input wire [31:0] din,

    input wire we,

    output wire [31:0] dout

);

​

    `ifdef PLATFORM_XILINX

        // Xilinx-specific block RAM instantiation

        RAMB36E1 #(

            .READ_WIDTH_A(36),

            .WRITE_WIDTH_A(36),

            .RAM_MODE("TDP")

        ) xilinx_bram (

            .CLKARDCLK(clk),

            .ADDRARDADDR({addr, 5'b0}),

            .DIADI(din),

            .DOADO(dout),

            .ENARDEN(1'b1),

            .WEA({4{we}}),

            .REGCEAREGCE(1'b0),

            .RSTRAMARSTRAM(1'b0),

            .RSTREGARSTREG(1'b0)

        );

        

        initial $display("Using Xilinx Block RAM");

        

    `elsif PLATFORM_ALTERA

        // Intel/Altera-specific RAM megafunction

        altsyncram #(

            .operation_mode("SINGLE_PORT"),

            .width_a(32),

            .widthad_a(10)

        ) altera_ram (

            .clock0(clk),

            .address_a(addr),

            .data_a(din),

            .wren_a(we),

            .q_a(dout)

        );

        

        initial $display("Using Altera synchronous RAM");

        

    `elsif PLATFORM_ASIC

        // Generic RTL for ASIC

        reg [31:0] memory [0:1023];

        reg [31:0] dout_reg;

        

        always @(posedge clk) begin

            if (we) begin

                memory[addr] <= din;

            end

            dout_reg <= memory[addr];

        end

        

        assign dout = dout_reg;

        

        initial $display("Using generic RTL memory for ASIC");

        

    `else

        initial $error("No platform defined! Define PLATFORM_XILINX, PLATFORM_ALTERA, or PLATFORM_ASIC");

    `endif

 

endmodule

7.4.2.3. Pattern 3: Nested Conditionals

 Verilog

`define ENABLE_UART

`define UART_FIFO_DEPTH_16

//`define UART_FLOW_CONTROL

 

module uart_wrapper (

    input wire clk,

    input wire rst_n

    

    `ifdef ENABLE_UART

        ,input wire rx

        ,output wire tx

        

        `ifdef UART_FLOW_CONTROL

            ,input wire cts

            ,output wire rts

        `endif

    `endif

);

 

    `ifdef ENABLE_UART

        // UART is enabled

        

        `ifdef UART_FIFO_DEPTH_16

            localparam FIFO_DEPTH = 16;

        `elsif UART_FIFO_DEPTH_32

            localparam FIFO_DEPTH = 32;

        `elsif UART_FIFO_DEPTH_64

            localparam FIFO_DEPTH = 64;

        `else

            localparam FIFO_DEPTH = 8;  // Default

        `endif

        

        uart_core #(

            .FIFO_DEPTH(FIFO_DEPTH)

        ) uart_inst (

            .clk(clk),

            .rst_n(rst_n),

            .rx(rx),

            .tx(tx)

            

            `ifdef UART_FLOW_CONTROL

                ,.cts(cts)

                ,.rts(rts)

            `endif

        );

        

        initial begin

            $display("UART enabled with FIFO depth = %0d", FIFO_DEPTH);RetryAContinueEditverilog            `ifdef UART_FLOW_CONTROL

                $display("Hardware flow control: ENABLED");

            `else

                $display("Hardware flow control: DISABLED");

            `endif

        end

        

    `else

        // UART disabled - tie off ports if needed

        initial $display("UART disabled");

    `endif

 

endmodule

7.4.2.4. Pattern 4: Version Control and Compatibility

 Verilog

verilog// Version definitions

`define VERSION_MAJOR 2

`define VERSION_MINOR 1

`define VERSION_PATCH 0

 

// Backward compatibility flags

`define SUPPORT_V1_PROTOCOL

//`define DEPRECATED_FEATURES

 

module versioned_module (

    input wire clk,

    input wire rst_n,

    input wire [1:0] protocol_version,

    input wire [31:0] command

);

 

    // Version reporting

    initial begin

        $display("Module Version: %0d.%0d.%0d", 

                 `VERSION_MAJOR, `VERSION_MINOR, `VERSION_PATCH);

                 

        `ifdef SUPPORT_V1_PROTOCOL

            $display("V1 Protocol Support: YES");

        `else

            $display("V1 Protocol Support: NO");

        `endif

        

        `ifdef DEPRECATED_FEATURES

            $warning("Deprecated features are enabled - remove for production!");

        `endif

    end

    

    // Protocol handling

    always @(posedge clk) begin

        case (protocol_version)

            2'b00: begin

                `ifdef SUPPORT_V1_PROTOCOL

                    // Legacy V1.0 protocol

                    handle_v1_command(command);

                `else

                    $error("V1 protocol not supported in this build");

                `endif

            end

            

            2'b01: begin

                // V2.0 protocol (current)

                handle_v2_command(command);

            end

            

            2'b10: begin

                // V2.1 protocol (with extensions)

                `if (`VERSION_MINOR >= 1)

                    handle_v2_1_command(command);

                `else

                    $error("V2.1 protocol not supported - upgrade to v2.1.x");

                `endif

            end

            

            default: begin

                $error("Unknown protocol version: %0d", protocol_version);

            end

        endcase

    end

    

    `ifdef DEPRECATED_FEATURES

        // Old API that will be removed

        task handle_legacy_api;

            input [31:0] data;

            begin

                $warning("Using deprecated API - migrate to new API");

                // Legacy implementation

            end

        endtask

    `endif

 

endmodule

7.4.3. Best Practices Summary

​

7.4.3.1. Organization

 Verilog

// Always start with include guards

`ifndef MY_MODULE_H

`define MY_MODULE_H

 

// Then timescale

`timescale 1ns / 1ps

 

// Then includes

`include "project_config.vh"

 

// Then defines

`define LOCAL_CONSTANT 100

 

// Then module code

module my_module (...);

    // ...

endmodule

 

`endif // MY_MODULE_H

7.4.3.2. Naming Conventions

 Verilog

// Use descriptive, uppercase names

`define UART_BAUD_RATE_9600     9600

`define UART_BAUD_RATE_115200   115200

 

// Prefix related constants

`define SPI_MODE_0   2'b00

`define SPI_MODE_1   2'b01

`define SPI_MODE_2   2'b10

`define SPI_MODE_3   2'b11

 

// Use meaningful ifdef names

`ifdef ENABLE_DEBUG_UART

`ifdef PLATFORM_XILINX_7SERIES

7.4.3.3. Documentation

 Verilog

/**

 * Feature: ENABLE_CACHE

 * Description: Enables L1 data cache

 * Impact: +10K gates, +15% performance

 * Dependencies: Requires ENABLE_MEMORY_CONTROLLER

 */

`define ENABLE_CACHE

7.4.3.4. Configuration Validation

 Verilog

// Validate conflicting options

`ifdef OPTION_A

    `ifdef OPTION_B

        `error "Cannot enable both OPTION_A and OPTION_B"

    `endif

`endif

 

// Validate dependencies

`ifdef FEATURE_X

    `ifndef REQUIRED_FEATURE_Y

        `error "FEATURE_X requires REQUIRED_FEATURE_Y"

    `endif

`endif

timescale

System task & function 

© Copyright 2025 VLSI Mentor. All Rights Reserved.©

Connect with us

  • Instagram
  • Facebook
  • Twitter
  • LinkedIn
  • YouTube
bottom of page