Tips for creating a FPGA ULPI USB interface.
There are a number of USB PHYs that have a UTMI+ Low Pin Interface (ULPI) interface that are readily interfaced to an FPGA. Incidentally ULPI is an acronym within an acronym within a further acronym, where UTMI+ stands for USB 2.0 Transceiver Macrocell Interface and USB is short for the ubiquitous Universal Serial Bus.
There are two reference documents that are essential to understanding this interface and its quirks.
The first: Universal Serial Bus Specification Revision 2.0 that can be obtained from http://www.usb.org/developers/docs/usb20_docs/
And the second: UTMI+ Low Pin Interface Specification, Revision 1.1 from
http://www.ulpi.org/documents.html
Since we later discuss a memory stick so this article outlines a command used in this article.
USB Mass Storage Class – Bulk Only Transport 1.0 and can be found at
http://www.usb.org/developers/devclass_docs/
This link also outlines other classes.
The USB 2.0 interface is capable of 3 speeds:
HS (High Speed) - 480 Mbps
FS (Full Speed) - 12 Mbps and
LS (Low Speed) - 1.5 Mbps
The use of High Speed and Full Speed is perhaps unfortunate and can catch out the unwary.
It is useful to be reminded that any interface claiming conformance USB 2.0 does not necessarily mean that such an interface automatically is capable of running at High Speed (480 Mbs). It is merely a refinement of a standard that includes Low, Fast and now High Speed USB interfaces.
The ULPI interface corresponds to the following pins:
nRESET | I |
CFG | I |
DIR | O |
NXT | O |
STP | I |
CS | I |
DATA[7:0] | I/O |
The ULPI/USB device must be initialised. We have chosen a TI TUSB1210 where a low signal applied to nRESET will set the internal registers to their default values. nRESET must be applied for a minimum of 200ns.
The PHY has a number of internal registers associated with various modes and interface speeds. We need only worry about two of the registers if we intend to setup the PHY as a peripheral.
The following diagram illustrates a write to a register:
And the following a read from a register:
A TXCMD byte must be sent preceding any data or write to a register according to the table below.
Transmit command byte (TX CMD)
Byte Name | Command Code data(7:6) | Command Payload data(5:0) | Command Description | ||||
Special | 00b
|
| No operation. 00h is the idle value of the data bus. The Link drives NOOP by default. | ||||
| Reserved command space. Values other than those above will give undefined behaviour. | ||||||
Transmit | 01b |
| Transmit USB data that does not have a PID, such as chirp and resume signalling. The PHY starts transmitting on the USB beginning with the next data byte. | ||||
| Transmit USB packet. data(3:0) indicates USB packet identifier PID(3:0). | ||||||
| Reserved Command space. Values other than those above will give undefined behaviour. | ||||||
RegWrite | 10b |
| Extended register write command. 8-bit address available in the next cycle. | ||||
| Register write command with 6-bit immediate address. | ||||||
RegRead | 11b |
| Extended register read command. 8-bit address available in the next cycle. | ||||
|
|
| Register read command with 6-bit immediate address. |
When the DIR signal is asserted and the NXT signal stays low a RXCMD byte is received that generally represents the PHY status. This is useful to determine the state of the D- and D+ lines to determine if the peripheral has been connected to a host.
Receive Command Byte (RX CMD)
data | Name | Description and Value | ||||||
1:0 | LineState | UTMI+ LineState signals. data(0) = LineState(0) (DP) data(1) = LineState(1) (DM) | ||||||
3:2
| Vbus State
| Encoded Vbus Voltage state | ||||||
Value | VBUS Voltage | SessEnd | SessValid | VbusValid | ||||
00 | VBUS < VB_SESS_END | 1 | 0 | 0 | ||||
01 | VB_SESS_END ≤ VBUS < VSESS_VLD | 0 | 0 | 0 | ||||
10 | VSESS_VLD ≤ VBUS < VA_VBUS_VLD | X | 1 | 0 | ||||
11 | VA_VBUS_VLD ≤ VBUS | X | X | 1 | ||||
5:4 | RxEvent | Encoded UTMI event signals | ||||||
Value | RxActive | Rx Error | HostDisconnect | |||||
00 | 0 | 0 | 0 | |||||
01 | 1 | 0 | 0 | |||||
11 | 1 | 1 | 0 | |||||
10 | X | X | 1 | |||||
6 | ID | Set to the value of IdGnd (UTMI+ IdDig). Valid 50ms after IdPullup is set to 1b. | ||||||
7 | Alt_int | Asserted when a non-USB interrupt occurs. This bit must be set when an unmasked event occurs on any bit in the Carkit Interrupt Latch register. The Link must read the Carkit Interrupt Latch register to determine the source of the interrupt. |
These are the signals we should expect after reset has been released on the PHY
Figure 3- ULPI signals after reset
One significant aspect of reset is the toggling of DIR and a receipt of a RXCMD byte from the PHY. Once that is detected then we may move forward. The IDLE state is where the Link places 0x00 onto the Data lines. We use a registered DIR signal (so that it is delayed by one clock) as an enable for the driving of data from the FPGA.
We must then initialise the peripheral. High-speed capable devices initially attach as full-speed devices where DP is pulled up to Vbus through a 1.5k resistor.
Line State is decoded as follows in LS and FS modes:
LineState[1:0] | DP (D+) | DM (D-) | State | |
00 | SE0 | 0 | 0 | USB Reset |
01 | J (FS idle) | 1 | 0 | J State |
10 | K (LS idle) | 0 | 1 | K State |
11 | SE1 | 1 | 1 | SE1 |
Line State is decoded as follows in HS mode:
LineState[1:0] | DP (D+) | DM (D-) | State | |
00 | SE0 | 0 | 0 | HS Squelch asserted |
01 | J | 1 | 0 | HS Squelch de-asserted |
10 | K | 0 | 1 | Invalid State |
11 | SE1 | 1 | 1 | Invalid State |
In HS Chirp state:
LineState[1:0] | DP (D+) | DM (D-) | State | |
00 | SE0 | 0 | 0 | HS Squelch asserted |
01 | J | 1 | 0 | HS Squelch de-asserted and HS receiver = 1 |
10 | K | 0 | 1 | HS Squelch de-asserted and HS receiver = 0 |
11 | SE1 | 1 | 1 | Invalid State |
The lesser bit LineState[0] represent DP (D+), and LineState[1] represent DM (D-).
The sequence of events:
Apply reset for more than 200ns. The PHY will pull DIR high to ensure the Link will not drive data lines.
Wait for DIR to go low.
Wait for a RXCMD, ie a toggle of the DIR pin.
The peripheral must start as a FS device by pulling DP high through an internal resister controlled by FUNC_CTRL register. First we must disable OTG features by writing x”00” to the OTG_CTRL (x”0a) register. A TXCMD byte of x”8a” is sent then a data byte of x”00” as per Figure 1.
Send x”45” to FUNC_CTRL (x”04”) where this sets the peripheral device in the Fast Speed mode by making D+ high and D- low ( LineState “01” – J).
The PHY should respond with a RXCMD showing a LineState of J (“01”, FS Idle).
Attaching peripheral to the host
An outline of the High Speed initialisation and handshake process is shown in Figure 4
1If the peripheral is attached to the Host, or now becomes attached to the Host, the Host should wait 100 ms for a stable LineState of “01” and then drive both D+ and D- low. This is the SE0 level and the PHY should produce a RXCMD to confirm a LineState SE0 (“00”)..
2In order to signal to the Host the peripheral is capable of High Speed the device must signal a “K” (a LineState of “10”) chirp for a minimum of 1.0ms by writing x”54” to FUNC_CTRL (x”04”), then writing a NOPID TXCMD and data of x”00”. After, say 1.5ms, we drive STP to a “1” for a single clock to end the chirp.
3The Host then sends a sequence of alternating J and K signals and once the peripheral detects these J and K signals it can then set the PHY to receive and transmit High Speed serial data.
4This can be accomplished by writing x”41” to FUNC_CTRL (x”04”).
5We have a peripheral in HS mode.
An excerpt from the UTMI document is below to describe the initialisation process.
Hi-Speed Detection Handshake, or Chirp, is shown below though not to scale, and not all RX CMD updates are shown. Bus turnaround cycles are also not shown, and must occur for one cycle after every assertion and de-assertion of dir. The following sequence of events must be followed.
1. FS/LS Detect – The host detects a peripheral attachment as low speed if D- is high and as full speed if D+ is high. If a host detects a low speed peripheral, it does not follow the remainder of this protocol.
2. Host Drives – If a host detects a full speed peripheral, it resets the peripheral by writing to the Function Control register and setting XcvrSelect = 00b (HS) and TermSelect = 0b which drives SE0 on the bus (D+ and D- connected to ground via 45Ω). The host also sets OpMode = 10b for correct chirp transmit and receive11. The start of SE0 is labelled T0. The peripheral PHY asserts dir and informs the Link of the LineState change using an RX CMD.
3. Peripheral Responds – After detecting SE0 for no less than 2.5us, if the peripheral is Hi-Speed capable, the peripheral Link sets XcvrSelect to 00b (HS) and OpMode to 10b (chirp), and follows this immediately with a TX CMD (NOPID), transmitting a chirp K for no less than 1ms. and the chirp K must end it no more than 7ms after reset time T0. If the peripheral is in Low Power Mode, it must wake up its clock within 5.6ms, leaving 200us for the Link to start transmitting the chirp K, and 1.2ms for the chirp K to complete (worst case with 10% slow clock).
4. Host Responds – If the host does not detect the peripheral chirp, it must continue the assertion of SE0 until the end of reset. If the host detects the peripheral chirp K for no less than 2.5us, then no more than 100us after the bus leaves the chirp K state, the host sends a TX CMD (NOPID) with an alternating sequence of chirp K’s and J’s. Each individual chirp K or J must last no less than 40us and no longer than 60us.
5. HS Idle – The peripheral must detect a minimum of chirp K-J-K-J-K-J. Each individual chirp K and J must be detected for at least 2.5us. After seeing that minimum sequence, the peripheral Link sets TermSelect = 0b and OpMode = 00b. The peripheral is now in Hi-Speed mode and sees !squelch (01b) on LineState. When the peripheral sees squelch (10b) on LineState, it knows the host has completed chirp and waits for Hi-Speed USB traffic to begin. After transmitting the chirp sequence, the host changes OpMode to 00b and begins sending USB packets.
We now receive High Speed SOFs every 125us.
The USB 2.0 reference document states in section 9.1.1.3 states “After the device has been powered, it must not respond to any bus transactions until it has received a reset from the bus. After receiving a reset, the device is then addressable at the default address.” However, if you attach a peripheral to a Windows 7 machine after initialisation to High Speed, you will see a SETUP PID and a Get Descriptor (Device) transaction. The devices we have seen respond with the full transaction though some start off replying with NAKs.
As already intimated, on a Windows machine a peripheral will receive a SETUP PID as per either of the two diagrams below. Generally for High Speed it will be the latter.
A convenient USB peripheral is a USB memory stick. This is a generic no-name device but its Vendor ID gives it away as being a NetCom Technology (HK) Ltd. For identifying Vendor IDs these can be found at:
http://www.usb.org/developers/tools/
If I connect this device to a host I get the following transaction:
| Transaction | Host | Direction | Device | Data |
Get Descriptor (Device) | Setup transaction | Setup |
| 2D 01 E8 | |
Data0 |
| C3 80 06 00 01 00 00 40 00 DD 94 | |||
| ACK | 42 | |||
IN transaction | IN |
| 69 01 E8 | ||
| DATA1 | 4b 12 01 00 02 00 00 00 40 44 86 0b 80 00 01 01 02 03 01 38 e6 | |||
ACK |
| D2 | |||
OUT transaction | OUT |
| E1 01 E8 | ||
DATA1 |
| 4B 00 00 | |||
| ACK | 42 |
The PID here is a Packet Identifier and Packet ID should not be confused with Product ID.
A Packet ID is just 4 bits, where the Data[3:0] denotes the packet being sent. Data[7:4] are the complement of this. The first PID sent after connecting the cable should be a Start of Frame (SOF) token and be seen as x”a5”.
PID Type | PID Name | PID[3:0] | Description |
Token | OUT | “0001” (x”1”) | Address + endpoint number in host -> function transaction |
IN | “1001” (x”9”) | Address + endpoint number in function -> host transaction | |
SOF | “0101” (x”5”) | Start of frame marker and frame number | |
SETUP | “1101” (x”d”) | Address + endpoint number in host -> function transaction for setup to a control endpoint | |
Data | DATA0 | “0011” (x”3”) | Data packet PID even |
DATA1 | “1011” (x”b”) | Data packet PID odd | |
Handshake | ACK | “0010” (x”2”) | Receiver accepts error free data packet |
NAK | “1010” (x”a”) | Rx device cannot accept data or Tx device cannot send data | |
STALL | “1110” (x”e”) | Endpoint is stalled | |
Special | PRE | “1100” (x”c”) | Host-issued preamble. Enables downstream bus traffic to LS devices. |
ERR | “1100” (x”c”) | (Handshake) Split Transaction Error Handshake (reuses PRE value) | |
SPLIT | “1000” (x”8”) | High-speed Split Transaction Token (see Section 8.4.2) | |
PING | “0100” (x”4”) | High-speed flow control probe for a bulk/control endpoint (see Section 8.5.1) | |
Reserved | “0000” (x”0”) | Reserved PID |
Table 1 : Packet Identifier types
It’s worth noting that data is sent LSB first.
Sync | PID | Frame No. | CRC5 | EOP |
| 8 bits | 11 bits | 5 bits |
|
SOF Packet
SOF packets are sent by the Host every 1 ms on Full Speed links. On High Speed links each 1 ms period is split into 8 microframes and a SOF is sent at the beginning of each microframe. Each of these microframe SOFs within this 1 ms period will have the same Frame Number.
The Host must now reset the peripheral by stopping any further communication including stop sending any further SOFs. Where there has been 3.0ms of IDLE state without taffic the device must enter Full Speed Idle mode where the High Speed terminations are removed and the pullup on D- is enabled.
Where the host pulls down both D+ and D- then this is a Reset. Where the Host releases both lines allowing a LineState of “01” then the peripheral is placed in Suspend mode. It is unfortunate that the ULPI reference document makes little mention of the Reset procedure.
The Reset procedure:
1When there has been no activity on either D+ or D- lines, the peripheral must leave the High Speed state and enter the Fast Speed state with an active pullup on the D+ signal by writing x”45” to FUNC_CTRL (x”04”) register.
2If after 500µs the Linestate[1:0] is “00” then the host is applying a reset to the peripheral and continue as stage 3 in the initialisation process. Note: if the line is anything other than “00” then the peripheral should be placed in Suspend mode.
We should expect the following transactions from a Scandisk memory stick as per USB Simply Buffered (USB) - Device Enumeration by Shakthi Kannan.
The steps involved in USB device enumeration are as follows:
1SetAddress
6SetConfiguration
| Transaction | Host |
| Device |
|
Set Address | Setup transaction | Setup |
| 2D 00 10 | |
Data0 |
| C3 00 05 01 00 00 00 00 00 EB 25 | |||
| ACK | 42 | |||
IN transaction | IN |
| 69 00 10 | ||
| DATA1 | 4B 00 00 | |||
ACK |
| D2 | |||
Get Descriptor (Device) | Setup transaction | Setup |
| 2D 01 E8 | |
Data0 |
| C3 80 06 00 01 00 00 12 00 E0 F4 | |||
| ACK | 42 | |||
IN transaction | IN |
| 69 01 E8 | ||
| DATA1 | 4B 12 01 00 02 00 00 00 40 44 86 0B 80 00 01 01 02 03 01 38 E6 | |||
ACK |
| D2 | |||
OUT transaction | OUT |
| E1 01 E8 | ||
DATA1 |
| 4B 00 00 | |||
| ACK | 42 | |||
Get Descriptor (Config 1) | Setup transaction | Setup |
| 2D 01 E8 | |
Data0 |
| C3 80 06 00 02 00 00 FF 00 E9 A4 | |||
| ACK | 42 | |||
IN transaction | IN |
| 69 01 E8 | ||
| DATA1 | 4B 09 02 20 00 01 01 00 80 FA 09 04 00 00 02 08 06 50 00 07 05 82 02 00 02 00 07 05 01 02 00 02 00 8C 69 | |||
ACK |
| D2 | |||
OUT transaction | OUT |
| E1 01 E8 | ||
DATA1 |
| 4B 00 00 | |||
| ACK | D2 | |||
Get Descriptor (String 3 -1) | Setup transaction | Setup |
| 2D 01 E8 | |
Data0 |
| C3 80 06 03 03 09 04 FF 00 96 0A | |||
| ACK | 42 | |||
IN transaction | IN |
| 69 01 E8 | ||
| DATA1 | 4B 22 03 31 00 30 00 4A 00 32 00 31 00 34 00 30 00 30 00 30 00 30 00 30 00 30 00 33 00 36 00 38 00 42 00 B3 51 | |||
ACK |
| D2 | |||
OUT transaction | OUT |
| E1 01 E8 | ||
DATA1 |
| 4B 00 00 | |||
| ACK | 42 | |||
Get Descriptor (String Language IDs) | Setup transaction | Setup |
| 2D 01 E8 | |
Data0 |
| C3 80 06 00 03 00 00 FF 00 D4 64 | |||
| ACK | 42 | |||
IN transaction | IN |
| 69 01 E8 | ||
| DATA1 | 4B 04 03 09 04 78 | |||
ACK |
| D2 | |||
OUT transaction | OUT |
| E1 01 E8 | ||
DATA1 |
| 4B 00 00 | |||
| ACK | 42 | |||
Get Descriptor (String 2) | Setup transaction | Setup |
| 2D 01 E8 | |
Data0 |
| C3 80 06 02 03 09 04 FF 00 97 DB | |||
| ACK | 42 | |||
IN transaction | IN |
| 69 01 E8 | ||
| DATA1 | 4B 3E 03 55 00 53 00 42 00 20 00 46 00 6C 00 61 00 73 00 68 00 20 00 44 00 69 00 73 00 6B 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 3E BB | |||
ACK |
| D2 | |||
OUT transaction | OUT |
| E1 01 E8 | ||
DATA1 |
| 4B 00 00 | |||
| ACK | 42 | |||
Get Descriptor (Device) | Setup transaction | Setup |
| 2D 01 E8 | |
Data0 |
| C3 80 06 00 01 00 00 12 00 E0 F4 | |||
| ACK | 42 | |||
IN transaction | IN |
| 69 01 E8 | ||
| DATA1 | 4B 12 01 00 02 00 00 00 40 44 86 0B 80 00 01 01 02 03 01 38 E6 | |||
ACK |
| D2 | |||
OUT transaction | OUT |
| E1 01 E8 | ||
DATA1 |
| 4B 00 00 | |||
| ACK | 42 | |||
Get Descriptor (Config 2) | Setup transaction | Setup |
| 2D 01 E8 | |
Data0 |
| C3 80 06 00 02 00 00 09 00 AE 04 | |||
| ACK | 42 | |||
IN transaction | IN |
| 69 01 E8 | ||
| DATA1 | 4B 09 02 20 00 01 01 00 80 FA E2 FB | |||
ACK |
| D2 | |||
OUT transaction | OUT |
| E1 01 E8 | ||
DATA1 |
| 4B 00 00 | |||
| ACK | D2 | |||
Get Descriptor (Config 3) | Setup transaction | Setup |
| 2D 01 E8 | |
Data0 |
| C3 80 06 00 02 00 00 20 00 B1 94 | |||
| ACK | 42 | |||
IN transaction | IN |
| 69 01 E8 | ||
| DATA1 | 4B 09 02 20 00 01 01 00 80 FA 09 04 00 00 02 08 06 50 00 07 05 82 02 00 02 00 07 05 01 02 00 02 00 8C 69 | |||
ACK |
| D2 | |||
OUT transaction | OUT |
| E1 01 E8 | ||
DATA1 |
| 4B 00 00 | |||
| ACK | D2 | |||
Get Descriptor (String Language IDs 2) | Setup transaction | Setup |
| 2D 01 E8 | |
Data0 |
| C3 80 06 00 03 00 00 02 00 94 F4 | |||
| ACK | 42 | |||
IN transaction | IN |
| 69 01 E8 | ||
| DATA1 | 4B 04 03 BC 8E | |||
ACK |
| D2 | |||
OUT transaction | OUT |
| E1 01 E8 | ||
DATA1 |
| 4B 00 00 | |||
| ACK | 42 | |||
Get Descriptor (String Language IDs 3) | Setup transaction | Setup |
| 2D 01 E8 | |
Data0 |
| C3 80 06 00 03 00 00 04 00 97 54 | |||
| ACK | 42 | |||
IN transaction | IN |
| 69 01 E8 | ||
| DATA1 | 4B 04 03 09 04 09 78 | |||
ACK |
| D2 | |||
OUT transaction | OUT |
| E1 01 E8 | ||
DATA1 |
| 4B 00 00 | |||
| ACK | 42 | |||
Get Descriptor (String 3 - 2) | Setup transaction | Setup |
| 2D 01 E8 | |
Data0 |
| C3 80 06 03 03 09 04 02 00 D6 9A | |||
| ACK | 42 | |||
IN transaction | IN |
| 69 01 E8 | ||
| DATA1 | 4B 22 03 A6 EE | |||
ACK |
| D2 | |||
OUT transaction | OUT |
| E1 01 E8 | ||
DATA1 |
| 4B 00 00 | |||
| ACK | 42 | |||
Get Descriptor (String 3 - 3) | Setup transaction | Setup |
| 2D 01 E8 | |
Data0 |
| C3 80 06 03 03 09 04 22 00 CF 5A | |||
| ACK | 42 | |||
IN transaction | IN |
| 69 01 E8 | ||
| DATA1 | 4B 22 03 31 00 30 00 4A 00 32 00 31 00 34 00 30 00 30 00 30 00 30 00 30 00 30 00 33 00 36 00 38 00 42 00 B3 51 | |||
ACK |
| D2 | |||
OUT transaction | OUT |
| E1 01 E8 | ||
DATA1 |
| 4B 00 00 | |||
| ACK | 42 | |||
Set Configuration | Setup transaction | Setup |
| 2D 01 E8 | |
Data0 |
| C3 00 09 01 00 00 00 00 00 27 25 | |||
| ACK | 42 | |||
IN transaction | IN |
| 69 01 E8 | ||
| DATA1 | 4B 00 00 | |||
ACK |
| D2 | |||
Get Max LUN | Setup transaction | Setup |
| 2D 01 E8 | |
Data0 |
| C3 A1 FE 00 00 00 00 01 00 6A 1F | |||
| ACK | 42 | |||
IN transaction | IN |
| 69 01 E8 | ||
| DATA1 | 4B 00 40 BF | |||
ACK |
| D2 | |||
OUT transaction | OUT |
| E1 01 E8 | ||
DATA1 |
| 4B 00 00 | |||
| ACK | D2 | |||
Clear Feature | Setup transaction | Setup |
| 2D 01 E8 | |
Data0 |
| C3 02 01 00 00 82 00 00 00 06 95 | |||
| ACK | 42 | |||
IN transaction | IN |
| 69 01 E8 | ||
| DATA1 | 4B 00 00 | |||
ACK |
| D2 |
1.SetAddress
1.1.SETUP
The first packet we receive after a reset is an x”2d”, a setup packet of the following format:
PID (d) | !PID(2) | Function Address | Endpoint | CRC |
4 | 4 | 7 | 4 | 5 |
The first Token packet is “2d 00 10” where the Host provides the device a temporary address of b“000_0000”.
1.2.DATA0
The next packet starts with “c3” indicating a DATA0 packet.
PID (3) | !PID(c) | SETUP Data | CRC |
4 | 4 | 64 | 16 |
Where the SETUP Data is:
Description | bmRequestType | bRequest | wValue | wIndex | wLength |
Length in bytes | 1 | 1 | 2 | 2 | 2 |
This setup packet we receive is “c3 00 05 06 00 00 00 00 00 ea 92”
bmRequestType has the following fields
Description | D7 | D6..D5 | D4……..D0 |
Length in bits | 1 | 2 | 5 |
D7: Data transfer direction
0 = Host-to-device
1 = Device-to-host
D6..D5: Type
0 = Standard
1 = Class
2 = Vendor
3 = Reserved
D4..D0: Recipient
0 = Device
1 = Interface
2 = Endpoint
3 = Other
4..31 = Reserved
The device address is limited to 127, with 0 being that of an uninitialized peripheral. A peripheral may have one or a number of endpoints depending on its architecture. The functional address is set by the host with the SET_ADDRESS packet as per the table below:
bmRequest Type 1 byte | bRequest
1 byte | wValue
2 bytes | wIndex
2 bytes | wLength
2 bytes | Data |
00000000 00000001 00000010 | 0x01 CLEAR_FEATURE | Feature Selector | Zero Interface Endpoint | 0 | None |
10000000 | 0x08 GET_CONFIGURATION | 0 | 0 | 1 | Configuration Value |
10000000 | 0x06 GET_DESCRIPTOR | Descriptor Type and Descriptor Index | 0 or Language ID | Descriptor Length | Descriptor |
10000001 | 0x0a GET_INTERFACE | 0 | Interface | 1 | Alternate Interface |
10000000 | 0x00 GET_STATUS | 0 | Zero Interface Endpoint | 2 | Device, Interface, or Endpoint status |
00000000 | 0x05 SET_ADDRESS | Device Address | 0 | 0 | None |
00000000 | 0x09 SET_CONFIGURATION | Configuration Value | 0 | 0 | None |
00000000 | 0x07 SET_DESCRIPTOR | Descriptor Type and Descriptor Index | 0 or Language ID | Descriptor Length | Descriptor |
00000000 00000001 00000010 | 0x03 SET_FEATURE | Feature Selector | Zero Interface Endpoint | 0 | None |
00000001 | 0x0B SET_INTERFACE | Alternate | Interface | 0 | None |
10000001 | 0x0C SYNCH_FRAME | 0 | Endpoint | 2 | Frame Number |
Before moving on, we need to compute and check the CRC-5 value. A polynomial of x5 + x2 + 1 is used. There a number of sites that compute the CRC from 8 bit data and an example is:
http://www.easics.be/webtools/crctool
This creates a downloadable function. However its implementation is not so intuitive. Most of these functions expect a little endian byte so we have to swap the bit ordering. Furthermore when calculating the CRC the answer must be inverted and byte order swapped.
The CRC must be preloaded with x”1f” and in this case the endian nature of the 8 bit data should be reversed as D(7) is the first serial bit, ie the LSB). It is worth noting that some generator functions have D(0) as the LSB.
After performing a CRC-5 on the data and the CRC bits, the result should be “01100”. The CRC-5 is not performed on the PID.
It is important to wait a short time before the device sends an ACK. Note the ULPI interface requires 4D to be sent and not 2D. The ULPI string “4D” is converted by the PHY to “2D”.
1.3.ACK
The device returns an ACK.
1.4.IN
The Host should send an IN token “69 00 10” which says it is ready to receive data.
1.5.DATA1
The device send a DATA1 packet “4B 00 00”, a null packet.
1.6.ACK
The Host returns anACK.
2.GetDescriptor (Device)
2.1.SETUP
The host sends a SETUP packet - 2D 01 E8
2.2.DATA0
The host sends a DATA0 packet of C3 80 06 00 01 00 00 12 00 E0 F4. This request is for a device to host direction, indicated by the second byte of “80”, sending the device a GET_DESCRIPTOR indicated by the third byte “06”. Byte 4 is 01 indicating the Descriptor type is DEVICE. Byte 7 is “12” indicating that the host is expecting 18 bytes.
2.3.ACK
The Device returns an ACK
2.4.IN
The Host should send an IN token “69 01 E8” which says it is ready to receive data.
2.5.DATA1
The device send a DATA1 packet “4B 12 01 00 02 00 00 00 40 44 86 0B 80 00 01 01 02 03 01 38 E6”. A description of this packet is:
Offset | Field | Size | Value | Description |
0 | bLength | 1 | Number | Length of this descriptor (bytes) |
1 | bDescriptorType | 1 | Constant | DEVICE descriptor type |
2 | bcdUSB | 2 | BCD | USB Specification Release Number in BCD format (210H) |
4 | bDeviceClass | 1 | Class | Class code (by USB-IF) |
5 | bDeviceSubClass | 1 | SubClass | Subclass code (by USB-IF) |
6 | bDeviceProtocol | 1 | Protocol | Protocol code (by USB-IF) |
7 | bMaxPacketSize0 | 1 | Number | Maximum packet size for endpoint zero (8, 16, 32 or 64) |
8 | idVendor | 2 | ID | Vendor ID (by USB-IF) |
10 | idProduct | 2 | ID | Product ID (by manufacturer) |
12 | bcdDevice | 2 | BDC | Device release number in BCD |
14 | iManufacturer | 1 | Index | Index of string descriptor describing manufacturer |
15 | iProduct | 1 | Index | Index of string descriptor describing product |
16 | iSerialNumber | 1 | Index | Index of string descriptor describing the device's serial number |
17 | bNumConfigurations | 1 | Number | Number of possible configurations |
2.6.ACK
The Host responds with an ACK “D2”.
Note: If the bcdUSB field is different to the current link speed, the host will try and renegotiate the speed. This can fool the unwary to wonder why no ACK has been received. You might see the characteristic chirp after sending the DATA1 packet!
2.7.OUT
The Host send an OUT packet, E1 01 E8.
2.8.DATA1
The Host sends a DATA1 packet “4B 00 00”, a null packet.
2.9.ACK
The Device sends a “42” packet.
3.GetDescriptor (Configuration (1))
3.1.SETUP
The host sends a SETUP packet - 2D 01 E8
3.2.DATA0
The host sends a DATA0 packet C3 80 06 00 02 00 00 FF 00 E9 A4. This request is for a device to host direction, indicated by the second byte of “80”, sending the device a GET_DESCRIPTOR indicated by the third byte “06”. Byte 4 is 02 indicating the Descriptor type is CONFIGURATION and the Descriptor index is 0. The length of requested data is 255 indicated by byte 7, though in practice the peripheral will only send as many bytes as necessary up to this maximum.
3.3.ACK
The Device returns an ACK 42
3.4.IN
The Host should send an IN token “69 01 E8” which says it is ready to receive data.
3.5.DATA1
The device send a DATA1 packet “4B 09 02 20 00 01 01 00 80 FA 09 04 00 00 02 08 06 50 00 07 05 82 02 00 02 00 07 05 01 02 00 02 00 8C 69”.
The first 9 bytes in this case is the Configuration Descriptor:
Offset | Field | Size | Value | Hex |
0 | bLength | 1 | 9 (bytes) | 0x09 |
1 | bDescriptorType | 1 | CONFIGURATION descriptor (2) | 0x02 |
2 | wTotalLength | 2 | 32 (bytes) | 0x0020 |
4 | bNumInterfaces | 1 | 1 | 0x01 |
5 | bConfigurationValue | 1 | 1 | 0x01 |
6 | iConfiguration | 1 | 0 | 0x00 |
7 | bmAttributes | 1 | Bus Powered | 0x80 |
8 | bMaxPower | 2 | 500 mA | 0xFA |
Next follows then Interface Descriptor (9 bytes):
Offset | Field | Size | Value | Hex |
0 | bLength | 1 | 9 (bytes) | 0x09 |
1 | bDescriptorType | 1 | INTERFACE descriptor (4) | 0x04 |
2 | bInterfaceNumber | 1 | 0 | 0x00 |
3 | bAlternateSetting | 1 | 0 | 0x00 |
4 | bNumEndpoints | 1 | 2 | 0x02 |
5 | bInterfaceClass | 1 | Mass Storage(08) | 0x08 |
6 | bInterfaceSubClass | 1 | SCSI transparent command set(06) | 0x06 |
7 | bInterfaceProtocol | 1 | Bulk-Only Transport(50) | 0x50 |
8 | iInterface | 2 | 0 | 0x00 |
And then details of the two Endpoints (7 bytes each):
Offset | Field | Size | Value | Hex |
0 | bLength | 1 | 7 (bytes) | 0x07 |
1 | bDescriptorType | 1 | ENDPOINT descriptor (5) | 0x05 |
2 | bEndpointAddress | 1 | IN endpoint (D7), first endpoint (D0) | 0x82 |
3 | bmAttributes | 1 | Data endpoint (D5...D4), No synchronization (D3...D2), Bulk transfer type (D1...D0) | 0x02 |
4 | wMaxPacketSize | 2 | 512 bytes | 0x0200 |
6 | bInterval | 1 | 0 | 0x00 |
Offset | Field | Size | Value | Hex |
0 | bLength | 1 | 7 (bytes) | 0x07 |
1 | bDescriptorType | 1 | ENDPOINT descriptor (5) | 0x05 |
2 | bEndpointAddress | 1 | IN endpoint (D7), first endpoint (D0) | 0x01 |
3 | bmAttributes | 1 | Data endpoint (D5...D4), No synchronization (D3...D2), Bulk transfer type (D1...D0) | 0x02 |
4 | wMaxPacketSize | 2 | 512 bytes | 0x0200 |
6 | bInterval | 1 | 0 | 0x00 |
3.6.ACK
The Host responds with an ACK “D2”.
3.7.OUT
The Host send an OUT packet, E1 01 E8.
3.8.DATA1
The Host sends a DATA1 packet “4B 00 00”, a null packet.
3.9.ACK
The Device sends a “42” packet.
4.GetDescriptor (String 3)
4.1.SETUP
The host sends a SETUP packet - 2D 01 E8
4.2.DATA0
The host sends a DATA0 packet C3 80 06 03 03 09 04 FF 00 96 0A. This request is for a device to host direction, indicated by the second byte of “80”, sending the device a GET_DESCRIPTOR indicated by the third byte “06”. Byte 4 is 03 indicating the Descriptor type is String and the Descriptor index is 3. The length of requested data is 255 bytes indicated by byte 7.
4.3.ACK
The Device returns an ACK 42
4.4.IN
The Host should send an IN token “69 01 E8” which says it is ready to receive data.
4.5.DATA1
The Device sends 4B 22 03 31 00 30 00 4A 00 32 00 31 00 34 00 30 00 30 00 30 00 30 00 30 00 30 00 33 00 36 00 38 00 42 00 B3 51. A description of this packet:
Offset | Field | Size | Value | Hex |
0 | bLength | 1 | 34 bytes | 0x22 |
1 | bDescriptorType | 1 | STRING descriptor | 0x03 |
2 | bString | 2 | 1 | 0x0031 |
4 | bString | 2 | 0 | 0x0030 |
6 | bString | 2 | J | 0x004A |
. | . | . | . | . |
. | . | . | . | . |
. | . | . | . | . |
. | . | . | . | . |
28 | bString | 2 | 6 | 0x0036 |
30 | bString | 2 | 8 | 0x0038 |
32 | bString | 2 | B | 0x0042 |
4.6.ACK
The Host responds with an ACK “D2”.
4.7.OUT
The Host send an OUT packet, E1 01 E8.
4.8.DATA1
The Host sends a DATA1 packet “4B 00 00”, a null packet.
4.9.ACK
The Device sends a “42” packet.
5.Get Descriptor (String Language IDs - 1)
5.1.SETUP
The host sends a SETUP packet - 2D 01 E8
5.2.DATA0
The host sends a DATA0 packet C3 80 06 00 03 00 00 FF 00 D4 64. This request is for a device to host direction, indicated by the second byte of “80”, sending the device a GET_DESCRIPTOR indicated by the third byte “06”. Byte 4 is 03 indicating the Descriptor type is String and the Descriptor index is 0. The length of requested data is 255 bytes indicated by byte 7.
5.3.ACK
The Device returns an ACK 42
5.4.IN
The Host should send an IN token “69 01 E8” which says it is ready to receive data.
5.5.DATA1
The Device sends 4B 04 03 09 04 09 78
Offset | Field | Size | Value | Hex |
0 | bLength | 1 | 4 bytes | 0x04 |
1 | bDescriptorType | 1 | STRING descriptor | 0x03 |
2 | wLANGID[0] | 2 | English (US) | 0x0409 |
5.6.ACK
The Host responds with an ACK “D2”.
5.7.OUT
The Host send an OUT packet, E1 01 E8.
5.8.DATA1
The Host sends a DATA1 packet “4B 00 00”, a null packet.
5.9.ACK
The Device sends a “42” packet.
6.GetDescriptor (String 2)
6.1.SETUP
The host sends a SETUP packet - 2D 01 E8
6.2.DATA0
The host sends a DATA0 packet C3 80 06 02 03 09 04 FF 00 97 DB. This request is for a device to host direction, indicated by the second byte of “80”, sending the device a GET_DESCRIPTOR indicated by the third byte “06”. Byte 4 is 03 indicating the Descriptor type is String and the Descriptor index is 0. The length of requested data is 255 bytes indicated by byte 7.
6.3.ACK
The Device returns an ACK 42
6.4.IN
The Host should send an IN token “69 01 E8” which says it is ready to receive data.
6.5.DATA1
The Device sends 4B 3E 03 55 00 53 00 42 00 20 00 46 00 6C 00 61 00 73 00 68 00 20 00 44 00 69 00 73 00 6B 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 3E BB. A description of this packet:
Offset | Field | Size | Value | Hex |
0 | bLength | 1 | 62 bytes | 0x3E |
1 | bDescriptorType | 1 | STRING descriptor | 0x03 |
2 | bString | 2 | U | 0x0055 |
4 | bString | 2 | S | 0x0053 |
6 | bString | 2 | B | 0x0042 |
8 | bString | 2 | <space> | 0x0020 |
10 | bString | 2 | F | 0x0046 |
12 | bString | 2 | l | 0x006C |
14 | bString | 2 | a | 0x0061 |
16 | bString | 2 | s | 0x0073 |
18 | bString | 2 | h | 0x0068 |
20 | bString | 2 | <space> | 0x0020 |
22 | bString | 2 | D | 0x0044 |
. | . | . | . | . |
. | . | . | . | . |
6.6.ACK
The Host responds with an ACK “D2”.
6.7.OUT
The Host send an OUT packet, E1 01 E8.
6.8.DATA1
The Host sends a DATA1 packet “4B 00 00”, a null packet.
6.9.ACK
The Device sends a “42” packet.
7.GetDescriptor (Device)
This request is a repeat of Paragraph 2.
8.GetDescriptor (Configuration (2))
This is essentially the same as Paragraph 3, except the Host is only asking for the periphal’s Configuration Descriptor, ie the first 9 bytes.
9.GetDescriptor (Configuration (3))
This is essentially the same as Paragraph 3, where the Host is requesting all the Configuration information. Note that the Host is asking for 32 bytes of data, ie the actual length of the Configuration Descriptor.
10.Get Descriptor (String Language IDs - 2)
This is a repeat of Paragraph 4.
11.GetDescriptor (String 3 - 2)
This is a repeat of Paragraph 5 except only the first 2 bytes are being requested, being the string length and String Descriptor.
12.GetDescriptor (String 3 - 3)
This is a repeat of Paragraph 5 for the full string.
13.GetMaxLUN
13.1.SETUP
The host sends a SETUP packet - 2D 01 E8
13.2.DATA0
The host sends a DATA0 packet C3 A1 FE 00 00 00 00 01 00 6A 1F. The device may implement several logical units that share common device characteristics. This retrieves the number of logical units in the peripheral and is specific to the USB Mass Storage Class.
bmRequest Type 1 byte | bRequest 1 byte | wValue 2 bytes | wIndex 2 bytes | wLength 2 bytes | Data |
10100001 | 0xFE | 0x0000 | Interface | 0x0001 | None |
13.3.ACK
The Device returns an ACK 42
13.4.IN
The Host should send an IN token “69 01 E8” which says it is ready to receive data.
13.5.DATA1
The Device sends 4B 00 40 BF. Our device has no multiple LUNs and so says “0x00”. It is confusing where if there were 4 LUNs the reply would be 0x03, numbered 0 to 3.
13.6.ACK
The Host responds with an ACK “D2”.
13.7.OUT
The Host send an OUT packet, E1 01 E8.
13.8.DATA1
The Host sends a DATA1 packet “4B 00 00”, a null packet.
13.9.ACK
The Device sends a “42” packet.
14.Clear Feature
This request is used to clear or disable a specific feature.
14.1.SETUP
The host sends a SETUP packet - 2D 01 E8
14.2.DATA0
The host sends a DATA0 packet C3 02 01 00 00 82 00 00 00 06 95.
bmRequest Type 1 byte | bRequest 1 byte | wValue 2 bytes | wIndex 2 bytes | wLength 2 bytes | Data |
00000000B 00000001B 00000010B | CLEAR_FEATURE (0x02) | Feature Selector | Zero Interface Endpoint | 0x0000 | None |
14.3.ACK
The Device returns an ACK 42
14.4.IN
The Host should send an IN token “69 01 E8” which says it is ready to receive data.
14.5.DATA1
The Device sends a null reply 4B 00 00.
14.6.ACK
The Host responds with an ACK “D2”.
14.7.OUT
The Host send an OUT packet, E1 01 E8.
14.8.DATA1
The Host sends a DATA1 packet “4B 00 00”, a null packet.
14.9.ACK
The Device sends a “42” packet.
The device will now be recognised by the host.
An important consideration is that RXCMD packets can interrupt and abort register reads and writes. Depending of the timing of events there are a number of variations.
Figure 7 TXCMD cycle aborted by a RXCMD
For a TXCMD applied to the data bus the PHY can abort register write by pulling DIR at the same time the PHY would assert NXT. The register write must be aborted and sent again after Data returns to the idle state.
Conclusion.
I wrote this to gain an understanding of the USB port and how to emulate a peripheral. I hope it is useful to others as I could find little information about the initialisation process.
Any improvements or suggestions are invited.