Core USB 2.0 Device Gateware

The LUNA gateware library provides a flexible base USB Device model, which is designed to provide the basis for creating both application-specific and general-purpose USB hardware.

USB devices are created using two core components:

  • A USBDevice instance, which provides hardware that handles low-level USB communications, and which is designed to be applicable to all devices; and

  • One or more endpoint interfaces, which handle high-level USB communications – and provide the logic the tailors the device to its intended application.

The USBDevice communicates with low-level transciever hardware via the FPGA-friendly USB Transceiver Macrocell Interface (UTMI). Translators can be used to transparently adapt the FPGA interface to other common bus formats; including the common ULPI low-pin-count variant of UTMI.

USB 2.0 architecture diagram

The overall architecture of a LUNA USB 2.0 device, highlighting the USBDevice components, their connections to the endpoint interfaces, and optional bus translator.

Conceptual Components

The USBDevice class contains the low-level communications hardware necessary to implement a USB device; including hardware for maintaining device state, detecting events, reading data from the host, and generating responses.

Token Detector

The Token Detector detects token packets from the host; and is responsible for:

  • Detecting start of frame packets, which are used to maintain consistent timing across USB devices.

  • Detecting the start of USB transactions.

  • Identifying the device and endpoint to which each transaction is addressed.

As each USB transaction starts with a token packet; it is implicitly the Token Detector’s responsiblity to notify endpoint interfaces of imminent incoming data (OUT transactions) and requests for data (IN transactions).

Handshake Detector

The Handshake Detector detects handshake packets from the host; and is responsible for identifying the host’s response to packets from the device – indicating whether the host successfully received a packet sent from the device.

Data Packet Receiver

The Data Packet Receiver is responsible for receiving data packets from the device – including the payloads of both OUT and SETUP transactions – and translating them to a simple data stream.

The Data Receiver handles error detection; and thus validates the checksums of each packet using the Data CRC Unit.

Device State Manager

The Device State Manager is responsible for storing global device state – primarily, the device’s current address and configuration. The device state manager accepts changes to the device’s address/configuration from each endpoint interface; and automatically resets the relevant parameters when a USB reset is received.

Handshake Generator

The Handshake Generator provides a simple, strobe-based interface that allows endpoints to easily emit handshake packets – allowing the device to acknowledge packets (ACK), issue stalls (STALL) , and to rate limit communications (NAK/NYET).

Data Packet Transmitter

The Data Packet Generator is responsible for generating outgoing USB packets from simple data streams; including emitting data packet IDs, sending data, and appending data CRCs. This class automatically appends the required data CRC-16s.

Data CRC Unit

The Data CRC Unit is shared among the packet receiver and packet generator; and handles computing the CRC-16 for USB data streams.

Interpacket Timer

The Interpacket Timer is responsible for maintaining maximum and minimum interpacket delays; ensuring that the device can correctly provide bus turnover times; and knows the window in which handshake packets are expected to arrive.

Reset/Suspend Sequencer

The Reset/Suspend Sequencer is responsible for detecing USB reset and suspend events; and where applicable, participating in the USB reset protocol’s high-speed detection handshake.

The sequencer:

  • Detects USB resets; and communicates to the Device State Manager that it should return the device to an un-addressed, un-configured state.

  • Performs the high speed detection handshake, which allows the device to switch to High Speed operation; and thus is necessary for the device to operate at high speed.

  • Manages the high-speed terminations; as part of the reset-handshake and suspend protocols.

  • Detects the periods of inactivity that indicate the device is being suspended; and automatically disengages high-speed terminations while the device is in suspend.

usb2.device Components

Contains the organizing hardware used to add USB Device functionality to your own designs; including the core USBDevice class.

class luna.gateware.usb.usb2.device.USBDevice(*args, src_loc_at=0, **kwargs)

Bases: Elaboratable

Core gateware common to all LUNA USB2 devices.

