[ Chapter start ] [ Previous page ] [ Next page ] 12.6 VHDL and Logic SynthesisMost 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:
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 ResetYou 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. 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 VHDLIn 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; architecture Synthesis_Bad of And_Bad is begin process (a) -- this should be process (a, b) 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 VHDLMultiplexers can be synthesized using a case statement (avoiding the VHDL reserved word 'select' ), as the following example illustrates: (i: BIT_VECTOR(3 downto 0); sel: BIT_VECTOR(1 downto 0); s: out BIT); architecture Synthesis_1 of Mux4 is when "00" => s <= i(0); when "01" => s <= i(1); when "10" => s <= i(2); when "11" => s <= i(3); The following code, using a concurrent signal assignment is equivalent: architecture Synthesis_2 of Mux4 is i(0) when "00", i(1) when "01", i(2) when "10", i(3) when "11"; 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 ; (InBus : in STD_LOGIC_VECTOR(7 downto 0); Sel : in INTEGER range 0 to 7; architecture Synthesis_1 of Mux8 is 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 VHDLThe following code implies a decoder: 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)); architecture Synthesis_1 of Decoder is ("00000001", TO_INTEGER (UNSIGNED(Din)) "11111111" when '0', "00000000" when others ; There are reasons for this seemingly complex code:
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: use IEEE.NUMERIC_STD. all ; use IEEE.STD_LOGIC_1164. all ; entity Concurrent_Decoder is port ( Din : in STD_LOGIC_VECTOR (2 downto 0); Dout : out STD_LOGIC_VECTOR (7 downto 0)); architecture Synthesis_1 of Concurrent_Decoder is variable T : STD_LOGIC_VECTOR(7 downto 0); T := "00000000"; T( TO_INTEGER (UNSIGNED(Din))) := '1'; else Dout <= ( others => 'Z'); 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. 12.6.5 Adders in VHDLTo add two n -bit numbers and keep the overflow bit, we need to assign to a signal with more bits, as follows: 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)); architecture Synthesis_1 of Adder_1 is begin C <= ('0' & A) + ('0' & B); 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): 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: 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: 12.6.6 Sequential Logic in VHDLSensitivity to an edge implies sequential logic in VHDL. A synthesis tool can locate edges in VHDL by finding a process statement that has either:
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); 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; architecture Synthesis_2 of DFF_With_Reset is -- This reset is gated with the clock and is synchronous: if (Reset = '0') then Q <= '0'; else Q <= D; end if ; 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:
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 VHDLThe 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): module halfgate (myInput, myOutput); input myInput; output myOutput; wire myOutput; 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. --compass compile_off -- synopsys etc. INSTANCE_NAME : string := "halfgate_u" ); --compass compile_on -- synopsys etc. port ( myInput : in Std_Logic := 'U'; myOutput : out Std_Logic := 'U' ); architecture halfgate_u of halfgate_u is port ( I : in Std_Logic; ZN : out Std_Logic ); end component ; u2: in01d0 port map ( I => myInput, ZN => myOutput ); --compass compile_off -- synopsys etc. configuration halfgate_u_CON of halfgate_u is for u2 : in01d0 use configuration cb60hd230d.in01d0_CON ZN_cap => 0.0100 + myOutput_cap, INSTANCE_NAME => INSTANCE_NAME&"/u2" ) --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: 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); 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: use IEEE.STD_LOGIC_1164. all ; use COMPASS_LIB.STDCOMP. all ; port (Trig, Reset: STD_LOGIC; QN0_5x: out STD_LOGIC; Q : inout STD_LOGIC_VECTOR(0 to 3)); architecture structure of Ripple_4 is signal QN : STD_LOGIC_VECTOR(0 to 3); port ( I : in Std_Logic; ZN : out Std_Logic ); end component ; port ( I : in Std_Logic; ZN : out Std_Logic ); end component ; --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: 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);
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): 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 )); 12.6.8 Shift Registers and Clocking in VHDLThe following code implies a serial-in/parallel-out (SIPO) shift register: use IEEE.STD_LOGIC_1164. all ; use IEEE.NUMERIC_STD. all ; SI : in STD_LOGIC; -- serial in PO : buffer STD_LOGIC_VECTOR(3 downto 0)); -- parallel out architecture Synthesis_1 of SIPO_1 is if (Clk = '1' ) then PO <= SI & PO(3 downto 1); end if ; 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; 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 )); 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: use IEEE.STD_LOGIC_1164. all ; use IEEE.NUMERIC_STD. all ; clk : in STD_LOGIC ; res : in STD_LOGIC ; SI : in STD_LOGIC ; PO : out STD_LOGIC_VECTOR(3 downto 0)); architecture Synthesis_1 of SIPO_R is signal PO_t : STD_LOGIC_VECTOR(3 downto 0); process (PO_t) begin PO <= PO_t; end process ; if (res = '0') then PO_t <= ( others => '0'); elsif (rising_edge(clk)) then PO_t <= SI & PO_t(3 downto 1);
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 FunctionsIf you wish to perform BIT_VECTOR or STD_LOGIC_VECTOR arithmetic you have three choices:
Here is an example of addition using a ripple-carry architecture: 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) ) ; architecture Behave_A of Adder4 is 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; lt := L(i); rt := R(i); st := lt xor rt; sum(i):= st xor cry; cry:= (lt and rt) or (st and cry); begin mySum <= DIY (in1, in2); -- do it yourself (DIY) add 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: use IEEE.STD_LOGIC_1164. all ; use IEEE.NUMERIC_STD. all ; in1, in2 : in UNSIGNED(3 downto 0) ; mySum : out UNSIGNED(3 downto 0) ) ; architecture Behave_B of Adder4 is begin mySum <= in1 + in2; -- This uses an overloaded '+'. In this case, the synthesized logic will depend on the logic synthesizer. 12.6.10 Adder/Subtracter and Don’t CaresThe 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: use IEEE.STD_LOGIC_1164. all ; use IEEE.NUMERIC_STD. all ; entity Adder_Subtracter is port ( xin : in UNSIGNED(15 downto 0); clk, addsub, clr: in STD_LOGIC; result : out UNSIGNED(15 downto 0)); architecture Behave_A of Adder_Subtracter is signal addout, result_t: UNSIGNED(15 downto 0); addout <= (xin + result_t) when '1', ( others => '-') when others ; if (clr = '0') then result_t <= ( others => '0'); elsif rising_edge(clk) then result_t <= addout;
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: architecture Behave_B of Adder_Subtracter is signal result_t: UNSIGNED(15 downto 0); if (clr = '0') then result_t <= ( others => '0'); when '1' => result_t <= (xin + result_t); when '0' => result_t <= (xin - result_t); when others => result_t <= ( others => '-'); 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. [ Chapter start ] [ Previous page ] [ Next page ] |
© 2024 Internet Business Systems, Inc. 670 Aberdeen Way, Milpitas, CA 95035 +1 (408) 882-6554 — Contact Us, or visit our other sites: |
|
Privacy PolicyAdvertise |