Simulation vs Synthesis Code in Verilog
Introduction¶
When designing digital circuits using Verilog, you’ll often hear two important terms — simulation and synthesis.
 Both are essential steps in a hardware design flow, but they serve very different purposes.
- Simulation verifies how your Verilog code behaves before hardware is built.
 - Synthesis converts your Verilog code into real hardware logic that can be implemented on an FPGA or ASIC.
 
This article explains the difference between simulation and synthesis code in Verilog, which constructs are synthesizable, which are not, and how to write code that works for both worlds.
Simulation vs Synthesis¶
| Aspect | Simulation | Synthesis | 
|---|---|---|
| Purpose | To verify logical behavior of design | To generate gate-level hardware implementation | 
| Tool Type | Verilog simulator e.g., ModelSim, Icarus Verilog, Verilator | Synthesis tool e.g., Synopsys Design Compiler (ASIC), Yosys (ASIC), Xilinx Vivado (FPGA), Intel Quartus (FPGA) | 
| Output | Waveforms, logs, functional verification | Netlist (gates, flip-flops, connections) | 
| Speed | Software-based, slower | Hardware implementation, actual circuit | 
| Timing | Simulated using delays or clock cycles | Determined by real hardware constraints | 
| Scope | Any Verilog code can simulate | Only synthesizable subset of Verilog supported | 
In short:
- Simulation = “How can I verify and initialize my design before hardware implementation?”
 - Synthesis = “Can my design be built in hardware?”
 
What Happens During Simulation?¶
Simulation is the process of running your Verilog code on a software simulator to check its functional correctness.
A simulator interprets your code like a program — it executes procedural statements, tracks signal changes, and outputs waveforms over time.
Example¶
module counter_tb;
  reg clk, rst;
  wire [3:0] count;
  counter dut (.clk(clk), .rst(rst), .count(count));
  initial begin
    clk = 0;
    forever #5 clk = ~clk;  // Toggle every 5 ns
  end
  initial begin
    rst = 1;
    #10 rst = 0;
    #100 $finish;
  end
endmodule
What Happens During Synthesis?¶
Synthesis translates Verilog RTL (Register Transfer Level) code into actual hardware — gates, flip-flops, and multiplexers.
The synthesis tool:
- Reads your RTL code.
 - Infers the intended hardware (based on always @(posedge clk) and combinational logic).
 - Maps it to technology cells available in a target library (FPGA or ASIC).
 - Generates a netlist (a gate-level representation).
 
Example¶
module counter (
  input clk, rst,
  output reg [3:0] count
);
  always @(posedge clk or posedge rst) begin
    if (rst)
      count <= 0;
    else
      count <= count + 1;
  end
endmodule
This is synthesizable RTL — it describes hardware directly (a 4-bit counter). The synthesizer infers flip-flops for count and an adder for count + 1.
Simulation-Only vs Synthesizable Constructs¶
Verilog contains many constructs that simulators understand but synthesizers ignore or reject. Here’s a comparison of simulation-only and synthesizable code:
| Type | Simulation-Only (Ignored by Synthesis) | Synthesizable (Hardware-Realistic) | 
|---|---|---|
| Delays | #10 a = b; | "#10" is Non-synthesizable | 
| Timing controls | @(posedge clk) in testbench | In sequential always block | 
| Initial blocks | initial begin ... end | For testbench only. Non-synthesizable for ASIC implementation | 
| System tasks | $display, $monitor, $finish, $random | Simulation only | 
| Force/Release | force a = 1; | Simulation only | 
| File I/O | $readmemb, $writememh | Simulation-only (some allowed for memory initialization) | 
| Continuous assignments | assign y = a & b; | Synthesizable | 
| Combinational always block | always @(*) | Synthesizable | 
| Sequential always block | always @(posedge clk) | Synthesizable | 
| Blocking/Non-blocking | = and <= | Synthesizable (used properly) | 
Examples¶
Let’s see how the same construct behaves differently under simulation and synthesis.
Using delays¶
always @(posedge clk)
  #5 q <= ~d;  // delay by 5 ns
Simulation Delays q assignment by 5 ns. Synthesis Ignores the #5 completely. Hardware flip-flops delay depends on real clock and propagation time and cannot be programmed to 5ns.
Using $display¶
always @(posedge clk)
  $display("Value = %b", data);
Simulation Prints value of data to the console. Synthesis $display is ignored. Use $display only in testbenches, not in design modules.
Non-synthesizable Loops¶
integer i;
initial begin
  for (i = 0; i < 10; i = i + 1)
    #10 $display("i = %d", i);
end
Simulation Loops execute with 10 ns delay each iteration. Synthesis Entire initial block ignored; initial and delays are non-synthesizable.
Synthesizable Loops¶
always @(*) begin
  for (i = 0; i < 8; i = i + 1)
    sum[i] = a[i] & b[i];
end
Writing Synthesizable Verilog Code¶
To ensure your Verilog code synthesizes properly, follow these guidelines:
Do¶
- Use always @(posedge clk) for sequential logic.
 - Use always @(*) or assign for combinational logic.
 - Provide default assignments in combinational blocks to avoid unintended latches.
 - Use non-blocking (<=) assignments in sequential logic.
 - Keep loops bounded (finite iterations).
 - Define resets for registers.
 
Don't¶
- Don’t use initial, #delay, $display, $finish, $random in synthesizable modules.
 - Don’t mix blocking and non-blocking assignments in the same block.
 - Don’t rely on simulation timing (#10, #100) — use real clocks.
 - Don’t use dynamic constructs (e.g., while with variable bounds, file I/O).
 
Simulation-Only Testbench vs Synthesizable Design¶
module adder_4bit (
  input [3:0] a, b,
  output [4:0] sum
);
  assign sum = a + b;
endmodule
module tb_adder_4bit;
  reg [3:0] a, b;
  wire [4:0] sum;
  adder_4bit dut (.a(a), .b(b), .sum(sum));
  initial begin
    $display("Testing 4-bit adder");
    a = 4'b0011; b = 4'b0101;
    #10 $display("Sum = %b", sum);
    #10 $finish;
  end
endmodule
- The adder can be synthesized to real hardware.
 - The testbench cannot — it’s meant for functional verification only.
 
Common Simulation vs Synthesis Mismatches¶
| Issue | Cause | Result | 
|---|---|---|
| Simulation works, hardware fails | Used delays or initial blocks | Synthesizer ignored them | 
| Latch inferred unintentionally | Missing else in always @(*) | Different logic than expected | 
| Wrong assignment behavior | Used blocking = in sequential logic | Race conditions | 
| Uninitialized signals | Relied on initial values | Hardware starts with unknown state | 
| Infinite loops | Unbounded while loop | Synthesis error or hang | 
Always verify both functional correctness (via simulation) and synthesizability (via synthesis reports).
Key Takeaways¶
- Simulation lets you verify your logic before hardware implementation.
 - Synthesis turns Verilog RTL into actual logic gates.
 - Only a subset of Verilog is synthesizable — delays, system tasks, and initial blocks are not.
 - Always separate design code and testbench code.
 - Test thoroughly in simulation, then verify synthesis reports before implementation.