Non-synthesizable VHDL is code that simulates correctly but that a synthesis tool cannot turn into gates and flip-flops, or turns into something different from what the simulator showed. The usual culprits are timing delays (wait for, after), initial values relied on in ASICs, processes with several different clock conditions, clock edges combined with else or wait, and testbench-only features such as file I/O. The fix is almost always to rewrite the code in one of the standard register and combinational templates that every tool recognises.
RTL synthesis works by matching high-level code against templates or patterns. It is important to use idioms that your synthesis tool recognises. If you are not careful, you can write code that has the same behaviour as one of the idioms in simulation but which results in inefficient or incorrect hardware.
Key takeaways
- Synthesis tools accept a subset of VHDL; the full language was designed for simulation and modelling.
- Physical time (
wait for 10 ns,after 2 ns) has no meaning in gates and is ignored or rejected. Count clock cycles instead. - Use one clock edge condition per clocked process, with any asynchronous reset tested before it and nothing in an
elsebranch of the edge test. - Initial values are ignored in ASIC flows; most FPGA tools honour them for registers. Use an explicit reset when the design must be portable.
- Some restrictions are language rules (a process with a sensitivity list cannot contain
wait), and others are tool idioms that vary by vendor and version.
Why some VHDL cannot be synthesised
VHDL was created as a description and simulation language. A simulator can do anything the language defines: advance time by an exact number of nanoseconds, read files, create dynamic data structures and print messages. A synthesis tool has to map the code to a fixed set of hardware primitives (logic gates, multiplexers, flip-flops, latches, RAMs, DSP blocks). Anything without a hardware equivalent is either ignored, with a warning, or rejected.
Most synthesis tools agree on a large set of idioms and will reliably generate hardware for them. This post is based on the idioms that Synopsys, Xilinx (now AMD), Altera and Mentor Graphics (now Siemens EDA) were all able to synthesise. The IEEE once published a common RTL synthesis subset, IEEE 1076.6, but in practice each vendor's synthesis user guide is the authority for its tool.
We consider combinational loops to be unsynthesizable. Although it is obviously possible to build a circuit with a combinational loop, in most cases the behaviour of such a circuit is undefined: it may oscillate or latch depending on gate delays, and timing analysis cannot check it.
Non-synthesizable constructs and their fixes
1. Initial values on signals
Initial values on signals (unsynthesizable in ASIC flows):
signal bad_signal : std_logic := '0';
Reason: In most implementation technologies, when a circuit powers up, the values on signals are effectively random. Some FPGAs are an exception. In SRAM-based FPGAs, the configuration bitstream loads every flip-flop to a known value, and tools from AMD (Vivado) and Altera (Quartus) use the declared initial value for that. ASIC synthesis ignores the initial value, so the simulation and the silicon disagree after power-up.
Synthesizable alternative: give the register an explicit reset.
process (clk)
begin
if rising_edge(clk) then
if rst = '1' then -- synchronous reset
q <= '0';
else
q <= d;
end if;
end if;
end process;
2. Wait for a length of time
wait for a length of time (unsynthesizable):
wait for 10 ns;
Reason: Delays through circuits depend on both the circuit and its operating environment, particularly process, supply voltage and temperature. There is no gate that waits exactly 10 ns. The same applies to after clauses such as y <= a after 2 ns;, which synthesis tools ignore.
Synthesizable alternative: derive delays from the clock. At 100 MHz one cycle is 10 ns, so a 1 µs delay is 100 cycles, counted with a register.
process (clk)
begin
if rising_edge(clk) then
if start = '1' then
count <= 99; -- 100 cycles at 100 MHz = 1 us
done <= '0';
elsif count /= 0 then
count <= count - 1;
else
done <= '1';
end if;
end if;
end process;
Here count is declared as integer range 0 to 99, so the tool builds a 7-bit counter.
3. Different wait conditions in one process
Wait statements with different conditions in a process (unsynthesizable):
-- different clock signals
process
begin
wait until rising_edge(clk1);
x <= a;
wait until rising_edge(clk2);
x <= a;
end process;
-- different clock edges
process
begin
wait until rising_edge(clk);
x <= a;
wait until falling_edge(clk);
x <= a;
end process;
Reason: Processes with multiple wait statements are turned into finite state machines. The wait statements mark transitions between states, and the target signals in the process are outputs of flip-flops. Using different wait conditions would require the flip-flops to use different clock signals at different times. Multiple clock signals for a single flip-flop would be difficult to synthesise, inefficient to build and fragile to operate. Even several wait until statements on the same clock edge (an implicit state machine) are supported by only some tools, so an explicit state machine is safer.
Synthesizable alternative: one process per clock domain, with proper clock domain crossing between them if the data must meet. For data on both edges of one clock, use two registers and a multiplexer, or better, the FPGA's dedicated DDR output register (ODDR or equivalent).
process (clk) -- rising-edge register
begin
if rising_edge(clk) then
x_r <= a;
end if;
end process;
process (clk) -- falling-edge register
begin
if falling_edge(clk) then
x_f <= a;
end if;
end process;
4. Multiple "if rising edge" statements in the same process
Multiple if rising edge statements in a process (unsynthesizable in older tools):
process (clk)
begin
if rising_edge(clk) then
q0 <= d0;
end if;
if rising_edge(clk) then
q1 <= d1;
end if;
end process;
Reason: The idioms for synthesis tools generally expect a single if rising edge statement in each process. The simpler the VHDL code is, the easier it is to synthesise hardware, and synthesis tool developers make idiomatic restrictions to keep their jobs manageable. Several current tools accept this example because both tests use the same clock, but it is not portable and offers nothing over the single-edge version.
Synthesizable alternative:
process (clk)
begin
if rising_edge(clk) then
q0 <= d0;
q1 <= d1;
end if;
end process;
5. "if rising edge" and "wait" in the same process
An if rising edge statement and a wait statement in the same process (illegal):
process (clk)
begin
if rising_edge(clk) then
q0 <= d0;
end if;
wait until rising_edge(clk);
q0 <= d1;
end process;
Reason: Synthesis idioms expect a single type of flop-generating statement in each process. This particular example also breaks a language rule: a process with a sensitivity list (process (clk)) may not contain any wait statement, so even a simulator will reject it at analysis. Choose one style per process: a sensitivity list with if rising_edge, or no sensitivity list with a single wait until rising_edge at the top.
6. "if rising edge" with an "else" clause
The if statement has a rising edge condition and an else clause (unsynthesizable):
process (clk)
begin
if rising_edge(clk) then
q0 <= d0;
else
q0 <= d1;
end if;
end process;
Reason: Generally, an if-then-else statement synthesises to a multiplexer, and the condition tested becomes the select signal. In an if rising edge with else, the select signal would need to detect a rising edge on clk, which is not feasible to synthesise. In simulation, the else branch runs on every falling edge, which is not what the author usually meant. The same applies to an elsif placed after the clock test.
Synthesizable alternative: keep all choices inside the clock edge, or put an asynchronous reset before it.
process (clk, rst)
begin
if rst = '1' then -- asynchronous reset first
q0 <= '0';
elsif rising_edge(clk) then
if sel = '1' then
q0 <= d0;
else
q0 <= d1; -- multiplexer in front of the flip-flop
end if;
end if;
end process;
7. "if rising edge" inside a "for" loop
An if rising edge statement in a for loop (unsynthesizable with Synopsys tools at the time of writing):
process (clk) begin
for i in 0 to 7 loop
if rising_edge(clk) then
q(i) <= d;
end if;
end loop;
end process;
Reason: Just an idiom of the synthesis tool. For loops themselves are synthesizable when their bounds are constant: the tool unrolls them into parallel hardware. For loops in general are described in Ashenden's VHDL books (The VHDL Cookbook and The Designer's Guide to VHDL).
Correction to the original post: this code was described as an 8-bit serial-to-parallel converter that copies d into one bit of q on each clock cycle. It does not do that. Every iteration of the loop runs in the same clock edge, so all eight bits of q are loaded with the same value of d on every cycle. A real serial-to-parallel converter is a shift register:
process (clk)
begin
if rising_edge(clk) then
q <= q(6 downto 0) & d; -- shift in one bit per clock
end if;
end process;
After eight clocks, q holds the last eight serial bits, with the oldest in q(7). Add a 3-bit counter and a valid flag if the parallel word must be marked when complete. If you do want the loop form, move the loop inside the edge test (if rising_edge(clk) then for i in 0 to 7 loop ... end loop; end if;), which every tool accepts.
Other constructs to keep out of RTL
| Construct | In simulation | In synthesis | Use instead |
|---|---|---|---|
after delays | Delays the assignment | Ignored | Clock-cycle counters, pipeline registers |
File I/O (textio) | Reads and writes files | Rejected (some FPGA tools read files only to initialise ROM contents) | Constants or generated packages |
Access types and new | Dynamic data structures | Rejected | Fixed-size arrays |
real signals | Floating-point values | Only for constant calculations | unsigned, signed or fixed-point packages |
assert and report | Messages and checks | Ignored or used only on constants | Keep them for simulation; they do no harm |
now | Current simulation time | Rejected | A free-running counter |
| While loops with data-dependent bounds | Run until the condition fails | Rejected (cannot be unrolled) | For loops with constant bounds, or a state machine |
| Incomplete sensitivity list in combinational logic | Output updates only on listed signals | Builds full combinational logic (mismatch) | process (all) in VHDL-2008, or list every input |
Common mistakes
- Trusting simulation alone. A design that passes RTL simulation with
wait foror initial values can fail in silicon. Run synthesis early and read its warnings. - Ignoring "ignored" warnings. When a tool says it ignored a delay or initial value, the netlist no longer matches the RTL you simulated.
- Mixing testbench and design code. Keep testbench-only constructs in separate files that are never passed to synthesis.
- Accidental latches. A combinational process that does not assign an output in every branch infers a latch. It is synthesizable but rarely intended.
- Typographic quotes. Code copied from word processors often turns
'0'into curly quotes, which no VHDL tool accepts.
FAQ
Is "wait until rising_edge(clk)" synthesizable?
Yes, as the first statement of a process without a sensitivity list. It is equivalent to the if rising_edge(clk) template. Problems start when a process has several waits on different conditions.
Are initial values ever safe to use?
In FPGA designs, most vendors honour initial values on registers and RAM contents because the bitstream sets them at configuration. In ASIC or portable IP, use a reset. Initial values on constants and on simulation-only signals are always fine.
Why does my code simulate but give different results after synthesis?
The common causes are ignored delays and initial values, incomplete sensitivity lists, and latches. Gate-level simulation or formal equivalence checking between RTL and netlist will expose the mismatch.
Where can I find what my tool supports?
Each vendor publishes a synthesis guide listing supported VHDL constructs, for example the AMD Vivado Synthesis User Guide (UG901) and the Quartus Prime handbook. Check the version you use, because support grows over time.
Synthesizable alternatives for each case are given above. If your tool behaves differently, or you have another construct that caught you out, please discuss it in the comments below.

