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
