OpenCores
URL https://opencores.org/ocsvn/opb_usblite/opb_usblite/trunk

Subversion Repositories opb_usblite

[/] [opb_usblite/] [trunk/] [pcores/] [opb_usblite_v1_00_a/] [hdl/] [vhdl/] [usb_transact.vhdl] - Rev 2

Compare with Previous | Blame | View Log

--
--  USB 2.0 Transaction-level logic
--
--  This entity deals with transactions. A transaction consists of up to
--  three packets: token, data, handshake. This component supports four
--  transaction types:
--    * IN    (device-to-host bulk/interrupt/control transfer)
--    * OUT   (host-to-device bulk/interrupt/control transfer)
--    * SETUP (host-to-device control operation)
--    * PING  (flow control for host-to-device bulk/control transfer, HS only)
--  Isochronous transactions are not supported.
--
--  The low-level interface signals are named P_xxx and connect to
--  the usb_packet component.
--
--  The application interface signals are named T_xxx and operate as
--  follows:
--
--    * At the start of a transaction, either T_IN, T_OUT, T_SETUP or T_PING
--      rises to 1, indicating the transaction type.  At the same time,
--      T_ENDPT is set to the endpoint number for this transaction.
--      These signals are held for the duration of the transaction.
--
--  OUT and SETUP transactions:
--    * Each incoming byte is put on RXDAT and announced by asserting RXRDY.
--      These signals are valid for only one clock cycle.
--    * OSYNC is set to the transmitter's sync bit and held until the end
--      of the transaction.
--    * The last two bytes are CRC bytes; these should be ignored.
--    * Successfull completion is indicated by asserting T_FIN for one cycle.
--    * Receive errors are indicated by deasserting T_OUT/T_SETUP without
--      ever asserting T_FIN. In this case, the application must discard any
--      data already accepted during this transaction.
--    * It is probably safe to assume that the first assertion of T_RXRDY
--      does not immediately coincide with the rising T_OUT/T_SETUP signal.
--      The implementation of usb_control and usb_serial depend on this
--      assumption. The assumption may be false if PHY_RXACTIVE is low for
--      only one clock between token and data, and re-assertion of PHY_RXACTIVE
--      coincides with assertion of PHY_RXVALID. This is not explicitly
--      prohibited in the UTMI spec, but it just seems extremely unlikely.
--
--  OUT transactions:
--    * If the application is not ready to accept data, it should assert
--      either NAK or STALL. These signals must be set up as soon as the last
--      byte of the packet has been received, and kept stable until the end
--      of the transaction.
--    * If the application is ready to accept this OUT packet, but not ready
--      to accept a subsequent OUT packet, it may assert T_NYET. This signal
--      must be set up as soon as the last byte of the packet has been received
--      and kept stable until the end of the transaction. NYET is only valid
--      in high speed mode; in full speed mode, this entity will ignore the
--      NYET signal and send ACK instead.
--    * Note: NAK/STALL/NYET must not be used during SETUP transactions;
--      the standard specifies that SETUP transactions must always be ACK-ed
--      and errors reported during the subsequent data transaction.
--
--  IN transactions:
--    * The application should assert SEND, put the sync bit on ISYNC
--      and put the first byte on TXDAT. The component will assert TXRDY
--      to acknowledge each byte; in the following cycle, the application
--      must either provide the next data byte or release SEND to indicate
--      the end of the packet.
--      After T_IN rises, the application must respond within 2 clock cycles.
--    * The application must not include CRC bytes.
--    * If the application is not ready to send data, it should assert
--      either NAK or STALL and keep it asserted until the end of the
--      transaction.
--    * An empty packet can be sent by keeping SEND, NAK and STALL
--      deasserted; the component will interpret this as a zero-length SEND.
--    * Successfull completion of an IN transaction is indicated by
--      asserting FIN for one cycle.
--    * Timeout is indicated by deasserting T_IN without ever asserting FIN.
--      In this case, the application must assume that the IN transaction
--      failed.
--
--  PING transactions:
--    * In high speed mode only, the host may send a PING transaction to which
--      the application must respond with either ACK or NAK to indicate whether
--      it is willing to receive a full sized OUT transaction.
--    * When a PING is received, T_PING is raised and at the same time T_ENDPT
--      becomes valid. The application must respond within 2 clock cycles.
--    * If the application is not ready to received data, it should assert T_NAK
--      and keep it asserted until the end of the transaction. If the application
--      does not assert either T_NAK or T_STALL, an ACK response will be sent.
--
 