The USBDevice module contains the low-level communications hardware necessary to implement a USB device; including hardware for maintaining device state, detecting events, reading data from the host, and generating responses.

This class can be instantiated directly, and used to build a USB device, or can be subclassed to create custom device types.

To configure a USBDevice from a CPU or other wishbone master, see USBDeviceController; which can easily be attached using its attach method.

Parameters:
  • bus ([UTMI interface, ULPI Interface]) – The UTMI or ULPI PHY connection to be used for communications.

  • handle_clocking (bool, Optional) – True iff we should attempt to connect up the usb clock domain to the PHY automatically based on the clk signals’s I/O direction. This option may not work for non-simple connections; in which case you will need to connect the clock signal yourself.

connect

Held high to keep the current USB device connected; or held low to disconnect.

Type:

Signal(), input

low_speed_only

If high, the device will operate at low speed.

Type:

Signal(), input

full_speed_only

If high, the device will be prohibited from operating at high speed.

Type:

Signal(), input

frame_number

The current USB frame number.

Type:

Signal(11), output

microframe_number

The current USB microframe number. Always 0 on non-HS connections.

Type:

Signal(3), output

sof_detected

Pulses for one cycle each time a SOF is detected; and thus our frame number has changed.

Type:

Signal(), output

new_frame

Strobe that indicates a new frame (not microframe) is detected.

Type:

Signal(), output

reset_detected

Asserted when the USB device receives a bus reset.

Type:

Signal(), output

# State signals.
suspended

High when the device is in USB suspend. This can be (and by the spec must be) used to trigger the device to enter lower-power states.

Type:

Signal(), output

tx_activity_led

Signal that can be used to drive an activity LED for TX.

Type:

Signal(), output

rx_activity_led

Signal that can be used to drive an activity LED for RX.

Type:

Signal(), output

add_control_endpoint()

Adds a basic control endpoint to the device.

Does not add any request handlers. If you want standard request handlers; add_standard_control_endpoint automatically adds standard request handlers.

Return type:

Returns the endpoint object for the control endpoint.

add_endpoint(endpoint)

Adds an endpoint interface to the device.

Parameters:

endpoint (Elaborateable) – The endpoint interface to be added. Can be any piece of gateware with a EndpointInterface attribute called interface.

add_standard_control_endpoint(descriptors: DeviceDescriptorCollection, **kwargs)

Adds a control endpoint with standard request handlers to the device.

Parameters will be passed on to StandardRequestHandler.

Return value

The endpoint object created.

usb2.packet Components

Contains the gatware module necessary to interpret and generate low-level USB packets.

class luna.gateware.usb.usb2.packet.DataCRCInterface

Bases: Record

Record providing an interface to a USB CRC-16 generator.

start

Strobe that indicates that a new CRC computation should be started.

Type:

Signal(), input to CRC generator

crc

The current CRC-16 value; updated with each sent or received byte.

Type:

Signal(), output from CRC generator

class luna.gateware.usb.usb2.packet.HandshakeExchangeInterface(*, is_detector)

Bases: Record

Record that carries handshakes detected -or- generated between modules.

ack

When connected to a generator, pulsing this strobe will trigger generating of an ACK. When connected to a detector, this strobe will be pulsed when an ACK is detected from the host.

Type:

Signal()

nak

When connected to a generator, pulsing this strobe will trigger generating of an NAK. When connected to a detector, this strobe will be pulsed when an NAK is detected from the host.

Type:

Signal()

stall

When connected to a generator, pulsing this strobe will trigger generation of a STALL. Unused in a detector, currently.

Type:

Signal()

nyet

When connected to a generator, pulsing this strobe will trigger generation of a NYET. Unused in a detector, currently.

Type:

Signal()

Parameters:

is_detector (bool) – If true, this will be considered an interface to a detector that identifies handshakes. Otherwise, this will be considered an interface to a generator that accepts handshake requests.

class luna.gateware.usb.usb2.packet.InterpacketTimerInterface

