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:
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.
- data_crc
Control connection for our data-CRC unit.
- Type:
- 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
.The post-multiplexer endpoint interface.
- Type:
- 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 existingeptri
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 theadd_standard_request_handlers
.- interface
The interface from this endpoint to the core device hardware.
- Type:
- 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
See the luna-soc documentation.
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:
- 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. Whenflush
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:
- 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:
- 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:
- 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.