URL
https://opencores.org/ocsvn/opb_usblite/opb_usblite/trunk
Subversion Repositories opb_usblite
Compare Revisions
- This comparison shows the changes necessary to convert path
/opb_usblite/tags/R1/pcores/opb_usblite_v1_00_a/hdl/vhdl
- from Rev 6 to Rev 7
- ↔ Reverse comparison
Rev 6 → Rev 7
/opb_usblite_core.vhd
0,0 → 1,525
-- |
-- opb_usblite - opb_uartlite replacement |
-- |
-- opb_usblite is using components from Rudolf Usselmann see |
-- http://www.opencores.org/cores/usb_phy/ |
-- and Joris van Rantwijk see http://www.xs4all.nl/~rjoris/fpga/usb.html |
-- |
-- Copyright (C) 2010 Ake Rehnman |
-- |
-- This program is free software: you can redistribute it and/or modify |
-- it under the terms of the GNU Lesser General Public License as published by |
-- the Free Software Foundation, either version 3 of the License, or |
-- (at your option) any later version. |
-- |
-- This program is distributed in the hope that it will be useful, |
-- but WITHOUT ANY WARRANTY; without even the implied warranty of |
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
-- GNU Lesser General Public License for more details. |
-- |
-- You should have received a copy of the GNU Lesser General Public License |
-- along with this program. If not, see <http://www.gnu.org/licenses/>. |
-- |
library IEEE; |
use IEEE.std_logic_1164.all; |
use IEEE.numeric_std.all; |
|
entity OPB_USBLITE_Core is |
generic ( |
C_PHYMODE : std_logic := '1'; |
C_VENDORID : std_logic_vector(15 downto 0) := X"1234"; |
C_PRODUCTID : std_logic_vector(15 downto 0) := X"5678"; |
C_VERSIONBCD : std_logic_vector(15 downto 0) := X"0200"; |
C_SELFPOWERED : boolean := false; |
C_RXBUFSIZE_BITS: integer range 7 to 12 := 10; |
C_TXBUFSIZE_BITS: integer range 7 to 12 := 10 |
); |
port ( |
Clk : in std_logic; |
Reset : in std_logic; |
Usb_Clk : in std_logic; |
-- OPB signals |
OPB_CS : in std_logic; |
OPB_ABus : in std_logic_vector(0 to 1); |
OPB_RNW : in std_logic; |
OPB_DBus : in std_logic_vector(7 downto 0); |
SIn_xferAck : out std_logic; |
SIn_DBus : out std_logic_vector(7 downto 0); |
Interrupt : out std_logic; |
-- USB signals |
txdp : out std_logic; |
txdn : out std_logic; |
txoe : out std_logic; |
rxd : in std_logic; |
rxdp : in std_logic; |
rxdn : in std_logic |
); |
end entity OPB_USBLITE_Core; |
|
library unisim; |
use unisim.all; |
|
architecture akre of OPB_USBLITE_Core is |
|
component 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 component usb_serial; |
|
component usb_phy is |
port ( |
clk : in std_logic; |
rst : in std_logic; |
phy_tx_mode : in std_logic; |
usb_rst : out std_logic; |
|
-- Transciever Interface |
txdp : out std_logic; |
txdn : out std_logic; |
txoe : out std_logic; |
rxd : in std_logic; |
rxdp : in std_logic; |
rxdn : in std_logic; |
|
-- UTMI Interface |
DataOut_i : in std_logic_vector (7 downto 0); |
TxValid_i : in std_logic; |
TxReady_o : out std_logic; |
RxValid_o : out std_logic; |
RxActive_o : out std_logic; |
RxError_o : out std_logic; |
DataIn_o : out std_logic_vector (7 downto 0); |
LineState_o : out std_logic_vector (1 downto 0) |
); |
end component usb_phy; |
|
constant RX_FIFO_ADR : std_logic_vector(0 to 1) := "00"; |
constant TX_FIFO_ADR : std_logic_vector(0 to 1) := "01"; |
constant STATUS_REG_ADR : std_logic_vector(0 to 1) := "10"; |
constant CTRL_REG_ADR : std_logic_vector(0 to 1) := "11"; |
|
-- ADDRESS MAP |
-- =========== |
-- RX FIFO base + $0 |
-- TX FIFO base + $4 |
-- CONTROL REG base + $8 |
-- STATUS REG base + $C |
|
|
-- Read Only |
signal status_Reg : std_logic_vector(7 downto 0); |
-- bit 0 rx_Data_Present |
-- bit 1 rx_Buffer_Full |
-- bit 2 tx_Buffer_Empty |
-- bit 3 tx_Buffer_Full |
-- bit 4 interrupt flag |
-- bit 5 not used |
-- bit 6 online flag |
-- bit 7 suspend flag |
|
-- Write Only |
-- bit 0 Reset_TX_FIFO -- not used |
-- bit 1 Reset_RX_FIFO -- not used |
-- bit 2-3 Dont'Care |
-- bit 4 enable_rxinterrupts |
-- bit 5 Dont'Care |
-- bit 6 enable_txinterrupts |
-- bit 7 tx_enable -- not used |
|
signal enable_txinterrupts : std_logic; |
signal enable_rxinterrupts : std_logic; |
|
signal read_RX_FIFO : std_logic; |
signal reset_RX_FIFO : std_logic; |
signal TX_EN : std_logic; |
signal write_TX_FIFO : std_logic; |
signal reset_TX_FIFO : std_logic; |
signal tx_BUFFER_FULL : std_logic; |
signal tx_Buffer_Empty : std_logic; |
signal rx_Data_Present : std_logic; |
signal rx_BUFFER_FULL : std_logic; |
|
signal xfer_Ack : std_logic; |
signal xfer_Ack1 : std_logic; |
signal xfer_Ack2 : std_logic; |
signal Interrupt_r : std_logic; |
|
signal read_rx_fifo_r : std_logic; |
signal read_rx_fifo_rr : std_logic; |
signal read_rx_fifo_rrr : std_logic; |
signal write_tx_fifo_r : std_logic; |
signal write_tx_fifo_rr : std_logic; |
signal write_tx_fifo_rrr : std_logic; |
|
signal usbrst : std_logic; |
signal rxval : std_logic; |
|
signal rxdat : std_logic_vector (7 downto 0); |
signal rxrdy : std_logic; |
signal txval : std_logic; |
signal txempty : std_logic; |
signal txfull : std_logic; |
signal rxfull : std_logic; |
signal txdat : std_logic_vector (7 downto 0); |
signal txrdy : std_logic; |
|
signal phy_datain : std_logic_vector (7 downto 0); |
signal phy_dataout : std_logic_vector (7 downto 0); |
signal phy_txvalid : std_logic; |
signal phy_txready : std_logic; |
signal phy_rxactive : std_logic; |
signal phy_rxvalid : std_logic; |
signal phy_rxerror : std_logic; |
signal phy_linestate : std_logic_vector (1 downto 0); |
signal phy_reset : std_logic; |
signal phy_resetn : std_logic; |
signal phy_usb_rst : std_logic; |
|
signal highspeed : std_logic; |
signal suspend : std_logic; |
signal online : std_logic; |
signal rxlen : std_logic_vector((C_RXBUFSIZE_BITS-1) downto 0); |
signal txroom : std_logic_vector((C_TXBUFSIZE_BITS-1) downto 0); |
|
attribute TIG : string; |
attribute TIG of Reset : signal is "yes"; |
attribute TIG of write_TX_FIFO : signal is "yes"; |
attribute TIG of read_RX_FIFO : signal is "yes"; |
attribute TIG of rxdat : signal is "yes"; |
attribute TIG of txdat : signal is "yes"; |
attribute TIG of rxval : signal is "yes"; |
attribute TIG of txrdy : signal is "yes"; |
attribute TIG of txempty : signal is "yes"; |
attribute TIG of txfull : signal is "yes"; |
attribute TIG of rxfull : signal is "yes"; |
|
attribute TIG of online : signal is "yes"; |
attribute TIG of suspend : signal is "yes"; |
|
constant C_TXFULL : std_logic_vector((C_TXBUFSIZE_BITS-1) downto 0) := (others=>'0'); |
constant C_TXEMPTY : std_logic_vector((C_TXBUFSIZE_BITS-1) downto 0) := (others=>'1'); |
constant C_RXFULL : std_logic_vector((C_RXBUFSIZE_BITS-1) downto 0) := (others=>'1'); |
constant C_RXEMPTY : std_logic_vector((C_RXBUFSIZE_BITS-1) downto 0) := (others=>'0'); |
|
begin -- architecture akre |
|
----------------------------------------------------------------------------- |
-- Instanciating Components |
----------------------------------------------------------------------------- |
|
usb_serial_inst : usb_serial |
generic map ( |
VENDORID => C_VENDORID, |
PRODUCTID => C_PRODUCTID, |
VERSIONBCD => C_VERSIONBCD, |
HSSUPPORT => false, |
SELFPOWERED => C_SELFPOWERED, |
RXBUFSIZE_BITS => C_RXBUFSIZE_BITS, |
TXBUFSIZE_BITS => C_TXBUFSIZE_BITS |
) |
port map ( |
CLK => usb_clk, --in |
RESET => reset, --in |
USBRST => usbrst, --out |
HIGHSPEED => highspeed, --out |
SUSPEND => suspend, --out |
ONLINE => online, --out |
RXVAL => rxval, --out |
RXDAT => rxdat, --out |
RXRDY => rxrdy, --in |
RXLEN => rxlen, --out |
TXVAL => txval, --in |
TXDAT => txdat, --in |
TXRDY => txrdy, --out |
TXROOM => txroom, --out |
TXCORK => '0', --in |
PHY_DATAIN => phy_datain, --in |
PHY_DATAOUT => phy_dataout, --out |
PHY_TXVALID => phy_txvalid, --out |
PHY_TXREADY => phy_txready, --in |
PHY_RXACTIVE => phy_rxactive, --in |
PHY_RXVALID => phy_rxvalid, --in |
PHY_RXERROR => phy_rxerror, --in |
PHY_LINESTATE => phy_linestate, --in |
PHY_OPMODE => open, --out |
PHY_XCVRSELECT => open, --out |
PHY_TERMSELECT => open, --out |
PHY_RESET => phy_reset --out |
); |
|
phy_resetn <= not(phy_reset); |
|
usb_phy_inst : usb_phy |
port map( |
clk => Usb_Clk, --in 48MHz |
rst => phy_resetn, --in |
phy_tx_mode => C_PHYMODE, --in |
usb_rst => phy_usb_rst, --out |
txdp => txdp, --out |
txdn => txdn, --out |
txoe => txoe, --out |
rxd => rxd, --in |
rxdp => rxdp, --in |
rxdn => rxdn, --in |
DataOut_i => phy_dataout, --in |
TxValid_i => phy_txvalid, --in |
TxReady_o => phy_txready, --out |
RxValid_o => phy_rxvalid, --out |
RxActive_o => phy_rxactive, --out |
RxError_o => phy_rxerror, --out |
DataIn_o => phy_datain, --out |
LineState_o => phy_linestate --out |
); |
|
----------------------------------------------------------------------------- |
-- Status register / Control register |
----------------------------------------------------------------------------- |
status_Reg(0) <= rx_Data_Present; |
status_Reg(1) <= rx_BUFFER_FULL; |
status_Reg(2) <= tx_Buffer_Empty; |
status_Reg(3) <= tx_BUFFER_FULL; |
status_Reg(4) <= Interrupt_r; |
status_Reg(5) <= '0'; |
status_Reg(6) <= online; |
status_Reg(7) <= suspend; |
|
|
----------------------------------------------------------------------------- |
-- Control / Status Register Handling |
----------------------------------------------------------------------------- |
|
process (clk, reset) is |
begin |
if (reset = '1') then -- asynchronous reset (active high) |
reset_TX_FIFO <= '1'; |
reset_RX_FIFO <= '1'; |
enable_rxinterrupts <= '0'; |
enable_txinterrupts <= '0'; |
TX_EN <= '0'; |
xfer_Ack2 <= '0'; |
elsif (clk'event and clk = '1') then -- rising clock edge |
reset_TX_FIFO <= '0'; |
reset_RX_FIFO <= '0'; |
xfer_Ack2 <= '0'; |
if (OPB_CS = '1') and (OPB_RNW = '0') and (OPB_ABus = CTRL_REG_ADR) then |
reset_TX_FIFO <= OPB_DBus(0); |
reset_RX_FIFO <= OPB_DBus(1); |
enable_rxinterrupts <= OPB_DBus(4); |
enable_txinterrupts <= OPB_DBus(6); |
TX_EN <= OPB_DBus(7); |
xfer_Ack2 <= '1'; |
end if; |
if (OPB_CS = '1') and (OPB_RNW = '1') and (OPB_ABus = STATUS_REG_ADR) then |
xfer_Ack2 <= '1'; |
end if; |
end if; |
end process; |
|
----------------------------------------------------------------------------- |
-- Interrupt handling |
----------------------------------------------------------------------------- |
|
process (clk, reset) |
begin |
if reset = '1' then -- asynchronous reset (active high) |
Interrupt_r <= '0'; |
elsif clk'event and clk = '1' then -- rising clock edge |
Interrupt_r <= (enable_rxinterrupts and rx_Data_Present) or |
(enable_txinterrupts and tx_Buffer_Empty); |
end if; |
end process; |
|
Interrupt <= Interrupt_r; |
|
----------------------------------------------------------------------------- |
-- Handling the OPB bus interface |
----------------------------------------------------------------------------- |
|
process (clk, OPB_CS) is |
begin |
if (OPB_CS='0') then |
xfer_Ack <= '0'; |
SIn_DBus <= (others => '0'); |
elsif (clk'event and clk='1') then |
xfer_Ack <= xfer_Ack1 or xfer_Ack2; |
SIn_DBus <= (others => '0'); |
if (OPB_RNW='1') then |
if (OPB_ABus = STATUS_REG_ADR) then |
SIn_DBus(7 downto 0) <= status_reg; |
else |
SIn_DBus(7 downto 0) <= rxdat; |
end if; |
end if; |
end if; |
end process; |
|
SIn_xferAck <= xfer_Ack; |
|
----------------------------------------------------------------------------- |
-- Generating read and write pulses to the FIFOs |
----------------------------------------------------------------------------- |
|
process(clk,reset) |
begin |
if (reset='1') then |
read_RX_FIFO <= '0'; |
write_TX_FIFO <= '0'; |
xfer_Ack1 <= '0'; |
elsif (clk'event and clk='1') then |
tx_BUFFER_EMPTY <= txempty; |
tx_BUFFER_FULL <= txfull; |
rx_Data_Present <= rxval; |
rx_Buffer_Full <= rxfull; |
write_TX_FIFO <= '0'; |
read_RX_FIFO <= '0'; |
xfer_Ack1 <= '0'; |
if (OPB_CS='1' and OPB_RNW='0' and OPB_ABus=TX_FIFO_ADR) then |
txdat <= OPB_DBus(7 downto 0); |
write_TX_FIFO <= '1'; |
xfer_Ack1 <= '1'; |
end if; |
if (OPB_CS='1' and OPB_RNW='1' and OPB_ABus=RX_FIFO_ADR) then |
read_RX_FIFO <= '1'; |
xfer_Ack1 <= '1'; |
end if; |
end if; |
end process; |
|
----------------------------------------------------------------------------- |
-- Synchronization logic across clock domains |
----------------------------------------------------------------------------- |
|
process(usb_clk, reset) |
begin |
if (reset='1') then |
read_RX_FIFO_r <= '0'; |
read_RX_FIFO_rr <= '0'; |
read_RX_FIFO_rrr <= '0'; |
write_TX_FIFO_r <= '0'; |
write_TX_FIFO_rr <= '0'; |
write_TX_FIFO_rrr <= '0'; |
elsif (usb_clk'event and usb_clk='1') then |
rxrdy <= '0'; |
txval <= '0'; |
txfull <= '0'; |
rxfull <= '0'; |
txfull <= '0'; |
txempty <= '0'; |
if (rxlen = C_RXFULL) then |
rxfull <= '1'; |
end if; |
if (txroom = C_TXFULL) then |
-- txfull <= '1'; |
txfull <= online and not(suspend); |
end if; |
if (txroom = C_TXEMPTY) then |
txempty <= '1'; |
end if; |
write_TX_FIFO_r <= write_TX_FIFO; |
write_TX_FIFO_rr <= write_TX_FIFO_r; |
write_TX_FIFO_rrr <= write_TX_FIFO_rr; |
read_RX_FIFO_r <= read_RX_FIFO; |
read_RX_FIFO_rr <= read_RX_FIFO_r; |
read_RX_FIFO_rrr <= read_RX_FIFO_rr; |
if (read_RX_FIFO_rrr='1' and read_RX_FIFO_rr='0') then |
rxrdy <= '1'; |
end if; |
if (write_TX_FIFO_rrr='1' and write_TX_FIFO_rr='0') then |
txval <= '1'; |
end if; |
end if; |
end process; |
|
end architecture akre; |
|
|
|
/tb_USBLITE_Core.vhd
0,0 → 1,948
-- |
-- opb_usblite - opb_uartlite replacement |
-- |
-- opb_usblite is using components from Rudolf Usselmann see |
-- http://www.opencores.org/cores/usb_phy/ |
-- and Joris van Rantwijk see http://www.xs4all.nl/~rjoris/fpga/usb.html |
-- |
-- Copyright (C) 2010 Ake Rehnman |
-- |
-- This program is free software: you can redistribute it and/or modify |
-- it under the terms of the GNU Lesser General Public License as published by |
-- the Free Software Foundation, either version 3 of the License, or |
-- (at your option) any later version. |
-- |
-- This program is distributed in the hope that it will be useful, |
-- but WITHOUT ANY WARRANTY; without even the implied warranty of |
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
-- GNU Lesser General Public License for more details. |
-- |
-- You should have received a copy of the GNU Lesser General Public License |
-- along with this program. If not, see <http://www.gnu.org/licenses/>. |
-- |
|
library IEEE; |
use IEEE.std_logic_1164.all; |
use IEEE.numeric_std.all; |
use STD.TEXTIO.all; |
use IEEE.STD_LOGIC_TEXTIO.all; |
|
|
entity tb_USBLITE_Core is |
end entity tb_USBLITE_Core; |
|
library unisim; |
use unisim.all; |
|
architecture akre of tb_USBLITE_Core is |
|
component usb_phy is |
port ( |
clk : in std_logic; |
rst : in std_logic; |
phy_tx_mode : in std_logic; |
usb_rst : out std_logic; |
|
-- Transciever Interface |
txdp : out std_logic; |
txdn : out std_logic; |
txoe : out std_logic; |
rxd : in std_logic; |
rxdp : in std_logic; |
rxdn : in std_logic; |
|
-- UTMI Interface |
DataOut_i : in std_logic_vector (7 downto 0); |
TxValid_i : in std_logic; |
TxReady_o : out std_logic; |
RxValid_o : out std_logic; |
RxActive_o : out std_logic; |
RxError_o : out std_logic; |
DataIn_o : out std_logic_vector (7 downto 0); |
LineState_o : out std_logic_vector (1 downto 0) |
); |
end component usb_phy; |
|
component OPB_USBLITE_Core is |
generic ( |
C_PHYMODE : std_logic := '1'; |
C_VENDORID : std_logic_vector(15 downto 0) := X"1234"; |
C_PRODUCTID : std_logic_vector(15 downto 0) := X"5678"; |
C_VERSIONBCD : std_logic_vector(15 downto 0) := X"0200"; |
C_SELFPOWERED : boolean := false; |
C_RXBUFSIZE_BITS: integer range 7 to 12 := 10; |
C_TXBUFSIZE_BITS: integer range 7 to 12 := 10 |
); |
port ( |
Clk : in std_logic; |
Reset : in std_logic; |
Usb_Clk : in std_logic; |
-- OPB signals |
OPB_CS : in std_logic; |
OPB_ABus : in std_logic_vector(0 to 1); |
OPB_RNW : in std_logic; |
OPB_DBus : in std_logic_vector(7 downto 0); |
SIn_xferAck : out std_logic; |
SIn_DBus : out std_logic_vector(7 downto 0); |
Interrupt : out std_logic; |
-- USB signals |
txdp : out std_logic; |
txdn : out std_logic; |
txoe : out std_logic; |
rxd : in std_logic; |
rxdp : in std_logic; |
rxdn : in std_logic |
); |
end component OPB_USBLITE_Core; |
|
signal clk : std_logic; |
signal usbclk : std_logic; |
signal reset : std_logic; |
signal rxdp : std_logic; |
signal rxdn : std_logic; |
signal rxd : std_logic; |
signal txdp : std_logic; |
signal txdn : std_logic; |
signal txoe : std_logic; |
|
signal usbtxdata : std_logic_vector (7 downto 0); |
signal usbtxvalid : std_logic; |
signal usbtxready : std_logic; |
signal usbrxvalid : std_logic; |
signal usbrxactive : std_logic; |
signal usbrxerror : std_logic; |
signal usbrxdata : std_logic_vector (7 downto 0); |
signal usblinestate : std_logic_vector (1 downto 0); |
|
signal Bus2IP_CS : std_logic := '0'; |
signal Bus2IP_CE : std_logic := '0'; |
signal Bus2IP_Addr : std_logic_vector(0 to 31) := X"00000000"; |
signal Bus2IP_RNW : std_logic := '0'; |
signal Bus2IP_Data : std_logic_vector(0 to 31); |
signal Bus2IP_BE : std_logic_vector(3 downto 0) := "0000"; |
signal IP2Bus_Ack : std_logic := '0'; |
signal IP2Bus_Data : std_logic_vector(0 to 31) := X"00000000"; |
signal Interrupt : std_logic := '0'; |
|
signal resetn : std_logic; |
signal usbrxdata_r : std_logic_vector(7 downto 0); |
|
-- type txmemarray is array (0 to 2048) of std_logic_vector(7 downto 0); |
type txmemarray is array (0 to 255) of std_logic_vector(7 downto 0); |
shared variable txmem : txmemarray; |
|
-- type bufferarray is array (0 to 16384) of std_logic_vector(7 downto 0); |
type bufferarray is array (0 to 255) of std_logic_vector(7 downto 0); |
shared variable buffer1 : bufferarray; |
shared variable buffer0 : bufferarray; |
shared variable buffer1_last : integer; |
shared variable len : integer; |
shared variable setup_pid : std_logic; |
shared variable error_cnt : integer; |
|
constant USBF_T_PID_OUT : std_logic_vector(3 downto 0):="0001"; |
constant USBF_T_PID_IN : std_logic_vector(3 downto 0):="1001"; |
constant USBF_T_PID_SOF : std_logic_vector(3 downto 0):="0101"; |
constant USBF_T_PID_SETUP : std_logic_vector(3 downto 0):="1101"; |
constant USBF_T_PID_DATA0 : std_logic_vector(3 downto 0):="0011"; |
constant USBF_T_PID_DATA1 : std_logic_vector(3 downto 0):="1011"; |
constant USBF_T_PID_DATA2 : std_logic_vector(3 downto 0):="0111"; |
constant USBF_T_PID_MDATA : std_logic_vector(3 downto 0):="1111"; |
constant USBF_T_PID_ACK : std_logic_vector(3 downto 0):="0010"; |
constant USBF_T_PID_NACK : std_logic_vector(3 downto 0):="1010"; |
constant USBF_T_PID_STALL : std_logic_vector(3 downto 0):="1110"; |
constant USBF_T_PID_NYET : std_logic_vector(3 downto 0):="0110"; |
constant USBF_T_PID_PRE : std_logic_vector(3 downto 0):="1100"; |
constant USBF_T_PID_ERR : std_logic_vector(3 downto 0):="1100"; |
constant USBF_T_PID_SPLIT : std_logic_vector(3 downto 0):="1000"; |
constant USBF_T_PID_PING : std_logic_vector(3 downto 0):="0100"; |
constant USBF_T_PID_RES : std_logic_vector(3 downto 0):="0000"; |
|
constant GET_STATUS : std_logic_vector(7 downto 0) := X"00"; |
constant CLEAR_FEATURE : std_logic_vector(7 downto 0) := X"01"; |
constant SET_FEATURE : std_logic_vector(7 downto 0) := X"03"; |
constant SET_ADDRESS : std_logic_vector(7 downto 0) := X"05"; |
constant GET_DESCRIPTOR : std_logic_vector(7 downto 0) := X"06"; |
constant SET_DESCRIPTOR : std_logic_vector(7 downto 0) := X"07"; |
constant GET_CONFIG : std_logic_vector(7 downto 0) := X"08"; |
constant SET_CONFIG : std_logic_vector(7 downto 0) := X"09"; |
constant GET_INTERFACE : std_logic_vector(7 downto 0) := X"0a"; |
constant SET_INTERFACE : std_logic_vector(7 downto 0) := X"0b"; |
constant SYNCH_FRAME : std_logic_vector(7 downto 0) := X"0c"; |
|
procedure utmi_recv_pack (variable size : inout integer) is |
begin |
size := 0; |
while (usbrxactive /= '1') loop |
wait until rising_edge(usbclk); |
end loop; |
while (usbrxactive= '1') loop |
while (usbrxvalid /= '1' and usbrxactive = '1') loop |
wait until rising_edge(usbclk); |
end loop; |
if (usbrxvalid = '1' and usbrxactive = '1') then |
txmem(size) := usbrxdata; |
size := size + 1; |
end if; |
wait until rising_edge(usbclk); |
end loop; |
end procedure utmi_recv_pack; |
|
procedure utmi_send_pack (constant size : integer; signal usbtxdata: out std_logic_vector; signal usbtxvalid: out std_logic) is |
variable n : integer; |
begin |
for n in 0 to size-1 loop |
wait until rising_edge(usbclk); |
usbtxvalid <= '1'; |
usbtxdata <= txmem(n); |
while (usbtxready/='1') loop |
wait until rising_edge(usbclk); |
end loop; |
end loop; |
wait until rising_edge(usbclk); |
usbtxvalid <= '0'; |
end procedure utmi_send_pack; |
|
function crc5 (crc_in:std_logic_vector; din:std_logic_vector) return std_logic_vector is |
variable crc5x : std_logic_vector (4 downto 0); |
begin |
crc5x(0) := din(10) xor din(9) xor din(6) xor din(5) xor din(3) xor din(0) xor crc_in(0) xor crc_in(3) xor crc_in(4); |
crc5x(1) := din(10) xor din(7) xor din(6) xor din(4) xor din(1) xor crc_in(0) xor crc_in(1) xor crc_in(4); |
crc5x(2) := din(10) xor din(9) xor din(8) xor din(7) xor din(6) xor din(3) xor din(2) xor din(0) xor crc_in(0) xor crc_in(1) xor crc_in(2) xor crc_in(3) xor crc_in(4); |
crc5x(3) := din(10) xor din(9) xor din(8) xor din(7) xor din(4) xor din(3) xor din(1) xor crc_in(1) xor crc_in(2) xor crc_in(3) xor crc_in(4); |
crc5x(4) := din(10) xor din(9) xor din(8) xor din(5) xor din(4) xor din(2) xor crc_in(2) xor crc_in(3) xor crc_in(4); |
return crc5x; |
end function crc5; |
|
function crc16 (crc_in: std_logic_vector; din:std_logic_vector) return std_logic_vector is |
variable crc_out : std_logic_vector (15 downto 0); |
begin |
crc_out(0) := din(7) xor din(6) xor din(5) xor din(4) xor din(3) xor |
din(2) xor din(1) xor din(0) xor crc_in(8) xor crc_in(9) xor |
crc_in(10) xor crc_in(11) xor crc_in(12) xor crc_in(13) xor |
crc_in(14) xor crc_in(15); |
crc_out(1) := din(7) xor din(6) xor din(5) xor din(4) xor din(3) xor din(2) xor |
din(1) xor crc_in(9) xor crc_in(10) xor crc_in(11) xor |
crc_in(12) xor crc_in(13) xor crc_in(14) xor crc_in(15); |
crc_out(2) := din(1) xor din(0) xor crc_in(8) xor crc_in(9); |
crc_out(3) := din(2) xor din(1) xor crc_in(9) xor crc_in(10); |
crc_out(4) := din(3) xor din(2) xor crc_in(10) xor crc_in(11); |
crc_out(5) := din(4) xor din(3) xor crc_in(11) xor crc_in(12); |
crc_out(6) := din(5) xor din(4) xor crc_in(12) xor crc_in(13); |
crc_out(7) := din(6) xor din(5) xor crc_in(13) xor crc_in(14); |
crc_out(8) := din(7) xor din(6) xor crc_in(0) xor crc_in(14) xor crc_in(15); |
crc_out(9) := din(7) xor crc_in(1) xor crc_in(15); |
crc_out(10) := crc_in(2); |
crc_out(11) := crc_in(3); |
crc_out(12) := crc_in(4); |
crc_out(13) := crc_in(5); |
crc_out(14) := crc_in(6); |
crc_out(15) := din(7) xor din(6) xor din(5) xor din(4) xor din(3) xor din(2) xor |
din(1) xor din(0) xor crc_in(7) xor crc_in(8) xor crc_in(9) xor |
crc_in(10) xor crc_in(11) xor crc_in(12) xor crc_in(13) xor |
crc_in(14) xor crc_in(15); |
return crc_out; |
end function crc16; |
|
procedure recv_packet(variable pid: inout std_logic_vector(3 downto 0); variable size: inout integer) is |
variable del,n : integer; |
variable crc16r : std_logic_vector(15 downto 0); |
variable x,y : std_logic_vector(7 downto 0); |
variable s : LINE; |
begin |
crc16r := X"ffff"; |
utmi_recv_pack(size); |
for n in 1 to size-3 loop |
y := txmem(n); |
x(7) := y(0); |
x(6) := y(1); |
x(5) := y(2); |
x(4) := y(3); |
x(3) := y(4); |
x(2) := y(5); |
x(1) := y(6); |
x(0) := y(7); |
crc16r := crc16(crc16r, x); |
end loop; |
n := size-2; |
|
y := crc16r(15 downto 8); |
x(7) := y(0); |
x(6) := y(1); |
x(5) := y(2); |
x(4) := y(3); |
x(3) := y(4); |
x(2) := y(5); |
x(1) := y(6); |
x(0) := y(7); |
crc16r(15 downto 8) := not(x); |
|
y := crc16r(7 downto 0); |
x(7) := y(0); |
x(6) := y(1); |
x(5) := y(2); |
x(4) := y(3); |
x(3) := y(4); |
x(2) := y(5); |
x(1) := y(6); |
x(0) := y(7); |
crc16r(7 downto 0) := not(x); |
|
if (crc16r /= txmem(n)&txmem(n+1)) then |
--$display("ERROR: CRC Mismatch: Expected: %h, Got: %h%h (%t)",crc16r, txmem[n], txmem[n+1], $time); |
WRITE(s,string'("ERROR: CRC Mismatch got:")); |
HWRITE(s,crc16r); |
WRITE(s,string'(" expected:")); |
HWRITE(s,txmem(n)&txmem(n+1)); |
report s.all; |
end if; |
|
for n in 0 to size-3 loop |
buffer1(buffer1_last+n) := txmem(n+1); |
end loop; |
n := size-3; |
|
buffer1_last := buffer1_last+n; |
|
-- Check PID |
x := txmem(0); |
|
if (x(7 downto 4) /= not(x(3 downto 0))) then |
--$display("ERROR: Pid Checksum mismatch: Top: %h Bottom: %h (%t)",x[7:4], x[3:0], $time); |
WRITE(s,string'("ERROR: Pid Checksum mismatch: Top: ")); |
HWRITE(s,x(7 downto 4)); |
WRITE(s,string'(" Bottom:")); |
HWRITE(s,x(3 downto 0)); |
report s.all; |
end if; |
|
pid := x(3 downto 0); |
size:= size-3; |
|
end procedure recv_packet; |
|
procedure send_token (constant fa:std_logic_vector(7 downto 0); constant ep:std_logic_vector(3 downto 0); constant pid:std_logic_vector(3 downto 0)) is |
variable tmp_data:std_logic_vector(15 downto 0); |
variable x,y:std_logic_vector(10 downto 0); |
variable len:integer; |
begin |
tmp_data := fa(6 downto 0)&ep&"00000"; |
if (pid=USBF_T_PID_ACK) then |
len := 1; |
else |
len := 3; |
end if; |
y := fa(6 downto 0)&ep; |
x(10) := y(4); |
x(9) := y(5); |
x(8) := y(6); |
x(7) := y(7); |
x(6) := y(8); |
x(5) := y(9); |
x(4) := y(10); |
x(3) := y(0); |
x(2) := y(1); |
x(1) := y(2); |
x(0) := y(3); |
y(4 downto 0) := crc5("11111", x); |
tmp_data(4 downto 0) := not(y(4 downto 0)); |
tmp_data(15 downto 5) := x; |
txmem(0) := (not(pid)&pid); -- PID |
txmem(1) := ( tmp_data(8)&tmp_data(9)&tmp_data(10)&tmp_data(11)&tmp_data(12)&tmp_data(13)&tmp_data(14)&tmp_data(15)); |
txmem(2) := ( tmp_data(0)&tmp_data(1)&tmp_data(2)&tmp_data(3)&tmp_data(4)&tmp_data(5)&tmp_data(6)&tmp_data(7)); |
utmi_send_pack(len,usbtxdata,usbtxvalid); |
end procedure send_token; |
|
procedure send_data (constant pid:std_logic_vector(3 downto 0); constant len:integer; constant mode:integer) is |
variable n : integer; |
variable crc16r : std_logic_vector(15 downto 0); |
variable x,y : std_logic_vector(7 downto 0); |
begin |
txmem(0) := not(pid)&pid; -- PID |
crc16r := X"ffff"; |
for n in 0 to len-1 loop |
if(mode=1) then |
y := buffer1(buffer1_last+n); |
else |
y := std_logic_vector(to_unsigned(n,8)); |
end if; |
|
-- x(7 downto 0) := y(0 to 7); |
x(7) := y(0); |
x(6) := y(1); |
x(5) := y(2); |
x(4) := y(3); |
x(3) := y(4); |
x(2) := y(5); |
x(1) := y(6); |
x(0) := y(7); |
txmem(n+1) := y; |
crc16r := crc16(crc16r, x); |
end loop; |
|
buffer1_last := buffer1_last + len - 1; |
y := crc16r(15 downto 8); |
-- x(7 downto 0) := y(0 to 7); |
x(7) := y(0); |
x(6) := y(1); |
x(5) := y(2); |
x(4) := y(3); |
x(3) := y(4); |
x(2) := y(5); |
x(1) := y(6); |
x(0) := y(7); |
txmem(len+1) := not(x); |
|
y := crc16r(7 downto 0); |
-- x(7 downto 0) := y(0 to 7); |
x(7) := y(0); |
x(6) := y(1); |
x(5) := y(2); |
x(4) := y(3); |
x(3) := y(4); |
x(2) := y(5); |
x(1) := y(6); |
x(0) := y(7); |
txmem(len+2) := not(x); |
|
utmi_send_pack(len+3,usbtxdata,usbtxvalid); |
end procedure send_data; |
|
procedure data_in (constant fa:std_logic_vector(7 downto 0); constant pl_size:integer) is |
variable rlen : integer; |
variable pid : std_logic_vector(3 downto 0); |
variable expected_pid : std_logic_vector(3 downto 0); |
variable s : LINE; |
begin |
buffer1_last := 0; |
send_token( fa, -- Function Address |
X"00", -- Logical Endpoint Number |
USBF_T_PID_IN -- PID |
); |
recv_packet(pid,rlen); |
if (setup_pid='1') then |
expected_pid := X"b"; -- DATA 1 |
else |
expected_pid := X"3"; -- DATA 0 |
end if; |
if (pid /= expected_pid) then |
--$display("ERROR: Data IN PID mismatch. Expected: %h, Got: %h (%t)", expect_pid, pid, $time); |
report "ERROR: Data IN PID mismatch."; |
error_cnt := error_cnt + 1; |
end if; |
|
setup_pid := not(setup_pid); |
if (rlen /= pl_size) then |
report "ERROR: Data IN Size mismatch."; |
--$display("ERROR: Data IN Size mismatch. Expected: %d, Got: %d (%t)", pl_size, rlen, $time); |
error_cnt := error_cnt + 1; |
end if; |
|
DEALLOCATE(s); |
WRITE(s,string'("RCV bytes: ")); |
WRITE(s,rlen); |
report s.all; |
for n in 0 to rlen-1 loop |
-- $display("RCV Data[%0d]: %h",n,buffer1[n]); |
DEALLOCATE(s); |
WRITE(s,string'("RCV Data[")); |
WRITE(s,n); |
WRITE(s,string'("]=")); |
HWRITE(s,buffer1(n)); |
report s.all; |
end loop; |
|
-- repeat(5) @(posedge clk); |
wait for 1 us; |
|
send_token( fa, -- Function Address |
X"00", -- Logical Endpoint Number |
USBF_T_PID_ACK -- PID |
); |
|
-- repeat(5) @(posedge clk); |
wait for 1 us; |
end procedure data_in; |
|
procedure data_out(fa:std_logic_vector(7 downto 0); pl_size:integer) is |
variable len : integer; |
begin |
send_token( fa, -- Function Address |
X"00", -- Logical Endpoint Number |
USBF_T_PID_OUT -- PID |
); |
wait until rising_edge(usbclk); |
|
if (setup_pid='0') then |
send_data(USBF_T_PID_DATA0, pl_size, 1); |
else |
send_data(USBF_T_PID_DATA1, pl_size, 1); |
end if; |
|
setup_pid := not(setup_pid); |
|
-- Wait for ACK |
|
utmi_recv_pack(len); |
|
if(txmem(0) /= X"d2") then |
--$display("ERROR: ACK mismatch. Expected: %h, Got: %h (%t)",8'hd2, txmem[0], $time); |
report "ERROR: SETUP: ACK mismatch"; |
error_cnt := error_cnt + 1; |
end if; |
|
if(len /= 1) then |
--$display("ERROR: SETUP: Length mismatch. Expected: %h, Got: %h (%t)",8'h1, len, $time); |
report "ERROR: SETUP: Length mismatch"; |
error_cnt := error_cnt + 1; |
end if; |
|
-- repeat(5) @(posedge clk); |
wait for 1 us; |
end procedure data_out; |
|
procedure send_setup (constant fa:std_logic_vector(7 downto 0); |
constant req_type:std_logic_vector(7 downto 0); |
constant request:std_logic_vector(7 downto 0); |
constant wValue:std_logic_vector(15 downto 0); |
constant wIndex:std_logic_vector(15 downto 0); |
constant wLength:std_logic_vector(15 downto 0)) is |
begin |
send_token(fa,X"0",USBF_T_PID_SETUP); |
wait for 1 us; |
buffer1(0) := req_type; |
buffer1(1) := request; |
buffer1(3) := wValue(15 downto 8); |
buffer1(2) := wValue(7 downto 0); |
buffer1(5) := wIndex(15 downto 8); |
buffer1(4) := wIndex(7 downto 0); |
buffer1(7) := wLength(15 downto 8); |
buffer1(6) := wLength(7 downto 0); |
buffer1_last := 0; |
send_data(USBF_T_PID_DATA0, 8, 1); |
utmi_recv_pack(len); |
if (txmem(0) /= x"d2") then |
--$display("ERROR: SETUP: ACK mismatch. Expected: %h, Got: %h (%t)", 8'hd2, txmem[0], $time); |
report "ERROR: SETUP: ACK mismatch"; |
error_cnt := error_cnt + 1; |
end if; |
if (len /= 1) then |
--$display("ERROR: SETUP: Length mismatch. Expected: %h, Got: %h (%t)", 8'h1, len, $time); |
report "ERROR: SETUP: Length mismatch. len="&integer'image(len); |
error_cnt := error_cnt + 1; |
end if; |
|
wait until rising_edge(usbclk); |
setup_pid := '1'; |
wait until rising_edge(usbclk); |
|
end procedure send_setup; |
|
procedure send_sof(constant frmn:integer) is |
variable frmnv : std_logic_vector(10 downto 0); |
variable tmp_data : std_logic_vector(15 downto 0); |
variable x,y : std_logic_vector(10 downto 0); |
begin |
frmnv := std_logic_vector(to_unsigned(frmn,11)); |
y := frmnv; |
x(10) := y(0); |
x(9) := y(1); |
x(8) := y(2); |
x(7) := y(3); |
x(6) := y(4); |
x(5) := y(5); |
x(4) := y(6); |
x(3) := y(7); |
x(2) := y(8); |
x(1) := y(9); |
x(0) := y(10); |
|
tmp_data(15 downto 5) := x; |
y(4 downto 0) := crc5( X"1F", x ); |
tmp_data(4 downto 0) := not(y(4 downto 0)); |
txmem(0) := not(USBF_T_PID_SOF)&USBF_T_PID_SOF; -- PID |
txmem(1) := tmp_data(8)&tmp_data(9)&tmp_data(10)&tmp_data(11)& |
tmp_data(12)&tmp_data(13)&tmp_data(14)&tmp_data(15); |
txmem(2) := tmp_data(0)&tmp_data(1)&tmp_data(2)&tmp_data(3)& |
tmp_data(4)&tmp_data(5)&tmp_data(6)&tmp_data(7); |
txmem(1) := frmnv(7 downto 0); |
txmem(2) := tmp_data(0)&tmp_data(1)&tmp_data(2)&tmp_data(3)& |
tmp_data(4)& frmnv(10 downto 8); |
utmi_send_pack(3,usbtxdata,usbtxvalid); |
end procedure send_sof; |
|
|
procedure buswrite(constant adr : in std_logic_vector(31 downto 0); |
constant data : in std_logic_vector(31 downto 0); |
signal Bus2IP_Clk : in std_logic; |
signal Bus2IP_Addr : out std_logic_vector(0 to 31); |
signal Bus2IP_Data : out std_logic_vector(0 to 31); |
signal Bus2IP_BE : out std_logic_vector(0 to 3); |
signal Bus2IP_CS : out std_logic; |
signal Bus2IP_CE : out std_logic; |
signal Bus2IP_RNW : out std_logic; |
signal IP2Bus_Data : in std_logic_vector(0 to 31); |
signal IP2Bus_Ack : in std_logic) is |
begin |
wait until (Bus2IP_Clk='1' and Bus2IP_Clk'event); |
wait for 1 ns; |
Bus2IP_Addr <= adr; |
Bus2IP_Data <= data; |
Bus2IP_RNW <= '0'; |
Bus2IP_CE <= '1'; |
Bus2IP_CS <= '1'; |
Bus2IP_BE <= "1111"; |
wait until (Bus2IP_Clk'event and Bus2IP_Clk='1' and IP2Bus_Ack='1'); |
wait for 1 ns; |
Bus2IP_Addr <= (others=>'0'); |
Bus2IP_Data <= (others=>'0'); |
Bus2IP_RNW <= '0'; |
Bus2IP_CE <= '0'; |
Bus2IP_CS <= '0'; |
Bus2IP_BE <= "0000"; |
end buswrite; |
|
procedure busread (constant adr : in std_logic_vector(31 downto 0); |
variable data : out std_logic_vector(31 downto 0); |
signal Bus2IP_Clk : in std_logic; |
signal Bus2IP_Addr : out std_logic_vector(0 to 31); |
signal Bus2IP_Data : out std_logic_vector(0 to 31); |
signal Bus2IP_BE : out std_logic_vector(0 to 3); |
signal Bus2IP_CS : out std_logic; |
signal Bus2IP_CE : out std_logic; |
signal Bus2IP_RNW : out std_logic; |
signal IP2Bus_Data : in std_logic_vector(0 to 31); |
signal IP2Bus_Ack : in std_logic) is |
begin |
wait until (Bus2IP_Clk='1' and Bus2IP_Clk'event); |
wait for 1 ns; |
Bus2IP_Addr <= adr; |
Bus2IP_Data <= (others=>'0'); |
Bus2IP_RNW <= '1'; |
Bus2IP_CS <= '1'; |
Bus2IP_CE <= '1'; |
Bus2IP_BE <= "1111"; |
wait until (Bus2IP_Clk'event and Bus2IP_Clk='1' and IP2Bus_Ack='1'); |
data := IP2Bus_Data; |
wait for 1 ns; |
Bus2IP_Addr <= (others=>'0'); |
Bus2IP_Data <= (others=>'0'); |
Bus2IP_RNW <= '0'; |
Bus2IP_CS <= '0'; |
Bus2IP_CE <= '0'; |
Bus2IP_BE <= "0000"; |
end busread; |
|
procedure uartread (variable d : out std_logic_vector (7 downto 0)) is |
variable tmp : std_logic_vector(31 downto 0); |
-- variable ss : LINE; |
begin |
loop |
busread(X"00000002", tmp, clk, Bus2IP_Addr, Bus2IP_Data, Bus2IP_BE, Bus2IP_CS, Bus2IP_CE, Bus2IP_RNW, IP2Bus_Data, IP2Bus_Ack); |
exit when ((tmp and X"00000001") /= X"00000000"); |
end loop; |
-- DEALLOCATE(ss); |
-- WRITE(ss,string'("uartstatus=")); |
-- HWRITE(ss,tmp); |
-- report ss.all; |
busread(X"00000000", tmp, clk, Bus2IP_Addr, Bus2IP_Data, Bus2IP_BE, Bus2IP_CS, Bus2IP_CE, Bus2IP_RNW, IP2Bus_Data, IP2Bus_Ack); |
d := tmp(7 downto 0); |
end procedure uartread; |
|
procedure uartwrite (variable d : std_logic_vector (7 downto 0)) is |
variable tmp : std_logic_vector(31 downto 0); |
begin |
loop |
busread(X"00000002", tmp, clk, Bus2IP_Addr, Bus2IP_Data, Bus2IP_BE, Bus2IP_CS, Bus2IP_CE, Bus2IP_RNW, IP2Bus_Data, IP2Bus_Ack); |
exit when ((tmp and X"00000004") /= X"00000000"); |
end loop; |
tmp := X"00000000"; |
tmp(7 downto 0) := d; |
buswrite(X"00000001", tmp, clk, Bus2IP_Addr, Bus2IP_Data, Bus2IP_BE, Bus2IP_CS, Bus2IP_CE, Bus2IP_RNW, IP2Bus_Data, IP2Bus_Ack); |
|
end procedure uartwrite; |
|
shared variable nn : integer; |
shared variable pid : std_logic_vector(3 downto 0); |
shared variable ss : LINE; |
shared variable dd : std_logic_vector(7 downto 0); |
shared variable rlen : integer; |
begin |
|
|
resetn <= not(reset); |
rxd <= rxdp; |
|
usb_phy_inst : usb_phy |
port map ( |
clk => usbclk, |
rst => resetn, |
phy_tx_mode => '1', |
usb_rst => open, |
txdp => rxdp, |
txdn => rxdn, |
txoe => open, |
rxd => txdp, |
rxdp => txdp, |
rxdn => txdn, |
|
-- UTMI Interface |
DataOut_i => usbtxdata, |
TxValid_i => usbtxvalid, |
TxReady_o => usbtxready, |
RxValid_o => usbrxvalid, |
RxActive_o => usbrxactive, |
RxError_o => usbrxerror, |
DataIn_o => usbrxdata, |
LineState_o => usblinestate |
); |
|
|
OPB_USBLITE_Core_inst : OPB_USBLITE_Core |
port map ( |
Clk => clk, |
Reset => reset, |
Usb_Clk => usbclk, |
OPB_CS => Bus2IP_CS, |
OPB_ABus => Bus2IP_Addr(30 to 31), |
OPB_RNW => Bus2IP_RNW, |
OPB_DBus => Bus2IP_Data(24 to 31), |
SIn_xferAck => IP2Bus_Ack, |
SIn_DBus => IP2Bus_Data(24 to 31), |
Interrupt => Interrupt, |
txdp => txdp, |
txdn => txdn, |
txoe => txoe, |
rxd => rxd, |
rxdp => rxdp, |
rxdn => rxdn |
); |
|
|
process |
begin |
clk <= '0'; |
wait for 6.66666ns; |
clk <= '1'; |
wait for 6.66666ns; |
end process; |
|
process |
begin |
usbclk <= '0'; |
wait for 10.41666ns; |
usbclk <= '1'; |
wait for 10.41666ns; |
end process; |
|
process(usbclk) |
begin |
if (usbclk'event and usbclk='1') then |
if (usbrxvalid='1') then |
usbrxdata_r <= usbrxdata; |
end if; |
end if; |
end process; |
|
process |
begin |
wait for 100ns; |
reset <= '1'; |
wait for 100ns; |
reset <= '0'; |
wait for 100 us; |
|
report "Setting Address ..."; |
wait for 1 us; |
send_setup( X"00", -- Function Address |
X"00", -- Request Type |
SET_ADDRESS, -- Request |
X"0012", -- wValue |
X"0000", -- wIndex |
X"0000" -- wLength |
); |
|
-- Status OK |
data_in( X"00", -- Function Address |
0 -- Expected payload size |
); |
|
report("Getting DEVICE descriptor ..."); |
send_setup( X"12", -- Function Address |
X"80", -- Request Type |
GET_DESCRIPTOR, -- Request |
X"0100", -- wValue |
X"0000", -- wIndex |
X"0008" -- wLength |
); |
|
data_in( X"12", -- Function Address |
8 -- Expected payload size |
); |
|
-- Status OK |
data_out( X"12", -- Function Address |
0 -- Expected payload size |
); |
|
report("Getting whole DEVICE descriptor ..."); |
send_setup( X"12", -- Function Address |
X"80", -- Request Type |
GET_DESCRIPTOR, -- Request |
X"0100", -- wValue |
X"0000", -- wIndex |
X"0012" -- wLength |
); |
|
data_in( X"12", -- Function Address |
18 -- Expected payload size |
); |
|
-- Status OK |
data_out( X"12", -- Function Address |
0 -- Expected payload size |
); |
|
report "Getting CONFIGURATION descriptor ..."; |
send_setup( X"12", -- Function Address |
X"80", -- Request Type |
GET_DESCRIPTOR, -- Request |
X"0200", -- wValue |
X"0000", -- wIndex |
X"0008" -- wLength |
); |
|
data_in( X"12", -- Function Address |
8 -- Expected payload size |
); |
|
-- Status OK |
data_out( X"12", -- Function Address |
0 -- Expected payload size |
); |
|
report "Getting whole CONFIGURATION descriptor ..."; |
send_setup( X"12", -- Function Address |
X"80", -- Request Type |
GET_DESCRIPTOR, -- Request |
X"0200", -- wValue |
X"0000", -- wIndex |
X"0043" -- wLength |
); |
|
data_in( X"12", -- Function Address |
64 -- Expected payload size |
); |
|
-- Status OK |
data_out( X"12", -- Function Address |
0 -- Expected payload size |
); |
|
data_in( X"12", -- Function Address |
3 -- Expected payload size |
); |
|
-- Status OK |
data_out( X"12", -- Function Address |
0 -- Expected payload size |
); |
|
report "Set configuration 1..."; |
send_setup( X"12", -- Function Address |
X"00", -- Request Type |
SET_CONFIG, -- Request |
X"0001", -- wValue |
X"0000", -- wIndex |
X"0000" -- wLength |
); |
|
data_in( X"12", -- Function Address |
0 -- Expected payload size |
); |
|
wait for 1 us; |
|
report "EP1 OUT..."; |
for nn in 0 to 63 loop |
buffer1(nn) := std_logic_vector(to_unsigned(nn+32,8)); |
end loop; |
buffer1_last := 0; |
pid := "0000"; |
|
send_sof(0); -- Send SOF |
wait until rising_edge(usbclk); |
|
send_token( X"12", -- Function Address |
X"1", -- Logical Endpoint Number |
USBF_T_PID_OUT -- PID |
); |
|
wait until rising_edge(usbclk); |
|
if (pid="0000") then |
send_data(USBF_T_PID_DATA0, 64, 1); |
else |
send_data(USBF_T_PID_DATA1, 64, 1); |
end if; |
|
pid := not(pid); |
|
-- Wait for ACK |
utmi_recv_pack(len); |
|
for nn in 0 to 63 loop |
uartread(dd); |
DEALLOCATE(ss); |
WRITE(ss,string'("uartread=")); |
HWRITE(ss,dd); |
report ss.all; |
end loop; |
|
wait for 1 us; |
|
report "EP1 IN..."; |
|
for nn in 0 to 63 loop |
dd := std_logic_vector(to_unsigned(nn,8)); |
DEALLOCATE(ss); |
WRITE(ss,string'("uartwrite=")); |
HWRITE(ss,dd); |
report ss.all; |
uartwrite(dd); |
end loop; |
|
|
-- Send Data |
send_sof(1); -- Send SOF |
send_token( X"12", -- Function Address |
X"1", -- Logical Endpoint Number |
USBF_T_PID_IN -- PID |
); |
|
recv_packet(pid,rlen); |
|
for nn in 0 to 63 loop |
dd := txmem(nn+1); |
DEALLOCATE(ss); |
WRITE(ss,string'("usb recv=")); |
HWRITE(ss,dd); |
report ss.all; |
end loop; |
|
send_token( X"12", -- Function Address |
X"1", -- Logical Endpoint Number |
USBF_T_PID_ACK -- PID |
); |
|
wait for 10us; |
|
reset <= '1'; |
wait for 1us; |
reset <= '0'; |
|
wait; |
end process; |
|
|
end architecture akre; |
/opb_usblite.vhd
0,0 → 1,204
-- |
-- opb_usblite - opb_uartlite replacement |
-- |
-- opb_usblite is using components from Rudolf Usselmann see |
-- http://www.opencores.org/cores/usb_phy/ |
-- and Joris van Rantwijk see http://www.xs4all.nl/~rjoris/fpga/usb.html |
-- |
-- Copyright (C) 2010 Ake Rehnman |
-- |
-- This program is free software: you can redistribute it and/or modify |
-- it under the terms of the GNU Lesser General Public License as published by |
-- the Free Software Foundation, either version 3 of the License, or |
-- (at your option) any later version. |
-- |
-- This program is distributed in the hope that it will be useful, |
-- but WITHOUT ANY WARRANTY; without even the implied warranty of |
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
-- GNU Lesser General Public License for more details. |
-- |
-- You should have received a copy of the GNU Lesser General Public License |
-- along with this program. If not, see <http://www.gnu.org/licenses/>. |
-- |
library IEEE; |
use IEEE.std_logic_1164.all; |
|
entity OPB_USBLITE is |
generic ( |
C_OPB_AWIDTH : integer := 32; |
C_OPB_DWIDTH : integer := 32; |
C_BASEADDR : std_logic_vector(0 to 31) := X"FFFF_0000"; |
C_HIGHADDR : std_logic_vector := X"FFFF_00FF"; |
C_SYSRST : std_logic := '1'; -- enable external reset |
C_PHYMODE : std_logic := '1'; -- phy mode |
C_VENDORID : std_logic_vector(15 downto 0) := X"1234"; -- VID |
C_PRODUCTID : std_logic_vector(15 downto 0) := X"5678"; -- PID |
C_VERSIONBCD : std_logic_vector(15 downto 0) := X"0200"; -- device version |
C_SELFPOWERED : boolean := false; -- self or bus powered |
C_RXBUFSIZE_BITS: integer range 7 to 12 := 10; -- size of rx buf (2^10 = 1024 bytes) |
C_TXBUFSIZE_BITS: integer range 7 to 12 := 10 -- size of tx buf (2^10 = 1024 bytes) |
); |
port ( |
-- Global signals |
OPB_Clk : in std_logic; |
OPB_Rst : in std_logic; |
SYS_Rst : in std_logic; |
USB_Clk : in std_logic; |
-- OPB signals |
OPB_ABus : in std_logic_vector(0 to 31); |
OPB_BE : in std_logic_vector(0 to 3); |
OPB_RNW : in std_logic; |
OPB_select : in std_logic; |
OPB_seqAddr : in std_logic; |
OPB_DBus : in std_logic_vector(0 to 31); |
Sl_DBus : out std_logic_vector(0 to 31); |
Sl_errAck : out std_logic; |
Sl_retry : out std_logic; |
Sl_toutSup : out std_logic; |
Sl_xferAck : out std_logic; |
|
-- Interrupt |
Interrupt : out std_logic; |
|
-- USB signals |
txdp : out std_logic; -- connect to VPO |
txdn : out std_logic; -- connect to VMO/FSEO |
txoe : out std_logic; -- connect to OE |
rxd : in std_logic; -- connect to RCV |
rxdp : in std_logic; -- connect to VP |
rxdn : in std_logic -- connect to VM |
); |
|
end entity OPB_USBLITE; |
|
library Common_v1_00_a; |
use Common_v1_00_a.pselect; |
|
library unisim; |
use unisim.all; |
|
library opb_usblite_v1_00_a; |
use opb_usblite_v1_00_a.opb_usblite_core; |
|
architecture akre of OPB_USBLITE is |
|
component pselect is |
generic ( |
C_AB : integer; |
C_AW : integer; |
C_BAR : std_logic_vector); |
port ( |
A : in std_logic_vector(0 to C_AW-1); |
AValid : in std_logic; |
ps : out std_logic); |
end component pselect; |
|
component OPB_USBLITE_Core is |
generic ( |
C_PHYMODE : std_logic := '1'; |
C_VENDORID : std_logic_vector(15 downto 0) := X"1234"; |
C_PRODUCTID : std_logic_vector(15 downto 0) := X"5678"; |
C_VERSIONBCD : std_logic_vector(15 downto 0) := X"0200"; |
C_SELFPOWERED : boolean := false; |
C_RXBUFSIZE_BITS: integer range 7 to 12 := 10; |
C_TXBUFSIZE_BITS: integer range 7 to 12 := 10 |
); |
port ( |
Clk : in std_logic; |
Reset : in std_logic; |
Usb_Clk : in std_logic; |
-- OPB signals |
OPB_CS : in std_logic; |
OPB_ABus : in std_logic_vector(0 to 1); |
OPB_RNW : in std_logic; |
OPB_DBus : in std_logic_vector(7 downto 0); |
SIn_xferAck : out std_logic; |
SIn_DBus : out std_logic_vector(7 downto 0); |
Interrupt : out std_logic; |
-- USB signals |
txdp : out std_logic; |
txdn : out std_logic; |
txoe : out std_logic; |
rxd : in std_logic; |
rxdp : in std_logic; |
rxdn : in std_logic |
); |
end component OPB_USBLITE_Core; |
|
function nbits (x, y : std_logic_vector(0 to C_OPB_AWIDTH-1)) return integer is |
begin |
for i in 0 to C_OPB_AWIDTH-1 loop |
if x(i) /= y(i) then |
return i; |
end if; |
end loop; |
return(C_OPB_AWIDTH); |
end function nbits; |
|
constant C_NBITS : integer := nbits(C_HIGHADDR, C_BASEADDR); |
|
signal OPB_CS : std_logic; |
signal core_rst : std_logic; |
|
begin -- architecture akre |
|
|
----------------------------------------------------------------------------- |
-- OPB bus interface |
----------------------------------------------------------------------------- |
|
-- Do the OPB address decoding |
pselect_I : pselect |
generic map ( |
C_AB => C_NBITS, |
C_AW => C_OPB_AWIDTH, |
C_BAR => C_BASEADDR) |
port map ( |
A => OPB_ABus, |
AValid => OPB_select, |
ps => OPB_CS); |
|
|
Sl_errAck <= '0'; |
Sl_retry <= '0'; |
Sl_toutSup <= '0'; |
Sl_DBus(0 to 23) <= (others=>'0'); |
|
----------------------------------------------------------------------------- |
-- Instanciating the USB core |
----------------------------------------------------------------------------- |
|
core_rst <= SYS_Rst when C_SYSRST='1' else OPB_Rst; |
|
OPB_USBLITE_Core_inst : OPB_USBLITE_Core |
generic map ( |
C_PHYMODE => C_PHYMODE, |
C_VENDORID => C_VENDORID, |
C_PRODUCTID => C_PRODUCTID, |
C_VERSIONBCD => C_VERSIONBCD, |
C_SELFPOWERED => C_SELFPOWERED, |
C_RXBUFSIZE_BITS => C_RXBUFSIZE_BITS, |
C_TXBUFSIZE_BITS => C_TXBUFSIZE_BITS |
) |
port map ( |
Clk => OPB_Clk, |
Reset => core_rst, |
Usb_Clk => USB_Clk, |
-- OPB signals |
OPB_CS => OPB_CS, |
OPB_ABus => OPB_ABus(28 to 29), |
OPB_RNW => OPB_RNW, |
OPB_DBus => OPB_DBus(24 to 31), |
SIn_xferAck => Sl_xferAck, |
SIn_DBus => Sl_DBus(24 to 31), |
Interrupt => Interrupt, |
-- USB signals |
txdp => txdp, |
txdn => txdn, |
txoe => txoe, |
rxd => rxd, |
rxdp => rxdp, |
rxdn => rxdn |
); |
|
end architecture akre; |
/usb_serial.vhdl
0,0 → 1,1061
-- |
-- 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; |
|
/usb_transact.vhdl
0,0 → 1,628
-- |
-- 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; |
/usb_control.vhdl
0,0 → 1,623
-- |
-- USB 2.0 Default control endpoint |
-- |
-- This entity implements the minimal required functionality of |
-- the default control endpoint. |
-- |
-- The low-level interface signals are named T_xxx and should be conditionally |
-- connected to the usb_transact interface in the following way: |
-- * Always connect output signal C_ADDR to T_ADDR; |
-- * Always connect input signals T_FIN, T_OSYNC, T_RXRDY, T_RXDAT, T_TXRDY; |
-- * If T_ENDPT = 0, connect input signals T_IN, T_OUT, T_SETUP, T_PING; |
-- otherwise pull these inputs to zero. |
-- * If T_ENDPT = 0, connect output signals T_NAK, T_STALL, T_NYET, T_SEND, |
-- T_ISYNC, T_TXDAT; otherwise another endpoint should drive these. |
-- * If T_ENDPT = 0 and C_DSCBUSY = 0, connect output signal T_TXDAT; |
-- otherwise if T_ENDPT = 0 and C_DSCBUSY = 1, drive T_TXDAT |
-- from descriptor memory; |
-- otherwise another endpoint drives T_TXDAT. |
-- |
-- A device descriptor and a configuration descriptor must be provided |
-- in external memory. If high speed mode is supported, an other-speed |
-- device qualifier and other-speed configuration descriptor must also |
-- be provided. In addition, string descriptors may optionally be provided. |
-- Each descriptor may be at most 255 bytes long. |
-- A maximum packet size of 64 bytes is assumed for control transfers. |
-- |
-- This entity uses the following protocol to access descriptor data: |
-- * When C_DSCBUSY is high, the entity is accessing descriptor data. |
-- A descriptor is selected by signals C_DSCTYP and C_DSCINX; |
-- a byte within this descriptor is selected by signal C_DSCOFF. |
-- * Based on C_DSCTYP and C_DSCINX, the application must assign |
-- the length of the selected descriptor to C_DSCLEN. If the selected |
-- descriptor does not exist, the application must set C_DSCLEN to zero. |
-- C_DSCLEN must be valid one clock after rising C_DSCBUSY and must |
-- remain stable as long as C_DSCBUSY, C_DSCTYP and C_DSCINX remain |
-- unchanged. |
-- * When C_DSCRD is asserted, the application must put the selected |
-- byte from the selected descriptor on T_TXDAT towards usb_transact. |
-- The application must respond in the first clock cycle following |
-- assertion of C_DSCRD. |
-- * When C_DSCRD is not asserted, but C_DSCBUSY is still high, |
-- the application must keep T_TXDAT unchanged. Changes to C_DSCOFF |
-- must not affect T_TXDAT while C_DSCRD is low. |
-- |
-- The standard device requests are handled as follows: |
-- |
-- Clear Feature: |
-- When clearing the ENDPOINT_HALT feature, reset the endpoint's |
-- sync bits (as required by spec). Otherwise ignore but report |
-- success status. |
-- BAD: should return STALL when referring to invalid endpoint/interface. |
-- |
-- Get Configuration: |
-- Return 1 if configured, 0 if not configured. |
-- |
-- Get Descriptor: |
-- Handled by application through descriptor data interface as |
-- described above. |
-- |
-- Get Interface: |
-- Always return zero byte. |
-- BAD: should return STALL when referring to invalid endpoint/interface. |
-- |
-- Get Status: |
-- Return device status / endpoint status / zero. |
-- BAD: should return STALL when referring to invalid endpoint/interface. |
-- |
-- Set Address: |
-- Store new address. |
-- |
-- Set Configuration: |
-- Switch between Configured and Address states; clear all endpoint |
-- sync bits (as required by spec). Accepts only configuration values |
-- 0 and 1. |
-- |
-- Set Descriptor: |
-- Not implemented; returns STALL. (Correct; request is optional.) |
-- |
-- Set Feature: |
-- Only ENDPOINT_HALT feature implemented; otherwise returns STALL. |
-- BAD: every high speed device must support TEST_MODE. |
-- |
-- Set Interface: |
-- Not implemented; returns STALL. |
-- (Correct; request is optional if no interfaces have alternate settings.) |
-- |
-- Synch Frame: |
-- Not implemented; returns STALL. |
-- (Correct, assuming no isosynchronous endpoints.) |
-- |
-- Non-standard requests are silently ignored but return success status. |
-- This is incorrect, but necessary to get host software to accept usb_serial |
-- as CDC-ACM device. |
-- |
|
library ieee; |
use ieee.std_logic_1164.all, ieee.numeric_std.all; |
|
entity usb_control is |
|
generic ( |
|
-- Highest endpoint number in use. |
NENDPT : integer range 1 to 15 ); |
|
port ( |
|
-- 60 MHz UTMI clock. |
CLK : in std_logic; |
|
-- Synchronous reset of this entity. |
RESET : in std_logic; |
|
-- Current device address. |
C_ADDR : out std_logic_vector(6 downto 0); |
|
-- High when in Configured state. |
C_CONFD : out std_logic; |
|
-- Trigger clearing of sync/halt bits for IN endpoint. |
C_CLRIN : out std_logic_vector(1 to NENDPT); |
|
-- Trigger clearing of sync/halt bits for OUT endpoint. |
C_CLROUT : out std_logic_vector(1 to NENDPT); |
|
-- Current status of halt bit for IN endpoints. |
C_HLTIN : in std_logic_vector(1 to NENDPT); |
|
-- Current status of halt bit for IN endpoints. |
C_HLTOUT : in std_logic_vector(1 to NENDPT); |
|
-- Trigger setting of halt bit for IN endpoints. |
C_SHLTIN : out std_logic_vector(1 to NENDPT); |
|
-- Trigger setting of halt bit for OUT endpoints. |
C_SHLTOUT : out std_logic_vector(1 to NENDPT); |
|
-- High when accessing descriptor memory. |
-- Note that C_DSCBUSY may go low in between packets of a single descriptor. |
C_DSCBUSY : out std_logic; |
|
-- Descriptor read enable. Asserted to request a descriptor byte; |
-- in the next clock cycle, the application must update T_TXDAT. |
C_DSCRD : out std_logic; |
|
-- LSB bits of the requested descriptor type. Valid when C_DSCBUSY is high. |
C_DSCTYP : out std_logic_vector(2 downto 0); |
|
-- Requested descriptor index. Valid when C_DSCBUSY is high. |
C_DSCINX : out std_logic_vector(7 downto 0); |
|
-- Offset within requested descriptor. Valid when C_DSCBUSY and C_DSCRD are high. |
C_DSCOFF : out std_logic_vector(7 downto 0); |
|
-- Set to length of current descriptor by application. |
C_DSCLEN : in std_logic_vector(7 downto 0); |
|
-- High if the device is not drawing bus power. |
C_SELFPOWERED : in std_logic; |
|
-- Connect to T_IN from usb_transact when T_ENDPT = 0, otherwise pull to 0. |
T_IN : in std_logic; |
|
-- Connect to T_OUT from usb_transact when T_ENDPT = 0, otherwise pull to 0. |
T_OUT : in std_logic; |
|
-- Connect to T_SETUP from usb_transact when T_ENDPT = 0, otherwise pull to 0. |
T_SETUP : in std_logic; |
|
-- Connect to T_PING from usb_transact when T_ENDPT = 0, otherwise pull to 0. |
T_PING : in std_logic; |
|
-- Connect to T_FIN from ubs_transact. |
T_FIN : in std_logic; |
|
-- Connect to T_NAK towards usb_transact when T_ENDPT = 0. |
T_NAK : out std_logic; |
|
-- Connect to T_STALL towards usb_transact when T_ENDPT = 0. |
T_STALL : out std_logic; |
|
-- Connect to T_NYET towards usb_transact when T_ENDPT = 0. |
T_NYET : out std_logic; |
|
-- Connect to T_SEND towards usb_transact when T_ENDPT = 0. |
T_SEND : out std_logic; |
|
-- Connect to T_ISYNC towards usb_transact when T_ENDPT = 0. |
T_ISYNC : out std_logic; |
|
-- Connect to T_OSYNC from usb_transact. |
T_OSYNC : in std_logic; |
|
-- Connect to T_RXRDY from usb_transact. |
T_RXRDY : in std_logic; |
|
-- Connect to T_RXDAT from usb_transact. |
T_RXDAT : in std_logic_vector(7 downto 0); |
|
-- Connect to T_TXRDY from usb_transact. |
T_TXRDY : in std_logic; |
|
-- Connect to T_TXDAT towards usb_transact when T_ENDPT = 0 and C_DSCBUSY = '0'. |
T_TXDAT : out std_logic_vector(7 downto 0) ); |
|
end entity usb_control; |
|
architecture usb_control_arch of usb_control is |
|
-- Constants for control request |
constant req_getstatus : std_logic_vector(3 downto 0) := "0000"; |
constant req_clearfeature : std_logic_vector(3 downto 0) := "0001"; |
constant req_setfeature : std_logic_vector(3 downto 0) := "0011"; |
constant req_setaddress : std_logic_vector(3 downto 0) := "0101"; |
constant req_getdesc : std_logic_vector(3 downto 0) := "0110"; |
constant req_getconf : std_logic_vector(3 downto 0) := "1000"; |
constant req_setconf : std_logic_vector(3 downto 0) := "1001"; |
constant req_getiface : std_logic_vector(3 downto 0) := "1010"; |
|
-- State machine |
type t_state is ( |
ST_IDLE, ST_STALL, |
ST_SETUP, ST_SETUPERR, ST_NONSTANDARD, ST_ENDSETUP, ST_WAITIN, |
ST_SENDRESP, ST_STARTDESC, ST_SENDDESC, ST_DONESEND ); |
signal s_state : t_state := ST_IDLE; |
|
-- Current control request |
signal s_ctlrequest : std_logic_vector(3 downto 0); |
signal s_ctlparam : std_logic_vector(7 downto 0); |
signal s_desctyp : std_logic_vector(2 downto 0); |
signal s_answerlen : unsigned(7 downto 0); |
signal s_sendbyte : std_logic_vector(7 downto 0) := "00000000"; |
|
-- Device state |
signal s_addr : std_logic_vector(6 downto 0) := "0000000"; |
signal s_confd : std_logic := '0'; |
|
-- Counters |
signal s_setupptr : unsigned(2 downto 0); |
signal s_answerptr : unsigned(7 downto 0); |
|
begin |
|
-- Status signals |
C_ADDR <= s_addr; |
C_CONFD <= s_confd; |
|
-- Memory interface |
C_DSCBUSY <= T_IN when (s_state = ST_WAITIN) else |
'1' when (s_state = ST_STARTDESC or s_state = ST_SENDDESC) else |
'0'; |
C_DSCRD <= '1' when (s_state = ST_STARTDESC) else T_TXRDY; |
C_DSCTYP <= s_desctyp; |
C_DSCINX <= s_ctlparam; |
C_DSCOFF <= std_logic_vector(s_answerptr); |
|
-- Transaction interface |
T_NAK <= '0'; |
T_STALL <= '1' when (s_state = ST_STALL) else '0'; |
T_NYET <= '0'; |
T_SEND <= '1' when ((s_state = ST_SENDRESP) or (s_state = ST_SENDDESC)) |
else '0'; |
T_ISYNC <= not std_logic(s_answerptr(6)); |
T_TXDAT <= s_sendbyte; |
|
-- On every rising clock edge |
process is |
begin |
wait until rising_edge(CLK); |
|
-- Set endpoint reset/halt lines to zero by default |
C_CLRIN <= (others => '0'); |
C_CLROUT <= (others => '0'); |
C_SHLTIN <= (others => '0'); |
C_SHLTOUT <= (others => '0'); |
|
-- State machine |
if RESET = '1' then |
|
-- Reset this entity |
s_state <= ST_IDLE; |
s_addr <= "0000000"; |
s_confd <= '0'; |
|
-- Trigger endpoint reset lines |
C_CLRIN <= (others => '1'); |
C_CLROUT <= (others => '1'); |
|
else |
|
case s_state is |
|
when ST_IDLE => |
-- Idle; wait for SETUP transaction; |
-- OUT transactions are ignored but acknowledged; |
-- IN transactions send an empty packet. |
s_answerptr <= to_unsigned(0, s_answerptr'length); |
if T_SETUP = '1' then |
-- Start of SETUP transaction |
s_state <= ST_SETUP; |
s_setupptr <= to_unsigned(0, s_setupptr'length); |
end if; |
|
when ST_STALL => |
-- Stalled; wait for next SETUP transaction; |
-- respond to IN/OUT transactions with a STALL handshake. |
if T_SETUP = '1' then |
-- Start of SETUP transaction |
s_state <= ST_SETUP; |
s_setupptr <= to_unsigned(0, s_setupptr'length); |
end if; |
|
when ST_SETUP => |
-- In SETUP transaction; parse request structure. |
s_answerptr <= to_unsigned(0, s_answerptr'length); |
if T_RXRDY = '1' then |
-- Process next request byte |
case s_setupptr is |
when "000" => |
-- bmRequestType |
s_ctlparam <= T_RXDAT; |
if T_RXDAT(6 downto 5) /= "00" then |
-- non-standard device request |
s_state <= ST_NONSTANDARD; |
end if; |
when "001" => |
-- bRequest |
s_ctlrequest <= T_RXDAT(3 downto 0); |
if T_RXDAT(7 downto 4) /= "0000" then |
-- Unknown request |
s_state <= ST_SETUPERR; |
end if; |
when "010" => |
-- wValue lsb |
if s_ctlrequest /= req_getstatus then |
s_ctlparam <= T_RXDAT; |
end if; |
when "011" => |
-- wValue msb |
if s_ctlrequest = req_getdesc then |
if T_RXDAT(7 downto 3) /= "00000" then |
-- Unsupported descriptor type |
s_state <= ST_SETUPERR; |
end if; |
end if; |
-- Store descriptor type (assuming GET_DESCRIPTOR request) |
s_desctyp <= T_RXDAT(2 downto 0); |
when "100" => |
-- wIndex lsb |
case s_ctlrequest is |
when req_clearfeature => |
if s_ctlparam = "00000000" then |
-- Clear ENDPOINT_HALT feature; |
-- store endpoint selector |
s_ctlparam <= T_RXDAT; |
else |
-- Unknown clear feature request |
s_ctlparam <= "00000000"; |
end if; |
when req_setfeature => |
if s_ctlparam = "00000000" then |
-- Set ENDPOINT_HALT feature; |
-- store endpoint selector |
s_ctlparam <= T_RXDAT; |
else |
-- Unsupported set feature request |
s_state <= ST_SETUPERR; |
end if; |
when req_getstatus => |
if s_ctlparam(1 downto 0) = "00" then |
-- Get device status |
s_sendbyte <= "0000000" & C_SELFPOWERED; |
s_ctlparam <= "00000000"; |
elsif s_ctlparam(1 downto 0) = "10" then |
-- Get endpoint status |
s_sendbyte <= "00000000"; |
s_ctlparam <= T_RXDAT; |
else |
-- Probably get interface status |
s_sendbyte <= "00000000"; |
s_ctlparam <= "00000000"; |
end if; |
when others => |
-- Don't care about index. |
end case; |
when "101" => |
-- wIndex msb; don't care |
when "110" => |
-- wLength lsb |
s_answerlen <= unsigned(T_RXDAT); |
when "111" => |
-- wLength msb |
if T_RXDAT /= "00000000" then |
s_answerlen <= "11111111"; |
end if; |
s_state <= ST_ENDSETUP; |
when others => |
-- Impossible |
end case; |
-- Increment position within SETUP packet |
s_setupptr <= s_setupptr + 1; |
elsif T_FIN = '1' then |
-- Got short SETUP packet; answer with STALL status. |
s_state <= ST_STALL; |
elsif T_SETUP = '0' then |
-- Got corrupt SETUP packet; ignore. |
s_state <= ST_IDLE; |
end if; |
|
when ST_SETUPERR => |
-- In SETUP transaction; got request error |
if T_FIN = '1' then |
-- Got good SETUP packet that causes request error |
s_state <= ST_STALL; |
elsif T_SETUP = '0' then |
-- Got corrupt SETUP packet; ignore |
s_state <= ST_IDLE; |
end if; |
|
when ST_NONSTANDARD => |
-- Ignore non-standard requests |
if T_SETUP = '0' then |
s_state <= ST_IDLE; |
end if; |
|
when ST_ENDSETUP => |
-- Parsed request packet; wait for end of SETUP transaction |
if T_FIN = '1' then |
-- Got complet SETUP packet; handle it |
case s_ctlrequest is |
when req_getstatus => |
-- Prepare status byte and move to data stage |
-- If s_ctlparam = 0, the status byte has already |
-- been prepared in state S_SETUP. |
for i in 1 to NENDPT loop |
if unsigned(s_ctlparam(3 downto 0)) = i then |
if s_ctlparam(7) = '1' then |
s_sendbyte <= "0000000" & C_HLTIN(i); |
else |
s_sendbyte <= "0000000" & C_HLTOUT(i); |
end if; |
end if; |
end loop; |
s_state <= ST_WAITIN; |
when req_clearfeature => |
-- Reset endpoint |
for i in 1 to NENDPT loop |
if unsigned(s_ctlparam(3 downto 0)) = i then |
if s_ctlparam(7) = '1' then |
C_CLRIN(i) <= '1'; |
else |
C_CLROUT(i) <= '1'; |
end if; |
end if; |
end loop; |
s_state <= ST_IDLE; |
when req_setfeature => |
-- Set endpoint HALT |
for i in 1 to NENDPT loop |
if unsigned(s_ctlparam(3 downto 0)) = i then |
if s_ctlparam(7) = '1' then |
C_SHLTIN(i) <= '1'; |
else |
C_SHLTOUT(i) <= '1'; |
end if; |
end if; |
end loop; |
s_state <= ST_IDLE; |
when req_setaddress => |
-- Move to status stage |
s_state <= ST_WAITIN; |
when req_getdesc => |
-- Move to data stage |
s_state <= ST_WAITIN; |
when req_getconf => |
-- Move to data stage |
s_state <= ST_WAITIN; |
when req_setconf => |
-- Set device configuration |
if s_ctlparam(7 downto 1) = "0000000" then |
s_confd <= s_ctlparam(0); |
s_state <= ST_IDLE; |
C_CLRIN <= (others => '1'); |
C_CLROUT <= (others => '1'); |
else |
-- Unknown configuration number |
s_state <= ST_STALL; |
end if; |
when req_getiface => |
-- Move to data stage |
s_state <= ST_WAITIN; |
when others => |
-- Unsupported request |
s_state <= ST_STALL; |
end case; |
elsif T_SETUP = '0' then |
-- Got corrupt SETUP packet; ignore |
s_state <= ST_IDLE; |
end if; |
|
when ST_WAITIN => |
-- Got valid SETUP packet; waiting for IN transaction. |
s_answerptr(5 downto 0) <= "000000"; |
if T_SETUP = '1' then |
-- Start of next SETUP transaction |
s_state <= ST_SETUP; |
s_setupptr <= to_unsigned(0, s_setupptr'length); |
elsif T_IN = '1' then |
-- Start of IN transaction; respond to the request |
case s_ctlrequest is |
when req_getstatus => |
-- Respond with status byte, followed by zero byte. |
s_state <= ST_SENDRESP; |
when req_setaddress => |
-- Effectuate change of device address |
s_addr <= s_ctlparam(6 downto 0); |
s_state <= ST_IDLE; |
when req_getdesc => |
-- Respond with descriptor |
s_state <= ST_STARTDESC; |
when req_getconf => |
-- Respond with current configuration |
s_sendbyte <= "0000000" & s_confd; |
s_state <= ST_SENDRESP; |
when req_getiface => |
-- Respond with zero byte |
s_sendbyte <= "00000000"; |
s_state <= ST_SENDRESP; |
when others => |
-- Impossible |
end case; |
end if; |
|
when ST_SENDRESP => |
-- Respond to IN with a preset byte, |
-- followed by zero or more nul byte(s) |
if T_IN = '0' then |
-- Aborted IN transaction; wait for retry |
s_state <= ST_WAITIN; |
elsif T_TXRDY = '1' then |
-- Need next data byte |
s_sendbyte <= "00000000"; |
if (s_answerptr(0) = '1') or (s_answerlen(0) = '1') then |
-- Reached end of transfer. |
-- Note that we only ever send 1 or 2 byte answers. |
s_state <= ST_DONESEND; |
end if; |
s_answerptr(5 downto 0) <= s_answerptr(5 downto 0) + 1; |
end if; |
|
when ST_STARTDESC => |
-- Fetching first byte of packet. |
if T_IN = '0' then |
-- Aborted IN transaction; wait for retry |
s_state <= ST_WAITIN; |
elsif unsigned(C_DSCLEN) = 0 then |
-- Invalid descriptor. |
s_state <= ST_STALL; |
elsif (s_answerptr = unsigned(C_DSCLEN)) or |
(s_answerptr = s_answerlen) then |
-- Send an empty packet to complete the transfer. |
s_state <= ST_DONESEND; |
else |
-- Send a normal descriptor packet. |
s_state <= ST_SENDDESC; |
end if; |
s_answerptr(5 downto 0) <= s_answerptr(5 downto 0) + 1; |
|
when ST_SENDDESC => |
-- Respond to IN with descriptor |
if T_IN = '0' then |
-- Aborted IN transaction; wait for retry |
s_state <= ST_WAITIN; |
elsif T_TXRDY = '1' then |
-- Need next data byte |
if (s_answerptr(5 downto 0) = 0) or |
(s_answerptr = unsigned(C_DSCLEN)) or |
(s_answerptr = s_answerlen) then |
-- Just sent the last byte of the packet |
s_state <= ST_DONESEND; |
else |
s_answerptr(5 downto 0) <= s_answerptr(5 downto 0) + 1; |
end if; |
end if; |
|
when ST_DONESEND => |
-- Done sending packet; wait until IN transaction completes. |
-- Note: s_answerptr contains the number of bytes sent so-far, |
-- unless this is a multiple of 64, in which case s_answerptr |
-- contains 64 less than the number of bytes sent; and unless |
-- the last packet sent was an empty end-of-transfer packet, |
-- in which case s_answerptr contains 1 more than the number |
-- of bytes sent. |
if T_FIN = '1' then |
-- Host acknowledged transaction. |
if s_answerptr(5 downto 0) = 0 then |
-- The last sent packet was a full sized packet. |
-- If s_answerptr + 64 = s_answerlen, the transfer |
-- is now complete; otherwise the host will expect |
-- more data. In either case, we go back to WAITIN. |
-- This can't go wrong because WAITIN also listens |
-- for the next SETUP and handles it properly. |
s_state <= ST_WAITIN; |
else |
-- The last sent packet was not full sized; |
-- it was either empty or reached the end of |
-- the descriptor. In either case, the transfer |
-- is now complete. |
s_state <= ST_IDLE; |
end if; |
s_answerptr <= s_answerptr + 64; |
elsif T_IN = '0' then |
-- Transaction failed; wait for retry. |
s_state <= ST_WAITIN; |
end if; |
|
end case; |
|
end if; |
|
end process; |
|
end architecture usb_control_arch; |
/usb_pkg.vhdl
0,0 → 1,163
-- |
-- USB 2.0 VHDL package |
-- |
|
library ieee; |
use ieee.std_logic_1164.all, ieee.numeric_std.all; |
|
package usb_pkg is |
|
-- Initialization, handshake, reset. |
component usb_init is |
generic ( |
HSSUPPORT : boolean := false ); -- Support high speed mode |
port ( |
CLK : in std_logic; -- 60 MHz UTMI clock |
RESET : in std_logic; -- Synchronous reset |
I_USBRST : out std_logic; -- High when bus reset signal detected |
I_HIGHSPEED : out std_logic; -- High when attached at high speed |
I_SUSPEND : out std_logic; -- High when suspended |
P_CHIRPK : out std_logic; |
PHY_RESET : out 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 ); |
end component usb_init; |
|
-- Packet-level logic and CRC handling. |
component usb_packet is |
port ( |
CLK : in std_logic; -- 60 MHz UTMI clock |
RESET : in std_logic; -- Synchronous reset of this entity |
P_CHIRPK : in std_logic; -- High to force chirp K transmission |
P_RXACT : out std_logic; -- High while receiving a packet |
P_RXRDY : out std_logic; -- Indicates arrival of a byte |
P_RXFIN : out std_logic; -- Indicates successfull completion |
P_RXDAT : out std_logic_vector(7 downto 0); -- Received byte value |
P_TXACT : in std_logic; -- High while transmitting a packet |
P_TXRDY : out std_logic; -- Request for next data byte |
P_TXDAT : in std_logic_vector(7 downto 0); -- Data byte to transmit |
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 ); |
end component usb_packet; |
|
-- Transaction-level logic. |
component usb_transact is |
generic ( |
HSSUPPORT : boolean := false ); -- Support high speed mode |
port ( |
CLK : in std_logic; -- 60 MHz UTMI clock |
RESET : in std_logic; -- Synchronous reset of this entity |
T_IN : out std_logic; -- High during IN transactions |
T_OUT : out std_logic; -- High during OUT transactions |
T_SETUP : out std_logic; -- High during SETUP transactions |
T_PING : out std_logic; -- High during PING transactions |
T_FIN : out std_logic; -- Indicates successfull completion |
T_ADDR : in std_logic_vector(6 downto 0); -- Device address |
T_ENDPT : out std_logic_vector(3 downto 0); -- Endpoint number |
T_NAK : in std_logic; -- Triggers a NAK response to IN/OUT |
T_STALL : in std_logic; -- Triggers a STALL response to IN/OUT |
T_NYET : in std_logic; -- Triggers a NYET response to OUT |
T_SEND : in std_logic; -- High while application has data to send |
T_ISYNC : in std_logic; -- Sync bit to use for IN transactions |
T_OSYNC : out std_logic; -- Sync bit used in the OUT transaction |
T_RXRDY : out std_logic; -- Indicates arrival of received byte |
T_RXDAT : out std_logic_vector(7 downto 0); -- Received data |
T_TXRDY : out std_logic; -- Requests next data byte to transmit |
T_TXDAT : in std_logic_vector(7 downto 0); -- Data to transmit |
I_HIGHSPEED : in std_logic; |
P_RXACT : in std_logic; |
P_RXRDY : in std_logic; |
P_RXFIN : in std_logic; |
P_RXDAT : in std_logic_vector(7 downto 0); |
P_TXACT : out std_logic; |
P_TXRDY : in std_logic; |
P_TXDAT : out std_logic_vector(7 downto 0) ); |
end component usb_transact; |
|
-- Default control endpoint. |
component usb_control is |
generic ( |
NENDPT : integer range 1 to 15 ); -- Highest endpoint number in use |
port ( |
CLK : in std_logic; -- 60 MHz UTMI clock |
RESET : in std_logic; -- Synchronous reset of this entity |
C_ADDR : out std_logic_vector(6 downto 0); -- Current device address |
C_CONFD : out std_logic; -- High when in Configured state |
C_CLRIN : out std_logic_vector(1 to NENDPT); -- Trigger clearing of sync bit for IN endpoint |
C_CLROUT : out std_logic_vector(1 to NENDPT); -- Trigger clearing of sync bit for OUT endpoint |
C_HLTIN : in std_logic_vector(1 to NENDPT); -- Current status of halt bit for IN endpoint |
C_HLTOUT : in std_logic_vector(1 to NENDPT); -- Current status of halt bit for OUT endpoint |
C_SHLTIN : out std_logic_vector(1 to NENDPT); -- Trigger setting of halt bit for IN endpoint |
C_SHLTOUT : out std_logic_vector(1 to NENDPT); -- Trigger setting of halt bit for OUT endpoint |
C_DSCBUSY : out std_logic; -- High when accessing descriptor memory |
C_DSCRD : out std_logic; -- Descriptor read enable |
C_DSCTYP : out std_logic_vector(2 downto 0); -- Requested descriptor type |
C_DSCINX : out std_logic_vector(7 downto 0); -- Requested descriptor index |
C_DSCOFF : out std_logic_vector(7 downto 0); -- Offset within requested descriptor |
C_DSCLEN : in std_logic_vector(7 downto 0); -- Length of selected descriptor |
C_SELFPOWERED : in std_logic; -- High if the device is not drawing bus power |
T_IN : in std_logic; |
T_OUT : in std_logic; |
T_SETUP : in std_logic; |
T_PING : in std_logic; |
T_FIN : in std_logic; |
T_NAK : out std_logic; |
T_STALL : out std_logic; |
T_NYET : out std_logic; |
T_SEND : out std_logic; |
T_ISYNC : out std_logic; |
T_OSYNC : in std_logic; |
T_RXRDY : in std_logic; |
T_RXDAT : in std_logic_vector(7 downto 0); |
T_TXRDY : in std_logic; |
T_TXDAT : out std_logic_vector(7 downto 0) ); |
end component usb_control; |
|
-- Serial data transfer core. |
component usb_serial is |
generic ( |
VENDORID : std_logic_vector(15 downto 0); -- Vendor ID |
PRODUCTID : std_logic_vector(15 downto 0); -- Product ID |
VERSIONBCD : std_logic_vector(15 downto 0); -- Product version |
HSSUPPORT : boolean := false; -- Support high speed mode |
SELFPOWERED : boolean := false; -- Device does not use bus power |
RXBUFSIZE_BITS: integer range 7 to 12 := 11; -- Size of receive buffer |
TXBUFSIZE_BITS: integer range 7 to 12 := 10 ); -- Size of transmit buffer |
port ( |
CLK : in std_logic; -- 60 MHz UTMI clock |
RESET : in std_logic; -- Synchronous reset |
USBRST : out std_logic; -- Reset signal detected on bus |
HIGHSPEED : out std_logic; -- Device operating in high speed mode |
SUSPEND : out std_logic; -- Device is suspended |
ONLINE : out std_logic; -- Device is in Configured state |
RXVAL : out std_logic; -- Received byte available on RXDAT |
RXDAT : out std_logic_vector(7 downto 0); |
RXRDY : in std_logic; -- Application ready for next byte |
RXLEN : out std_logic_vector(RXBUFSIZE_BITS-1 downto 0); |
TXVAL : in std_logic; -- Application has data to send |
TXDAT : in std_logic_vector(7 downto 0); |
TXRDY : out std_logic; -- Entity ready to accept next byte |
TXROOM : out std_logic_vector(TXBUFSIZE_BITS-1 downto 0); |
TXCORK : in std_logic; -- Suppress data transmission |
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 component usb_serial; |
|
end package usb_pkg; |
/usb_init.vhdl
0,0 → 1,386
-- |
-- USB 2.0 Initialization, handshake and reset detection. |
-- |
-- This entity provides the following functions: |
-- |
-- * USB bus attachment: At powerup and after a RESET signal, switch to |
-- non-driving mode, wait for 17 ms, then attach to the USB bus. This |
-- should ensure that the host notices our reattachment and initiates |
-- a reset procedure. |
-- |
-- * High speed handshake (if HSSUPPORT enabled): attempt to enter |
-- high speed mode after a bus reset. |
-- |
-- * Monitor the linestate for reset and/or suspend signalling. |
-- |
-- The low-level interface connects to an UTMI compliant USB PHY such as |
-- the SMSC GT3200. The UTMI interface must be configured for 60 MHz operation |
-- with an 8-bit data bus. |
-- |
|
library ieee; |
use ieee.std_logic_1164.all, ieee.numeric_std.all; |
|
entity usb_init is |
|
generic ( |
|
-- Support high speed mode. |
HSSUPPORT : boolean := false ); |
|
port ( |
|
-- 60 MHz UTMI clock. |
CLK : in std_logic; |
|
-- Synchronous reset; triggers detach and reattach to the USB bus. |
RESET : in std_logic; |
|
-- High for one clock if a reset signal is detected on the USB bus. |
I_USBRST : out std_logic; |
|
-- High when attached to the host in high speed mode. |
I_HIGHSPEED : out std_logic; |
|
-- High when suspended. |
-- Reset of this signal is asynchronous. |
-- This signal may be used to drive (inverted) the UTMI SuspendM pin. |
I_SUSPEND : out std_logic; |
|
-- High to tell usb_packet that it must drive a continuous K state. |
P_CHIRPK : out std_logic; |
|
-- Connect to the UTMI Reset signal. |
PHY_RESET : out std_logic; |
|
-- Connect to the UTMI LineState signal. |
PHY_LINESTATE : in std_logic_vector(1 downto 0); |
|
-- Cconnect to the UTMI OpMode signal. |
PHY_OPMODE : out std_logic_vector(1 downto 0); |
|
-- Connect to the UTMI XcvrSelect signal (0 = high speed, 1 = full speed). |
PHY_XCVRSELECT : out std_logic; |
|
-- Connect to the UTMI TermSelect signal (0 = high speed, 1 = full speed). |
PHY_TERMSELECT : out std_logic ); |
|
end entity usb_init; |
|
architecture usb_init_arch of usb_init is |
|
-- Time from bus idle until device suspend (3 ms). |
constant TIME_SUSPEND : unsigned(19 downto 0) := to_unsigned(180000, 20); |
|
-- Time from start of SE0 until detection of reset signal (2.5 us + 10%). |
constant TIME_RESET : unsigned(7 downto 0) := to_unsigned(165, 8); |
|
-- Time to wait for good SE0 when waking up from suspend (6 ms). |
constant TIME_SUSPRST: unsigned(19 downto 0) := to_unsigned(360000, 20); |
|
-- Duration of chirp K from device during high speed detection (1 ms + 10%). |
constant TIME_CHIRPK : unsigned(19 downto 0) := to_unsigned(66000, 20); |
|
-- Minimum duration of chirp J/K during high speed detection (2.5 us + 10%). |
constant TIME_FILT : unsigned(7 downto 0) := to_unsigned(165, 8); |
|
-- Time to wait for chirp until giving up (1.1 ms). |
constant TIME_WTFS : unsigned(19 downto 0) := to_unsigned(66000, 20); |
|
-- Time to wait after reverting to full-speed before sampling the bus (100 us). |
constant TIME_WTRSTHS : unsigned(19 downto 0) := to_unsigned(6000, 20); |
|
-- State machine |
type t_state is ( |
ST_INIT, ST_FSRESET, ST_FULLSPEED, ST_SUSPEND, ST_SUSPRESET, |
ST_SENDCHIRP, ST_RECVCHIRP, ST_HIGHSPEED, ST_HSREVERT ); |
signal s_state : t_state := ST_INIT; |
|
-- Timers. |
signal s_timer1 : unsigned(7 downto 0); |
signal s_timer2 : unsigned(19 downto 0) := to_unsigned(0, 20); |
|
-- Count J/K chirps. |
signal s_chirpcnt : unsigned(2 downto 0); |
|
-- High if the device is operating in high speed (or suspended from high speed). |
signal s_highspeed : std_logic := '0'; |
|
-- High if the device is currently suspended. |
-- Reset of this signal is asynchronous. |
signal s_suspend : std_logic := '0'; |
|
-- Input registers. |
signal s_linestate : std_logic_vector(1 downto 0); |
|
-- Output registers. |
signal s_reset : std_logic := '1'; |
signal s_opmode : std_logic_vector(1 downto 0) := "01"; |
signal s_xcvrselect : std_logic := '1'; |
signal s_termselect : std_logic := '1'; |
signal s_chirpk : std_logic := '0'; |
|
begin |
|
I_USBRST <= s_reset; |
I_HIGHSPEED <= s_highspeed; |
I_SUSPEND <= s_suspend; |
P_CHIRPK <= s_chirpk; |
PHY_RESET <= s_reset; |
PHY_OPMODE <= s_opmode; |
PHY_XCVRSELECT <= s_xcvrselect; |
PHY_TERMSELECT <= s_termselect; |
|
-- Synchronous process. |
process is |
variable v_clrtimer1 : std_logic; |
variable v_clrtimer2 : std_logic; |
begin |
wait until rising_edge(CLK); |
|
-- By default, do not clear the timers. |
v_clrtimer1 := '0'; |
v_clrtimer2 := '0'; |
|
-- Register linestate input. |
s_linestate <= PHY_LINESTATE; |
|
-- Default assignments to registers. |
s_reset <= '0'; |
s_chirpk <= '0'; |
|
if RESET = '1' then |
|
-- Reset PHY. |
s_reset <= '1'; |
s_opmode <= "01"; |
s_xcvrselect <= '1'; |
s_termselect <= '1'; |
|
-- Go to ST_INIT state and wait until bus attachment. |
v_clrtimer1 := '1'; |
v_clrtimer2 := '1'; |
s_highspeed <= '0'; |
s_state <= ST_INIT; |
|
else |
|
case s_state is |
|
when ST_INIT => |
-- Wait before attaching to bus. |
s_opmode <= "01"; -- non-driving |
s_xcvrselect <= '1'; -- full speed |
s_termselect <= '1'; -- full speed |
v_clrtimer1 := '1'; |
if s_timer2 = to_unsigned(0, s_timer2'length) - 1 then |
-- Timer2 overflows after ~ 17 ms; attach to bus. |
v_clrtimer2 := '1'; |
s_state <= ST_FULLSPEED; |
end if; |
|
when ST_FSRESET => |
-- Waiting for end of reset before full speed operation. |
s_highspeed <= '0'; |
s_opmode <= "00"; -- normal |
s_xcvrselect <= '1'; -- full speed |
s_termselect <= '1'; -- full speed |
v_clrtimer1 := '1'; |
v_clrtimer2 := '1'; |
if s_linestate /= "00" then |
-- Reset signal ended. |
s_state <= ST_FULLSPEED; |
end if; |
|
when ST_FULLSPEED => |
-- Operating in full speed. |
s_highspeed <= '0'; |
s_opmode <= "00"; -- normal |
s_xcvrselect <= '1'; -- full speed |
s_termselect <= '1'; -- full speed |
if s_linestate /= "00" then |
-- Bus not in SE0 state; clear reset timer. |
v_clrtimer1 := '1'; |
end if; |
if s_linestate /= "01" then |
-- Bus not in J state; clear suspend timer. |
v_clrtimer2 := '1'; |
end if; |
if s_timer1 = TIME_RESET then |
-- Bus has been in SE0 state for TIME_RESET; |
-- this is a reset signal. |
s_reset <= '1'; |
if HSSUPPORT then |
s_state <= ST_SENDCHIRP; |
else |
s_state <= ST_FSRESET; |
end if; |
elsif s_timer2 = TIME_SUSPEND then |
-- Bus has been idle for TIME_SUSPEND; |
-- go to suspend state. |
s_state <= ST_SUSPEND; |
end if; |
|
when ST_SUSPEND => |
-- Suspended; waiting for resume signal. |
-- Possibly our clock will be disabled; wake up |
-- is initiated by the asynchronous reset of s_suspend. |
s_opmode <= "00"; -- normal |
s_xcvrselect <= '1'; -- full speed |
s_termselect <= '1'; -- full speed |
v_clrtimer1 := '1'; |
v_clrtimer2 := '1'; |
if s_linestate /= "01" then |
-- Bus not in J state; resume. |
if HSSUPPORT and s_highspeed = '1' then |
-- High speed resume protocol. |
if s_linestate = "10" then |
-- Bus in K state; resume to high speed. |
s_state <= ST_HIGHSPEED; |
elsif s_linestate = "00" then |
-- Bus in SE0 state; start reset detection. |
s_state <= ST_SUSPRESET; |
end if; |
else |
-- Resume to full speed. |
s_state <= ST_FULLSPEED; |
end if; |
end if; |
|
when ST_SUSPRESET => |
-- Wake up in SE0 state; wait for proper reset signal. |
s_opmode <= "00"; -- normal |
s_xcvrselect <= '1'; -- full speed |
s_termselect <= '1'; -- full speed |
if s_linestate /= "00" then |
-- Bus not in SE0 state; clear reset timer. |
v_clrtimer1 := '1'; |
end if; |
if s_timer1 = TIME_RESET then |
-- Bus has been in SE0 state for TIME_RESET; |
-- this is a reset signal. |
s_reset <= '1'; |
v_clrtimer2 := '1'; |
s_state <= ST_SENDCHIRP; |
end if; |
if s_timer2 = TIME_SUSPRST then |
-- Still no proper reset signal; go back to sleep. |
s_state <= ST_SUSPEND; |
end if; |
|
when ST_SENDCHIRP => |
-- Sending chirp K for a duration of TIME_CHIRPK. |
s_highspeed <= '0'; |
s_opmode <= "10"; -- disable bit stuffing |
s_xcvrselect <= '0'; -- high speed |
s_termselect <= '1'; -- full speed |
s_chirpk <= '1'; -- send chirp K |
v_clrtimer1 := '1'; |
if s_timer2 = TIME_CHIRPK then |
-- end of chirp K |
v_clrtimer2 := '1'; |
s_chirpcnt <= "000"; |
s_state <= ST_RECVCHIRP; |
end if; |
|
when ST_RECVCHIRP => |
-- Waiting for K-J-K-J-K-J chirps. |
-- Note: DO NOT switch Opmode to normal yet; there |
-- may be pending bits in the transmission buffer. |
s_opmode <= "10"; -- disable bit stuffing |
s_xcvrselect <= '0'; -- high speed |
s_termselect <= '1'; -- full speed |
if ( s_chirpcnt(0) = '0' and s_linestate /= "10" ) or |
( s_chirpcnt(0) = '1' and s_linestate /= "01" ) then |
-- Not the linestate we want. |
v_clrtimer1 := '1'; |
end if; |
if s_timer2 = TIME_WTFS then |
-- High speed detection failed; go to full speed. |
v_clrtimer1 := '1'; |
v_clrtimer2 := '1'; |
s_state <= ST_FSRESET; |
elsif s_timer1 = TIME_FILT then |
-- We got the chirp we wanted. |
if s_chirpcnt = 5 then |
-- This was the last chirp; |
-- we got a successful high speed handshake. |
v_clrtimer2 := '1'; |
s_state <= ST_HIGHSPEED; |
end if; |
s_chirpcnt <= s_chirpcnt + 1; |
v_clrtimer1 := '1'; |
end if; |
|
when ST_HIGHSPEED => |
-- Operating in high speed. |
s_highspeed <= '1'; |
s_opmode <= "00"; -- normal |
s_xcvrselect <= '0'; -- high speed |
s_termselect <= '0'; -- high speed |
if s_linestate /= "00" then |
-- Bus not idle; clear revert timer. |
v_clrtimer2 := '1'; |
end if; |
if s_timer2 = TIME_SUSPEND then |
-- Bus has been idle for TIME_SUSPEND; |
-- revert to full speed. |
v_clrtimer2 := '1'; |
s_state <= ST_HSREVERT; |
end if; |
|
when ST_HSREVERT => |
-- Revert to full speed and wait for 100 us. |
s_opmode <= "00"; -- normal |
s_xcvrselect <= '1'; -- full speed |
s_termselect <= '1'; -- full speed |
if s_timer2 = TIME_WTRSTHS then |
v_clrtimer2 := '1'; |
if s_linestate = "00" then |
-- Reset from high speed. |
s_reset <= '1'; |
s_state <= ST_SENDCHIRP; |
else |
-- Suspend from high speed. |
s_state <= ST_SUSPEND; |
end if; |
end if; |
|
end case; |
|
end if; |
|
-- Increment or clear timer1. |
if v_clrtimer1 = '1' then |
s_timer1 <= to_unsigned(0, s_timer1'length); |
else |
s_timer1 <= s_timer1 + 1; |
end if; |
|
-- Increment or clear timer2. |
if v_clrtimer2 = '1' then |
s_timer2 <= to_unsigned(0, s_timer2'length); |
else |
s_timer2 <= s_timer2 + 1; |
end if; |
|
end process; |
|
-- Drive the s_suspend flipflop (synchronous set, asynchronous reset). |
process (CLK, PHY_LINESTATE) is |
begin |
if PHY_LINESTATE /= "01" then |
-- The bus is not in full speed idle state; |
-- reset the s_suspend flipflop. |
s_suspend <= '0'; |
elsif rising_edge(CLK) then |
if s_state = ST_SUSPEND then |
-- Bus is idle and FSM is in suspend state; |
-- enable the s_suspend flipflop. |
s_suspend <= '1'; |
end if; |
end if; |
end process; |
|
end architecture usb_init_arch; |
|
/usb_packet.vhdl
0,0 → 1,460
-- |
-- USB 2.0 Packet-level logic. |
-- |
-- This entity hides the details of the UTMI interface and handles |
-- computation and verificaton of CRCs. |
-- |
-- The low-level interface signals are named PHY_xxx and may be |
-- connected to an UTMI compliant USB PHY, such as the SMSC GT3200. |
-- |
-- The application interface signals are named P_xxx. |
-- The receiving side of the interface operates as follows: |
-- * At the start of an incoming packet, RXACT is set high. |
-- * When a new byte arrives, RXRDY is asserted and the byte is put |
-- on RXDAT. These signals are valid for only one clock cycle; the |
-- application must accept them immediately. |
-- * The first byte of a packet is the PID. Subsequent bytes contain |
-- data and CRC. This entity verifies the CRC, but does not |
-- discard it from the data stream. |
-- * Some time after correctly receiving the last byte of a packet, |
-- RXACT is deasserted; at the same time RXFIN is asserted for one cycle |
-- to confirm the packet. |
-- * If a corrupt packet is received, RXACT is deasserted without |
-- asserting RXFIN. |
-- |
-- The transmission side of the interface operates as follows: |
-- * The application starts transmission by setting TXACT to 1 and setting |
-- TXDAT to the PID value (with correctly mirrored high order bits). |
-- * The entity asserts TXRDY when it needs the next payload byte. |
-- On the following clock cycle, the application must then provide the |
-- next payload byte on TXDAT, or deassert TXACT to indicate the end of |
-- the packet. The signal on TXDAT must be held stable until the next |
-- assertion of TXRDY. |
-- * CRC bytes should not be included in the payload; the entity will |
-- add them automatically. |
-- * As part of the high speed handshake, the application may request |
-- transmission of a continuous chirp K state by asserting CHIRPK. |
-- |
-- Implementation note: |
-- Transmission timing is a bit tricky due to the following issues: |
-- * After the PHY asserts PHY_TXREADY, we must immediately provide |
-- new data or deassert PHY_TXVALID on the next clock cycle. |
-- * The PHY may assert PHY_TXREADY during subsequent clock cycles, |
-- even though the average byte period is more than 40 cycles. |
-- * We want to register PHY inputs and outputs to ensure valid timing. |
-- |
-- To satisfy these requirements, we make the application run one byte |
-- ahead. While keeping the current byte in the output register PHY_DATAOUT, |
-- the application already provides the following data byte. That way, we |
-- can respond to PHY_TXREADY immediately in the next cycle, with the |
-- application following up in the clock cycle after that. |
-- |
|
library ieee; |
use ieee.std_logic_1164.all, ieee.numeric_std.all; |
|
entity usb_packet is |
|
port ( |
|
-- 60 MHz UTMI clock. |
CLK : in std_logic; |
|
-- Synchronous reset of this entity. |
RESET : in std_logic; |
|
-- High to force chirp K transmission. |
P_CHIRPK : in std_logic; |
|
-- High while receiving a packet. |
P_RXACT : out std_logic; |
|
-- Indicates next byte received; data must be read from RXDAT immediately. |
P_RXRDY : out std_logic; |
|
-- High for one cycle to indicate successful completion of packet. |
P_RXFIN : out std_logic; |
|
-- Received byte value. Valid if RXRDY is high. |
P_RXDAT : out std_logic_vector(7 downto 0); |
|
-- High while transmitting a packet. |
P_TXACT : in std_logic; |
|
-- Request for next data byte; application must change TXDAT on the next clock cycle. |
P_TXRDY : out std_logic; |
|
-- Data byte to transmit. Hold stable until next assertion of TXRDY. |
P_TXDAT : in std_logic_vector(7 downto 0); |
|
-- Connect to UTMI DataIn signal. |
PHY_DATAIN : in std_logic_vector(7 downto 0); |
|
-- Connect to UTMI DataOut signal. |
PHY_DATAOUT : out std_logic_vector(7 downto 0); |
|
-- Connect to UTMI TxValid signal. |
PHY_TXVALID : out std_logic; |
|
-- Connect to UTMI TxReady signal. |
PHY_TXREADY : in std_logic; |
|
-- Connect to UTMI RxActive signal. |
PHY_RXACTIVE : in std_logic; |
|
-- Connect to UTMI RxValid signal. |
PHY_RXVALID : in std_logic; |
|
-- Connect to UTMI RxError signal. |
PHY_RXERROR : in std_logic ); |
|
end entity usb_packet; |
|
architecture usb_packet_arch of usb_packet is |
|
-- State machine |
type t_state is ( |
ST_NONE, ST_CHIRPK, |
ST_RWAIT, ST_RTOKEN, ST_RDATA, ST_RSHAKE, |
ST_TSTART, ST_TDATA, ST_TCRC1, ST_TCRC2 ); |
signal s_state : t_state := ST_NONE; |
signal s_txfirst : std_logic := '0'; |
|
-- Registered inputs |
signal s_rxactive : std_logic; |
signal s_rxvalid : std_logic; |
signal s_rxerror : std_logic; |
signal s_datain : std_logic_vector(7 downto 0); |
signal s_txready : std_logic; |
|
-- Byte pending for transmission |
signal s_dataout : std_logic_vector(7 downto 0); |
|
-- True if an incoming packet would be valid if it ended now. |
signal s_rxgoodpacket : std_logic; |
|
-- CRC computation |
constant crc5_gen : std_logic_vector(4 downto 0) := "00101"; |
constant crc5_res : std_logic_vector(4 downto 0) := "01100"; |
constant crc16_gen : std_logic_vector(15 downto 0) := "1000000000000101"; |
constant crc16_res : std_logic_vector(15 downto 0) := "1000000000001101"; |
signal crc5_buf : std_logic_vector(4 downto 0); |
signal crc16_buf : std_logic_vector(15 downto 0); |
|
-- Update CRC 5 to account for a new byte |
function crc5_upd( |
c : in std_logic_vector(4 downto 0); |
b : in std_logic_vector(7 downto 0) ) |
return std_logic_vector |
is |
variable t : std_logic_vector(4 downto 0); |
variable y : std_logic_vector(4 downto 0); |
begin |
t := ( |
b(0) xor c(4), |
b(1) xor c(3), |
b(2) xor c(2), |
b(3) xor c(1), |
b(4) xor c(0) ); |
y := ( |
b(5) xor t(1) xor t(2), |
b(6) xor t(0) xor t(1) xor t(4), |
b(5) xor b(7) xor t(0) xor t(3) xor t(4), |
b(6) xor t(1) xor t(3) xor t(4), |
b(7) xor t(0) xor t(2) xor t(3) ); |
return y; |
end function; |
|
-- Update CRC-16 to account for new byte |
function crc16_upd( |
c : in std_logic_vector(15 downto 0); |
b : in std_logic_vector(7 downto 0) ) |
return std_logic_vector |
is |
variable t : std_logic_vector(7 downto 0); |
variable y : std_logic_vector(15 downto 0); |
begin |
t := ( |
b(0) xor c(15), |
b(1) xor c(14), |
b(2) xor c(13), |
b(3) xor c(12), |
b(4) xor c(11), |
b(5) xor c(10), |
b(6) xor c(9), |
b(7) xor c(8) ); |
y := ( |
c(7) xor t(0) xor t(1) xor t(2) xor t(3) xor t(4) xor t(5) xor t(6) xor t(7), |
c(6), c(5), c(4), c(3), c(2), |
c(1) xor t(7), |
c(0) xor t(6) xor t(7), |
t(5) xor t(6), |
t(4) xor t(5), |
t(3) xor t(4), |
t(2) xor t(3), |
t(1) xor t(2), |
t(0) xor t(1), |
t(1) xor t(2) xor t(3) xor t(4) xor t(5) xor t(6) xor t(7), |
t(0) xor t(1) xor t(2) xor t(3) xor t(4) xor t(5) xor t(6) xor t(7) ); |
return y; |
end function; |
|
begin |
|
-- Assign output signals |
P_RXACT <= s_rxactive; |
P_RXFIN <= (not s_rxactive) and (not s_rxerror) and s_rxgoodpacket; |
P_RXRDY <= s_rxactive and s_rxvalid; |
P_RXDAT <= s_datain; |
|
-- Assert P_TXRDY during ST_TSTART to acknowledge the PID byte, |
-- during the first cycle of ST_TDATA to acknowledge the first |
-- data byte, and whenever we need a new data byte during ST_TDATA. |
P_TXRDY <= '1' when (s_state = ST_TSTART) |
else (s_txfirst or s_txready) when (s_state = ST_TDATA) |
else '0'; |
|
-- On every rising clock edge |
process is |
variable v_dataout : std_logic_vector(7 downto 0); |
variable v_txvalid : std_logic; |
variable v_crc_upd : std_logic; |
variable v_crc_data : std_logic_vector(7 downto 0); |
variable v_crc5_new : std_logic_vector(4 downto 0); |
variable v_crc16_new : std_logic_vector(15 downto 0); |
begin |
wait until rising_edge(CLK); |
|
-- Default assignment to temporary variables |
v_dataout := s_dataout; |
v_txvalid := '0'; |
v_crc_upd := '0'; |
v_crc_data := "00000000"; |
v_crc5_new := "00000"; |
v_crc16_new := "0000000000000000"; |
|
-- Default assignment to s_txfirst |
s_txfirst <= '0'; |
|
-- Register inputs |
s_rxactive <= PHY_RXACTIVE; |
s_rxvalid <= PHY_RXVALID; |
s_rxerror <= PHY_RXERROR; |
s_datain <= PHY_DATAIN; |
s_txready <= PHY_TXREADY; |
|
-- State machine |
if RESET = '1' then |
|
-- Reset entity |
s_state <= ST_NONE; |
s_rxgoodpacket <= '0'; |
|
else |
|
case s_state is |
when ST_NONE => |
-- Waiting for incoming or outgoing packet |
|
-- Initialize CRC buffers |
crc5_buf <= "11111"; |
crc16_buf <= "1111111111111111"; |
s_rxgoodpacket <= '0'; |
|
if P_CHIRPK = '1' then |
-- Send continuous chirp K. |
s_state <= ST_CHIRPK; |
|
elsif s_rxactive = '1' then |
-- Receiver starting |
|
if s_rxerror = '1' then |
-- Receive error at PHY level |
s_state <= ST_RWAIT; |
elsif s_rxvalid = '1' then |
-- Got PID byte |
if s_datain(3 downto 0) = not s_datain(7 downto 4) then |
case s_datain(1 downto 0) is |
when "01" => -- token packet |
s_state <= ST_RTOKEN; |
when "11" => -- data packet |
s_state <= ST_RDATA; |
when "10" => -- handshake packet |
s_state <= ST_RSHAKE; |
s_rxgoodpacket <= '1'; |
when others => -- PING token or special packet |
-- If this is a PING token, it will work out fine; |
-- otherwise it will be flagged as a bad packet |
-- either here or in usb_transact. |
s_state <= ST_RTOKEN; |
end case; |
else |
-- Corrupt PID byte |
s_state <= ST_RWAIT; |
end if; |
end if; |
|
elsif P_TXACT = '1' then |
-- Transmission starting; put data in output buffer |
v_txvalid := '1'; |
v_dataout := P_TXDAT; |
s_state <= ST_TSTART; |
end if; |
|
when ST_CHIRPK => |
-- Sending continuous chirp K. |
if P_CHIRPK = '0' then |
s_state <= ST_NONE; |
end if; |
|
when ST_RTOKEN => |
-- Receiving a token packet |
if s_rxactive = '0' then |
-- End of packet |
s_rxgoodpacket <= '0'; |
s_state <= ST_NONE; |
elsif s_rxerror = '1' then |
-- Error at PHY level |
s_rxgoodpacket <= '0'; |
s_state <= ST_RWAIT; |
elsif s_rxvalid = '1' then |
-- Just received a byte; update CRC |
v_crc5_new := crc5_upd(crc5_buf, s_datain); |
crc5_buf <= v_crc5_new; |
if v_crc5_new = crc5_res then |
s_rxgoodpacket <= '1'; |
else |
s_rxgoodpacket <= '0'; |
end if; |
end if; |
|
when ST_RDATA => |
-- Receiving a data packet |
if s_rxactive = '0' then |
-- End of packet |
s_rxgoodpacket <= '0'; |
s_state <= ST_NONE; |
elsif s_rxerror = '1' then |
-- Error at PHY level |
s_rxgoodpacket <= '0'; |
s_state <= ST_RWAIT; |
elsif s_rxvalid = '1' then |
-- Just received a byte; update CRC |
v_crc_upd := '1'; |
v_crc_data := s_datain; |
end if; |
|
when ST_RWAIT => |
-- Wait until the end of the current packet |
if s_rxactive = '0' then |
s_state <= ST_NONE; |
end if; |
|
when ST_RSHAKE => |
-- Receiving a handshake packet |
if s_rxactive = '0' then |
-- Got good handshake |
s_rxgoodpacket <= '0'; |
s_state <= ST_NONE; |
elsif s_rxerror = '1' or s_rxvalid = '1' then |
-- Error or unexpected data byte in handshake packet |
s_rxgoodpacket <= '0'; |
s_state <= ST_RWAIT; |
end if; |
|
when ST_TSTART => |
-- Transmission starting; |
-- PHY module sees our PHY_TXVALID signal; |
-- PHY_TXREADY is undefined; |
-- we assert P_TXRDY to acknowledge the PID byte |
v_txvalid := '1'; |
-- Check packet type |
case P_TXDAT(1 downto 0) is |
when "11" => -- data packet |
s_state <= ST_TDATA; |
s_txfirst <= '1'; |
when "10" => -- handshake packet |
s_state <= ST_RWAIT; |
when others => -- should not happen |
end case; |
|
when ST_TDATA => |
-- Sending a data packet |
v_txvalid := '1'; |
if (s_txready = '1') or (s_txfirst = '1') then |
-- Need next byte |
if P_TXACT = '0' then |
-- No more data; send first CRC byte |
for i in 0 to 7 loop |
v_dataout(i) := not crc16_buf(15-i); |
end loop; |
s_state <= ST_TCRC1; |
else |
-- Put next byte in output buffer |
v_dataout := P_TXDAT; |
-- And update the CRC |
v_crc_upd := '1'; |
v_crc_data := P_TXDAT; |
end if; |
end if; |
|
when ST_TCRC1 => |
-- Sending the first CRC byte of a data packet |
v_txvalid := '1'; |
if s_txready = '1' then |
-- Just queued the first CRC byte; move to 2nd byte |
for i in 0 to 7 loop |
v_dataout(i) := not crc16_buf(7-i); |
end loop; |
s_state <= ST_TCRC2; |
end if; |
|
when ST_TCRC2 => |
-- Sending the second CRC byte of a data packet |
if s_txready = '1' then |
-- Just sent the 2nd CRC byte; end packet |
s_state <= ST_RWAIT; |
else |
-- Last byte is still pending |
v_txvalid := '1'; |
end if; |
|
end case; |
|
end if; |
|
-- CRC-16 update |
if v_crc_upd = '1' then |
v_crc16_new := crc16_upd(crc16_buf, v_crc_data); |
crc16_buf <= v_crc16_new; |
if s_state = ST_RDATA and v_crc16_new = crc16_res then |
-- If this is the last byte of the packet, it is a valid packet. |
s_rxgoodpacket <= '1'; |
else |
s_rxgoodpacket <= '0'; |
end if; |
end if; |
|
-- Drive data output to PHY |
if RESET = '1' then |
-- Reset. |
PHY_TXVALID <= '0'; |
PHY_DATAOUT <= "00000000"; |
elsif s_state = ST_CHIRPK then |
-- Continuous chirp-K. |
PHY_TXVALID <= P_CHIRPK; |
PHY_DATAOUT <= "00000000"; |
elsif (PHY_TXREADY = '1') or (s_state = ST_NONE and P_TXACT = '1') then |
-- Move a data byte from the buffer to the output lines when the PHY |
-- accepts the previous byte, and also at the start of a new packet. |
PHY_TXVALID <= v_txvalid; |
PHY_DATAOUT <= v_dataout; |
end if; |
|
-- Keep pending output byte in register. |
s_dataout <= v_dataout; |
|
end process; |
|
end architecture usb_packet_arch; |
|