Najczęściej popełniane błędy w VHDL’u Ernest Jamro Kat. Elektroniki AGH
Wzory poprawnej syntezy ActiveHDL: Menu/Tools/Language Assistant: Synthesis Templates ISE Procject Navigator: Menu/Edit/Language Templates: VHDL / Synthesis Constructs / (Coding Example)
Poprawna składnia przerzutnika typu D process (CLK, RESET) begin if RESET='1' then – reset asynchroniczny DOUT <= '0'; elsif (CLK'event and CLK='1') then DOUT <= DIN; end if; end process;
Najczęstsze błędy process (CLK, RESET) – brak sygnału reset begin if RESET='1' then – reset asynchroniczny DOUT <= '0'; elsif (CLK'event and CLK='1') then DOUT <= DIN; end if; end process;
Przerzutnik D z Clock Enable (CE) process (<clock>, <reset>) begin if <reset>=‘1' then <output> <= '0'; elsif (<clock>'event and <clock>='1') then if <clock_enable> = '1' then -- lub inna logika synchroniczna <output> <= <input>; end if; end process;
Błędy: Przerzutnik z CE process (<clock>, <reset>, <clock_enable>) begin if <reset>='0' then <output> <= '0'; elsif (<clock>'event and <clock>='1‘ and <clock_enable> = '1' ) <output> <= <input>; end if; end process;
Błąd: Wymuszenie tego samego sygnału w dwóch procesach A0: process(...) begin .... if ( ...) a<= ‘0’; end if; end process; A1: process(...) begin a<= ‘1’;
Przerzutnik typu Latch process (GATE, DIN) begin if GATE='1' then --GATE active High DOUT <= DIN; end if; end process; Przerzutników Latch należy raczej unikać – z reguły powstają one w wyniku błędu a nie zamierzonego projektowania
Multiplekser (lub inna logika kombinacyjna) process (SEL, A, B, C) begin case SEL is when "00" => MUX_OUT <= A; when "01" => MUX_OUT <= B; when "10" => MUX_OUT <= C; when others => MUX_OUT <= 'X'; end case; end process;
Multiplekser Błędy process (SEL, A, B, C) – brak jednego z sygnałów wejściowych (latch) begin case SEL is when "00" => MUX_OUT <= A; when "01" => MUX_OUT <= B; when "10" => MUX_OUT <= C; when others => MUX_OUT <= 'X'; end case; end process;
Process – magiczne słowo Poza procesem nie można używać następujących składni: If ... Then ... Elsif ... End if For .... Loop Uwaga: Wiele operacji (szczególnie logiki kombinacyjnej) można umieścić poza procesem
Alternatywne rozwiązanie Multiplekser Mux_out<= A when sel = "00" else B when sel = "01" else C when sel = "10" else '-'; LUB WITH sel SELECT Mux_out <= A when "00", B when "01", C when "10" '-' when others; inB AFTER 10 NS WHEN OTHERS;
Umiejscawianie komponentów 1 entity FULL_ADDER port ( a, b, cin: in std_logic; s, cout: out std_logic); end FULL_ADDER; architecture arch of FULL_ADDER is begin s<= a xor b xor c; cout<= a when a=b else cin; end arch;
Umiejscawianie komponentów 2 entity MY_ADDER generic (width: integer); port ( a, b: in std_logic_vector(0 to width-1); s: out std_logic_vector(0 to width-1); end MY_ADDER; architecture arch of MY_ADDER is component FULL_ADDER -- deklaracja komponentu port ( a, b, cin: in std_logic; s, cout: out std_logic); end component; signal carry: std_logic_vector(0 to dwidth); begin
Umiejscawianie komponentów 3 Ciąg dalszy z poprzedniej strony (rozwiązanie nieoptymalne) Carry(0)<= ‘0’; Gi: for i in 0 to width-1 generate – wielokrotne użycie elementu! g: full_adder port map (a=> a(i), b=> b(i), cin=> carry(i), cout=> carry(i+1), s=> s(i)); end generate; end arch; Lepsze rozwiązanie (użyta dedykowana logika dodająca) s<= a+ b;
Wartości domyślne, uaktualnienie modułów Stary komponent component and_gate port ( din1, din2: in std_logic; dout: out std_logic); end component; Nowy komponent component and_gate generic (invert_output: integer:= 0); -- dodanie dodatkowego parametru port ( din1, din2, din3: in std_logic:= ‘1’; -- wartość domyślna ‘1’
Użycie zmodyfikowanego komponentu Stary kod używa nowego elementu z nowymi wartościami w formie domyślnej A: and_gate port map (din1=> a, din2=> b, dout => y); Równoważne i zalecane w nowym kodzie: generic map (invert_output=>0) port map (din1=> a, din2=> b, din3=> ‘1’, dout => y);
Po co wartości domyślne Rozważany element jest elementem nadrzędnym podczas symulacji lub implementacji Zgodność z poprzednimi wersjami tego samego elementu bez konieczności zmiany całego starego kodu
Ustawianie wartości parametrów Architecture arch of my_gate is component and_gate generic (invert_output: integer:= 1); -- nie zmieniamy wartości parametru tutaj port ( din1, din2, din3: in std_logic:= ‘0’; -- nie zmieniamy wartości domyślnej tutaj dout: out std_logic); end component; begin a: and_gate -- użycie elementu generic map (invert_output=> 1) -- umieszczamy wartość każdego parametru port map (din1=> a, din2=> b, din3=> ‘0’); -- staramy się określić wartość każdego wejścia End arch;
Dodawanie bibliotek library ieee; use ieee.std_logic_1164.all; -- użycie std_logic use ieee.std_logic_unsigned.all; -- każda wartość std_logic_vector jest traktowana jako integer bez znaku use ieee.std_logic_signed.all; -- każda wartość std_logic_vector jest traktowana jako integer ze znaku Nie można równocześnie użyć obu bibliotek: std_logic_unsigned oraz std_logic_signed. W tym wypadku należy użyć biblioteki: use ieee.std_logic_arith.all; oraz zamiast słowa kluczowego std_logic_vector należy użyć słów unsigned lub signed (wada: konieczność używania konwersji std_logic_vector unsigned (lub signed))
Programowanie pod kątem sprzętu Pamięć RAM 16x1 (distributed RAM) type mem_type is array (0 to 15) od std_logic_vector(0 to 7) signal mem: mem_type; begin process (<clock>) if (<clock>'event and <clock> = '1') then if (<write_enable> = '1') then mem(conv_integer(<address>)) <= <input_data>; end if; end if; end process; <ram_output> <= mem(conv_integer(<address>));
Programowanie pod kątem sprzętu Pamięć blokowa BRAM (dwuportowa) PortA: process (<clockA>) begin if (<clockA>'event and <clockA> = '1') then if (<write_enableA> = '1') then <ram_name>(conv_integer(<addressA>)) <= <input_dataA>; end if; <addressA_sig> <= <addressA>; end process; PortB: process (<clockB>) begin if (<clockB>'event and <clockB> = '1') then <addressB_sig> <= <addressB>; <ram_outputA> <= <ram_name>(conv_integer(<addressA_sig>)); <ram_outputB> <= <ram_name>(conv_integer(<addressB_sig>));
Programowanie pod kątem sprzętu Biblioteka: unisim a pamięć blokowa BRAM entity RAMB16_S36_S36 is generic ( INIT_00 : bit_vector := X"0000000000000000000000000000000000000000000000000000000000000000"; .... INIT_3F : bit_vector := X"0000000000000000000000000000000000000000000000000000000000000000" ); port( DOA : out STD_LOGIC_VECTOR (31 downto 0); DOB : out STD_LOGIC_VECTOR (31 downto 0); DOPA : out STD_LOGIC_VECTOR (3 downto 0); DOPB : out STD_LOGIC_VECTOR (3 downto 0); ADDRA : in STD_LOGIC_VECTOR (8 downto 0); ADDRB : in STD_LOGIC_VECTOR (8 downto 0); CLKA : in STD_ULOGIC; CLKB : in STD_ULOGIC; DIA : in STD_LOGIC_VECTOR (31 downto 0); DIB : in STD_LOGIC_VECTOR (31 downto 0); DIPA : in STD_LOGIC_VECTOR (3 downto 0); DIPB : in STD_LOGIC_VECTOR (3 downto 0); ENA : in STD_ULOGIC; ENB : in STD_ULOGIC; SSRA : in STD_ULOGIC; SSRB : in STD_ULOGIC; WEA : in STD_ULOGIC; WEB : in STD_ULOGIC
Programowanie pod kątem sprzętu Biblioteka unisim: Pętla DLL (delay lock loop – działająca podobnie jak PLL) entity CLKDLL is port ( CLK0 : out std_ulogic := '0'; CLK180 : out std_ulogic := '0'; CLK270 : out std_ulogic := '0'; CLK2X : out std_ulogic := '0'; CLK90 : out std_ulogic := '0'; CLKDV : out std_ulogic := '0'; LOCKED : out std_ulogic := '0'; CLKFB : in std_ulogic := '0'; CLKIN : in std_ulogic := '0'; RST : in std_ulogic := '0‘ )
Programowanie pod kątem sprzętu library UNISIM; use unisim.all; -- for global set reset signal component ROC port ( O : out std_ulogic := '1' ); end component Każdy przerzutnik powinien być zerowany (ustawiany) asynchronicznie tym sygnałem – jest to potrzebne zarówno do celów symulacyjnych jak i dla potrzeb ustawiania (wartość ‘1’) po procesie konfiguracji.
Reset synchroniczny i asynchroniczny Reset asynchroniczny należy używać jako reset inicjalizujący pracę układu FPGA po konfiguracji – jest on wspomagany sprzętowo Reset synchroniczny (np. opb_rst) należy używać jako sygnał zerujący w pozostałych przypadkach np. podczas zerowania liczników, powtórnej inicjalizacji automatów, itd. Nie należy mieszać resetów synchronicznych i asynchronicznych
Poziomy logiczne Standard std_logic zawiera wiele poziomów nie tylko ‘0’, ‘1’, co może powodować inne zachowanie układu podczas symulacji funkcjonalnej, po syntezie i w rzeczywistym układzie. Przykład: if ce=‘1’ then Q<= D; end if; Co się stanie jeżeli dana wejściowa ce jest typu: ‘H’, ‘X’, ‘Z’ itd
Operacje na wektorach - przesunięcia signal a, b: std_logic_vector(dwidth-1 downto 0); -- deklaracja wektorów, dwidth- szerokość wektora Przesuniecie o jeden bit w lewo (pomnożenie przez 2): a<= b(dwidth-2 downto 0) & ‘0’ Przesuwający o jeden bit w prawo (dzielenie liczb dodatnich przez 2): a<= ‘0’ & ‘b(dwidth-1 downto 1) ; Dzielenie liczb w kodzie uzupełnień do 2 przez 2: a<= b(dwidth-1) & b(dwidth-1) & b(dwidth-2 downto 1); -- kopiowanie bitu znaku b(dwidth-1) Przesunięcie o n-bitów w lewo (n- constant lub generic): a(dwidth-1 downto n)<= b(dwidth-1-n downto 0); a(n-1 downto 0)<= (others=>’0’); Podzielenie przez 2n liczby w kodzie U2: a(dwidth-1 downto dwidth-n-1)<= (others=> b(dwidth-1)); a(dwidth-2-n downto 0)<= b(dwidth-2 downto n);
Operacje logiczne na wektorach signal c, a, b: std_logic_vector(dwidth-1 downto 0); -- deklaracja wektorów c<= a and b; Równoważne: Gi: for i in 0 to dwidth-1 generate c(i)<= a(i) and b(i); end generate;
Operacje logiczne w ramach jednego wektora Operacje logicze w ramach jednego wektora: library IEEE; use IEEE.STD_LOGIC_1164.all; use IEEE.STD_LOGIC_MISC.all; -- dodatkowa biblioteka signal a: std_logic_vector(dwidth-1 downto 0); signal y: std_logic; y<= OR_REDUCE(a); Równoważne: y<= a(0) or a(1) or a(2) or ..... or a(dwidth-1); Process(a) variable b: std_logic; begin b:= ‘0’; for i in 0 to dwidth-1 loop b:= b or a(i); end loop; end process;