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 or OutFIFOInterface.

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

USB0 Register Map

Offset

Range

Access

Name

Description

0x0000

[0:0]

read-write

connect

Set this bit to ‘1’ to allow the associated USB device to connect to a host.

0x0004

[1:0]

read-only

speed

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

low_speed_only

Set this bit to ‘1’ to force the device to operate at low speed.

0x000c

[0:0]

read-write

full_speed_only

Set this bit to ‘1’ to force the device to operate at full speed.

0x0010

[0:0]

read-only

status

usb0 status register field

0x0014

[0:0]

read-write

pending

usb0 pending register field

0x0018

[0:0]

read-write

enable

usb0 enable register field

USBx_EP_CONTROL - SetupFIFOInterface

USB0_EP_CONTROL Register Map

Offset

Range

Access

Name

Description

0x0000

[7:0]

read-only

data

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

reset

Local reset control for the SETUP handler; writing a ‘1’ to this register clears the handler state.

0x0008

[3:0]

read-only

epno

The number of the endpoint associated with the current SETUP packet.

0x000c

[0:0]

read-only

have

1 iff data is available in the FIFO.

0x0010

[0:0]

read-only

pend

1 iff an interrupt is pending

0x0014

[7:0]

read-write

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.

0x0020

[0:0]

read-only

status

usb0_ep_control status register field

0x0024

[0:0]

read-write

pending

usb0_ep_control pending register field

0x0028

[0:0]

read-write

enable

usb0_ep_control enable register field

USBx_EP_IN - InFIFOInterface

USB0_EP_IN Register Map

Offset

Range

Access

Name

Description

0x0000

[7:0]

write-only

data

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

epno

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

reset

A write to this register clears the FIFO without transmitting.

0x000c

[0:0]

read-write

stall

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

idle

This value is 1 if no packet is actively being transmitted.

0x0014

[0:0]

read-only

have

This value is 1 if data is present in the transmit FIFO.

0x0018

[0:0]

read-only

pend

1 iff an interrupt is pending

0x001c

[0:0]

read-write

pid

Contains the current PID toggle bit for the given endpoint.

0x0020

[15:0]

read-only

nak

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

status

usb0_ep_in status register field

0x0044

[0:0]

read-write

pending

usb0_ep_in pending register field

0x0048

[0:0]

read-write

enable

usb0_ep_in enable register field

USBx_EP_OUT - OutFIFOInterface

USB0_EP_OUT Register Map

Offset

Range

Access

Name

Description

0x0000

[7:0]

read-only

data

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

data_ep

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

reset

Local reset for the OUT handler; clears the out FIFO.

0x000c

[3:0]

read-write

epno

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

enable

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

prime

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

stall

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

have

1 iff data is available in the FIFO.

0x0020

[0:0]

read-only

pend

1 iff an interrupt is pending

0x0024

[7:0]

read-write

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.

0x0028

[0:0]

read-write

pid

Contains the current PID toggle bit for the given endpoint.

0x0040

[0:0]

read-only

status

usb0_ep_out status register field

0x0044

[0:0]

read-write

pending

usb0_ep_out pending register field

0x0048

[0:0]

read-write

enable

usb0_ep_out enable register field

Interrupts

Each of the eptri peripherals can generate the following interrupts:

USBDeviceController Registers

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:

Register Operations

Operation

Description

r(PERIPHERAL.register)

Read a value from the register belonging to PERIPHERAL.

w(PERIPHERAL.register, bits)

Write bits to the register belonging to PERIPHERAL.

Device Connection

Sequence diagram for USB device connection

Bus Reset

Sequence diagram for USB bus reset

Control OUT Transfers

Sequence diagram for Control OUT transfers

Control IN Transfers

Sequence diagram for Control IN transfers

Bulk OUT Transfers

Sequence diagram for Bulk OUT transfers

Bulk IN Transfers

Sequence diagram for Bulk IN transfers