Skip to content

Commit c06fa31

Browse files
committed
Merge branch 'feat/usb-explicit-fifo-config' into 'master'
feat(usb/hal): Add HAL API to configure custom FIFO layout Closes IDF-9042 See merge request espressif/esp-idf!38404
2 parents 8a4f94f + cb4d901 commit c06fa31

File tree

12 files changed

+230
-142
lines changed

12 files changed

+230
-142
lines changed

components/hal/include/hal/usb_dwc_hal.h

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -29,15 +29,6 @@ extern "C" {
2929

3030
// ----------------------- Configs -------------------------
3131

32-
/**
33-
* @brief Possible FIFO biases
34-
*/
35-
typedef enum {
36-
USB_HAL_FIFO_BIAS_DEFAULT, /**< Default (balanced) FIFO sizes */
37-
USB_HAL_FIFO_BIAS_RX, /**< Bigger RX FIFO for IN transfers */
38-
USB_HAL_FIFO_BIAS_PTX, /**< Bigger periodic TX FIFO for ISOC OUT transfers */
39-
} usb_hal_fifo_bias_t;
40-
4132
/**
4233
* @brief MPS limits based on FIFO configuration
4334
*
@@ -261,20 +252,35 @@ void usb_dwc_hal_deinit(usb_dwc_hal_context_t *hal);
261252
void usb_dwc_hal_core_soft_reset(usb_dwc_hal_context_t *hal);
262253

263254
/**
264-
* @brief Set FIFO bias
255+
* @brief Check if FIFO configuration is valid
256+
*
257+
* This function checks that the sum of FIFO sizes does not exceed available space.
258+
* It does not modify hardware state and is safe to call from HAL or upper layers.
259+
*
260+
* @param[in] hal Pointer to HAL context (must be initialized)
261+
* @param[in] config Pointer to FIFO config to validate
262+
* @return true if config is valid, false otherwise
263+
*/
264+
bool usb_dwc_hal_fifo_config_is_valid(const usb_dwc_hal_context_t *hal, const usb_dwc_hal_fifo_config_t *config);
265+
266+
267+
/**
268+
* @brief Set the FIFO sizes of the USB-DWC core
269+
*
270+
* This function programs the FIFO sizing registers (RX FIFO, Non-Periodic TX FIFO,
271+
* and Periodic TX FIFO) based on the provided configuration. It must be called
272+
* during USB initialization, before any channels are allocated or transfers started.
265273
*
266-
* This function will set the sizes of each of the FIFOs (RX FIFO, Non-periodic TX FIFO, Periodic TX FIFO) and must be
267-
* called at least once before allocating the channel. Based on the type of endpoints (and the endpoints' MPS), there
268-
* may be situations where this function may need to be called again to resize the FIFOs. If resizing FIFOs dynamically,
269-
* it is the user's responsibility to ensure there are no active channels when this function is called.
274+
* The sum of all FIFO sizes must not exceed the hardware-defined limit
275+
* (see HWCFG3.DfifoDepth and EPINFO_CTL).
270276
*
271-
* @note After a port reset, the FIFO size registers will reset to their default values, so this function must be called
272-
* again post reset.
277+
* @note This function must be called exactly once during initialization and after
278+
* each USB port reset. It is typically used internally by the USB Host stack.
273279
*
274-
* @param[in] hal Context of the HAL layer
275-
* @param[in] fifo_bias FIFO bias configuration
280+
* @param[inout] hal Pointer to the HAL context
281+
* @param[in] config Pointer to the FIFO configuration to apply (must be valid)
276282
*/
277-
void usb_dwc_hal_set_fifo_bias(usb_dwc_hal_context_t *hal, const usb_hal_fifo_bias_t fifo_bias);
283+
void usb_dwc_hal_set_fifo_config(usb_dwc_hal_context_t *hal, const usb_dwc_hal_fifo_config_t *config);
278284

279285
/**
280286
* @brief Get MPS limits

components/hal/usb_dwc_hal.c

Lines changed: 36 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -185,64 +185,53 @@ void usb_dwc_hal_core_soft_reset(usb_dwc_hal_context_t *hal)
185185
}
186186
}
187187

188-
void usb_dwc_hal_set_fifo_bias(usb_dwc_hal_context_t *hal, const usb_hal_fifo_bias_t fifo_bias)
188+
bool usb_dwc_hal_fifo_config_is_valid(const usb_dwc_hal_context_t *hal, const usb_dwc_hal_fifo_config_t *config)
189189
{
190-
HAL_ASSERT(hal->channels.hdls);
191-
const uint16_t fifo_size_lines = hal->constant_config.fifo_size;
192-
/*
193-
* Recommended FIFO sizes (see 2.1.2.4 for programming guide)
194-
*
195-
* RXFIFO: ((LPS/4) * 2) + 2
196-
* NPTXFIFO: (LPS/4) * 2
197-
* PTXFIFO: (LPS/4) * 2
198-
*
199-
* Recommended sizes fit 2 packets of each type. For S2 and S3 we can't fit even one MPS ISOC packet (1023 FS and 1024 HS).
200-
* So the calculations below are compromises between the available FIFO size and optimal performance.
201-
*/
202-
203-
// Information for maintainers: this calculation is here for backward compatibility
204-
// It should be removed when we allow HAL users to configure the FIFO sizes IDF-9042
205-
const int otg_dfifo_depth = hal->constant_config.hsphy_type ? 1024 : 256;
206-
207-
usb_dwc_hal_fifo_config_t fifo_config;
208-
switch (fifo_bias) {
209-
// Define minimum viable (fits at least 1 MPS) FIFO sizes for non-biased FIFO types
210-
// Allocate the remaining size to the biased FIFO type
211-
case USB_HAL_FIFO_BIAS_DEFAULT:
212-
fifo_config.nptx_fifo_lines = otg_dfifo_depth / 4;
213-
fifo_config.ptx_fifo_lines = otg_dfifo_depth / 8;
214-
fifo_config.rx_fifo_lines = fifo_size_lines - fifo_config.ptx_fifo_lines - fifo_config.nptx_fifo_lines;
215-
break;
216-
case USB_HAL_FIFO_BIAS_RX:
217-
fifo_config.nptx_fifo_lines = otg_dfifo_depth / 16;
218-
fifo_config.ptx_fifo_lines = otg_dfifo_depth / 8;
219-
fifo_config.rx_fifo_lines = fifo_size_lines - fifo_config.ptx_fifo_lines - fifo_config.nptx_fifo_lines;
220-
break;
221-
case USB_HAL_FIFO_BIAS_PTX:
222-
fifo_config.rx_fifo_lines = otg_dfifo_depth / 8 + 2; // 2 extra lines are allocated for status information. See USB-OTG Programming Guide, chapter 2.1.2.1
223-
fifo_config.nptx_fifo_lines = otg_dfifo_depth / 16;
224-
fifo_config.ptx_fifo_lines = fifo_size_lines - fifo_config.nptx_fifo_lines - fifo_config.rx_fifo_lines;
225-
break;
226-
default:
227-
abort();
190+
if (!hal || !config) {
191+
return false;
228192
}
193+
uint32_t used_lines = config->rx_fifo_lines + config->nptx_fifo_lines + config->ptx_fifo_lines;
194+
return (used_lines <= hal->constant_config.fifo_size);
195+
}
196+
197+
198+
void usb_dwc_hal_set_fifo_config(usb_dwc_hal_context_t *hal, const usb_dwc_hal_fifo_config_t *config)
199+
{
200+
// Check internal HAL state
201+
HAL_ASSERT(hal != NULL);
202+
HAL_ASSERT(hal->channels.hdls != NULL);
229203

230-
HAL_ASSERT((fifo_config.rx_fifo_lines + fifo_config.nptx_fifo_lines + fifo_config.ptx_fifo_lines) <= fifo_size_lines);
231-
//Check that none of the channels are active
204+
// Validate provided config
205+
HAL_ASSERT(config != NULL);
206+
207+
// Check if configuration exceeds available FIFO memory
208+
HAL_ASSERT(usb_dwc_hal_fifo_config_is_valid(hal, config));
209+
210+
// Ensure no active channels (must only be called before USB install completes)
232211
for (int i = 0; i < hal->constant_config.chan_num_total; i++) {
233212
if (hal->channels.hdls[i] != NULL) {
234213
HAL_ASSERT(!hal->channels.hdls[i]->flags.active);
235214
}
236215
}
237-
//Set the new FIFO lengths
238-
usb_dwc_ll_grxfsiz_set_fifo_size(hal->dev, fifo_config.rx_fifo_lines);
239-
usb_dwc_ll_gnptxfsiz_set_fifo_size(hal->dev, fifo_config.rx_fifo_lines, fifo_config.nptx_fifo_lines);
240-
usb_dwc_ll_hptxfsiz_set_ptx_fifo_size(hal->dev, fifo_config.rx_fifo_lines + fifo_config.nptx_fifo_lines, fifo_config.ptx_fifo_lines);
241-
//Flush the FIFOs
216+
217+
// Program FIFO size registers
218+
usb_dwc_ll_grxfsiz_set_fifo_size(hal->dev, config->rx_fifo_lines);// Set RX FIFO size (GRXFSIZ)
219+
// Set Non-Periodic TX FIFO (GNPTXFSIZ)
220+
// Offset = RX FIFO lines
221+
usb_dwc_ll_gnptxfsiz_set_fifo_size(hal->dev, config->rx_fifo_lines, config->nptx_fifo_lines);
222+
// Set Periodic TX FIFO (HPTXFSIZ)
223+
// Offset = RX + NPTX
224+
usb_dwc_ll_hptxfsiz_set_ptx_fifo_size(hal->dev,
225+
config->rx_fifo_lines + config->nptx_fifo_lines,
226+
config->ptx_fifo_lines);
227+
228+
// Flush all FIFOs
242229
usb_dwc_ll_grstctl_flush_nptx_fifo(hal->dev);
243230
usb_dwc_ll_grstctl_flush_ptx_fifo(hal->dev);
244231
usb_dwc_ll_grstctl_flush_rx_fifo(hal->dev);
245-
hal->fifo_config = fifo_config; // Implicit struct copy
232+
233+
// Save configuration to HAL context
234+
hal->fifo_config = *config;
246235
hal->flags.fifo_sizes_set = 1;
247236
}
248237

components/usb/hcd_dwc.c

Lines changed: 115 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -250,8 +250,8 @@ struct port_obj {
250250
uint32_t val;
251251
} flags;
252252
bool initialized;
253-
// FIFO biasing related
254-
usb_hal_fifo_bias_t fifo_bias; // Bias is saved so it can be reconfigured upon reset
253+
// FIFO related
254+
usb_dwc_hal_fifo_config_t fifo_config; // FIFO config to be applied at HAL level
255255
// Port callback and context
256256
hcd_port_callback_t callback;
257257
void *callback_arg;
@@ -554,6 +554,23 @@ static bool _port_check_all_pipes_halted(port_t *port);
554554
*/
555555
static bool _port_debounce(port_t *port);
556556

557+
/**
558+
* @brief Convert user-provided FIFO configuration to HAL format
559+
*
560+
* This function validates and converts a user-defined FIFO configuration
561+
* (provided via `hcd_config_t.fifo_config`) into the format expected by the HAL.
562+
* It ensures that both RX FIFO and Non-Periodic TX FIFO sizes are non-zero.
563+
*
564+
* @param[in] src Pointer to user-defined FIFO settings (HCD format)
565+
* @param[out] dst Pointer to HAL-compatible FIFO configuration structure
566+
*
567+
* @return
568+
* - ESP_OK: Conversion successful and values copied
569+
* - ESP_ERR_INVALID_SIZE: Either RX FIFO or Non-Periodic TX FIFO is zero
570+
*/
571+
572+
static esp_err_t convert_fifo_config_to_hal_config(const hcd_fifo_settings_t *src, usb_dwc_hal_fifo_config_t *dst);
573+
557574
/**
558575
* @brief Power ON the port
559576
*
@@ -744,16 +761,6 @@ static usb_speed_t get_usb_port_speed(usb_dwc_speed_t priv)
744761
}
745762
}
746763

747-
static usb_hal_fifo_bias_t get_hal_fifo_bias(hcd_port_fifo_bias_t public)
748-
{
749-
switch (public) {
750-
case HCD_PORT_FIFO_BIAS_BALANCED: return USB_HAL_FIFO_BIAS_DEFAULT;
751-
case HCD_PORT_FIFO_BIAS_RX: return USB_HAL_FIFO_BIAS_RX;
752-
case HCD_PORT_FIFO_BIAS_PTX: return USB_HAL_FIFO_BIAS_PTX;
753-
default: abort();
754-
}
755-
}
756-
757764
// ----------------- Interrupt Handlers --------------------
758765

759766
/**
@@ -954,6 +961,46 @@ static void intr_hdlr_main(void *arg)
954961
}
955962
}
956963

964+
// ----------------------- FIFO Config -------------------------
965+
966+
/**
967+
* @brief Calculate default FIFO configuration based on bias preference
968+
*
969+
* This function calculates the FIFO configuration (RX, non-periodic TX, and periodic TX)
970+
* according to the selected bias mode from Kconfig:
971+
*
972+
* - CONFIG_USB_HOST_HW_BUFFER_BIAS_IN: Prioritize RX FIFO space, useful for IN-heavy traffic.
973+
* - CONFIG_USB_HOST_HW_BUFFER_BIAS_PERIODIC_OUT: Prioritize periodic TX FIFO, suitable for periodic OUT transfers.
974+
* - USB_HOST_HW_BUFFER_BIAS_BALANCED: Balanced configuration between RX and both TX FIFOs.
975+
*
976+
* @param[inout] port Pointer to the port object whose fifo_config will be filled
977+
* @param[in] hal Pointer to an initialized HAL context providing hardware FIFO limits
978+
*/
979+
static void _calculate_fifo_from_bias(port_t *port, const usb_dwc_hal_context_t *hal)
980+
{
981+
const int otg_dfifo_depth = hal->constant_config.hsphy_type ? 1024 : 256;
982+
const uint16_t fifo_size_lines = hal->constant_config.fifo_size;
983+
984+
#if CONFIG_USB_HOST_HW_BUFFER_BIAS_IN
985+
// Prioritize RX FIFO (best for IN-heavy workloads)
986+
port->fifo_config.nptx_fifo_lines = otg_dfifo_depth / 16;
987+
port->fifo_config.ptx_fifo_lines = otg_dfifo_depth / 8;
988+
port->fifo_config.rx_fifo_lines = fifo_size_lines - port->fifo_config.ptx_fifo_lines - port->fifo_config.nptx_fifo_lines;
989+
990+
#elif CONFIG_USB_HOST_HW_BUFFER_BIAS_PERIODIC_OUT
991+
// Prioritize periodic TX FIFO (useful for high throughput periodic endpoints)
992+
port->fifo_config.rx_fifo_lines = otg_dfifo_depth / 8 + 2; // 2 extra lines are allocated for status information. See USB-OTG Programming Guide, chapter 2.1.2.1
993+
port->fifo_config.nptx_fifo_lines = otg_dfifo_depth / 16;
994+
port->fifo_config.ptx_fifo_lines = fifo_size_lines - port->fifo_config.nptx_fifo_lines - port->fifo_config.rx_fifo_lines;
995+
996+
#else // USB_HOST_HW_BUFFER_BIAS_BALANCED
997+
// Balanced configuration (default)
998+
port->fifo_config.nptx_fifo_lines = otg_dfifo_depth / 4;
999+
port->fifo_config.ptx_fifo_lines = otg_dfifo_depth / 8;
1000+
port->fifo_config.rx_fifo_lines = fifo_size_lines - port->fifo_config.ptx_fifo_lines - port->fifo_config.nptx_fifo_lines;
1001+
#endif
1002+
}
1003+
9571004
// --------------------------------------------- Host Controller Driver ------------------------------------------------
9581005

9591006
static port_t *port_obj_alloc(void)
@@ -1018,6 +1065,15 @@ esp_err_t hcd_install(const hcd_config_t *config)
10181065
ESP_LOGE(HCD_DWC_TAG, "Interrupt alloc error: %s", esp_err_to_name(err_ret));
10191066
goto intr_alloc_err;
10201067
}
1068+
// Apply custom FIFO config if provided, otherwise mark as default (all zeros)
1069+
memset(&p_hcd_obj_dmy->port_obj->fifo_config, 0, sizeof(p_hcd_obj_dmy->port_obj->fifo_config));
1070+
if (config->fifo_config != NULL) {
1071+
// Convert and validate user-provided config
1072+
err_ret = convert_fifo_config_to_hal_config(config->fifo_config, &p_hcd_obj_dmy->port_obj->fifo_config);
1073+
if (err_ret != ESP_OK) {
1074+
goto assign_err;
1075+
}
1076+
}
10211077
HCD_ENTER_CRITICAL();
10221078
if (s_hcd_obj != NULL) {
10231079
HCD_EXIT_CRITICAL();
@@ -1100,6 +1156,34 @@ static bool _port_debounce(port_t *port)
11001156
return is_connected;
11011157
}
11021158

