Introduction
VHDL was originally designed for describing the functionalty of circuits. The VHDL language is clearly defined and non-ambiguous, and many VHDL simulators exist today that support every defined VHDL construct.VHDL is frequently used for another purpose: Synthesis. Synthesis involves taking some higher level description down to a lower level description. For example - taking VHDL code and producing a netlist that can be mapped to an FPGA.
In CS120b we will be writing VHDL code to be synthesized and mapped down to a Xilinx Spartan2 FPGA. Sometime in this class you will write a piece of code that will simulate correctly, but when you download to an FPGA things will go wrong. This is because only a subset of the VHDL language is synthesizable. Just because your design is valid VHDL and passes simulation is no guarantee that your synthesized design will be correct. In this tutorial we will go over common mistakes people make that make their code unsynthesizable.
Process Satatements
A process sensitivity list specifies that a block of code should be reevaluated whenever a signal in the list changes. For example, the process statement process(a,b,c) would be evaluated ONLY when a,b, or c changes. The commands inside the process will be evaluated from top to bottom once per change. Check here for more detailed information about process sensitivity lists.Combinational Process StatementsAll signals assignments and reads inside a process statement will execute simultainously. What this means is if you assign a signal, it will not be updated until the next interation of the process. Consider the following code:
In the code above, x will NOT take the same vaule as y. Since y is assigned to the signal b, it will take the OLD value of b, not the new value b was just assigned to. If y were a variable rather than a signal though, the values would be identical.process(a) -- reevaluate when ANY input changes begin b <= a+'1'; x <= a+'1'; y <= b; end process;
Remember that combinational logic only depends on current inputs and has no clock input. Therefore we need to include all inputs in the process statement of a combinational circuit. Recall that process statements say "when one of these inputs change, reevaluate". When any input to our combinational circuit changes, we need to reevaluate. Here's an example for a 2-1 MUX:A common mistake here is to leave sel out of the process statement: process(a,b). This is an incorrect design - the output of a MUX depends on the select line, so sel should be included in the process statement. Also, this will not synthesize - you will likely get an error sel missing from process if you try. Think about it - how can you generate combinational logic that only depends on some but not all of its inputs? You can't, so don't expect the synthesis tool to.process(a,b,sel) -- reevaluate when ANY input changes begin case sel is when "0" => output <= a; -- mux when "1" => output <= b; when others => end case; end process;
Sequential Process Statements
Sequential circuit outputs depend on current and previous inputs, and thus require a clock. The most important thing to remember when designing sequential circuits is that everything should be synchronous to the clock with the exception of an asynchronous reset. Every sequential circuit that you design in this class should begin like this:The 'event TagThere are other ways to write sequential circuits (latch instead of FF, falling edge clock, etc) but if you stick to the framework above then you'll reduce the possibility for problems. You can also make your reset signal synchronous rather than asynchronous:process(clk,rst) begin if(rst='1') then -- set all outputs / -- signals / variables -- to initial value elsif(clk='1' and clk'event) then -- -- behaviour of circuit -- endif; end process;Here we have a reset signal that only activates on the rising edge of the clock.process(clk) begin if(clk='1' and clk'event) then if(rst='1') then -- reset else -- behaviour of circuit end if; end if; end process;
You can tag any input with 'event to return TRUE only when a change in that input has occured. In the sequential example above, we use it to synthesize flip flops rather than latches.Unsynthesizable ConstructsNow consider this example: you have a sequential circuit that increases a counter whenever a button is pressed. The button will likely be held down for multiple cycles, but you only want to increase the counter once whenever the button is pressed. Another way of saying this is that you want to increase the count when the button value changes from zero to one. You might think to implement this functionality with a 'event tag:
The only way to implement the above design is to use a flip-flop with button going to the clk input. However, such a design is not possible on an FPGA - due to design limitations, only gclk (clock input) pins on the FPGA can be mapped to the clk input on a flip flop. If you attempt to produce a .bit file from this design, you will likely get an error at the implementation step.if(button='1' and button'event) then -- increment counter end if;
Certain VHDL constructs are unsynthesizable and simply cannot be used for synthesis. For example, the wait statement cannot be synthesized. Since FPGAs have no internal timers, the only concept of time FPGAs have is from their clock inputs. Clock speeds can vary based on implementations, so wait statements cannot be synthesized. The best way to do timing in VHDL designs is to use a clock divider or simply to count clock cycles. These methods require you knowning the frequency of your input clock.Using Xilinx ISEOther unsynthesizable constructs include assert statements, anything to do with file I/O, etc. These are just common sense.
Below are some screenshots who should help you out. Click to enlarge image.:Useful Links
![]()
![]()
Xilinx Synthesis and Simulation Design Guide
Synthesizable VHDL Models