# 12.6  VHDL and Logic Synthesis

Most logic synthesizers insist we follow a set of rules when we use a logic system to ensure that what we synthesize matches the behavioral description. Here is a typical set of rules for use with the IEEE VHDL nine-value system:

• You can use logic values corresponding to states '1' , 'H' , '0' , and 'L' in any manner.
• Some synthesis tools do not accept the uninitialized logic state 'U' .
• You can use logic states 'Z' , 'X' , 'W' , and '-' in signal and variable assignments in any manner. 'Z' is synthesized to three-state logic.
• The states 'X' , 'W' , and '-' are treated as unknown or don’t care values.

The values 'Z' , 'X' , 'W' , and '-' may be used in conditional clauses such as the comparison in an if or case statement. However, some synthesis tools will ignore them and only match surrounding '1' and '0' bits. Consequently, a synthesized design may behave differently from the simulation if a stimulus uses 'Z' , 'X' , 'W' or '-' . The IEEE synthesis packages provide the STD_MATCH function for comparisons.

## 12.6.1  Initialization and Reset

You can use a VHDL process with a sensitivity list to synthesize clocked logic with a reset, as in the following code:

process (signal_1, signal_2) begin

if (signal_2'EVENT and signal_2 = '0')

then -- Insert initialization and reset statements.

elsif (signal_1'EVENT and signal_1 = '1')

then -- Insert clocking statements.

end if ;

end process ;

Using a specific pattern the synthesizer can infer that you are implying a positive-edge clock ( signal_1 ) and a negative-edge reset ( signal_2 ). In order to be able to recognize sequential logic in this way, most synthesizers restrict you to using a maximum of two edges in a sensitivity list.

## 12.6.2 Combinational Logic Synthesis in VHDL

In VHDL a level-sensitive process is a process statement that has a sensitivity list with signals that are not tested for event attributes ( 'EVENT or 'STABLE , for example) within the process . To synthesize combinational logic we use a VHDL level-sensitive process or a concurrent assignment statement. Some synthesizers do not allow reference to a signal inside a level-sensitive process unless that signal is in the sensitivity list. In this example, signal b is missing from the sensitivity list:

entity And_Bad is port (a, b: in BIT; c: out BIT); end And_Bad;

begin process (a) -- this should be process (a, b)

begin c <= a and b;

end process ;

This situation is similar but not exactly the same as omitting a variable from an event control in a Verilog always statement. Some logic synthesizers accept the VHDL version of And_Bad but not the Verilog version or vice versa. To ensure that the VHDL simulation will match the behavior of the synthesized logic, the logic synthesizer usually checks the sensitivity list of a level-sensitive process and issues a warning if signals seem to be missing.

## 12.6.3 Multiplexers in VHDL

Multiplexers can be synthesized using a case statement (avoiding the VHDL reserved word 'select' ), as the following example illustrates:

entity Mux4 is port

(i: BIT_VECTOR(3 downto 0); sel: BIT_VECTOR(1 downto 0); s: out BIT);

end Mux4;

architecture Synthesis_1 of Mux4 is

begin process (sel, i) begin

case sel is

when "00" => s <= i(0); when "01" => s <= i(1);

when "10" => s <= i(2); when "11" => s <= i(3);

end case ;

end process ;

end Synthesis_1;

The following code, using a concurrent signal assignment is equivalent:

architecture Synthesis_2 of Mux4 is

begin with sel select s <=

i(0) when "00", i(1) when "01", i(2) when "10", i(3) when "11";

end Synthesis_2;

In VHDL the case statement must be exhaustive in either form, so there is no question of any priority in the choices as there may be in Verilog.

For larger MUXes we can use an array, as in the following example:

library IEEE; use ieee.std_logic_1164. all ;

entity Mux8 is port

(InBus : in STD_LOGIC_VECTOR(7 downto 0);

Sel : in INTEGER range 0 to 7;

OutBit : out STD_LOGIC);

end Mux8;

architecture Synthesis_1 of Mux8 is

begin process (InBus, Sel)

begin OutBit <= InBus(Sel);

end process ;

end Synthesis_1;

Most synthesis tools can infer that, in this case, Sel requires three bits. If not, you have to declare the signal as a STD_LOGIC_VECTOR ,

Sel : in STD_LOGIC_VECTOR(2 downto 0);

and use a conversion routine from the STD_NUMERIC package like this:

OutBit <= InBus(TO_INTEGER ( UNSIGNED (Sel) ) ) ;

At some point you have to convert from an INTEGER to BIT logic anyway, since you cannot connect an INTEGER to the input of a chip! The VHDL case , if , and select statements produce similar results. Assigning don’t care bits ( 'x' ) in these statements will make it easier for the synthesizer to optimize the logic.

## 12.6.4 Decoders in VHDL

The following code implies a decoder:

library IEEE;

use IEEE.STD_LOGIC_1164. all ; use IEEE.NUMERIC_STD. all ;

entity Decoder is port (enable : in BIT;

Din: STD_LOGIC_VECTOR (2 downto 0);

Dout: out STD_LOGIC_VECTOR (7 downto 0));

end Decoder;

architecture Synthesis_1 of Decoder is

begin

with enable select Dout <=

STD_LOGIC_VECTOR

(UNSIGNED'

(shift_left

("00000001", TO_INTEGER (UNSIGNED(Din))

)

)

)

when '1',

"11111111" when '0', "00000000" when others ;

end Synthesis_1;

There are reasons for this seemingly complex code:

• Line 1 declares the IEEE library. The synthesizer does not parse the VHDL code inside the library packages, but the synthesis company should be able to guarantee that the logic will behave exactly the same way as a simulation that uses the IEEE libraries and does parse the code.
• Line 2 declares the STD_LOGIC_1164 package, for STD_LOGIC types, and the NUMERIC_STD package for conversion and shift functions. The shift operators ( sll and so on–the infix operators) were introduced in VHDL-93, they are not defined for STD_LOGIC types in the 1164 standard. The shift functions defined in NUMERIC_STD are not operators and are called shift_left and so on. Some synthesis tools support NUMERIC_STD , but not VHDL-93.
• Line 10 performs a type conversion to STD_LOGIC_VECTOR from UNSIGNED .
• Line 11 is a type qualification to tell the software that the argument to the type conversion function is type UNSIGNED .
• Line 12 is the shift function, shift_left , from the NUMERIC_STD package.
• Line 13 converts the STD_LOGIC_VECTOR , Din , to UNSIGNED before converting to INTEGER . We cannot convert directly from STD_LOGIC_VECTOR to INTEGER .
• The others clause in line 18 is required by the logic synthesizer even though type BIT may only be '0' or '1' .

If we model a decoder using a process, we can use a case statement inside the process. A MUX model may be used as a decoder if the input bits are set at '1' (active-high decoder) or at '0' (active-low decoder), as in the following example:

library IEEE;

use IEEE.NUMERIC_STD. all ; use IEEE.STD_LOGIC_1164. all ;

entity Concurrent_Decoder is port (

enable : in BIT;

Din : in STD_LOGIC_VECTOR (2 downto 0);

Dout : out STD_LOGIC_VECTOR (7 downto 0));

end Concurrent_Decoder;

architecture Synthesis_1 of Concurrent_Decoder is

begin process (Din, enable)

variable T : STD_LOGIC_VECTOR(7 downto 0);

begin

if (enable = '1') then

T := "00000000"; T( TO_INTEGER (UNSIGNED(Din))) := '1';

Dout <= T ;

else Dout <= ( others => 'Z');

end if ;

end process ;

end Synthesis_1;

Notice that T must be a variable for proper timing of the update to the output. The else clause in the if statement is necessary to avoid inferring latches.

To add two n -bit numbers and keep the overflow bit, we need to assign to a signal with more bits, as follows:

library IEEE;

use IEEE.NUMERIC_STD. all ; use IEEE.STD_LOGIC_1164. all ;

port (A, B: in UNSIGNED(3 downto 0); C: out UNSIGNED(4 downto 0));

begin C <= ('0' & A) + ('0' & B);

end Synthesis_1;

Notice that both A and B have to be SIGNED or UNSIGNED as we cannot add STD_LOGIC_VECTOR types directly using the IEEE packages. You will get an error if a result is a different length from the target of an assignment, as in the following example (in which the arguments are not resized):

adder_1: begin C <= A + B;

Error : Width mis-match: right expression is 4 bits wide, c is 5 bits wide

The following code may generate three adders stacked three deep:

z <= a + b + c + d;

Depending on how the expression is parsed, the first adder may perform x = a + b , a second adder y = x + c , and a third adder z = y + d . The following code should generate faster logic with three adders stacked only two deep:

z <= (a + b) + (c + d);

## 12.6.6 Sequential Logic in VHDL

Sensitivity to an edge implies sequential logic in VHDL. A synthesis tool can locate edges in VHDL by finding a process statement that has either:

• no sensitivity list with a wait until statement
• a sensitivity list and test for 'EVENT plus a specific level

Any signal assigned in an edge-sensitive process statement should also be reset—but be careful to distinguish between asynchronous and synchronous resets. The following example illustrates these points:

library IEEE; use IEEE.STD_LOGIC_1164. all ; entity DFF_With_Reset is

port (D, Clk, Reset : in STD_LOGIC; Q : out STD_LOGIC);

end DFF_With_Reset;

architecture Synthesis_1 of DFF_With_Reset is

begin process (Clk, Reset) begin

if (Reset = '0') then Q <= '0'; -- asynchronous reset

elsif rising_edge(Clk) then Q <= D;

end if ;

end process ;

end Synthesis_1;

architecture Synthesis_2 of DFF_With_Reset is

begin process begin

wait until rising_edge(Clk);

-- This reset is gated with the clock and is synchronous:

if (Reset = '0') then Q <= '0'; else Q <= D; end if ;

end process ;

end Synthesis_2;

Sequential logic results when we have to “remember” something between successive executions of a process statement. This occurs when a process statement contains one or more of the following situations:

• A signal is read but is not in the sensitivity list of a process statement.
• A signal or variable is read before it is updated.
• A signal is not always updated.
• There are multiple wait statements.

Not all of the models that we could write using the above constructs will be synthesizable. Any models that do use one or more of these constructs and that are synthesizable will result in sequential logic.

## 12.6.7 Instantiation in VHDL

The easiest way to find out how to hand instantiate a component is to generate a structural netlist from a simple HDL input—for example, the following Verilog behavioral description (VHDL could have been used, but the Verilog is shorter):

`timescale 1ns/1ns

module halfgate (myInput, myOutput);

input myInput; output myOutput; wire myOutput;

assign myOutput = ~myInput;

endmodule

We synthesize this module and generate the following VHDL structural netlist:

library IEEE; use IEEE.STD_LOGIC_1164. all ;

library COMPASS_LIB; use COMPASS_LIB.COMPASS. all ;

--compass compile_off -- synopsys etc.

use COMPASS_LIB.COMPASS_ETC. all ;

--compass compile_on -- synopsys etc.

entity halfgate_u is

--compass compile_off -- synopsys etc.

generic (

myOutput_cap : Real := 0.01;

INSTANCE_NAME : string := "halfgate_u" );

--compass compile_on -- synopsys etc.

port ( myInput : in Std_Logic := 'U';

myOutput : out Std_Logic := 'U' );

end halfgate_u;

architecture halfgate_u of halfgate_u is

component in01d0

port ( I : in Std_Logic; ZN : out Std_Logic ); end component ;

begin

u2: in01d0 port map ( I => myInput, ZN => myOutput );

end halfgate_u;

--compass compile_off -- synopsys etc.

library cb60hd230d;

configuration halfgate_u_CON of halfgate_u is

for halfgate_u

for u2 : in01d0 use configuration cb60hd230d.in01d0_CON

generic map (

ZN_cap => 0.0100 + myOutput_cap,

INSTANCE_NAME => INSTANCE_NAME&"/u2" )

port map ( I => I, ZN => ZN);

end for ;

end for ;

end halfgate_u_CON;

--compass compile_on -- synopsys etc.

This gives a template to follow when hand instantiating logic cells. Instantiating a standard component requires the name of the component and its parameters:

component ASDFF

generic (WIDTH : POSITIVE := 1;

RESET_VALUE : STD_LOGIC_VECTOR := "0" );

port (Q : out STD_LOGIC_VECTOR (WIDTH-1 downto 0);

D : in STD_LOGIC_VECTOR (WIDTH-1 downto 0);

CLK : in STD_LOGIC;

RST : in STD_LOGIC );

end component ;

Now you have enough information to be able to instantiate both logic cells from a cell library and standard components. The following model illustrates instantiation:

library IEEE, COMPASS_LIB;

use IEEE.STD_LOGIC_1164. all ; use COMPASS_LIB.STDCOMP. all ;

entity Ripple_4 is

port (Trig, Reset: STD_LOGIC; QN0_5x: out STD_LOGIC;

Q : inout STD_LOGIC_VECTOR(0 to 3));

end Ripple_4;

architecture structure of Ripple_4 is

signal QN : STD_LOGIC_VECTOR(0 to 3);

component in01d1

port ( I : in Std_Logic; ZN : out Std_Logic ); end component ;

component in01d5

port ( I : in Std_Logic; ZN : out Std_Logic ); end component ;

begin

--compass dontTouch inv5x -- synopsys dont_touch etc.

-- Named association for hand-instantiated library cells:

inv5x: IN01D5 port map ( I=>Q(0), ZN=>QN0_5x );

inv0 : IN01D1 port map ( I=>Q(0), ZN=>QN(0) );

inv1 : IN01D1 port map ( I=>Q(1), ZN=>QN(1) );

inv2 : IN01D1 port map ( I=>Q(2), ZN=>QN(2) );

inv3 : IN01D1 port map ( I=>Q(3), ZN=>QN(3) );

-- Positional association for standard components:

--                           Q          D        Clk   Rst

d0: asDFF port map (Q (0 to 0), QN(0 to 0), Trig, Reset);

d1: asDFF port map (Q (1 to 1), QN(1 to 1), Q(0), Reset);

d2: asDFF port map (Q (2 to 2), QN(2 to 2), Q(1), Reset);

d3: asDFF port map (Q (3 to 3), QN(3 to 3), Q(2), Reset);

end structure;

• Lines 5 and 8 . Type STD_LOGIC_VECTOR must be used for standard component ports, because the standard components are defined using this type.
• Line 5 . Mode inout has to be used for Q since it has to be read/write and this is a structural model. You cannot use mode buffer since the formal outputs of the standard components are declared to be of mode out .
• Line 14 . This synthesis directive prevents the synthesis tool from removing the 5X drive strength inverter inv5x . This statement ties the code to a particular synthesis tool.
• Lines 16 20 . Named association for the hand-instantiated library cells. The names ( IN01D5 and IN01D1 ) and port names ( I and ZN ) come from the cell library data book or from a template (such as the one created for the IN01D1 logic cell). These statements tie the code to a particular cell library.
• Lines 23 26 . Positional port mapping of the standard components. The port locations are from the synthesis standard component library documentation. These asDFF standard components will be mapped to D flip-flop library cells. These statements tie the code to a particular synthesis tool.

You would receive the following warning from the logic synthesizer when it synthesizes this input code (entity Ripple_4 ):

Warning : Net has more than one driver: d3_Q[0]; connected to: ripple_4_p.q[3], inv3.I, d3.Q

There is potentially more than one driver on a net because Q was declared as inout . There are a total of four warnings of this type for each of the flip-flop outputs. You can check the output netlist to make sure that you have the logic you expected as follows (the Verilog netlist is shorter and easier to read):

`timescale 1ns / 10ps

module ripple_4_u (trig, reset, qn0_5x, q);

input trig; input reset; output qn0_5x; inout [3:0] q;

wire [3:0] qn; supply1 VDD; supply0 VSS;

in01d5 inv5x (.I(q[0]),.ZN(qn0_5x));

in01d1 inv0 (.I(q[0]),.ZN(qn[0]));

in01d1 inv1 (.I(q[1]),.ZN(qn[1]));

in01d1 inv2 (.I(q[2]),.ZN(qn[2]));

in01d1 inv3 (.I(q[3]),.ZN(qn[3]));

dfctnb d0(.D(qn[0]),.CP(trig),.CDN(reset),.Q(q[0]),.QN(\d0.QN ));

dfctnb d1(.D(qn[1]),.CP(q[0]),.CDN(reset),.Q(q[1]),.QN(\d1.QN ));

dfctnb d2(.D(qn[2]),.CP(q[1]),.CDN(reset),.Q(q[2]),.QN(\d2.QN ));

dfctnb d3(.D(qn[3]),.CP(q[2]),.CDN(reset),.Q(q[3]),.QN(\d3.QN ));

endmodule

## 12.6.8 Shift Registers and Clocking in VHDL

The following code implies a serial-in/parallel-out (SIPO) shift register:

library IEEE;

use IEEE.STD_LOGIC_1164. all ; use IEEE.NUMERIC_STD. all ;

entity SIPO_1 is port (

Clk : in STD_LOGIC;

SI : in STD_LOGIC; -- serial in

PO : buffer STD_LOGIC_VECTOR(3 downto 0)); -- parallel out

end SIPO_1;

architecture Synthesis_1 of SIPO_1 is

begin process (Clk) begin

if (Clk = '1' ) then PO <= SI & PO(3 downto 1); end if ;

end process ;

end Synthesis_1;

Here is the Verilog structural netlist that results ( dfntnb is a positive-edge–triggered D flip-flop without clear or reset):

module sipo_1_u (clk, si, po);

input clk; input si; output [3:0] po;

supply1 VDD; supply0 VSS;

dfntnb po_ff_b0 (.D(po[1]),.CP(clk),.Q(po[0]),.QN(\po_ff_b0.QN));

dfntnb po_ff_b1 (.D(po[2]),.CP(clk),.Q(po[1]),.QN(\po_ff_b1.QN));

dfntnb po_ff_b2 (.D(po[3]),.CP(clk),.Q(po[2]),.QN(\po_ff_b2.QN));

dfntnb po_ff_b3 (.D(si),.CP(clk),.Q(po[3]),.QN(\po_ff_b3.QN ));

endmodule

The synthesized design consists of four flip-flops. Notice that (line 6 in the VHDL input) signal PO is of mode buffer because we cannot read a signal of mode out inside a process. This is acceptable for synthesis but not usually a good idea for simulation models. We can modify the code to eliminate the buffer port and at the same time we shall include a reset signal, as follows:

library IEEE;

use IEEE.STD_LOGIC_1164. all ; use IEEE.NUMERIC_STD. all ;

entity SIPO_R is port (

clk : in STD_LOGIC ; res : in STD_LOGIC ;

SI : in STD_LOGIC ; PO : out STD_LOGIC_VECTOR(3 downto 0));

end ;

architecture Synthesis_1 of SIPO_R is

signal PO_t : STD_LOGIC_VECTOR(3 downto 0);

begin

process (PO_t) begin PO <= PO_t; end process ;

process (clk, res) begin

if (res = '0') then PO_t <= ( others => '0');

elsif (rising_edge(clk)) then PO_t <= SI & PO_t(3 downto 1);

end if ;

end process ;

end Synthesis_1;

Notice the following:

• Line 10 uses a temporary signal, PO_t , to avoid using a port of mode buffer for the output signal PO . We could have used a variable instead of a signal and the variable would consume less overhead during simulation. However, we must complete an assignment to a variable inside the clocked process (not in a separate process as we can for the signal). Assignment between a variable and a signal inside a single process creates its own set of problems.
• Line 11 is sensitive to the clock, clk , and the reset, res . It is not sensitive to PO_t or SI and this is what indicates the sequential logic.
• Line 13 uses the rising_edge function from the STD_LOGIC_1164 package.

The software synthesizes four positive-edge–triggered D flip-flops for design entity SIPO_R(Synthesis_1) as it did for design entity SIPO_1(Synthesis_1) . The difference is that the synthesized flip-flops in SIPO_R have active-low resets. However, the simulation behavior of these two design entities will be different. In SIPO_R , the function rising_edge only evaluates to TRUE for a transition from '0' or 'L' to '1' or 'H' . In SIPO_1 we only tested for Clk = '1' . Since nearly all synthesis tools now accept rising_edge and falling_edge , it is probably wiser to use these functions consistently.

## 12.6.9 Adders and Arithmetic Functions

If you wish to perform BIT_VECTOR or STD_LOGIC_VECTOR arithmetic you have three choices:

• Use a vendor-supplied package (there are no standard vendor packages—even if a company puts its own package in the IEEE library).
• Convert to SIGNED (or UNSIGNED ) and use the IEEE standard synthesis packages (IEEE Std 1076.3-1997).
• Use overloaded functions in packages or functions that you define yourself.

Here is an example of addition using a ripple-carry architecture:

library IEEE;

use IEEE.STD_LOGIC_1164. all ; use IEEE.NUMERIC_STD. all ;

in1, in2 : in BIT_VECTOR(3 downto 0) ;

mySum : out BIT_VECTOR(3 downto 0) ) ;

function DIY(L,R: BIT_VECTOR(3 downto 0)) return BIT_VECTOR is

variable sum:BIT_VECTOR(3 downto 0); variable lt,rt,st,cry: BIT;

begin cry := '0';

for i in L'REVERSE_RANGE loop

lt := L(i); rt := R(i); st := lt xor rt;

sum(i):= st xor cry; cry:= (lt and rt) or (st and cry);

end loop ;

return sum;

end ;

begin mySum <= DIY (in1, in2); -- do it yourself (DIY) add

end Behave_A;

This model results in random logic.

An alternative is to use UNSIGNED or UNSIGNED from the IEEE NUMERIC_STD or NUMERIC_BIT packages as in the following example:

library IEEE;

use IEEE.STD_LOGIC_1164. all ; use IEEE.NUMERIC_STD. all ;

in1, in2 : in UNSIGNED(3 downto 0) ;

mySum : out UNSIGNED(3 downto 0) ) ;