1159+
static esp_err_t convert_fifo_config_to_hal_config(const hcd_fifo_settings_t *src, usb_dwc_hal_fifo_config_t *dst)
1160+
{
1161+
// Check at least RX and NPTX are non-zero
1162+
if (src->rx_fifo_lines == 0 || src->nptx_fifo_lines == 0) {
1163+
ESP_LOGE(HCD_DWC_TAG, "RX and Non-Periodic TX FIFO must be > 0");
1164+
return ESP_ERR_INVALID_SIZE;
1165+
}
1166+
// Assign valid values
1167+
dst->rx_fifo_lines = src->rx_fifo_lines;
1168+
dst->nptx_fifo_lines = src->nptx_fifo_lines;
1169+
dst->ptx_fifo_lines = src->ptx_fifo_lines; // OK even if zero
1170+
1171+
return ESP_OK;
1172+
}
1173+
1174+
/**
1175+
* @brief Check if the FIFO config is marked to use bias-based default values.
1176+
*
1177+
* If all FIFO line sizes are zero, the configuration is considered uninitialized,
1178+
* and default values will be calculated based on bias settings.
1179+
*/
1180+
static inline bool _is_fifo_config_by_bias(const usb_dwc_hal_fifo_config_t *cfg)
1181+
{
1182+
return (cfg->rx_fifo_lines == 0 &&
1183+
cfg->nptx_fifo_lines == 0 &&
1184+
cfg->ptx_fifo_lines == 0);
1185+
}
1186+
11031187
// ---------------------- Commands -------------------------
11041188

