Gateware Endpoint Interfaces

The LUNA architecture separates gateware into two distinct groups: the core device, responsible for the low-level communications common to all devices, and endpoint interfaces, which perform high-level communications, and which are often responsible for tailoring each device for its intended application:

../_images/USBDevice.svg

Every useful LUNA device features at least one endpoint interface capable of at least handling enumeration. Many devices will provide multiple endpoint interfaces – often one for each endpoint – but this is not a requirement. Incoming token, data, and handshake packets are routed to all endpoint interfaces; it is up to each endpoint interface to decide which packets to respond to.

Note: terms like “interface” are overloaded: the single term “interface” can refer both to hardware interfaces and to the USB concept of an Interface. The “interface” in “endpoint interface” is an instance of the former; they are conceptually distinct from USB interfaces. To reduce conflation, we’ll use the full phrase “endpoint interface” in this document.

As a single endpoint interface may handle packets for multiple endpoints; it is entirely possible to have a device that talks on multiple endpoints, but which uses only one endpoint interface.

Exclusivity

A LUNA USBDevice performs no arbitration – if two endpoint interfaces attempt to transmit at the same time, the result is undefined; and often will result in undesirable output. Accordingly, it’s important to ensure a “clear delineation of responsibility” across endpoint interfaces. This is often accomplished by ensuring only one endpoint interface handles a given endpoint or request type.

usb2.endpoint Components

Gateware for working with abstract endpoints.

class luna.gateware.usb.usb2.endpoint.EndpointInterface

Bases: object

Interface that connects a USB endpoint module to a USB device.

Many non-control endpoints won’t need to use the latter half of this structure; it will be automatically removed by the relevant synthesis tool.

tokenizer

Interface to our TokenDetector; notifies us of USB tokens.

Type:

TokenDetectorInterface, to detector

rx

Receive interface for this endpoint.

Type:

USBOutStreamInterface, input stream to endpoint

rx_complete

Strobe that indicates that the concluding rx-stream was valid (CRC check passed).

Type:

Signal(), input to endpoint

rx_ready_for_response

Strobe that indicates that we’re ready to respond to a complete transmission. Indicates that an interpacket delay has passed after an rx_complete strobe.

Type:

Signal(), input to endpoint

rx_invalid

Strobe that indicates that the concluding rx-stream was invalid (CRC check failed).

Type:

Signal(), input to endpoint

rx_pid_toggle

Value for the data PID toggle; 0 indicates we’re receiving a DATA0; 1 indicates Data1.

Type:

Signal(), input to endpoint

tx

Transmit interface for this endpoint.

Type:

USBInStreamInterface, output stream from endpoint

tx_pid_toggle

Value for the data PID toggle; 0 indicates we’ll send DATA0; 1 indicates DATA1. 2 indicates we’ll send DATA2, while 3 indicates we’ll send DATAM.

Type:

Signal(2), output from endpoint

handshakes_in

Carries handshakes detected from the host.

Type:

HandshakeExchangeInterface, input to endpoint

handshakes_out

Carries handshakes generate by this endpoint.

Type:

HandshakeExchangeInterface, output from endpoint

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 to endpoint

active_address

Contains the device’s current address.

Type:

Signal(7), input to endpoint

address_changed

Strobe; pulses high when the device’s address should be changed.

Type:

Signal(), output from endpoint.

new_address

When address_changed is high, this field contains the address that should be adopted.

Type:

Signal(7), output from endpoint

active_config

The configuration number of the active configuration.

Type:

Signal(8), input to endpoint

config_changed

Strobe; pulses high when the device’s configuration should be changed.

Type:

Signal(), output from endpoint

new_config

When config_changed is high, this field contains the configuration that should be applied.

Type:

Signal(8)

timer

Interface to our interpacket timer.

Type:

InterpacketTimerInterface

data_crc

Control connection for our data-CRC unit.

Type:

DataCRCInterface

class luna.gateware.usb.usb2.endpoint.USBEndpointMultiplexer(*args, src_loc_at=0, **kwargs)

Bases: Elaboratable

Multiplexes access to the resources shared between multiple endpoint interfaces.

Interfaces are added using add_interface.

shared

The post-multiplexer endpoint interface.

Type:

EndpointInterface

add_interface(interface: EndpointInterface)

Adds a EndpointInterface to the multiplexer.

Arbitration is not performed; it’s expected only one endpoint will be driving the transmit lines at a time.

or_join_interface_signals(m, signal_for_interface)

Joins together a set of signals on each interface by OR’ing the signals together.

Provided Endpoint Interfaces

The LUNA library ships with a few provided endpoint interfaces. These include:

  • The USBControlEndpoint, which provides gateware that facilitates handling USB control requests. To handle requests via this endpoint, the user attaches one or more request handlers interfaces; which are documented in their own section.

  • The FIFOInterface classes, which implement simple, FIFO-based software interfaces. These lightweight interfaces are meant to allow simple CPU control over one or more endpoints. These are based off of the ValentyUSB eptri interface; and will eventually be binary-compatible with existing eptri code.

usb2.control Components

Low-level USB transciever gateware – control transfer components.

class luna.gateware.usb.usb2.control.USBControlEndpoint(*args, src_loc_at=0, **kwargs)

Bases: Elaboratable

Gateware that manages control request data progression.

This class is used by creating one or more request handler modules; which define how requests are handled. These handlers can be bound using add_request_handler.

For convenience, this module can also automatically be populated with a StandardRequestHandler via the add_standard_request_handlers.

interface

The interface from this endpoint to the core device hardware.

Type:

EndpointInterface