begin mySum <= in1 + in2; -- This uses an overloaded '+'.

end Behave_B;

In this case, the synthesized logic will depend on the logic synthesizer.

## 12.6.10  Adder/Subtracter and Don’t Cares

The following code models a 16-bit sequential adder and subtracter. The input signal, xin , is added to output signal, result , when signal addsub is high; otherwise result is subtracted from xin . The internal signal addout temporarily stores the result until the next rising edge of the clock:

library IEEE;

use IEEE.STD_LOGIC_1164. all ; use IEEE.NUMERIC_STD. all ;

xin : in UNSIGNED(15 downto 0);

result : out UNSIGNED(15 downto 0));

signal addout, result_t: UNSIGNED(15 downto 0);

begin

result <= result_t;

addout <= (xin + result_t) when '1',

(xin - result_t) when '0',

( others => '-') when others ;

process (clr, clk) begin

if (clr = '0') then result_t <= ( others => '0');

elsif rising_edge(clk) then result_t <= addout;

end if ;

end process ;

end Behave_A;

Notice the following:

• Line 11 is a concurrent assignment to avoid using a port of mode buffer .
• Lines 12 15 define an exhaustive list of choices for the selected signal assignment statement. The default choice sets the result to '-' (don’t care) to allow the synthesizer to optimize the logic.

Line 18 includes a reference to signal addout that could be eliminated by moving the selected signal assignment statement inside the clocked process as follows:

signal result_t: UNSIGNED(15 downto 0);

begin

result <= result_t;

process (clr, clk) begin

if (clr = '0') then result_t <= ( others => '0');

elsif rising_edge(clk) then

when '1' => result_t <= (xin + result_t);

when '0' => result_t <= (xin - result_t);

when others => result_t <= ( others => '-');

end case ;

end if ;

end process ;

end Behave_B;

This code is simpler than architecture Behave_A , but the synthesized logic should be identical for both architectures. Since the logic that results is an adder/subtracter followed by a register (bank of flip-flops) the Behave_A model more clearly reflects the hardware.