11051189
static esp_err_t _port_cmd_power_on(port_t *port)
@@ -1177,8 +1261,16 @@ static esp_err_t _port_cmd_reset(port_t *port)
11771261
goto bailout;
11781262
}
11791263

1180-
// Reinitialize port registers.
1181-
usb_dwc_hal_set_fifo_bias(port->hal, port->fifo_bias); // Set FIFO biases
1264+
// Reinitialize port registers
1265+
if (!usb_dwc_hal_fifo_config_is_valid(port->hal, &port->fifo_config)) {
1266+
HCD_EXIT_CRITICAL();
1267+
ret = ESP_ERR_INVALID_SIZE;
1268+
ESP_LOGE(HCD_DWC_TAG, "Invalid FIFO config");
1269+
HCD_ENTER_CRITICAL();
1270+
goto bailout;
1271+
}
1272+
usb_dwc_hal_set_fifo_config(port->hal, &port->fifo_config);// Apply FIFO settings
1273+
11821274
usb_dwc_hal_port_set_frame_list(port->hal, port->frame_list, FRAME_LIST_LEN); // Set periodic frame list
11831275
usb_dwc_hal_port_periodic_enable(port->hal); // Enable periodic scheduling
11841276

@@ -1287,7 +1379,6 @@ esp_err_t hcd_port_init(int port_number, const hcd_port_config_t *port_config, h
12871379
TAILQ_INIT(&port_obj->pipes_active_tailq);
12881380
port_obj->state = HCD_PORT_STATE_NOT_POWERED;
12891381
port_obj->last_event = HCD_PORT_EVENT_NONE;
1290-
port_obj->fifo_bias = get_hal_fifo_bias(port_config->fifo_bias);
12911382
port_obj->callback = port_config->callback;
12921383
port_obj->callback_arg = port_config->callback_arg;
12931384
port_obj->context = port_config->context;
@@ -1297,10 +1388,18 @@ esp_err_t hcd_port_init(int port_number, const hcd_port_config_t *port_config, h
12971388
port_obj->initialized = true;
12981389
// Clear the frame list. We set the frame list register and enable periodic scheduling after a successful reset
12991390
memset(port_obj->frame_list, 0, FRAME_LIST_LEN * sizeof(uint32_t));
1391+
// If FIFO config is zeroed -> calculate from bias
1392+
if (_is_fifo_config_by_bias(&port_obj->fifo_config)) {
1393+
// Calculate default FIFO sizes based on Kconfig bias settings
1394+
_calculate_fifo_from_bias(port_obj, port_obj->hal);
1395+
}
13001396
esp_intr_enable(s_hcd_obj->isr_hdl);
13011397
*port_hdl = (hcd_port_handle_t)port_obj;
13021398
HCD_EXIT_CRITICAL();
1303-
1399+
ESP_LOGD(HCD_DWC_TAG, "FIFO config lines: RX=%u, PTX=%u, NPTX=%u",
1400+
port_obj->fifo_config.rx_fifo_lines,
1401+
port_obj->fifo_config.ptx_fifo_lines,
1402+
port_obj->fifo_config.nptx_fifo_lines);
13041403
vTaskDelay(pdMS_TO_TICKS(INIT_DELAY_MS)); // Need a short delay before host mode takes effect
13051404
return ESP_OK;
13061405
}
@@ -1455,28 +1554,6 @@ void *hcd_port_get_context(hcd_port_handle_t port_hdl)
14551554
return ret;
14561555
}
14571556