Parameters:
  • utmi (UTMI bus, or equivalent translator) – The UTMI bus we’ll monitor for data. We’ll consider this read-only.

  • endpoint_number (int, optional) – The endpoint number for this control interface; defaults to (and almost always should be) zero.

  • standalone (bool) – Debug parameter. If true, this module will operate without external components; i.e. without an internal data-CRC generator, or tokenizer. In this case, tokenizer and timer should be set to None; and will be ignored.

add_request_handler(request_handler)

Adds a ControlRequestHandler module to this control endpoint.

No arbitration is performed between request handlers; so it’s important that request handlers not overlap in the requests they handle.

add_standard_request_handlers(descriptors: DeviceDescriptorCollection, **kwargs)

Adds a handlers for the standard USB requests.

This will handle all Standard-type requests; so any additional request handlers must not handle Standard requests.

Parameters will be passed on to StandardRequestHandler.

usb2.interfaces.eptri Components

Bulk Endpoint Helpers / usb2.endpoints.stream Components

Endpoint interfaces for working with streams.

The endpoint interfaces in this module provide endpoint interfaces suitable for connecting streams to USB endpoints.

class luna.gateware.usb.usb2.endpoints.stream.USBMultibyteStreamInEndpoint(*args, src_loc_at=0, **kwargs)

Bases: Elaboratable

Endpoint interface that transmits a simple data stream to a host.

This interface is suitable for a single bulk or interrupt endpoint.

This variant accepts streams with payload sizes that are a multiple of one byte; data is always transmitted to the host in little-endian byte order.

This endpoint interface will automatically generate ZLPs when a stream packet would end without a short data packet. If the stream’s last signal is tied to zero, then a continuous stream of maximum-length-packets will be sent with no inserted ZLPs.

This implementation is double buffered; and can store a single packets worth of data while transmitting a second packet.

stream

Full-featured stream interface that carries the data we’ll transmit to the host.

Type:

StreamInterface, input stream

interface

Communications link to our USB device.

Type:

EndpointInterface

Parameters:
  • byte_width (int) – The number of bytes to be accepted at once.

  • endpoint_number (int) – The endpoint number (not address) this endpoint should respond to.

  • max_packet_size (int) – The maximum packet size for this endpoint. Should match the wMaxPacketSize provided in the USB endpoint descriptor.

class luna.gateware.usb.usb2.endpoints.stream.USBStreamInEndpoint(*args, src_loc_at=0, **kwargs)

Bases: Elaboratable

Endpoint interface that transmits a simple data stream to a host.

This interface is suitable for a single bulk or interrupt endpoint.

This endpoint interface will automatically generate ZLPs when a stream packet would end without a short data packet. If the stream’s last signal is tied to zero, then a continuous stream of maximum-length-packets will be sent with no inserted ZLPs.

The flush input may be asserted to to cause all pending data to be transmitted as soon as possible. When flush is asserted, packets of varying length will be sent as needed, according to the data available.

This implementation is double buffered; and can store a single packets worth of data while transmitting a second packet.

stream

Full-featured stream interface that carries the data we’ll transmit to the host.

Type:

StreamInterface, input stream

flush

Assert to cause all pending data to be transmitted as soon as possible.

Type:

Signal(), input

discard

Assert to cause all pending data to be discarded.

Type:

Signal(), input

interface

Communications link to our USB device.

Type:

EndpointInterface

Parameters:
  • endpoint_number (int) – The endpoint number (not address) this endpoint should respond to.

  • max_packet_size (int) – The maximum packet size for this endpoint. Should match the wMaxPacketSize provided in the USB endpoint descriptor.

class luna.gateware.usb.usb2.endpoints.stream.USBStreamOutEndpoint(*args, src_loc_at=0, **kwargs)

Bases: Elaboratable

Endpoint interface that receives data from the host, and produces a simple data stream.

This interface is suitable for a single bulk or interrupt endpoint.

stream

Full-featured stream interface that carries the data we’ve received from the host.

Type:

StreamInterface, output stream

interface

Communications link to our USB device.

Type:

EndpointInterface

Parameters:
  • endpoint_number (int) – The endpoint number (not address) this endpoint should respond to.

  • max_packet_size (int) – The maximum packet size for this endpoint. If this there isn’t max_packet_size space in the endpoint buffer, this endpoint will NAK (or participate in the PING protocol.)

  • buffer_size (int, optional) – The total amount of data we’ll keep in the buffer; typically two max-packet-sizes or more. Defaults to twice the maximum packet size.

Interrupt Endpoint Helpers / usb2.endpoints.status Components

Endpoint interfaces for providing status updates to the host.

These are mainly meant for use with interrupt endpoints; and allow a host to e.g. repeatedly poll a device for status.

class luna.gateware.usb.usb2.endpoints.status.USBSignalInEndpoint(*args, src_loc_at=0, **kwargs)

Bases: Elaboratable

Endpoint that transmits the value of a signal to a host whenever polled.

This is intended to be usable to implement a simple interrupt endpoint that polls for a status signal.

signal

The signal to be relayed to the host. This signal’s current value will be relayed each time the host polls our endpoint.

Type:

Signal(<variable width>), input

interface

Communications link to our USB device.

Type:

EndpointInterface

status_read_complete

Strobe that pulses high for a single usb-domain cycle each time a status read is complete.

Type:

Signal(), output

Parameters:
  • width (int) – The width of the signal we’ll relay up to the host, in bits.

  • endpoint_number (int) – The endpoint number (not address) this endpoint should respond to.

  • endianness (str, "big" or "little", optional) – The endianness with which to send the data. Defaults to little endian.

  • signal_domain (str, optional) – The name of the domain :attr:signal is clocked from. If this value is anything other than “usb”, the signal will automatically be synchronized to the USB clock domain.