Bases: Record

Record providing an interface to our interpacket timer.

See [USB2.0: 7.1.18] and the USBInterpacketTimer gateware for more information.

start

Strobe that indicates when the timer should be started. Usually started at the end of an Rx or Tx event.

Type:

Signal(), input to timer

tx_allowed

Strobe that goes high when it’s safe to transmit after an Rx event.

Type:

Signal(), output from timer

tx_timeout

Strobe that goes high when the transmit-after-receive window has passed.

Type:

Signal(), output from timer

rx_timeout

Strobe that goes high when the receive-after-transmit window has passed.

Type:

Signal(), output from timer

attach(*subordinates)

Attaches subordinate interfaces to the given timer interface.

Parameters:

subordinates ([InterpacketTimerInterface, Signal]) – Each InterpacketTimerInterface is provided will be fully connected to a given timer interface. Each Signal provided will be interpreted as a timer reset, and added to the list of all resets.

class luna.gateware.usb.usb2.packet.TokenDetectorInterface

Bases: Record

Record providing an interface to a USB token detector.

pid

The Packet ID of the most recent token.

Type:

Signal(4), detector output

address

The address associated with the relevant token.

Type:

Signal(7), detector output

endpoint

The endpoint indicated by the most recent token.

Type:

Signal(4), detector output

new_token

Strobe asserted for a single cycle when a new token packet has been received.

Type:

Signal(), detector output

ready_for_response

Strobe asserted for a single cycle one inter-packet delay after a token packet is complete. Indicates when the token packet can be responded to.

Type:

Signal(), detector output

frame

The current USB frame number.

Type:

Signal(11), detector output

new_frame

Strobe asserted for a single cycle when a new SOF has been received.

Type:

Signal(), detector output

is_in

High iff the current token is an IN.

Type:

Signal(), detector output

is_out

High iff the current token is an OUT.

Type:

Signal(), detector output

is_setup

High iff the current token is a SETUP.

Type:

Signal(), detector output

is_ping

High iff the current token is a PING.

Type:

Signal(), detector output

class luna.gateware.usb.usb2.packet.USBDataPacketCRC(*args, src_loc_at=0, **kwargs)

Bases: Elaboratable

Gateware that computes a running CRC-16.

By default, this module has no connections to the modules that use it.

These are added using add_interface; this module supports an arbitrary number of connection interfaces; see add_interface() for restrictions.

rx_data

Receive data input; can be carried directly from a UTMI interface.

Type:

Signal(8), input

rx_valid

Receive validity signal; can be carried directly from a UTMI interface.

Type:

Signal(), input

tx_data

Transmit data input; can be carried directly from a UTMI interface.

Type:

Signal(8), input

tx_valid

When high, the tx_data input is used to update the CRC.

Type:

Signal(), input

Parameters:

initial_value ([int, Const]) – The initial value of the CRC shift register; the USB default is used if not provided.

add_interface(interface: DataCRCInterface)

Adds an interface to the CRC generator module.

Each interface can reset the CRC; and can read the current CRC value. No arbitration is performed; it’s assumed that no more than one interface will be computing a running CRC at at time.

Parameters:

interface (DataCRCInterface) – The interface to be added; accepts control signals from other modules, and brings CRC output to them. This method can be called multiple times to generate multiplpe CRCs.

class luna.gateware.usb.usb2.packet.USBDataPacketDeserializer(*args, src_loc_at=0, **kwargs)

Bases: Elaboratable

Gateware that captures USB data packet contents and parallelizes them.

data_crc

Connection to the CRC generator.

Type:

DataCRCInterface

new_packet

Strobe that pulses high for a single cycle when a new packet is delivered.

Type:

Signal(), output

packet_id

The packet ID of the captured PID.

Type:

Signal(4), output

packet

Packet data for a the most recently received packet.

Type:

Signal(max_packet_size), output

length

The length of the packet data presented on the packet[] output.

Type:

