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
eptricontroller as a whole, including all peripherals.Peripheral refers to
USBDeviceController,SetupFIFOInterface,InFIFOInterfaceorOutFIFOInterface.
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
CONTROL Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0000 |
[0:0] |
read-write |
|
0x0000 |
[8:8] |
read-write |
|
0x0000 |
[9:9] |
read-write |
|
Control register
connect: Set this bit to '1' to allow the associated USB device to connect to a host.
low_speed_only: Set this bit to '1' to force the device to operate at low speed.
full_speed_only: Set this bit to '1' to force the device to operate at full speed.
STATUS Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0002 |
[1:0] |
read-only |
|
Status register
speed: Indicates the current speed of the USB device. 0 indicates High; 1 => Full,
2 => Low, and 3 => SuperSpeed (incl SuperSpeed+).
EV_ENABLE Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0008 |
[0:0] |
read-write |
|
EV_PENDING Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0009 |
[0:0] |
read-write |
|
USBx_EP_CONTROL - SetupFIFOInterface
CONTROL Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0000 |
[7:0] |
write-only |
|
Control register
address: 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.
STATUS Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0002 |
[7:0] |
read-only |
|
0x0002 |
[11:8] |
read-only |
|
0x0002 |
[12:12] |
read-only |
|
Status register
address: Holds the current device's active USB address.
epno: The endpoint number associated with the most recently captured SETUP packet.
have: `1` iff data is available in the FIFO.
RESET Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0004 |
[0:0] |
write-only |
|
Reset register
fifo: Local reset control for the SETUP handler; writing a '1' to this register clears
the handler state.
DATA Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0005 |
[7:0] |
read-only |
|
Data register
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 contain the core SETUP packet.
EV_ENABLE Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0010 |
[0:0] |
read-write |
|
EV_PENDING Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0011 |
[0:0] |
read-write |
|
USBx_EP_IN - InFIFOInterface
ENDPOINT Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0000 |
[3:0] |
write-only |
|
Endpoint register
number: Contains the endpoint the enqueued packet is to be transmitted on. Writing to this field
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.
STALL Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0001 |
[0:0] |
write-only |
|
Stall register
stalled: When this field contains '1', any IN tokens targeting `epno` will be responded to with a
STALL token, rather than DATA or a NAK.
For EP0, this field will automatically be cleared when a new SETUP token is received.
PID Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0002 |
[0:0] |
write-only |
|
Pid register
toggle: Sets the current PID toggle bit for the given endpoint.
STATUS Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0004 |
[15:0] |
read-only |
|
0x0004 |
[19:16] |
read-only |
|
0x0004 |
[24:24] |
read-only |
|
0x0004 |
[25:25] |
read-only |
|
0x0004 |
[26:26] |
read-only |
|
Status register
nak: Contains a bitmask of endpoints that have responded with a NAK since the
last read of this register.
epno: Contains the endpoint being transmitted on.
idle: This value is `1` if no packet is actively being transmitted.
have: This value is `1` if data is present in the transmit FIFO.
pid: Contains the current PID toggle bit for the given endpoint.
RESET Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0008 |
[0:0] |
write-only |
|
Reset register
fifo: A write to this field Clears the FIFO without transmitting.
DATA Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0009 |
[7:0] |
write-only |
|
Data 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.
EV_ENABLE Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0010 |
[0:0] |
read-write |
|
EV_PENDING Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0011 |
[0:0] |
read-write |
|
USBx_EP_OUT - OutFIFOInterface
CONTROL Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0000 |
[7:0] |
read-write |
|
Control register
address: 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.
ENDPOINT Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0001 |
[3:0] |
read-write |
|
Endpoint register
number: Selects the endpoint number to prime. This interface allows priming multiple endpoints
at once. That is, multiple endpoints can be ready to receive data at a time. See the `prime`
and `enable` bits for usage.
ENABLE Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0002 |
[0:0] |
write-only |
|
Enable register
enabled: 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.
PRIME Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0003 |
[0:0] |
write-only |
|
Prime register
primed: 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.
Note that this does not apply to the control endpoint. Once the control endpoint has
received a packet it will be un-primed and need to be re-primed before it can receive
again. This is to ensure that we can establish an order on the receipt of the setup
packet and any associated data.
Only one transaction / data packet is captured per `enable` write; repeated enabling is
necessary to capture multiple packets.
STALL Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0004 |
[0:0] |
write-only |
|
Stall register
stalled: 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.
PID Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0005 |
[0:0] |
write-only |
|
Pid register
toggle: Sets the current PID toggle bit for the given endpoint.
STATUS Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0006 |
[3:0] |
read-only |
|
0x0006 |
[8:8] |
read-only |
|
0x0006 |
[9:9] |
read-only |
|
Status register
epno: Contains the endpoint number associated with the data in the FIFO -- that is,
the endpoint number on which the relevant data was received.
have: `1` iff data is available in the FIFO.
pid: Contains the current PID toggle bit for the given endpoint.
RESET Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0008 |
[0:0] |
write-only |
|
Reset register
fifo: Local reset for the OUT handler; clears the out FIFO.
DATA Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0009 |
[7:0] |
read-only |
|
Data register
Read-only register. A FIFO that returns the bytes from the most recently captured OUT transaction.
Reading a byte from this register advances the FIFO.
byte: Contains the most recently received byte.
EV_ENABLE Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0020 |
[0:0] |
read-write |
|
EV_PENDING Register
Offset |
Range |
Access |
Field |
|---|---|---|---|
0x0021 |
[0:0] |
read-write |
|
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 |