library ieee;
use ieee.std_logic_1164.all, ieee.numeric_std.all;
 
entity usb_transact is
 
    generic (
 
        -- Support high speed mode.
        HSSUPPORT : boolean := false );
 
    port (
 
        -- 60 MHz UTMI clock.
        CLK :           in  std_logic;
 
        -- Synchronous reset of this entity.
        RESET :         in  std_logic;
 
        -- High during IN transactions.
        T_IN :          out std_logic;
 
        -- High during OUT transactions.
        T_OUT :         out std_logic;
 
        -- High during SETUP transactions.
        T_SETUP :       out std_logic;
 
        -- High during PING transactions.
        T_PING :        out std_logic;
 
        -- Indicates successfull completion of a transaction.
        T_FIN :         out std_logic;
 
        -- Device address.
        T_ADDR :        in  std_logic_vector(6 downto 0);
 
        -- Endpoint number for current transaction.
        T_ENDPT :       out std_logic_vector(3 downto 0);
 
        -- Triggers a NAK response to IN/OUT/PING.
        T_NAK :         in  std_logic;
 
        -- Triggers a STALL response to IN/OUT.
        T_STALL :       in  std_logic;
 
        -- Triggers a NYET response to OUT.
        T_NYET :        in  std_logic;
 
        -- High while application has data to send (in response to OUT).
        T_SEND :        in  std_logic;
 
        -- Sync bit to use for IN transactions.
        T_ISYNC :       in  std_logic;
 
        -- Sync bit used for the current OUT transaction.
        T_OSYNC :       out std_logic;
 
        -- Indicates next byte received.
        T_RXRDY :       out std_logic;
 
        -- Received data; valid when T_RXRDY = '1'.
        T_RXDAT :       out std_logic_vector(7 downto 0);
 
        -- Requests next byte to transmit; application must update T_TXDAT or T_SEND in next cycle.
        T_TXRDY :       out std_logic;
 
        -- Data byte to transmit; must be valid when T_SEND = '1'.
        T_TXDAT :       in  std_logic_vector(7 downto 0);
 
        -- Connect to I_HIGHSPEED from usb_init.
        I_HIGHSPEED :   in  std_logic;
 
        -- Connect to P_RXACT from usb_packet.
        P_RXACT :       in  std_logic;
 
        -- Connect to P_RXRDY from usb_packet.
        P_RXRDY :       in  std_logic;
 
        -- Connect to P_RXFIN from usb_packet.
        P_RXFIN :       in  std_logic;
 
        -- Connect to P_RXDAT from usb_packet.
        P_RXDAT :       in  std_logic_vector(7 downto 0);
 
        -- Connect to P_TXACT towards usb_packet.
        P_TXACT :       out std_logic;
 
        -- Connect to P_TXRDY from usb_packet.
        P_TXRDY :       in  std_logic;
 
        -- Connect to P_TXDAT towards usb_packet.
        P_TXDAT :       out std_logic_vector(7 downto 0) );
 
end entity usb_transact;
 