Signal(range(0, max_packet_length +1)), output

Parameters:
  • utmi (UTMIInterface, or equivalent) – The UTMI bus to observe.

  • max_packet_size (int) – The maximum packet (payload) size to be deserialized, in bytes.

  • create_crc_generator (bool) – If True, a submodule CRC generator will be created. Excellent for testing.

class luna.gateware.usb.usb2.packet.USBDataPacketGenerator(*args, src_loc_at=0, **kwargs)

Bases: Elaboratable

Module that converts a FIFO-style stream into a USB data packet.

Handles steps such as PID generation and CRC-16 injection.

As a special case, if the stream pulses last (with valid=1) without pulsing first, we’ll send a zero-length packet.

data_pid

The data packet number to use. The potential PIDS are: 0 = DATA0, 1 = DATA1, 2 = DATA2, 3 = MDATA; the interface is designed so that most endpoints can tie the MSb to zero and then perform PID toggling by toggling the LSb.

Type:

Signal(2), input

crc

Interface to our data CRC generator.

Type:

DataCRCInterface

stream

Stream input for the raw data to be transmitted.

Type:

USBInStreamInterface

tx

UTMI-subset transmit interface

Type:

UTMITransmitInterface

Parameters:

standalone (bool) – If True, this unit will include its internal CRC generator. Perfect for unit testing or debugging.

class luna.gateware.usb.usb2.packet.USBDataPacketReceiver(*args, src_loc_at=0, **kwargs)

Bases: Elaboratable

Gateware that converts received USB data packets into a data-stream packets.

It’s important to note that packet payloads are mostly directly carried over from UTMI. Since USB data is received -prior- to its CRC, one cannot know if a packet is valid until after it has been compeltely received. As a result, this interface will generate data of unknown validity, followed by a strobe on either packet_complete or crc_mismatch. The receiving interface must be prepared to handle crc_mismatch by discarding the received data.

data_crc

Connection to the CRC generator.

Type:

DataCRCInterface

timer

Connection to our interpacket timer.

Type:

InterpacketTimerInterface

stream

Stream that carries captured packet data.

Type:

USBOutDataStream, output

active_pid

The PID of the data currently being received.

Type:

Signal(4), output

packet_id

The packet ID of the most recently captured PID. Becomes valid simultaneous to a strobe on packet_complete or crc_mismatch.

Type:

Signal(4), output

packet_complete

Strobe that pulses high when a new packet is delivered with a valid CRC.

Type:

Signal(), output

crc_mismatch

Strobe that pulses high when the given packet has a CRC mismatch; and thus the data received this far should be discarded.

Type:

Signal(), output

ready_for_response

Strobe that indicates that an inter-packet delay has passed since packet_complete, and thus we’re now ready to respond with a handshake.

Type:

Signal(), output

Parameters:
  • utmi (UTMIInterface, or equivalent) – The UTMI bus to observe.

  • max_packet_size (int) – The maximum packet (payload) size to be deserialized, in bytes.

  • standalone (bool) – Debug value. If True, a submodule CRC generator will be created.

  • speed (USBSpeed) – USBSpeed signal or constant that specifies our speed in standalone mode.

class luna.gateware.usb.usb2.packet.USBHandshakeDetector(*args, src_loc_at=0, **kwargs)

Bases: Elaboratable

Gateware that detects handshake packets.

detected

Strobes that indicate which handshakes we’re detecting.

Type:

HandshakeExchangeInterface

Parameters:

utmi ([UTMIInterface, UTMITranslator]) – The UTMI interface to listen on.

ACK_PID = 2
NAK_PID = 10
NYET_PID = 6
STALL_PID = 14
class luna.gateware.usb.usb2.packet.USBHandshakeGenerator(*args, src_loc_at=0, **kwargs)

Bases: Elaboratable

Module that generates handshake packets, on request.

Attributes:

issue_ack: Signal(), input

Pulsed to generate an ACK handshake packet.

