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

CONTROL Register

Offset

Range

Access

Field

0x0000

[0:0]

read-write

connect

0x0000

[8:8]

read-write

low_speed_only

0x0000

[9:9]

read-write

full_speed_only

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

speed

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

mask

EV_PENDING Register

Offset

Range

Access

Field

0x0009

[0:0]

read-write

mask

USBx_EP_CONTROL - SetupFIFOInterface

CONTROL Register

Offset

Range

Access

Field

0x0000

[7:0]

write-only

address

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

address

0x0002

[11:8]

read-only

epno

0x0002

[12:12]

read-only

have

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

fifo

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

byte

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

mask

EV_PENDING Register

Offset

Range

Access

Field

0x0011

[0:0]

read-write

mask

USBx_EP_IN - InFIFOInterface

ENDPOINT Register

Offset

Range

Access

Field

0x0000

[3:0]

write-only

number

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

stalled

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

toggle

Pid register

       toggle: Sets the current PID toggle bit for the given endpoint.

STATUS Register

Offset

Range

Access

Field

0x0004

[15:0]

read-only

nak

0x0004

[19:16]

read-only

epno

0x0004

[24:24]

read-only

idle

0x0004

[25:25]

read-only

have

0x0004

[26:26]

read-only

pid

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

fifo

Reset register

       fifo: A write to this field Clears the FIFO without transmitting.

DATA Register

Offset

Range

Access

Field

0x0009

[7:0]

write-only

byte

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

mask

EV_PENDING Register

Offset

Range

Access

Field

0x0011

[0:0]

read-write

mask

USBx_EP_OUT - OutFIFOInterface

CONTROL Register

Offset

Range

Access

Field

0x0000

[7:0]

read-write

address

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

number

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

enabled

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

primed

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

stalled

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

toggle

Pid register

       toggle: Sets the current PID toggle bit for the given endpoint.

STATUS Register

Offset

Range

Access

Field

0x0006

[3:0]

read-only

epno

0x0006

[8:8]

read-only

have

0x0006

[9:9]

read-only

pid

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

fifo

Reset register

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

DATA Register

Offset

Range

Access

Field

0x0009

[7:0]

read-only

byte

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

mask

EV_PENDING Register

Offset

Range

Access

Field

0x0021

[0:0]

read-write

mask

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