Gateware Endpoint Interfaces

The SOL 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 SOL 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.


A SOL 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.

Gateware for working with abstract endpoints.

class sol_usb.gateware.usb.usb2.endpoint.EndpointInterface

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 (TokenDetectorInterface, to detector) – Interface to our TokenDetector; notifies us of USB tokens.

  • rx (USBOutStreamInterface, input stream to endpoint) – Receive interface for this endpoint.

  • rx_complete (Signal(), input to endpoint) – Strobe that indicates that the concluding rx-stream was valid (CRC check passed).

  • rx_ready_for_response (Signal(), input to endpoint) – 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.

  • rx_invalid (Signal(), input to endpoint) – Strobe that indicates that the concluding rx-stream was invalid (CRC check failed).

  • rx_pid_toggle (Signal(), input to endpoint) – Value for the data PID toggle; 0 indicates we’re receiving a DATA0; 1 indicates Data1.

  • tx (USBInStreamInterface, output stream from endpoint) – Transmit interface for this endpoint.

  • tx_pid_toggle (Signal(2), output from endpoint) – 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.

  • handshakes_in (HandshakeExchangeInterface, input to endpoint) – Carries handshakes detected from the host.

  • handshakes_out (HandshakeExchangeInterface, output from endpoint) – Carries handshakes generate by this endpoint.

  • speed (Signal(2), input to endpoint) – The device’s current operating speed. Should be a USBSpeed enumeration value – 0 for high, 1 for full, 2 for low.

  • active_address (Signal(7), input to endpoint) – Contains the device’s current address.

  • address_changed (Signal(), output from endpoint.) – Strobe; pulses high when the device’s address should be changed.

  • new_address (Signal(7), output from endpoint) – When address_changed is high, this field contains the address that should be adopted.

  • active_config (Signal(8), input to endpoint) – The configuration number of the active configuration.

  • config_changed (Signal(), output from endpoint) – Strobe; pulses high when the device’s configuration should be changed.

  • new_config (Signal(8)) – When config_changed is high, this field contains the configuration that should be applied.

  • timer (InterpacketTimerInterface) – Interface to our interpacket timer.

  • data_crc (DataCRCInterface) – Control connection for our data-CRC unit.

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

Multiplexes access to the resources shared between multiple endpoint interfaces.

Interfaces are added using add_interface.


shared (EndpointInterface) – The post-multiplexer endpoint interface.

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.