issue_nak: Signal(), input

Pulsed to generate a NAK handshake packet.

issue_stall: Signal(), input

Pulsed to generate a STALL handshake.

tx: UTMITransmitInterface

Interface to the relevant UTMI interface.

class luna.gateware.usb.usb2.packet.USBInterpacketTimer(*args, src_loc_at=0, **kwargs)

Bases: Elaboratable

Module that tracks inter-packet timings, enforcing spec-mandated packet gaps.

Ports other than speed are added dynamically via :method:add_interface`.

speed

The device’s current operating speed. Should be a USBSpeed enumeration value – 0 for high, 1 for full, 2 for low.

Type:

Signal(2), input

add_interface(interface: InterpacketTimerInterface)

Adds a connection to a user of this module.

This module performs no multiplexing; it’s assumed only one interface will be active at a time.

Parameters:

interface (InterpacketTimerInterface) – The InterPacketTimer interface to add to our module.

class luna.gateware.usb.usb2.packet.USBTokenDetector(*args, src_loc_at=0, **kwargs)

Bases: Elaboratable

Gateware that parses token packets and generates relevant events.

interface

The interface that contains token detection events, and information about detected tokens.

Type:

TokenDetectorInterface

speed

Carries a USBSpeed constant identifying the device’s current operating speed.

Type:

Signal(2), input

address

If :parameter:filter_by_address is true, this is an input that filters our event detector so it only reports tokens directed at a given address. If filter_by_address is false, this is an output that contains the address of the most recent token.

Type:

Signal(7), input -or- output

Parameters:
  • utmi (UTMIInterface) – The UTMI bus to observe.

  • filter_by_address (bool) – If true, this detector will only report events for the address supplied in the address[] field.

SOF_PID = 5
TOKEN_SUFFIX = 1

usb2.reset Components

Gateware that handles USB bus resets & speed detection.

class luna.gateware.usb.usb2.reset.USBResetSequencer(*args, src_loc_at=0, **kwargs)

Bases: Elaboratable

Gateware that detects reset signaling on the USB bus.

low_speed_only

If set, the device will be forced to operate as a low-speed device.

Type:

Signal(), input

prevent_high_speed

If set, the device will be prohibited from entering high-speed states; and will thus act like it’s a full speed device (low_speed_only = 0).

Type:

Signal(), input

bus_busy

Hold-off signal that indicates that driving the bus should be delayed.

Type:

Signal(), input

vbus_connected

Indicates that the device is connected to VBUS. When this is de-asserted, the device will be held in perpetual bus reset, and reset handshaking will be disabled.

Type:

Signal(), input

line_state

The UTMI linestate signals; used to read the current state of the USB D+ and D- lines.

Type:

Signal(2), input

bus_reset
Strobe; pulses high for one cycle when a bus reset is detected. This signal indicates that the

device should return to unaddressed, unconfigured, and should not longer be in High Speed mode.

Type:

Signal(), output

suspended

Held high while the USB device should be in suspend. This technically indicates that the device should drop down to consuming suspend current (<= 2.5mA), but very few devices are compliant with this requirement. Either way, a polite device might reduce its power consumption while in suspend.

Type:

Signal(), output

current_speed

A USBSpeed value that indicates the current operating speed. Used both to drive our device’s knowledge of operating speed and to drive our PHY’s speed selection.

Type:

Signal(2), output

operating_mode

The current UTMI operating mode. Used to select whether we’re driving the USB bus directly; or whether we’re letting the PHY handle NRZI/bit-stuffing.

Type:

Signal(2), output

termination_select

Determines the bus termination mode. In LS/FS, this determines the presence of our presence-detect pull-up. In HS mode, this determines whether the USB high-speed termination is present (0), or whether we’re in chirp mode (1).

Type:

Signal(), output, default=1

tx

– Our UTMI transmit interface; used to drive chirp signaling onto the bus.

Type:

UTMITransmitInterface, output stream