eptri - SoC controller for the LUNA USB Device
General Description
eptri
(endpoint-tri) is a three-interface CSR controller that allows a CPU or Wishbone design to control the endpoints of a LUNA USB Device.
Features
CONTROL peripheral for managing device connection, reset and connection speed.
SETUP interface peripheral for reading control transactions from the host.
OUT interface peripheral for reading data transfers from the host.
IN interface peripheral for writing data transactions to the host.
Introduction
Definitions
Controller refers to the
eptri
controller as a whole, including all peripherals.Peripheral refers to
USBDeviceController
,SetupFIFOInterface
,InFIFOInterface
orOutFIFOInterface
.
Block Diagram
┌────────────────────────────────────────┐ ┌──────────────────────────────────────┐
│USBDevice │ │EPTRI │
│ │ │ │
│ │ ├───────────────────────────┬──────────┤
│ │ │USBDeviceController │Registers │◀─────────▶
├─────────────┐ ┌───────────────────┤ │ ├──────────┤
│UTMIInterface│ │EndpointMultiplexer│ │ │IRQ │──────────▶
│ │ │ │ ├───────────────────────────┼──────────┤
┌──────────┐ │ │ │ │ │SetupFIFOInterface┌───────▶│Registers │◀─────────▶
│USB PHY │ │ │ │ │ ├──────────┐ │ ├──────────┤
◀─── D+ ──▶│ │ │ │ │ │ │8B FIFO │───────┘ │IRQ │──────────▶
│ │◀────▶│ │◀────▶│ │◀────▶├──────────┴────────────────┼──────────┤ SoC
◀─── D- ──▶│ │ │ │ │ │ │InFIFOInterface ┌────────│Registers │◀─────────▶
│ │ │ │ │ │ ├──────────┐ │ ├──────────┤
└──────────┘ │ │ │ │ │512B FIFO │◀──────┘ │IRQ │──────────▶
│ │ │ │ ├──────────┴────────────────┼──────────┤
│ │ │ │ │OutFIFOInterface ┌───────▶│Registers │◀─────────▶
│ │ │ │ ├──────────┐ │ ├──────────┤
│ │ │ │ │512B FIFO │───────┘ │IRQ │──────────▶
└─────────────┴──────┴───────────────────┘ └──────────┴────────────────┴──────────┘
Usage
The exact details for integrating a LUNA eptri peripheral into a design will depend on the SoC platform you are using.
For example, using luna-soc
:
class Top(Elaboratable):
def __init__(self):
# instantiate the SoC
self.soc = LunaSoC(
cpu=VexRiscv(reset_addr=0x00000000, variant="cynthion"),
clock_frequency=int(60e6),
)
# instantiate the eptri device controller peripheral
self.usb0 = USBDeviceController()
# instantiate the three endpoint interface peripherals
self.usb0_ep_control = SetupFIFOInterface()
self.usb0_ep_in = InFIFOInterface()
self.usb0_ep_out = OutFIFOInterface()
# add the peripherals to the SoC
self.soc.add_peripheral(self.usb0)
self.soc.add_peripheral(self.usb0_ep_control, as_submodule=False)
self.soc.add_peripheral(self.usb0_ep_in, as_submodule=False)
self.soc.add_peripheral(self.usb0_ep_out, as_submodule=False)
def elaborate(self, platform):
m = Module()
m.submodules.soc = self.soc
# instantiate a LUNA USB device
usb0_bus = platform.request(platform.default_usb_connection)
usb0_device = USBDevice(bus=usb0_bus)
# add the eptri endpoint interface peripherals to the LUNA USB device
# as endpoint handlers
usb0_device.add_endpoint(self.usb0_ep_control)
usb0_device.add_endpoint(self.usb0_ep_in)
usb0_device.add_endpoint(self.usb0_ep_out)
# connect the eptri device controller to the LUNA USB device
m.d.comb += self.usb0.attach(usb0_device)
m.submodules.usb0_device = usb0_device
return m
Registers
The eptri
controller provides four sets of registers corresponding to each peripheral.
USBx
- USBDeviceController
Offset |
Range |
Access |
Name |
Description |
---|---|---|---|---|
0x0000 |
[0:0] |
read-write |
|
Set this bit to ‘1’ to allow the associated USB device to connect to a host. |
0x0004 |
[1:0] |
read-only |
|
Indicates the current speed of the USB device. 0 indicates High; 1 => Full, 2 => Low, and 3 => SuperSpeed (incl SuperSpeed+). |
0x0008 |
[0:0] |
read-write |
|
Set this bit to ‘1’ to force the device to operate at low speed. |
0x000c |
[0:0] |
read-write |
|
Set this bit to ‘1’ to force the device to operate at full speed. |
0x0010 |
[0:0] |
read-only |
|
usb0 status register field |
0x0014 |
[0:0] |
read-write |
|
usb0 pending register field |
0x0018 |
[0:0] |
read-write |
|
usb0 enable register field |
USBx_EP_CONTROL
- SetupFIFOInterface
Offset |
Range |
Access |
Name |
Description |
---|---|---|---|---|
0x0000 |
[7:0] |
read-only |
|
A FIFO that returns the bytes from the most recently captured SETUP packet. Reading a byte from this register advances the FIFO. The first eight bytes read from this conain the core SETUP packet. |
0x0004 |
[0:0] |
write-only |
|
Local reset control for the SETUP handler; writing a ‘1’ to this register clears the handler state. |
0x0008 |
[3:0] |
read-only |
|
The number of the endpoint associated with the current SETUP packet. |
0x000c |
[0:0] |
read-only |
|
1 iff data is available in the FIFO. |
0x0010 |
[0:0] |
read-only |
|
1 iff an interrupt is pending |
0x0014 |
[7:0] |
read-write |
|
Controls the current device’s USB address. Should be written after a SET_ADDRESS request is received. Automatically resets back to zero on a USB reset. |
0x0020 |
[0:0] |
read-only |
|
usb0_ep_control status register field |
0x0024 |
[0:0] |
read-write |
|
usb0_ep_control pending register field |
0x0028 |
[0:0] |
read-write |
|
usb0_ep_control enable register field |
USBx_EP_IN
- InFIFOInterface
Offset |
Range |
Access |
Name |
Description |
---|---|---|---|---|
0x0000 |
[7:0] |
write-only |
|
Write-only register. Each write enqueues a byte to be transmitted; gradually building a single packet to be transmitted. This queue should only ever contain a single packet; it is the software’s responsibility to handle breaking requests down into packets. |
0x0004 |
[3:0] |
read-write |
|
Contains the endpoint the enqueued packet is to be transmitted on. Writing this register marks the relevant packet as ready to transmit; and thus should only be written after a full packet has been written into the FIFO. If no data has been placed into the DATA FIFO, a zero-length packet is generated. Note that any IN requests that do not match the endpoint number are automatically NAK’d. |
0x0008 |
[0:0] |
write-only |
|
A write to this register clears the FIFO without transmitting. |
0x000c |
[0:0] |
read-write |
|
When this register contains ‘1’, any IN tokens targeting epno will be responded to with a STALL token, rather than DATA or a NAK. For EP0, this register will automatically be cleared when a new SETUP token is received. |
0x0010 |
[0:0] |
read-only |
|
This value is 1 if no packet is actively being transmitted. |
0x0014 |
[0:0] |
read-only |
|
This value is 1 if data is present in the transmit FIFO. |
0x0018 |
[0:0] |
read-only |
|
1 iff an interrupt is pending |
0x001c |
[0:0] |
read-write |
|
Contains the current PID toggle bit for the given endpoint. |
0x0020 |
[15:0] |
read-only |
|
Read-only register. Contains a bitmask of endpoints that have responded with a NAK since the last read of this register. |
0x0040 |
[0:0] |
read-only |
|
usb0_ep_in status register field |
0x0044 |
[0:0] |
read-write |
|
usb0_ep_in pending register field |
0x0048 |
[0:0] |
read-write |
|
usb0_ep_in enable register field |
USBx_EP_OUT
- OutFIFOInterface
Offset |
Range |
Access |
Name |
Description |
---|---|---|---|---|
0x0000 |
[7:0] |
read-only |
|
A FIFO that returns the bytes from the most recently captured OUT transaction. Reading a byte from this register advances the FIFO. |
0x0004 |
[3:0] |
read-only |
|
Register that contains the endpoint number associated with the data in the FIFO – that is, the endpoint number on which the relevant data was received. |
0x0008 |
[0:0] |
write-only |
|
Local reset for the OUT handler; clears the out FIFO. |
0x000c |
[3:0] |
read-write |
|
Selects the endpoint number to prime. This interface only allows priming a single endpoint at once– that is, only one endpoint can be ready to receive data at a time. See the enable bit for usage. |
0x0010 |
[0:0] |
read-write |
|
Controls whether any data can be received on any primed OUT endpoint. This bit is automatically cleared on receive in order to give the controller time to read data from the FIFO. It must be re-enabled once the FIFO has been emptied. |
0x0014 |
[0:0] |
write-only |
|
Controls “priming” an out endpoint. To receive data on any endpoint, the CPU must first select the endpoint with the epno register; and then write a ‘1’ into the prime and enable register. This prepares our FIFO to receive data; and the next OUT transaction will be captured into the FIFO. When a transaction is complete, the enable bit is reset; the prime is not. This effectively means that enable controls receiving on _any_ of the primed endpoints; while prime can be used to build a collection of endpoints willing to participate in receipt. Only one transaction / data packet is captured per enable write; repeated enabling is necessary to capture multiple packets. |
0x0018 |
[0:0] |
read-write |
|
Controls STALL’ing the active endpoint. Setting or clearing this bit will set or clear STALL on the provided endpoint. Endpoint STALLs persist even after epno is changed; so multiple endpoints can be stalled at once by writing their respective endpoint numbers into epno register and then setting their stall bits. |
0x001c |
[0:0] |
read-only |
|
1 iff data is available in the FIFO. |
0x0020 |
[0:0] |
read-only |
|
1 iff an interrupt is pending |
0x0024 |
[7:0] |
read-write |
|
Controls the current device’s USB address. Should be written after a SET_ADDRESS request is received. Automatically resets back to zero on a USB reset. |
0x0028 |
[0:0] |
read-write |
|
Contains the current PID toggle bit for the given endpoint. |
0x0040 |
[0:0] |
read-only |
|
usb0_ep_out status register field |
0x0044 |
[0:0] |
read-write |
|
usb0_ep_out pending register field |
0x0048 |
[0:0] |
read-write |
|
usb0_ep_out enable register field |
Interrupts
Each of the eptri
peripherals can generate the following interrupts:
Interrupt |
Peripheral |
Description |
---|---|---|
USBx |
USBDeviceController |
Interrupt that triggers when the host issued a USB bus reset. |
USBx_EP_CONTROL |
SetupFIFOInterface |
Interrupt that triggers when the host wrote a new SETUP packet to the bus. |
USBx_EP_IN |
InFIFOInterface |
Interrupt that triggers after the peripheral has written a data packet to the bus and read back a PID ACK response from the host. |
USBx_EP_OUT |
OutFIFOInterface |
Interrupt that triggers when the peripheral has read a data packet from the host. |
Programming Guide
The programming guide provides sequence diagrams that detail the steps required to perform the various operations supported by the eptri
controller.
The following pseudo-code is used through-out to indicate register operations:
Operation |
Description |
---|---|
|
Read a value from the |
|
Write |