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_serial.vhdl] - Rev 2
Compare with Previous | Blame | View Log
-- -- USB 2.0 Serial data transfer entity -- -- This entity implements a USB 2.0 device that carries a bidirectional -- byte stream over the bus. It communicates with the host according to -- the USB Communication Device Class, specifically the ACM (Abstract -- Control Model) variant. -- -- The low-level interface signals are labeled PHY_xxx and can be connected -- to an UTMI-compliant PHY component. The PHY should be configured in 8-bit -- mode. -- -- The application interface supports simple byte-at-a-time sending and -- receiving. Three block RAMs are used to implement a receive buffer, -- a transmission buffer and descriptor ROM. -- -- The CLK input must be the 60 MHz clock generated by the UTMI transceiver. -- All application interface signals and PHY interface signals are -- synchronized to the rising edge of CLK, except for SUSPEND which has -- an asynchronous reset. -- -- Transmission: -- * The entity asserts TXRDY when it is ready to send data. -- * The application puts data on TXDAT and asserts TXVAL when it is -- ready to send data. -- * In each clock cycle in which TXRDY and TXVAL are both asserted, -- the entity takes a byte from TXDAT and queues it for transmission. -- -- Receiving: -- * The application asserts RXRDY when it is ready to receive data. -- * The entity puts data on RXDAT and asserts RXVAL when it has -- data available. It will only do this in response to RXRDY. -- * When RXVAL is high, the application must either accept a byte -- from RXDAT and be ready for the next byte in the following cycle, -- or it must deassert RXRDY to pause the receive queue. -- -- At power on, and after RESET, the device waits for 16 ms before -- attaching to the USB bus. A high signal on ONLINE indicates that -- the device has been fully configured by the host. -- -- Bugs and limitations: -- * The TEST_MODE feature (mandatory for high speed devices) is -- not implemented and returns an error condition to the host. -- * The SEND_ENCAPSULATED_COMMAND and GET_ENCAPSULATED_RESPONSE commands -- (mandatory CDC-ACM requests) are not supported but will return a -- success condition to the host. -- * The default control pipe does not verify requests from the host -- as strictly as required by the standard. As a result, invalid -- requests from the host may sometimes appear to succeed when -- they should have returned an error condition. -- -- Implementation note: -- At some point it may become useful to implement separate clock domains -- for the application resp. PHY side of this entity, using the FIFO buffers -- for clock domain crossing. As a first step, a distinction has been made -- between application-side signals (prefixed with q_) and PHY-side signals -- (prefixed with s_). Currently the corresponding signals from both sides -- are simply wired together with asynchronous assignments. By replacing -- these hardwired connections with carefully designed synchronization logic, -- a separation of clock domains could be realized. -- library ieee; use ieee.std_logic_1164.all, ieee.numeric_std.all; use work.usb_pkg.all; entity usb_serial is generic ( -- Vendor ID to report in device descriptor. VENDORID : std_logic_vector(15 downto 0); -- Product ID to report in device descriptor. PRODUCTID : std_logic_vector(15 downto 0); -- Product version to report in device descriptor. VERSIONBCD : std_logic_vector(15 downto 0); -- Support high speed mode. HSSUPPORT : boolean := false; -- Set to true if the device never draws power from the USB bus. SELFPOWERED : boolean := false; -- Size of receive buffer as 2-logarithm of the number of bytes. -- Must be at least 10 (1024 bytes) for high speed support. RXBUFSIZE_BITS: integer range 7 to 12 := 11; -- Size of transmit buffer as 2-logarithm of the number of bytes. TXBUFSIZE_BITS: integer range 7 to 12 := 10 ); port ( -- 60 MHz UTMI clock. CLK : in std_logic; -- Synchronous reset; clear buffers and re-attach to the bus. RESET : in std_logic; -- High for one clock when a reset signal is detected on the USB bus. -- Note: do NOT wire this signal to RESET externally. USBRST : out std_logic; -- High when the device is operating (or suspended) in high speed mode. HIGHSPEED : out std_logic; -- High while the device is suspended. -- Note: This signal is not synchronized to CLK. -- It may be used to asynchronously drive the UTMI SuspendM pin. SUSPEND : out std_logic; -- High when the device is in the Configured state. ONLINE : out std_logic; -- High if a received byte is available on RXDAT. RXVAL : out std_logic; -- Received data byte, valid if RXVAL is high. RXDAT : out std_logic_vector(7 downto 0); -- High if the application is ready to receive the next byte. RXRDY : in std_logic; -- Number of bytes currently available in receive buffer. RXLEN : out std_logic_vector((RXBUFSIZE_BITS-1) downto 0); -- High if the application has data to send. TXVAL : in std_logic; -- Data byte to send, must be valid if TXVAL is high. TXDAT : in std_logic_vector(7 downto 0); -- High if the entity is ready to accept the next byte. TXRDY : out std_logic; -- Number of free byte positions currently available in transmit buffer. TXROOM : out std_logic_vector((TXBUFSIZE_BITS-1) downto 0); -- Temporarily suppress transmissions at the outgoing endpoint. -- This gives the application an oppertunity to fill the transmit -- buffer in order to blast data efficiently in big chunks. TXCORK : in std_logic; PHY_DATAIN : in std_logic_vector(7 downto 0); PHY_DATAOUT : out std_logic_vector(7 downto 0); PHY_TXVALID : out std_logic; PHY_TXREADY : in std_logic; PHY_RXACTIVE : in std_logic; PHY_RXVALID : in std_logic; PHY_RXERROR : in std_logic; PHY_LINESTATE : in std_logic_vector(1 downto 0); PHY_OPMODE : out std_logic_vector(1 downto 0); PHY_XCVRSELECT: out std_logic; PHY_TERMSELECT: out std_logic; PHY_RESET : out std_logic ); end entity usb_serial; architecture usb_serial_arch of usb_serial is -- Byte array type type t_byte_array is array(natural range <>) of std_logic_vector(7 downto 0); -- Conditional expression. function choose_int(z: boolean; a, b: integer) return integer is begin if z then return a; else return b; end if; end function; -- Conditional expression. function choose_byte(z: boolean; a, b: std_logic_vector) return std_logic_vector is begin if z then return a; else return b; end if; end function; -- Maximum packet size according to protocol. constant MAX_FSPACKET_SIZE: integer := 64; constant MAX_HSPACKET_SIZE: integer := 512; -- Width required for a pointer that can cover either RX or TX buffer. constant BUFPTR_SIZE : integer := choose_int(RXBUFSIZE_BITS > TXBUFSIZE_BITS, RXBUFSIZE_BITS, TXBUFSIZE_BITS); -- Data endpoint number. constant data_endpt : std_logic_vector(3 downto 0) := "0001"; constant notify_endpt : std_logic_vector(3 downto 0) := "0010"; -- Descriptor ROM -- addr 0 .. 17 : device descriptor -- addr 20 .. 29 : device qualifier -- addr 32 .. 98 : full speed configuration descriptor -- addr 112 .. 178 : high speed configuration descriptor -- addr 179 : other_speed_configuration hack constant DESC_DEV_ADDR : integer := 0; constant DESC_DEV_LEN : integer := 18; constant DESC_QUAL_ADDR : integer := 20; constant DESC_QUAL_LEN : integer := 10; constant DESC_FSCFG_ADDR : integer := 32; constant DESC_FSCFG_LEN : integer := 67; constant DESC_HSCFG_ADDR : integer := 112; constant DESC_HSCFG_LEN : integer := 67; constant DESC_OTHERSPEED_ADDR : integer := 179; constant descrom_pre: t_byte_array(0 to 191) := -- 18 bytes device descriptor ( X"12", -- bLength = 18 bytes X"01", -- bDescriptorType = device descriptor choose_byte(HSSUPPORT, X"00", X"10"), -- bcdUSB = 1.10 or 2.00 choose_byte(HSSUPPORT, X"02", X"01"), X"02", -- bDeviceClass = Communication Device Class X"00", -- bDeviceSubClass = none X"00", -- bDeviceProtocol = none X"40", -- bMaxPacketSize0 = 64 bytes VENDORID(7 downto 0), -- idVendor VENDORID(15 downto 8), PRODUCTID(7 downto 0), -- idProduct PRODUCTID(15 downto 8), VERSIONBCD(7 downto 0), -- bcdDevice VERSIONBCD(15 downto 8), X"00", -- iManufacturer X"00", -- iProduct X"00", -- iSerialNumber X"01", -- bNumConfigurations = 1 -- 2 bytes padding X"00", X"00", -- 10 bytes device qualifier X"0a", -- bLength = 10 bytes X"06", -- bDescriptorType = device qualifier X"00", X"02", -- bcdUSB = 2.0 X"02", -- bDeviceClass = Communication Device Class X"00", -- bDeviceSubClass = none X"00", -- bDeviceProtocol = none X"40", -- bMaxPacketSize0 = 64 bytes X"01", -- bNumConfigurations = 1 X"00", -- bReserved -- 2 bytes padding X"00", X"00", -- 67 bytes full-speed configuration descriptor -- 9 bytes configuration header X"09", -- bLength = 9 bytes X"02", -- bDescriptorType = configuration descriptor X"43", X"00", -- wTotalLength = 67 bytes X"02", -- bNumInterfaces = 2 X"01", -- bConfigurationValue = 1 X"00", -- iConfiguration = none choose_byte(SELFPOWERED, X"c0", X"80"), -- bmAttributes X"fa", -- bMaxPower = 500 mA -- 9 bytes interface descriptor (communication control class) X"09", -- bLength = 9 bytes X"04", -- bDescriptorType = interface descriptor X"00", -- bInterfaceNumber = 0 X"00", -- bAlternateSetting = 0 X"01", -- bNumEndpoints = 1 X"02", -- bInterfaceClass = Communication Interface X"02", -- bInterfaceSubClass = Abstract Control Model X"01", -- bInterfaceProtocol = V.25ter (required for Linux CDC-ACM driver) X"00", -- iInterface = none -- 5 bytes functional descriptor (header) X"05", -- bLength = 5 bytes X"24", -- bDescriptorType = CS_INTERFACE X"00", -- bDescriptorSubtype = header X"10", X"01", -- bcdCDC = 1.10 -- 4 bytes functional descriptor (abstract control management) X"04", -- bLength = 4 bytes X"24", -- bDescriptorType = CS_INTERFACE X"02", -- bDescriptorSubtype = Abstract Control Mgmnt X"00", -- bmCapabilities = none -- 5 bytes functional descriptor (union) X"05", -- bLength = 5 bytes X"24", -- bDescriptorType = CS_INTERFACE X"06", -- bDescriptorSubtype = union X"00", -- bMasterInterface = 0 X"01", -- bSlaveInterface0 = 1 -- 5 bytes functional descriptor (call management) X"05", -- bLength = 5 bytes X"24", -- bDescriptorType = CS_INTERFACE X"01", -- bDescriptorSubType = Call Management X"00", -- bmCapabilities = no call mgmnt X"01", -- bDataInterface = 1 -- 7 bytes endpoint descriptor (notify IN) X"07", -- bLength = 7 bytes X"05", -- bDescriptorType = endpoint descriptor X"82", -- bEndpointAddress = IN 2 X"03", -- bmAttributes = interrupt data X"08", X"00", -- wMaxPacketSize = 8 bytes X"ff", -- bInterval = 255 frames -- 9 bytes interface descriptor (data class) X"09", -- bLength = 9 bytes X"04", -- bDescriptorType = interface descriptor X"01", -- bInterfaceNumber = 1 X"00", -- bAlternateSetting = 0 X"02", -- bNumEndpoints = 2 X"0a", -- bInterfaceClass = Data Interface X"00", -- bInterfaceSubClass = none X"00", -- bInterafceProtocol = none X"00", -- iInterface = none -- 7 bytes endpoint descriptor (data IN) X"07", -- bLength = 7 bytes X"05", -- bDescriptorType = endpoint descriptor X"81", -- bEndpointAddress = IN 1 X"02", -- bmAttributes = bulk data X"40", X"00", -- wMaxPacketSize = 64 bytes X"00", -- bInterval -- 7 bytes endpoint descriptor (data OUT) X"07", -- bLength = 7 bytes X"05", -- bDescriptorType = endpoint descriptor X"01", -- bEndpointAddress = OUT 1 X"02", -- bmAttributes = bulk data X"40", X"00", -- wMaxPacketSize = 64 bytes X"00", -- bInterval -- 13 bytes padding X"00", X"00", X"00", X"00", X"00", X"00", X"00", X"00", X"00", X"00", X"00", X"00", X"00", -- 67 bytes high-speed configuration descriptor -- 9 bytes configuration header X"09", -- bLength = 9 bytes X"02", -- bDescriptorType = configuration descriptor X"43", X"00", -- wTotalLength = 67 bytes X"02", -- bNumInterfaces = 2 X"01", -- bConfigurationValue = 1 X"00", -- iConfiguration = none choose_byte(SELFPOWERED, X"c0", X"80" ), -- bmAttributes = self-powered X"fa", -- bMaxPower = 500 mA -- 9 bytes interface descriptor (communication control class) X"09", -- bLength = 9 bytes X"04", -- bDescriptorType = interface descriptor X"00", -- bInterfaceNumber = 0 X"00", -- bAlternateSetting = 0 X"01", -- bNumEndpoints = 1 X"02", -- bInterfaceClass = Communication Interface X"02", -- bInterfaceSubClass = Abstract Control Model X"01", -- bInterfaceProtocol = V.25ter (required for Linux CDC-ACM driver) X"00", -- iInterface = none -- 5 bytes functional descriptor (header) X"05", -- bLength = 5 bytes X"24", -- bDescriptorType = CS_INTERFACE X"00", -- bDescriptorSubtype = header X"10", X"01", -- bcdCDC = 1.10 -- 4 bytes functional descriptor (abstract control management) X"04", -- bLength = 4 bytes X"24", -- bDescriptorType = CS_INTERFACE X"02", -- bDescriptorSubtype = Abstract Control Mgmnt X"00", -- bmCapabilities = none -- 5 bytes functional descriptor (union) X"05", -- bLength = 5 bytes X"24", -- bDescriptorType = CS_INTERFACE X"06", -- bDescriptorSubtype = union X"00", -- bMasterInterface = 0 X"01", -- bSlaveInterface0 = 1 -- 5 bytes functional descriptor (call management) X"05", -- bLength = 5 bytes X"24", -- bDescriptorType = CS_INTERFACE X"01", -- bDescriptorSubType = Call Management X"00", -- bmCapabilities = no call mgmnt X"01", -- bDataInterface = 1 -- 7 bytes endpoint descriptor (notify IN) X"07", -- bLength = 7 bytes X"05", -- bDescriptorType = endpoint descriptor X"82", -- bEndpointAddress = IN 2 X"03", -- bmAttributes = interrupt data X"08", X"00", -- wMaxPacketSize = 8 bytes X"0f", -- bInterval = 2**14 frames -- 9 bytes interface descriptor (data class) X"09", -- bLength = 9 bytes X"04", -- bDescriptorType = interface descriptor X"01", -- bInterfaceNumber = 1 X"00", -- bAlternateSetting = 0 X"02", -- bNumEndpoints = 2 X"0a", -- bInterfaceClass = Data Interface X"00", -- bInterfaceSubClass = none X"00", -- bInterafceProtocol = none X"00", -- iInterface = none -- 7 bytes endpoint descriptor (data IN) X"07", -- bLength = 7 bytes X"05", -- bDescriptorType = endpoint descriptor X"81", -- bEndpointAddress = IN 1 X"02", -- bmAttributes = bulk data X"00", X"02", -- wMaxPacketSize = 512 bytes X"00", -- bInterval -- 7 bytes endpoint descriptor (data OUT) X"07", -- bLength = 7 bytes X"05", -- bDescriptorType = endpoint descriptor X"01", -- bEndpointAddress = OUT 1 X"02", -- bmAttributes = bulk data X"00", X"02", -- wMaxPacketSize = 512 bytes X"00", -- bInterval = never NAK -- other_speed_configuration hack X"07", -- 12 bytes padding X"00", X"00", X"00", X"00", X"00", X"00", X"00", X"00", X"00", X"00", X"00", X"00" ); constant descrom: t_byte_array(0 to (choose_int(HSSUPPORT, 191, 111))) := descrom_pre(0 to choose_int(HSSUPPORT, 191, 111)); signal descrom_start: unsigned(choose_int(HSSUPPORT, 7, 6) downto 0); signal descrom_raddr: unsigned(choose_int(HSSUPPORT, 7, 6) downto 0); signal descrom_rdat: std_logic_vector(7 downto 0); -- RX buffer signal rxbuf: t_byte_array(0 to (2**RXBUFSIZE_BITS-1)); signal rxbuf_rdat: std_logic_vector(7 downto 0); -- TX buffer signal txbuf: t_byte_array(0 to (2**TXBUFSIZE_BITS-1)); signal txbuf_rdat: std_logic_vector(7 downto 0); -- Interface to usb_init signal usbi_usbrst : std_logic; signal usbi_highspeed : std_logic; signal usbi_suspend : std_logic; -- Interface to usb_packet signal usbp_chirpk : std_logic; signal usbp_rxact : std_logic; signal usbp_rxrdy : std_logic; signal usbp_rxfin : std_logic; signal usbp_rxdat : std_logic_vector(7 downto 0); signal usbp_txact : std_logic; signal usbp_txrdy : std_logic; signal usbp_txdat : std_logic_vector(7 downto 0); -- Interface to usb_transact signal usbt_in : std_logic; signal usbt_out : std_logic; signal usbt_setup : std_logic; signal usbt_ping : std_logic; signal usbt_fin : std_logic; signal usbt_endpt : std_logic_vector(3 downto 0); signal usbt_nak : std_logic; signal usbt_stall : std_logic; signal usbt_nyet : std_logic; signal usbt_send : std_logic; signal usbt_isync : std_logic; signal usbt_osync : std_logic; signal usbt_rxrdy : std_logic; signal usbt_rxdat : std_logic_vector(7 downto 0); signal usbt_txrdy : std_logic; signal usbt_txdat : std_logic_vector(7 downto 0); -- Interface to usb_control signal usbc_addr : std_logic_vector(6 downto 0); signal usbc_confd : std_logic; signal usbc_clr_in : std_logic_vector(1 to 2); signal usbc_clr_out : std_logic_vector(1 to 2); signal usbc_sethlt_in : std_logic_vector(1 to 2); signal usbc_sethlt_out: std_logic_vector(1 to 2); signal usbc_dscbusy : std_logic; signal usbc_dscrd : std_logic; signal usbc_dsctyp : std_logic_vector(2 downto 0); signal usbc_dscinx : std_logic_vector(7 downto 0); signal usbc_dscoff : std_logic_vector(7 downto 0); signal usbc_dsclen : std_logic_vector(7 downto 0); signal usbc_selfpowered : std_logic; signal usbc_in : std_logic; signal usbc_out : std_logic; signal usbc_setup : std_logic; signal usbc_ping : std_logic; signal usbc_nak : std_logic; signal usbc_stall : std_logic; signal usbc_nyet : std_logic; signal usbc_send : std_logic; signal usbc_isync : std_logic; signal usbc_txdat : std_logic_vector(7 downto 0); -- State machine type t_state is ( ST_IDLE, ST_STALL, ST_NAK, ST_INSTART, ST_INSEND, ST_INDONE, ST_OUTRECV, ST_OUTNAK ); signal s_state : t_state := ST_IDLE; -- Endpoint administration (PHY side). signal s_rxbuf_head : unsigned(RXBUFSIZE_BITS-1 downto 0) := to_unsigned(0, RXBUFSIZE_BITS); signal s_rxbuf_tail : unsigned(RXBUFSIZE_BITS-1 downto 0); signal s_txbuf_head : unsigned(TXBUFSIZE_BITS-1 downto 0); signal s_txbuf_tail : unsigned(TXBUFSIZE_BITS-1 downto 0) := to_unsigned(0, TXBUFSIZE_BITS); signal s_txbuf_stop : unsigned(TXBUFSIZE_BITS-1 downto 0); signal s_bufptr : unsigned(BUFPTR_SIZE-1 downto 0); signal s_txprev_full : std_logic := '0'; -- Last transmitted packet was full-size signal s_txprev_acked : std_logic := '1'; -- Last trantsmitted packet was ack-ed. signal s_isync : std_logic := '0'; signal s_osync : std_logic := '0'; signal s_halt_in : std_logic_vector(1 to 2) := "00"; signal s_halt_out : std_logic_vector(1 to 2) := "00"; signal s_nyet : std_logic := '0'; -- Buffer state (application side). signal q_rxbuf_head : unsigned(RXBUFSIZE_BITS-1 downto 0); signal q_rxbuf_tail : unsigned(RXBUFSIZE_BITS-1 downto 0) := to_unsigned(0, RXBUFSIZE_BITS); signal q_txbuf_head : unsigned(TXBUFSIZE_BITS-1 downto 0) := to_unsigned(0, TXBUFSIZE_BITS); signal q_txbuf_tail : unsigned(TXBUFSIZE_BITS-1 downto 0); -- Control signals (PHY side). signal s_reset : std_logic; signal s_txcork : std_logic; -- Status signals (application side). signal q_usbrst : std_logic; signal q_online : std_logic; signal q_highspeed : std_logic; -- Receive buffer logic (application side). signal q_rxval : std_logic := '0'; signal q_rxbuf_read : std_logic; signal q_txbuf_rdy : std_logic; begin -- Check buffer size. assert ((not HSSUPPORT) or (RXBUFSIZE_BITS >= 10)) report "High-speed device needs at least 1024 bytes RX buffer"; -- Bus reset logic usb_init_inst : usb_init generic map ( HSSUPPORT => HSSUPPORT ) port map ( CLK => CLK, RESET => s_reset, I_USBRST => usbi_usbrst, I_HIGHSPEED => usbi_highspeed, I_SUSPEND => usbi_suspend, P_CHIRPK => usbp_chirpk, PHY_RESET => PHY_RESET, PHY_LINESTATE => PHY_LINESTATE, PHY_OPMODE => PHY_OPMODE, PHY_XCVRSELECT => PHY_XCVRSELECT, PHY_TERMSELECT => PHY_TERMSELECT ); -- Packet level logic usb_packet_inst : usb_packet port map ( CLK => CLK, RESET => usbi_usbrst, P_CHIRPK => usbp_chirpk, P_RXACT => usbp_rxact, P_RXRDY => usbp_rxrdy, P_RXFIN => usbp_rxfin, P_RXDAT => usbp_rxdat, P_TXACT => usbp_txact, P_TXRDY => usbp_txrdy, P_TXDAT => usbp_txdat, PHY_DATAIN => PHY_DATAIN, PHY_DATAOUT => PHY_DATAOUT, PHY_TXVALID => PHY_TXVALID, PHY_TXREADY => PHY_TXREADY, PHY_RXACTIVE => PHY_RXACTIVE, PHY_RXVALID => PHY_RXVALID, PHY_RXERROR => PHY_RXERROR ); -- Transaction level logic usb_transact_inst : usb_transact generic map ( HSSUPPORT => HSSUPPORT ) port map ( CLK => CLK, RESET => usbi_usbrst, T_IN => usbt_in, T_OUT => usbt_out, T_SETUP => usbt_setup, T_PING => usbt_ping, T_FIN => usbt_fin, T_ADDR => usbc_addr, T_ENDPT => usbt_endpt, T_NAK => usbt_nak, T_STALL => usbt_stall, T_NYET => usbt_nyet, T_SEND => usbt_send, T_ISYNC => usbt_isync, T_OSYNC => usbt_osync, T_RXRDY => usbt_rxrdy, T_RXDAT => usbt_rxdat, T_TXRDY => usbt_txrdy, T_TXDAT => usbt_txdat, I_HIGHSPEED => usbi_highspeed, P_RXACT => usbp_rxact, P_RXRDY => usbp_rxrdy, P_RXFIN => usbp_rxfin, P_RXDAT => usbp_rxdat, P_TXACT => usbp_txact, P_TXRDY => usbp_txrdy, P_TXDAT => usbp_txdat ); -- Default control endpoint usb_control_inst : usb_control generic map ( NENDPT => 2 ) port map ( CLK => CLK, RESET => usbi_usbrst, C_ADDR => usbc_addr, C_CONFD => usbc_confd, C_CLRIN => usbc_clr_in, C_CLROUT => usbc_clr_out, C_HLTIN => s_halt_in, C_HLTOUT => s_halt_out, C_SHLTIN => usbc_sethlt_in, C_SHLTOUT => usbc_sethlt_out, C_DSCBUSY => usbc_dscbusy, C_DSCRD => usbc_dscrd, C_DSCTYP => usbc_dsctyp, C_DSCINX => usbc_dscinx, C_DSCOFF => usbc_dscoff, C_DSCLEN => usbc_dsclen, C_SELFPOWERED => usbc_selfpowered, T_IN => usbc_in, T_OUT => usbc_out, T_SETUP => usbc_setup, T_PING => usbc_ping, T_FIN => usbt_fin, T_NAK => usbc_nak, T_STALL => usbc_stall, T_NYET => usbc_nyet, T_SEND => usbc_send, T_ISYNC => usbc_isync, T_OSYNC => usbt_osync, T_RXRDY => usbt_rxrdy, T_RXDAT => usbt_rxdat, T_TXRDY => usbt_txrdy, T_TXDAT => usbc_txdat ); -- Assign usb_serial output signals. USBRST <= q_usbrst; HIGHSPEED <= q_highspeed; SUSPEND <= usbi_suspend; ONLINE <= q_online; RXVAL <= q_rxval; RXDAT <= rxbuf_rdat; RXLEN <= std_logic_vector(q_rxbuf_head - q_rxbuf_tail); TXRDY <= q_txbuf_rdy; TXROOM <= std_logic_vector(q_txbuf_tail - q_txbuf_head - 1); -- Assign usb_control input signals usbc_in <= usbt_in when (usbt_endpt = "0000") else '0'; usbc_out <= usbt_out when (usbt_endpt = "0000") else '0'; usbc_setup <= usbt_setup when (usbt_endpt = "0000") else '0'; usbc_ping <= usbt_ping when (usbt_endpt = "0000") else '0'; usbc_selfpowered <= '1' when SELFPOWERED else '0'; -- Assign usb_transact input lines usbt_nak <= usbc_nak when (usbt_endpt = "0000") else '1' when (s_state = ST_NAK) else '0'; usbt_stall <= usbc_stall when (usbt_endpt = "0000") else '1' when (s_state = ST_STALL) else '0'; usbt_nyet <= usbc_nyet when (usbt_endpt = "0000") else s_nyet; usbt_send <= usbc_send when (usbt_endpt = "0000") else '1' when (s_state = ST_INSEND) else '0'; usbt_isync <= usbc_isync when (usbt_endpt = "0000") else s_isync; usbt_txdat <= usbc_txdat when (usbt_endpt = "0000" and usbc_dscbusy = '0') else descrom_rdat when (usbt_endpt = "0000") else txbuf_rdat; -- Buffer logic. q_rxbuf_read <= (RXRDY or (not q_rxval)) when (q_rxbuf_tail /= q_rxbuf_head) else '0'; q_txbuf_rdy <= '1' when (q_txbuf_head + 1 /= q_txbuf_tail) else '0'; -- Connection between PHY-side and application-side signals. -- This could be a good place to insert clock domain crossing. q_rxbuf_head <= s_rxbuf_head; s_rxbuf_tail <= q_rxbuf_tail; s_txbuf_head <= q_txbuf_head; q_txbuf_tail <= s_txbuf_tail; s_txcork <= TXCORK; s_reset <= RESET; q_online <= usbc_confd; q_usbrst <= usbi_usbrst; q_highspeed <= usbi_highspeed; -- Lookup address/length of the selected descriptor (combinatorial). process (usbc_dsctyp, usbc_dscinx, usbi_highspeed) constant slen: integer := descrom_start'length; constant nlen: integer := USBC_DSCLEN'length; variable s: unsigned((slen-1) downto 0); variable n: unsigned((nlen-1) downto 0); begin s := to_unsigned(0, slen); n := to_unsigned(0, nlen); case usbc_dsctyp is when "001" => -- device descriptor s := to_unsigned(DESC_DEV_ADDR, slen); n := to_unsigned(DESC_DEV_LEN, nlen); when "010" => -- configuration descriptor if usbc_dscinx = X"00" then if HSSUPPORT and (usbi_highspeed = '1') then s := to_unsigned(DESC_HSCFG_ADDR, slen); n := to_unsigned(DESC_HSCFG_LEN, nlen); else s := to_unsigned(DESC_FSCFG_ADDR, slen); n := to_unsigned(DESC_FSCFG_LEN, nlen); end if; end if; when "110" => -- device qualifier if HSSUPPORT then s := to_unsigned(DESC_QUAL_ADDR, slen); n := to_unsigned(DESC_QUAL_LEN, nlen); end if; when "111" => -- other speed configuration if HSSUPPORT and (usbc_dscinx = X"00") then if usbi_highspeed = '1' then s := to_unsigned(DESC_FSCFG_ADDR, slen); n := to_unsigned(DESC_FSCFG_LEN, nlen); else s := to_unsigned(DESC_HSCFG_ADDR, slen); n := to_unsigned(DESC_HSCFG_LEN, nlen); end if; end if; when others => -- unsupported descriptor type end case; descrom_start <= s; usbc_dsclen <= std_logic_vector(n); end process; -- Main application-side synchronous process. process is begin wait until rising_edge(CLK); if RESET = '1' then -- Reset this entity. q_rxbuf_tail <= to_unsigned(0, RXBUFSIZE_BITS); q_txbuf_head <= to_unsigned(0, TXBUFSIZE_BITS); q_rxval <= '0'; else -- Read data from the RX buffer. if q_rxbuf_read = '1' then -- The RAM buffer reads a byte in this cycle. q_rxbuf_tail <= q_rxbuf_tail + 1; q_rxval <= '1'; elsif RXRDY = '1' then -- Byte consumed by application; no new data yet. q_rxval <= '0'; end if; -- Write data to the TX buffer. if (TXVAL = '1') and (q_txbuf_rdy = '1') then -- The RAM buffer writes a byte in this cycle. q_txbuf_head <= q_txbuf_head + 1; end if; end if; end process; -- Main PHY-side synchronous process. process is variable v_max_txsize : unsigned(TXBUFSIZE_BITS-1 downto 0); variable v_rxbuf_len_lim : unsigned(RXBUFSIZE_BITS-1 downto 0); variable v_rxbuf_tmp_head : unsigned(RXBUFSIZE_BITS-1 downto 0); variable v_rxbuf_pktroom : std_logic; begin wait until rising_edge(CLK); -- Determine the maximum packet size we can transmit. if HSSUPPORT and usbi_highspeed = '1' then v_max_txsize := to_unsigned(MAX_HSPACKET_SIZE, TXBUFSIZE_BITS); else v_max_txsize := to_unsigned(MAX_FSPACKET_SIZE, TXBUFSIZE_BITS); end if; -- Determine if there is room for another packet in the RX buffer. -- We need room for the largest possible incoming packet, plus -- two CRC bytes. if HSSUPPORT then v_rxbuf_len_lim := to_unsigned(2**RXBUFSIZE_BITS - MAX_HSPACKET_SIZE - 2, RXBUFSIZE_BITS); else v_rxbuf_len_lim := to_unsigned(2**RXBUFSIZE_BITS - MAX_FSPACKET_SIZE - 2, RXBUFSIZE_BITS); end if; if HSSUPPORT and s_state = ST_OUTRECV then -- Currently receiving a packet; compare against the temporary -- tail pointer to decide NYET vs ACK. v_rxbuf_tmp_head := resize(s_bufptr, RXBUFSIZE_BITS); else -- Not receiving a packet (or NYET not supported); -- compare against the tail pointer to decide NAK vs ACK. v_rxbuf_tmp_head := s_rxbuf_head; end if; if v_rxbuf_tmp_head - s_rxbuf_tail < v_rxbuf_len_lim then v_rxbuf_pktroom := '1'; else v_rxbuf_pktroom := '0'; end if; -- State machine if s_reset = '1' then -- Reset this entity. s_state <= ST_IDLE; s_rxbuf_head <= to_unsigned(0, RXBUFSIZE_BITS); s_txbuf_tail <= to_unsigned(0, TXBUFSIZE_BITS); s_txprev_full <= '0'; s_txprev_acked <= '1'; s_isync <= '0'; s_osync <= '0'; s_halt_in <= "00"; s_halt_out <= "00"; elsif usbi_usbrst = '1' then -- Reset protocol state. s_state <= ST_IDLE; s_txprev_full <= '0'; s_txprev_acked <= '1'; s_isync <= '0'; s_osync <= '0'; s_halt_in <= "00"; s_halt_out <= "00"; else case s_state is when ST_IDLE => -- Idle; wait for a transaction s_nyet <= '0'; if (usbt_endpt = data_endpt) and (usbt_in = '1') then -- Start of IN transaction if s_halt_in(1) = '1' then -- Endpoint halted s_state <= ST_STALL; elsif (s_txbuf_tail /= s_txbuf_head and s_txcork = '0') or s_txprev_full = '1' then -- Prepare to send data s_bufptr <= resize(s_txbuf_tail, s_bufptr'length); s_state <= ST_INSTART; else -- We have no data to send s_state <= ST_NAK; end if; elsif (usbt_endpt = data_endpt) and (usbt_out = '1') then -- Start of OUT transaction if s_halt_out(1) = '1' then -- Endpoint halted s_state <= ST_STALL; elsif v_rxbuf_pktroom = '1' then -- Prepare to receive data s_bufptr <= resize(s_rxbuf_head, s_bufptr'length); s_state <= ST_OUTRECV; else -- We have no room to store a new packet s_state <= ST_OUTNAK; end if; elsif HSSUPPORT and (usbt_endpt = data_endpt) and (usbt_ping = '1') then -- Start of PING transaction if v_rxbuf_pktroom = '1' then -- There is room in the RX buffer for another packet; do nothing (ACK). s_state <= ST_IDLE; else -- There is no room in the RX buffer; respond with NAK. s_state <= ST_NAK; end if; elsif (usbt_endpt = notify_endpt) and (usbt_in = '1') then -- The notify endpoint simply NAK's all IN transactions if s_halt_in(2) = '1' then -- Endpoint halted s_state <= ST_STALL; else s_state <= ST_NAK; end if; end if; -- Reset sync bits when the control endpoint tells us. s_isync <= s_isync and (not usbc_clr_in(1)); s_osync <= s_osync and (not usbc_clr_out(1)); -- Set/reset halt bits when the control endpoint tells us. s_halt_in(1) <= (s_halt_in(1) or usbc_sethlt_in(1)) and (not usbc_clr_in(1)); s_halt_in(2) <= (s_halt_in(2) or usbc_sethlt_in(2)) and (not usbc_clr_in(2)); s_halt_out(1) <= (s_halt_out(1) or usbc_sethlt_out(1)) and (not usbc_clr_out(1)); when ST_STALL => -- Wait for end of transaction if (usbt_in = '0') and (usbt_out = '0') and (usbt_ping = '0') then s_state <= ST_IDLE; end if; when ST_NAK => -- Wait for end of transaction if (usbt_in = '0') and (usbt_out = '0') and (usbt_ping = '0') then s_state <= ST_IDLE; end if; when ST_INSTART => -- Prepare to send data; read first byte from memory. if usbt_in = '0' then -- Transaction canceled. s_state <= ST_IDLE; elsif (s_txbuf_tail = s_txbuf_head) or (s_txprev_acked = '0' and resize(s_bufptr, TXBUFSIZE_BITS) = s_txbuf_stop) or (s_txprev_acked = '1' and s_txcork = '1') then -- The TX buffer is empty, or a previous empty packet -- is unacknowledged, or the TX buffer is corked; -- must send an empty packet. s_state <= ST_INDONE; else -- Send a non-empty packet. if s_txprev_acked = '1' then -- Set up a size limit for this packet. s_txbuf_stop <= s_txbuf_tail + v_max_txsize; end if; s_bufptr <= s_bufptr + 1; s_state <= ST_INSEND; end if; when ST_INSEND => -- Sending data if usbt_in = '0' then -- Transaction canceled. s_state <= ST_IDLE; elsif usbt_txrdy = '1' then -- Need to provide the next data byte; -- stop when we reach the end of the TX buffer; -- stop when we reach the packet size limit. if (resize(s_bufptr, TXBUFSIZE_BITS) = s_txbuf_head) or (resize(s_bufptr, TXBUFSIZE_BITS) = s_txbuf_stop) then -- No more bytes s_state <= ST_INDONE; else s_bufptr <= s_bufptr + 1; end if; end if; when ST_INDONE => -- Done sending packet; wait for ACK. if usbt_in = '0' then -- No acknowledgement s_txprev_acked <= '0'; -- Set limit for next packet to the same point s_txbuf_stop <= resize(s_bufptr, TXBUFSIZE_BITS); -- Done s_state <= ST_IDLE; elsif usbt_fin = '1' then -- Got acknowledgement s_txprev_acked <= '1'; -- Update buffer tail s_txbuf_tail <= resize(s_bufptr, TXBUFSIZE_BITS); -- Flip sync bit s_isync <= not s_isync; -- Remember if this was a full-sized packet. if s_txbuf_tail + v_max_txsize = resize(s_bufptr, TXBUFSIZE_BITS) then s_txprev_full <= '1'; else s_txprev_full <= '0'; end if; -- Done s_state <= ST_IDLE; end if; when ST_OUTRECV => -- Receiving data if usbt_out = '0' then -- Transaction ended. -- If the transaction was succesful, usbt_fin has been -- asserted in the previous cycle and has triggered -- an update of s_rxbuf_head. s_state <= ST_IDLE; elsif (usbt_fin = '1') and (usbt_osync = s_osync) then -- Good packet received; discard CRC bytes s_rxbuf_head <= resize(s_bufptr, RXBUFSIZE_BITS) - 2; s_osync <= not s_osync; elsif usbt_rxrdy = '1' then -- Got data byte s_bufptr <= s_bufptr + 1; if HSSUPPORT then -- Set NYET if there is no room to receive -- another packet after this one. s_nyet <= not v_rxbuf_pktroom; end if; end if; when ST_OUTNAK => -- Receiving data while we don't have room to store it if usbt_out = '0' then -- End of transaction s_state <= ST_IDLE; elsif (usbt_rxrdy = '1') and (usbt_osync = s_osync) then -- This is a new (non-duplicate) packet, but we can -- not store it; so respond with NAK. s_state <= ST_NAK; end if; end case; end if; end process; -- It is always a fight to get the synthesizer to infer block RAM. -- The problem is we need dual port RAM with read-enable signals. -- The recommended coding style, with registered read addresses, -- does not work in this case. -- The code below generates three RAM blocks on the Xilinx Spartan-3, -- but it is doubtful whether it will work on other FPGA families. -- Write to RX buffer. process (CLK) is begin if rising_edge(CLK) then if s_state = ST_OUTRECV and usbt_rxrdy = '1' then rxbuf(to_integer(resize(s_bufptr, RXBUFSIZE_BITS))) <= usbt_rxdat; end if; end if; end process; -- Read from RX buffer. process (CLK) is begin if rising_edge(CLK) then if q_rxbuf_read = '1' then rxbuf_rdat <= rxbuf(to_integer(q_rxbuf_tail)); end if; end if; end process; -- Write to TX buffer. process (CLK) is begin if rising_edge(CLK) then if TXVAL = '1' then txbuf(to_integer(q_txbuf_head)) <= TXDAT; end if; end if; end process; -- Read from TX buffer. process (CLK) is begin if rising_edge(CLK) then if (usbt_txrdy = '1') or (s_state = ST_INSTART) then txbuf_rdat <= txbuf(to_integer(resize(s_bufptr, TXBUFSIZE_BITS))); end if; end if; end process; -- Read from descriptor memory. process (CLK) is begin if rising_edge(CLK) then if usbc_dscrd = '1' then if HSSUPPORT and unsigned(usbc_dscoff) = 1 and usbc_dsctyp = "111" then -- Disguise the configuration descriptor as an -- other_speed_configuration descriptor. descrom_raddr <= to_unsigned(DESC_OTHERSPEED_ADDR, descrom_raddr'length); else descrom_raddr <= descrom_start + resize(unsigned(usbc_dscoff), descrom_raddr'length); end if; end if; end if; end process; assert to_integer(descrom_raddr) < descrom_rdat'length; descrom_rdat <= descrom(to_integer(descrom_raddr)); end architecture usb_serial_arch;