architecture usb_transact_arch of usb_transact is
 
    -- PID constants
    constant pid_out :  std_logic_vector(3 downto 0) := "0001";
    constant pid_in :   std_logic_vector(3 downto 0) := "1001";
    constant pid_setup: std_logic_vector(3 downto 0) := "1101";
    constant pid_ack :  std_logic_vector(3 downto 0) := "0010";
    constant pid_nak :  std_logic_vector(3 downto 0) := "1010";
    constant pid_stall: std_logic_vector(3 downto 0) := "1110";
    constant pid_nyet : std_logic_vector(3 downto 0) := "0110";
    constant pid_ping : std_logic_vector(3 downto 0) := "0100";
    constant pid_data : std_logic_vector(2 downto 0) :=  "011";
 
    function pid_mirror(v: std_logic_vector) return std_logic_vector
    is begin
	return (not v) & v;
    end function;
 
    -- State machine
    type t_state is (
      ST_IDLE, ST_SKIP,
      ST_GETTOKEN1, ST_GETTOKEN2, ST_GETTOKEN3, ST_GOTTOKEN,
      ST_SENDSHAKE,
      ST_GETDATA, ST_GOTDATA,
      ST_SENDDATA, ST_SENDING,
      ST_WAITACK, ST_WAITSKIP, ST_GETACK );
    signal s_state : t_state := ST_IDLE;
    signal s_active :   std_logic;
 
    -- Transaction state
    signal s_in :       std_logic := '0';
    signal s_out :      std_logic := '0';
    signal s_setup :    std_logic := '0';
    signal s_ping :     std_logic := '0';
    signal s_finished : std_logic := '0';
 
    -- Previous value of P_RXACT; needed to detect bad packet while waiting for host.
    signal s_prevrxact : std_logic;
 
    -- PID byte to use for outgoing packet (ST_SENDSHAKE or ST_SENDDATA)
    signal s_sendpid :  std_logic_vector(3 downto 0);
 
    -- Registered output signals
    signal s_endpt : std_logic_vector(3 downto 0) := "0000";
    signal s_osync : std_logic := '0';
 
    -- In full speed mode, we must time out an expected host response after
    -- 16 to 18 bit periods. In high speed mode, we must time out after
    -- 736 to 816 bit periods. We can not get accurate timing because we don't
    -- know the delay due to CRC, EOP, SYNC and UTMI pipeline. (We should use
    -- PHY_LINESTATE for timing, but we don't.) So we just use a much longer
    -- timeout; wait_timeout_fs = 511 cycles = 102 bit periods;
    -- wait_timeout_hs = 127 cycles = 1020 bit periods.
    constant wait_timeout_fs : unsigned(8 downto 0) := "111111111";
    constant wait_timeout_hs : unsigned(8 downto 0) := "001111111";
 
    -- In full speed mode, we must wait at least 2 and at most 6.5 bit periods
    -- before responding to the host. We have wait_send_fs = 14 cycles from
    -- rising T_IN/OUT/SETUP until valid T_NAK; equals 16 cycles from rising
    -- P_RXFIN until rising P_TXACT; equals 18 cycles from falling PHY_RXACTIVE
    -- until rising PHY_TXVALID. Including pipeline delay in the UTMI, we end
    -- up with 2 to 5 bit periods from SE0-to-J until SYNC.
    constant wait_send_fs : unsigned(8 downto 0) := "000001110";
 
    -- In high speed mode, we must wait at least 8 and at most 192 bit periods
    -- before responding to the host. We give the application wait_send_hs = 2
    -- cycles to get its act together; i.e. from rising T_IN/OUT/SETUP/PING
    -- until valid T_NAK/STALL/NYET/SEND. This corresponds to 4 cycles from
    -- P_RXFIN until P_TXACT; equals 6 cycles from falling PHY_RXACTIVE until
    -- rising PHY_TXVALID. Including pipeline delay in the UTMI, we end up
    -- with 78 to 127 bit periods between packets.
    constant wait_send_hs : unsigned(8 downto 0) := "000000010";
 
    -- Count down timer.
    signal wait_count : unsigned(8 downto 0);
 
begin
 
    -- Assign control signals
    s_active <=
        '1' when (s_state = ST_IDLE or s_state = ST_GOTTOKEN or
                  s_state = ST_SENDSHAKE or
                  s_state = ST_GETDATA or s_state = ST_GOTDATA or
                  s_state = ST_SENDDATA or s_state = ST_SENDING or
                  s_state = ST_WAITACK or s_state = ST_WAITSKIP or s_state = ST_GETACK)
        else '0';
    T_IN    <= s_in and s_active;
    T_OUT   <= s_out and s_active;
    T_SETUP <= s_setup and s_active;
    T_PING  <= s_ping and s_active;
    T_FIN   <= s_finished;  -- Note: T_FIN only occurs when s_state = ST_IDLE
    T_ENDPT <= s_endpt;
    T_OSYNC <= s_osync;
 
    -- Received bytes
    T_RXRDY <= P_RXRDY when (s_state = ST_GETDATA) else '0';
    T_RXDAT <= P_RXDAT;
 
    -- Byte to transmit: handshake PID, data PID or data byte
    T_TXRDY <= P_TXRDY when (s_state = ST_SENDING) else '0';
    P_TXACT <= '1' when (s_state = ST_SENDSHAKE or s_state = ST_SENDDATA or
                         (s_state = ST_SENDING and T_SEND = '1'))
               else '0';
    P_TXDAT <= pid_mirror(s_sendpid) when (s_state = ST_SENDSHAKE or s_state = ST_SENDDATA)
               else T_TXDAT;
 
 
    -- On every rising clock edge
    process is
    begin
        wait until rising_edge(CLK);
 
        s_prevrxact     <= P_RXACT;
 
        if RESET = '1' then
 
            -- Reset this component
            s_state     <= ST_IDLE;
            s_in        <= '0';
            s_out       <= '0';
            s_setup     <= '0';
            s_ping      <= '0';
            s_finished  <= '0';
 
        else
 
            case s_state is
 
                when ST_IDLE =>
                    -- Idle; wait for incoming packet
                    s_in        <= '0';
                    s_out       <= '0';
                    s_setup     <= '0';
                    s_ping      <= '0';
                    s_finished  <= '0';
                    if P_RXRDY = '1' then
                        case P_RXDAT(3 downto 0) is
                            when pid_out =>
                                -- OUT token
                                s_out   <= '1';
                                s_state <= ST_GETTOKEN1;
                            when pid_in =>
                                -- IN token
                                s_in    <= '1';
                                s_state <= ST_GETTOKEN1;
                            when pid_setup =>
                                -- SETUP token
                                s_setup <= '1';
                                s_state <= ST_GETTOKEN1;
                            when pid_ping =>
                                -- PING token
                                if HSSUPPORT then
                                    s_ping  <= '1';
                                    s_state <= ST_GETTOKEN1;
                                else
                                    -- no PINGing for full speed devices
                                    s_state <= ST_SKIP;
                                end if;
                            when others =>
                                -- unexpected packet
                                s_state <= ST_SKIP;
                        end case;
                    end if;
 
		when ST_SKIP =>
		    -- Skip incoming packet and go back to IDLE
		    if P_RXACT = '0' then
			s_state <= ST_IDLE;
		    end if;
 
		when ST_GETTOKEN1 =>
		    -- Receive and check 2nd byte of a token packet
		    if P_RXACT = '0' then
			-- Bad packet
			s_state <= ST_IDLE;
		    elsif P_RXRDY = '1' then
			-- Store endpoint number
			s_endpt(0) <= P_RXDAT(7);
			-- Check address
			if P_RXDAT(6 downto 0) = T_ADDR then
			    -- Packet is addressed to us
			    s_state <= ST_GETTOKEN2;
			else
			    -- Packet not addressed to us
			    s_state <= ST_SKIP;
			end if;
		    end if;
 
		when ST_GETTOKEN2 =>
		    -- Receive 3rd byte of token packet
		    if P_RXACT = '0' then
			-- Bad packet
			s_state <= ST_IDLE;
		    elsif P_RXRDY = '1' then
			-- Store endpoint number
			s_endpt(3 downto 1) <= P_RXDAT(2 downto 0);
			s_state <= ST_GETTOKEN3;
		    end if;
 
                when ST_GETTOKEN3 =>
                    -- Wait for end of incoming token packet
                    if P_RXFIN = '1' then
                        -- Token was ok
                        s_state <= ST_GOTTOKEN;
                    elsif P_RXACT = '0' then
                        -- Token was bad
                        s_state <= ST_IDLE;
                    end if;
                    if (s_in = '1') or (HSSUPPORT and (s_ping = '1')) then
                        if HSSUPPORT and (I_HIGHSPEED = '1') then
                            wait_count <= wait_send_hs;
                        else
                            wait_count <= wait_send_fs;
                        end if;
                    else
                        if HSSUPPORT and (I_HIGHSPEED = '1') then
                            wait_count <= wait_timeout_hs;
                        else
                            wait_count <= wait_timeout_fs;
                        end if;
                    end if;
 
		when ST_GOTTOKEN =>
                    -- Wait for data packet or wait for our turn to respond
		    if P_RXACT = '1' then
			if P_RXRDY = '1' then
			    -- Got PID byte
			    if ((s_out = '1') or (s_setup = '1')) and
                               (P_RXDAT(2 downto 0) = pid_data) then
				-- This is the DATA packet we were waiting for
				s_osync <= P_RXDAT(3);
				s_state <= ST_GETDATA;
			    else
				-- Got unexpected packet
				s_in    <= '0';
				s_out   <= '0';
				s_setup <= '0';
                                s_ping  <= '0';
				case P_RXDAT(3 downto 0) is
				    when pid_out =>
					-- unexpected OUT token
					s_out <= '1';
					s_state <= ST_GETTOKEN1;
				    when pid_in =>
					-- unexpected IN token
					s_in <= '1';
					s_state <= ST_GETTOKEN1;
				    when pid_setup =>
					-- unexpected SETUP token
					s_setup <= '1';
					s_state <= ST_GETTOKEN1;
                                    when pid_ping =>
                                        -- unexpected PING token
                                        if HSSUPPORT then
                                            s_ping  <= '1';
                                            s_state <= ST_GETTOKEN1;
                                        else
                                            -- no PINGing for full speed devices
                                            s_state <= ST_SKIP;
                                        end if;
				    when others =>
					-- unexpected packet
					s_state <= ST_SKIP;
				end case;
			    end if;
			end if;
                    elsif s_prevrxact = '1' then
                        -- got bad packet
                        s_state <= ST_IDLE;
		    elsif wait_count = 0 then
                        -- timer reached zero
                        if s_in = '1' then
                            -- IN transaction: send response
                            if T_STALL = '1' then
                                s_state   <= ST_SENDSHAKE;
                                s_sendpid <= pid_stall;
                            elsif T_NAK = '1' then
                                s_state   <= ST_SENDSHAKE;
                                s_sendpid <= pid_nak;
                            else
                                s_state   <= ST_SENDDATA;
                                s_sendpid <= T_ISYNC & pid_data;
                            end if;
                        elsif HSSUPPORT and (s_ping = '1') then
                            -- PING transaction: send handshake
                            s_state <= ST_SENDSHAKE;
                            if T_STALL = '1' then
                                s_sendpid <= pid_stall;
                            elsif T_NAK = '1' then
                                s_sendpid <= pid_nak;
                            else
                                s_sendpid <= pid_ack;
                            end if;
                        else
                            -- OUT/SETUP transaction:
                            -- timeout while waiting for DATA packet
                            s_state <= ST_IDLE;
                        end if;
                    end if;
                    -- count down timer
                    wait_count <= wait_count - 1;
 
                when ST_SENDSHAKE =>
                    -- Send handshake packet
                    if P_TXRDY = '1' then
                        -- Handshake done, transaction completed
                        s_finished <= '1';
                        s_state <= ST_IDLE;
                    end if;
 
                when ST_GETDATA =>
                    -- Wait for end of incoming data packet
                    if P_RXFIN = '1' then
                        -- Data packet was good, respond with handshake
                        s_state <= ST_GOTDATA;
                    elsif P_RXACT = '0' then
                        -- Data packet was bad, ignore it
                        s_state <= ST_IDLE;
                    end if;
                    if HSSUPPORT and (I_HIGHSPEED = '1') then
                        wait_count <= wait_send_hs;
                    else
                        wait_count <= wait_send_fs;
                    end if;
 
		when ST_GOTDATA =>
		    -- Wait for inter-packet delay before responding
		    if wait_count = 0 then
			-- Move to response state
			s_state <= ST_SENDSHAKE;
                        if T_STALL = '1' then
                            s_sendpid <= pid_stall;
                        elsif T_NAK = '1' then
                            s_sendpid <= pid_nak;
                        elsif HSSUPPORT and (I_HIGHSPEED = '1') and (T_NYET = '1') then
                            s_sendpid <= pid_nyet;
                        else
                            s_sendpid <= pid_ack;
                        end if;
		    end if;
		    wait_count <= wait_count - 1;
 
		when ST_SENDDATA =>
		    -- Start sending a data packet
		    if P_TXRDY = '1' then
			-- Sent PID byte, need first data byte
			s_state <= ST_SENDING;
		    end if;
 
                when ST_SENDING =>
                    -- Send payload of data packet
                    if T_SEND = '0' then
                        -- End of data packet
                        s_state <= ST_WAITACK;
                    end if;
                    if HSSUPPORT and (I_HIGHSPEED = '1') then
                        wait_count <= wait_timeout_hs;
                    else
                        wait_count <= wait_timeout_fs;
                    end if;
 
		when ST_WAITACK =>
		    -- Wait for ACK handshake
		    if P_RXACT = '1' then
			if P_RXRDY = '1' then
			    -- Got PID byte
			    case P_RXDAT(3 downto 0) is
				when pid_ack =>
				    -- ACK handshake
				    s_state <= ST_GETACK;
				when pid_out =>
				    -- unexpected OUT token
				    s_in    <= '0';
				    s_out   <= '1';
				    s_state <= ST_GETTOKEN1;
				when pid_in =>
				    -- unexpected IN token
				    s_in    <= '1';
				    s_state <= ST_GETTOKEN1;
				when pid_setup =>
				    -- unexpected SETUP token
				    s_in    <= '0';
				    s_setup <= '1';
				    s_state <= ST_GETTOKEN1;
                                when pid_ping =>
                                    -- unexpected PING token
                                    if HSSUPPORT then
                                        s_in    <= '0';
                                        s_ping  <= '1';
                                        s_state <= ST_GETTOKEN1;
                                    else
                                        -- no PINGing for full speed devices
                                        s_state <= ST_SKIP;
                                    end if;
                                when ("0" & pid_data) | ("1" & pid_data) =>
                                    -- unexpected DATA packet
                                    -- This could be our own transmitted packet
                                    -- (if it was very short), so skip this.
                                    s_state <= ST_WAITSKIP;
				when others =>
				    -- unexpected packet
				    s_state <= ST_SKIP;
			    end case;
			end if;
                    elsif s_prevrxact = '1' then
                        -- got bad packet
                        s_state <= ST_IDLE;
		    elsif wait_count = 0 then
                        -- timeout while waiting for ACK
                        s_state <= ST_IDLE;
		    end if;
                    -- count down timer
                    wait_count <= wait_count - 1;
 
                when ST_WAITSKIP =>
                    -- Skip the echo of our own transmitted packet
                    if wait_count = 0 then
                        -- timeout
                        s_state <= ST_SKIP;
                    elsif P_RXFIN = '1' then
                        -- end of packet
                        s_state <= ST_WAITACK;
                    elsif P_RXACT = '0' then
                        -- bad packet
                        s_state <= ST_IDLE;
                    end if;
                    -- count down timer
                    wait_count <= wait_count - 1;
 
                when ST_GETACK =>
                    -- Wait for end of incoming ACK packet
                    if P_RXFIN = '1' then
                        -- ACK handshake was good
                        s_finished <= '1';
                        s_state <= ST_IDLE;
                    elsif P_RXACT = '0' then
			-- ACK handshake was bad
                        s_state <= ST_IDLE;
                    end if;
 
	    end case;
 
	end if;
 
    end process;
 
end architecture usb_transact_arch;
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.