library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_arith.all; use IEEE.std_logic_unsigned.all; entity parport is port ( CLK : in std_logic; RESET : in std_logic; ADDRESS_BUS : in std_logic_vector(7 downto 0); DATA_BUS : in std_logic_vector(7 downto 0); BUSY : out std_logic; TR_ERROR : out std_logic; PP_DATA : out std_logic_vector(7 downto 0); PP_HANDSHAKE_OUT : out std_logic; PP_HANDSHAKE_IN : in std_logic ); end parport; architecture behavior of parport is type stato is (idle, s1, s2, error); signal present_state, next_state : stato; signal count,ncount : unsigned(7 downto 0); signal start_transmission : std_logic; signal timeout : std_logic; signal sampled_data_bus : std_logic_vector(7 downto 0); signal state_s1, sampled_state_s1 : std_logic; signal start_counter, enable_counter : std_logic; begin -- behavior -- macchina a stati---------------------------------------------------- -- parte sequenziale della macchina a stati, -- calcolo delle uscite e dello stato futuro process(present_state, start_transmission, timeout) begin case present_state is when idle => PP_DATA <= (others => 'Z'); BUSY <= '0'; -- inactive TR_ERROR <= '0'; -- inactive PP_HANDSHAKE_OUT <= '1'; -- inactive enable_counter <= '0'; -- inactive if PP_HANDSHAKE_IN = '0' then next_state <= error; -- violazione del protocollo di comunicazione elsif start_transmission = '1' then next_state <= s1; -- inizio la trasmissione else next_state <= idle; -- resto in questo stato end if; when s1 => PP_DATA <= sampled_data_bus; BUSY <= '1'; TR_ERROR <= '0'; PP_HANDSHAKE_OUT <= '0'; enable_counter <= '1'; if (PP_HANDSHAKE_IN = '0') then next_state <= s2; -- passo alla fase successiva del protocollo elsif timeout = '1' then next_state <= error; -- in caso di timeout vado nello stato di errore else next_state <= s1; -- resto in questo stato end if; when s2 => -- metto le linee dati in alta impedenza -- perchč in questo protocollo quando -- l'ack si attiva significa che il dato -- č stato prelevato. PP_DATA <= (others => 'Z'); BUSY <= '1'; TR_ERROR <= '0'; -- inizio a concludere l'handshake PP_HANDSHAKE_OUT <= '1'; enable_counter <= '1'; if PP_HANDSHAKE_IN = '1' then next_state <= idle; -- comunicazione terminata elsif timeout = '1' then next_state <= error; -- segnalo errore in caso di timeout else next_state <= s2; -- resto in questo stato end if; when error => -- disattivo tutto, tranne la segnalazione di errore PP_DATA <= (others => 'Z'); TR_ERROR <= '1'; BUSY <= '0'; PP_HANDSHAKE_OUT <= '1'; enable_counter <= '0'; if start_transmission = '1' then next_state <= s1; -- dallo stato di errore esco con una nuova operazione else next_state <= error; end if; when others => -- qualora il sistema si trovasse in uno stato non codificato -- occorre farlo passare in uno stato codificato PP_DATA <= (others => 'Z'); TR_ERROR <= '1'; BUSY <= '1'; PP_HANDSHAKE_OUT <= '1'; enable_counter <= '0'; next_state <= idle; end case; end process; -- parte sequenziale della macchina a stati, commutazione -- dallo stato presente al nuovo stato process(clk) begin if clk'event and clk='1' then if reset = '1' then present_state <= idle; else present_state <= next_state; end if; end if; end process; --------------------------------------------------------------------------- -- registro che campiona la parola dati dal bus dati----------------------- process(clk) begin if clk'event and clk = '1' then if reset = '1' then sampled_data_bus <= (others => '0'); else if start_transmission = '1' then sampled_data_bus <= data_bus; end if; end if; end if; end process; ---------------------------------------------------------------------------- -- decodifico l'inizio trasmissione------------------------------------------ start_transmission <= '1' when address_bus = "01000000" else '0'; ----------------------------------------------------------------------------- -- contatore per implementare il timeout------------------------------------- --decodifico lo stato s1, in cui devo iniziare a contare il timeout state_s1 <= '1' when present_state = s1 else '0'; -- genero una versione ritardata di un ciclo del segnale state_s1 process(clk) begin if clk'event and clk = '1' then if reset = '1' then sampled_state_s1 <= '0'; else sampled_state_s1 <= state_s1; end if; end if; end process; -- questo segnale risulta attivo nel primo clock dello stato s1, e nel ciclo -- successivo all'ultimo clock di s1, che corrisponde al primo clock di s2 (o di -- error). E' intuitivo vederlo osservando le forme d'onda di state_s1 e sampled_state_s1 -- Verrā utilizzato per azzerare il contatore, visto che il timeout -- deve essere azionato all'inizio di s1 e all'inizio di s2. start_counter <= (state_s1 and not(sampled_state_s1)) or (not(state_s1) and sampled_state_s1); -- incremento il valore del contatore, quando necessario process(present_state, count) begin if (enable_counter = '1') then ncount <= count + conv_unsigned(1,8); else ncount <= count; end if; end process; -- rilevo il timeout, 40 cicli = 1 usec process(count) begin if count = conv_unsigned(40,8) then timeout <= '1'; else timeout <= '0'; end if; end process; -- contatore sincrono, commuta sul fronte positivo di clock process(clk,reset) begin if clk'event and clk = '1' then if reset = '1' or start_counter = '1' then count <= conv_unsigned(0,8); else count <= ncount; end if; end if; end process; end behavior;