1458-
esp_err_t hcd_port_set_fifo_bias(hcd_port_handle_t port_hdl, hcd_port_fifo_bias_t bias)
1459-
{
1460-
esp_err_t ret;
1461-
usb_hal_fifo_bias_t hal_bias = get_hal_fifo_bias(bias);
1462-
1463-
// Configure the new FIFO sizes and store the pointers
1464-
port_t *port = (port_t *)port_hdl;
1465-
xSemaphoreTake(port->port_mux, portMAX_DELAY);
1466-
HCD_ENTER_CRITICAL();
1467-
// Check that port is in the correct state to update FIFO sizes
1468-
if (port->initialized && !port->flags.event_pending && port->num_pipes_idle == 0 && port->num_pipes_queued == 0) {
1469-
usb_dwc_hal_set_fifo_bias(port->hal, hal_bias);
1470-
port->fifo_bias = hal_bias;
1471-
ret = ESP_OK;
1472-
} else {
1473-
ret = ESP_ERR_INVALID_STATE;
1474-
}
1475-
HCD_EXIT_CRITICAL();
1476-
xSemaphoreGive(port->port_mux);
1477-
return ret;
1478-
}
1479-
14801557
// --------------------------------------------------- HCD Pipes -------------------------------------------------------
14811558

14821559
// ----------------------- Private -------------------------

0 commit comments

Comments
 (0)