Synthese av VHDL - Oppdatert
Syntetiseringen av VHDL kode
Vi ønsker å syntisere koden for å lage beskrivelse av koden tilpasset en FPGA-chip. Vi skal først syntisere koden med Quartus. Deretter skal vi simulere denne koden og sammenligne med den opprinnelige koden uten timing-informasjon. Vi bruker en ALU som eksempel.
Det er viktig at vi bruker Quartus og ModelSim fra samme utgivelse om vi ikke ønsker å kompilere våre egne simuleringsbibliotek. Du kan installere Quartus og ModelSim gratis og bruke lisens-server på UiB sitt nettverk. Vi bruker Quartus Prime 15.1 og ModelSim 10.4b.
Quartus
Lag et nytt prosjekt, kall det add_sub_alu og velg en passende mappe. Velg empty project, og deretter legg til add_sub_alu.vhd. Velg en CycloneIV-chip. Vi valgte EP4CE115F29. Det er chipen på Terasic DE2-115. Pass på at add_sub_alu.vhd er valgt som top-level og trykk så Start Compilation (Ctrl-L). Dette gjør at Quartus går gjennom alle stegene for å produsere filene vi er ute etter. I simulation/modelsim/ finner vi nå add_sub_alu.vho og add_sub_alu_vhd.sdo. Vho-filen(VHDL Output File) er filen som inneholder nå det opprinnelige designet, men også mange nye moduler med cycloneive-prefix. Disse entitetene beskriver ressurser på den fysiske FPGA-chipen. Sdo-filen(Standard Delay Format Output File) inneholder detaljer om delay fra hver modul på chippen.
Modelsim
Start opp Modelsim, lag nytt prosjekt og legg til vhdl fila for designet add_sub_alu.vhd og testbenken alu_tb.vhd. Så legg vi til fila som Quartus generte i prosjektdir til simulation/modelsim/add_sub_alu.vho.
Den Quartus-genererte filen vil ha samme entitetsnavn som den opprinnelige, og vil derfor ikke kunne simuleres samtid. Vi endrer derfor den genererte filen til å ha entiteten 'add_sub_alu_synth' og architecturen 'structure' (i vårt tilfelle endret den seg til structure automatisk).
Vi kan nå kompilere filene våre og deretter simulere testbenken. I testbenken vår ser vi at vi har kalt den syntiserte entiteten alu_synt. Dette skal vi bruke nå.
Simulering med timing
Velg Start Simulation - deretter work og alu_tb. Se så på SDF-fanen. Legg til add_sub_alu_vhd.sdo generert av Quartus tidligere. Under apply to region må du skrive: /alu_tb/alu_synt
Dette er altså området vi ønsker at sdo-filen skal gi informasjon om. Trykk så OK.
add wave * run -all
Vi kan nå sammenligne utsignalene for den usyntiserte og den syntiserte ALU-komponenten.
Konklusjon
Vi ser at vi får masse feil før 50 ns. Dette skyldes at den opprinnelige komponenten ikke har definerte signaler før de første klokkeflankene, mens den syntiserte komponenten ikke kan ha udefinerte signaler. Senere får vi feil når signalene skifter. Dette skyldes at den syntiserte komponenten bruker lengre tid på å sende ut riktig signal, og vil også glitche mellom verdier før den stabiliserer seg på riktig verdi.
Kode
Kode til add_sub_alu.vhdl
LIBRARY ieee; USE ieee.std_logic_1164.All; USE ieee.std_logic_unsigned.all; ENTITY add_sub_alu IS PORT (clk, rst : IN std_logic; enable_in : IN std_logic; start : IN std_logic; enable : IN std_logic; do_add : IN std_logic; do_subtract : IN std_logic; do_hold : IN std_logic; data_in : IN std_logic_vector(3 DOWNTO 0); data_out : OUT std_logic_vector (3 DOWNTO 0) BUS); END add_sub_alu; ARCHITECTURE algorithm OF add_sub_alu IS TYPE states IS (hold, reset, add, subtract); SIGNAL state_var : states; SIGNAL reg, int_reg : std_logic_vector(3 DOWNTO 0); SIGNAL latched_data_in: std_logic_vector(3 DOWNTO 0); BEGIN latch: PROCESS (enable_in, data_in)is BEGIN IF (enable_in = '1') THEN latched_data_in <= data_in; END IF; END PROCESS latch; fsm: PROCESS (clk, rst) is BEGIN IF (rst = '0') THEN state_var <= reset; ELSIF (clk'EVENT AND clk = '1') THEN CASE state_var IS WHEN hold => IF (start = '1') THEN state_var <= reset; END IF; WHEN reset => IF (do_add = '1') THEN state_var <= add; ELSIF (do_subtract = '1') THEN state_var <= subtract; END IF; WHEN add => IF (do_hold = '1') THEN state_var <= hold; ELSIF (do_subtract = '1') THEN state_var <= subtract; END IF; WHEN subtract => IF (do_hold = '1') THEN state_var <= hold; ELSIF (do_add = '1') THEN state_var <= add; END IF; WHEN OTHERS => state_var <= reset; END CASE; END IF; END PROCESS fsm; alu: PROCESS (state_var, latched_data_in, reg)is BEGIN CASE state_var IS WHEN add => int_reg <= reg + latched_data_in; WHEN subtract => int_reg <= reg - latched_data_in; WHEN reset => int_reg <= "0000"; WHEN hold => int_reg <= reg; WHEN OTHERS => int_reg <= reg; END CASE; END PROCESS alu; mem: PROCESS (clk) is BEGIN IF (clk'EVENT AND clk = '1') THEN reg <= int_reg; END IF; END PROCESS mem; tri: PROCESS (enable, reg) is BEGIN FOR i IN 3 DOWNTO 0 LOOP IF (enable = '1') THEN data_out(i) <= reg(i); ELSE data_out(i) <= 'Z'; END IF; END LOOP; END PROCESS tri; END algorithm;
Koden til alu_tb.vhdl
library ieee; use ieee.std_logic_1164.all; library work; use work.all; entity alu_tb is end entity alu_tb; architecture struct of alu_tb is --Deklaring av signal som skal koblast til komponentane. --Alle innsignal er felles, medan vi har 2 forskjellige utsignal. signal clk, reset : std_logic; signal enable_in : std_logic; signal start : std_logic; signal enable : std_logic; signal do_add : std_logic; signal do_subtract : std_logic; signal do_hold : std_logic; signal data_in : std_logic_vector(3 downto 0); signal data_out : std_logic_vector(3 downto 0); signal data_out_synt : std_logic_vector(3 downto 0); begin --Deklarer komponenten alu. alu : entity add_sub_alu(algorithm) --Kobler signala til den opprinnelige komponenten. port map ( clk => clk, rst => reset, enable_in => enable_in, start => start, enable => enable, do_add => do_add, do_subtract => do_subtract, do_hold => do_hold, data_in => data_in, data_out => data_out); --Deklarer komponenten alu_synt. alu_synt : entity add_sub_alu_synth(structure) --Kobler signala til den synthiserte komponenten. port map ( clk => clk, rst => reset, enable_in => enable_in, start => start, enable => enable, do_add => do_add, do_subtract => do_subtract, do_hold => do_hold, data_in => data_in, data_out => data_out_synt); --Klokkegenerator clock_gen : process begin clk <= '0', '1' after 50 ns; wait for 100 ns; end process clock_gen; --Setter testvektorane. reset <= '0', '1' after 60 ns; enable <= '1', '0' after 900 ns; enable_in <= '1', '0' after 400 ns; start <= '1', '0' after 300 ns; do_add <= '1', '0' after 660 ns; do_subtract <= '0'; do_hold <= '0'; data_in <= X"3"; --Test process for å samanlikne utsignala kvart nanosekund. test : process begin wait for 1 ns; assert (data_out = data_out_synt) report "Data ut er ulik" severity Error; end process test; end;