A synthesizable alternative to an if rising edge statement in a for-loop is to put the if-risingedge outside of the for loop.
ReplyDeleteprocess (clk)
begin
if rising_edge(clk) then
for i in 0 to 7 loop
q(i) <= d;
end loop;
end if;
end process;
--//Peace
Clocked for-loops are not synthesizable, but are very useful in simulation, particular to generate test vectors for test benches.
ReplyDeletenon-synthesizable codes are not translatable in hardware.......so why do we still use it?
ReplyDelete@ Himu, Such code is used as behavioral codes in RTL modelling. Mostly used for testbench components for functional simulation.
ReplyDeletethnx a lot @Ganesan
ReplyDeleteThis is the synthesizable alternative to the the wait statement in a for loop.
ReplyDeleteprocess
begin
-- output values from 0 to 4 on i
-- sending one value out each clock cycle
i <= to_unsigned(0,4);
wait until rising_edge(clk);
while (4 > i) loop
i <= i + 1;
wait until rising_edge(clk);
end loop;
end process;
Some coding styles are synthesizable and might produce desirable hardware with a particular synthesis tool, but either be unsynthesizable or produce undesirable hardware with another tool.
ReplyDelete1. variables
2. level-sensitive wait statements
3. missing signals in sens list
If you are using a single synthesis tool for an extended period of time, and want to get the full power of the tool, then it can be advantageous to write your code in a way that works for your tool, but might produce undesirable results with other tools.