Architecture¶
This document explains TinyUSB’s internal architecture, design principles, and how different components work together.
Design Principles¶
Memory Safety¶
TinyUSB is designed for resource-constrained embedded systems with strict memory requirements:
TinyUSB uses no dynamic allocation - all memory is statically allocated at compile time for predictability. All buffers have bounded, compile-time defined sizes to prevent overflow issues. The TinyUSB core avoids heap allocation, resulting in predictable memory usage where consumption is fully deterministic.
Thread Safety¶
TinyUSB achieves thread safety through a deferred interrupt model:
ISR deferral: USB interrupts are captured and deferred to task context
Single-threaded processing: All USB protocol handling occurs in task context
Queue-based design: Events are queued from ISR and processed in
tud_task()RTOS integration: Proper semaphore/mutex usage for shared resources
Portability¶
The stack is designed to work across diverse microcontroller families:
Hardware abstraction: MCU-specific code isolated in portable drivers
OS abstraction: RTOS dependencies isolated in OSAL layer
Modular design: Features can be enabled/disabled at compile time
Standard compliance: Strict adherence to USB specifications
Core Architecture¶
Layer Structure¶
TinyUSB follows a layered architecture from hardware to application:
Component Overview¶
Application Layer: Your main application code that uses TinyUSB APIs.
Class Drivers: Implement specific USB device classes (CDC, HID, MSC, etc.) and handle class-specific requests.
Device/Host Core: Implements USB protocol state machines, endpoint management, and core USB functionality.
OS Abstraction: Provides threading primitives and synchronization for different RTOS environments.
Device/Host Controller Driver: drivers that interface with MCU USB peripherals. Several MCUs may share a common driver.
Device Stack Architecture¶
This section is concerned with the Device Stack, i.e., the component of TinyUSB used in USB devices (that talk to a USB host).
Core Components¶
Device Controller Driver (DCD):
- MCU-specific USB device peripheral driver
- Handles endpoint configuration and data transfers
- Abstracts hardware differences between MCU families
- Located in src/portable/VENDOR/USBIP/
USB Device Core (USBD):
- Implements USB device state machine
- Handles standard USB requests (Chapter 9)
- Manages device configuration and enumeration
- Located in src/device/
Class Drivers:
- Implement USB class specifications
- Handle class-specific requests and data transfer
- Provide application APIs
- Located in src/class/*/
Data Flow¶
Control Transfers (Setup Requests):
USB Bus → DCD → USBD Core → Class Driver → Application
↓
Standard requests handled in core
↓
Class-specific requests → Class Driver
Data Transfers:
Application → Class Driver → USBD Core → DCD → USB Bus
USB Bus → DCD → USBD Core → Class Driver → Application
Event Processing¶
TinyUSB uses a deferred interrupt model for thread safety:
Interrupt Occurs: USB hardware generates interrupt
ISR Handler:
dcd_int_handler()captures event, minimal processingEvent Queuing: Events queued for later processing
Task Processing:
tud_task()(called by application code) processes queued eventsCallback Execution: Application callbacks executed in task context
USB IRQ → ISR → Event Queue → tud_task() → Class Callbacks → Application
Host Stack Architecture¶
This section is concerned with the Host Stack, i.e., the component of TinyUSB used in USB hosts, managing connected USB devices.
Core Components¶
Host Controller Driver (HCD):
- MCU-specific USB host peripheral driver
- Manages USB pipes and data transfers
- Handles host controller hardware
- Located in src/portable/VENDOR/FAMILY/
USB Host Core (USBH):
- Implements USB host functionality
- Manages device enumeration and configuration
- Handles pipe management and scheduling
- Located in src/host/
Hub Driver:
- Manages USB hub devices
- Handles port management and device detection
- Supports multi-level hub topologies
- Located in src/host/
Device Enumeration¶
The host stack follows USB enumeration process:
Device Detection: Hub or root hub detects device connection
Reset and Address: Reset device, assign unique address
Descriptor Retrieval: Get device, configuration, and class descriptors
Driver Matching: Find appropriate class driver for device
Configuration: Configure device and start communication
Class Operation: Normal class-specific communication
Device Connect → Reset → Get Descriptors → Load Driver → Configure → Operate
Class Architecture¶
Common Class Structure¶
All USB classes follow a similar architecture:
Device Classes:
- *_device.c: Device-side implementation
- *_device.h: Device API definitions
- Implement class-specific descriptors
- Handle class requests and data transfer
Host Classes:
- *_host.c: Host-side implementation
- *_host.h: Host API definitions
- Manage connected devices of this class
- Provide application interface
Class Driver Interface¶
See usbd.c.
Required Functions:
- init(): Initialize class driver
- reset(): Reset class state
- open(): Configure class endpoints
- control_xfer_cb(): Handle control requests
- xfer_cb(): Handle data transfer completion
Optional Functions:
- close(): Clean up class resources
- deinit(): Deinitialize class driver
- sof(): Start-of-frame processing
- xfer_isr(): Called from USB ISR context on transfer completion. Data will get queued for xfer_cb() only if this returns false.
Descriptor Management¶
Each class is responsible for: - Interface Descriptors: Define class type and endpoints - Class-Specific Descriptors: Additional class requirements - Endpoint Descriptors: Define data transfer characteristics
Memory Management¶
Static Allocation Model¶
TinyUSB uses only static memory allocation; it allocates fixed-size endpoint buffers for each configured endpoint, static buffers for class-specific data handling, a fixed buffer dedicated to control transfers, and static event queues for deferred interrupt processing.
Buffer Management¶
Endpoint Buffers:
- Allocated per endpoint at compile time
- Size defined by CFG_TUD_*_EP_BUFSIZE macros
- Used for USB data transfers
FIFO Buffers:
- Ring buffers for streaming data
- Size defined by CFG_TUD_*_RX/TX_BUFSIZE macros
- Separate read/write pointers
Threading Model¶
Task-Based Design¶
TinyUSB uses a cooperative task model; it provides main tasks - tud_task() for device and tuh_task() for host operation. These tasks must be called regularly (typically less than 1ms intervals) to ensure all USB events are processed in task context, where application callbacks also execute.
RTOS Integration¶
Bare Metal:
- Application calls tud_task() in main loop
- No threading primitives needed
- Simplest integration method
FreeRTOS: - USB task runs at high priority - Semaphores used for synchronization - Queue for inter-task communication
Other RTOS: - Similar patterns with RTOS-specific primitives - OSAL layer abstracts RTOS differences
Interrupt Handling¶
Interrupt Service Routine: - Minimal processing in ISR - Event capture and queuing only - Quick return to avoid blocking
Deferred Processing: - All complex processing in task context - Thread-safe access to data structures - Application callbacks in known context
Memory Usage Patterns¶
Flash Memory: - Core stack: 8-15KB depending on features - Each class: 1-4KB additional - Portable driver: 2-8KB depending on MCU
RAM Usage: - Core stack: 1-2KB - Endpoint buffers: User configurable - Class buffers: Depends on configuration