mirror of
https://github.com/espressif/esp-idf.git
synced 2025-07-29 18:27:20 +02:00
feat(usb_host): Move DMA capable memory to external ram on P4
- DWC-OTG internal DMA can access psram on esp32p4 - Move DMA memory buffs to psram, to save internal ram - HCD tests and MSC example runs in CI with psram enabled
This commit is contained in:
@ -12,6 +12,11 @@ set(priv_includes)
|
||||
# Thus, always add the (private) requirements, regardless of Kconfig
|
||||
set(priv_requires esp_driver_gpio esp_mm) # usb_phy driver relies on gpio driver API
|
||||
|
||||
# Explicitly add psram component for esp32p4, as the USB-DWC internal DMA can access PSRAM on esp32p4
|
||||
if(${target} STREQUAL "esp32p4")
|
||||
list(APPEND priv_requires esp_psram)
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_USB_OTG_SUPPORTED)
|
||||
list(APPEND srcs "hcd_dwc.c"
|
||||
"enum.c"
|
||||
|
@ -175,6 +175,16 @@ menu "USB-OTG"
|
||||
If enabled, the enumeration filter callback can be set via 'usb_host_config_t' when calling
|
||||
'usb_host_install()'.
|
||||
|
||||
config USB_HOST_DWC_DMA_CAP_MEMORY_IN_PSRAM
|
||||
depends on IDF_TARGET_ESP32P4 && SPIRAM
|
||||
bool "Allocate USB_DWC DMA capable memory in PSRAM"
|
||||
default n
|
||||
help
|
||||
In the ESP32P4, the USB-DWC internal DMA can access external RAM. Enabling this configuration can save
|
||||
internal RAM by allocating the memory buffers used by the USB-DWC peripheral's DMA to external RAM.
|
||||
However, it introduces minor performance degradation due to the overhead of accessing external RAM.
|
||||
# Todo: IDF-11368 (aligned memory alloc size)
|
||||
|
||||
# Hidden or compatibility options
|
||||
config USB_OTG_SUPPORTED
|
||||
# Invisible config kept for compatibility
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
@ -31,8 +32,6 @@
|
||||
|
||||
// --------------------- Constants -------------------------
|
||||
|
||||
#define XFER_DESC_LIST_CAPS (MALLOC_CAP_DMA | MALLOC_CAP_CACHE_ALIGNED | MALLOC_CAP_INTERNAL)
|
||||
|
||||
#define INIT_DELAY_MS 30 // A delay of at least 25ms to enter Host mode. Make it 30ms to be safe
|
||||
#define DEBOUNCE_DELAY_MS CONFIG_USB_HOST_DEBOUNCE_DELAY_MS
|
||||
#define RESET_HOLD_MS CONFIG_USB_HOST_RESET_HOLD_MS
|
||||
@ -47,6 +46,12 @@
|
||||
|
||||
// ----------------------- Configs -------------------------
|
||||
|
||||
#ifdef CONFIG_USB_HOST_DWC_DMA_CAP_MEMORY_IN_PSRAM // In esp32p4, the USB-DWC internal DMA can access external RAM
|
||||
#define XFER_DESC_LIST_CAPS (MALLOC_CAP_DMA | MALLOC_CAP_CACHE_ALIGNED | MALLOC_CAP_SPIRAM)
|
||||
#else
|
||||
#define XFER_DESC_LIST_CAPS (MALLOC_CAP_DMA | MALLOC_CAP_CACHE_ALIGNED | MALLOC_CAP_INTERNAL)
|
||||
#endif
|
||||
|
||||
#define FRAME_LIST_LEN USB_HAL_FRAME_LIST_LEN_32
|
||||
#define NUM_BUFFERS 2
|
||||
|
||||
@ -1512,7 +1517,7 @@ static dma_buffer_block_t *buffer_block_alloc(usb_transfer_type_t type)
|
||||
break;
|
||||
}
|
||||
|
||||
// DMA buffer lock: Software structure for managing the transfer buffer
|
||||
// DMA buffer block: Software structure for managing the transfer buffer
|
||||
dma_buffer_block_t *buffer = calloc(1, sizeof(dma_buffer_block_t));
|
||||
if (buffer == NULL) {
|
||||
return NULL;
|
||||
@ -1520,7 +1525,8 @@ static dma_buffer_block_t *buffer_block_alloc(usb_transfer_type_t type)
|
||||
|
||||
// Transfer descriptor list: Must be 512 aligned and DMA capable (USB-DWC requirement) and its size must be cache aligned
|
||||
void *xfer_desc_list = heap_caps_aligned_calloc(USB_DWC_QTD_LIST_MEM_ALIGN, desc_list_len * sizeof(usb_dwc_ll_dma_qtd_t), 1, XFER_DESC_LIST_CAPS);
|
||||
if (xfer_desc_list == NULL) {
|
||||
|
||||
if ((xfer_desc_list == NULL) || ((uintptr_t)xfer_desc_list & (USB_DWC_QTD_LIST_MEM_ALIGN - 1))) {
|
||||
free(buffer);
|
||||
heap_caps_free(xfer_desc_list);
|
||||
return NULL;
|
||||
|
@ -3,8 +3,9 @@
|
||||
|
||||
# USB: Host test application
|
||||
|
||||
There are two sets of tests in this application:
|
||||
There are three sets of tests in this application:
|
||||
1. Low-speed: Expects low-speed USB mouse with interrupt endpoint to be connected
|
||||
2. Full-speed: Expects full-speed USB flash disk with 2 bulk endpoints to be connected
|
||||
3. High-speed: Expects high-speed USB flash disk with 2 bulk endpoints to be connected
|
||||
|
||||
For running these tests locally, you will have to update device definitions (VID, PID, ...) in [test_usb_mock_classes.h](../common/test_usb_mock_classes.h).
|
||||
For running these tests locally, you will have to update device definitions (VID, PID, ...) in [dev_msc.c](../common/dev_msc.c), [dev_hid.c](../common/dev_hid.c) and [dev_isoc.c](../common/dev_isoc.c).
|
||||
|
@ -22,6 +22,10 @@
|
||||
#include "mock_msc.h"
|
||||
#include "unity.h"
|
||||
|
||||
// ----------------------------------------------------- Macros --------------------------------------------------------
|
||||
|
||||
// --------------------- Constants -------------------------
|
||||
|
||||
#define PORT_NUM 1
|
||||
#define EVENT_QUEUE_LEN 5
|
||||
#define ENUM_ADDR 1 // Device address to use for tests that enumerate the device
|
||||
@ -279,9 +283,15 @@ void test_hcd_pipe_free(hcd_pipe_handle_t pipe_hdl)
|
||||
}
|
||||
|
||||
#include "esp_private/esp_cache_private.h"
|
||||
#define DATA_BUFFER_CAPS (MALLOC_CAP_DMA | MALLOC_CAP_CACHE_ALIGNED)
|
||||
|
||||
#define ALIGN_UP(num, align) ((align) == 0 ? (num) : (((num) + ((align) - 1)) & ~((align) - 1)))
|
||||
|
||||
#ifdef CONFIG_USB_HOST_DWC_DMA_CAP_MEMORY_IN_PSRAM // In esp32p4, the USB-DWC internal DMA can access external RAM
|
||||
#define DATA_BUFFER_CAPS (MALLOC_CAP_DMA | MALLOC_CAP_CACHE_ALIGNED | MALLOC_CAP_SPIRAM)
|
||||
#else
|
||||
#define DATA_BUFFER_CAPS (MALLOC_CAP_DMA | MALLOC_CAP_CACHE_ALIGNED | MALLOC_CAP_INTERNAL)
|
||||
#endif
|
||||
|
||||
urb_t *test_hcd_alloc_urb(int num_isoc_packets, size_t data_buffer_size)
|
||||
{
|
||||
// Allocate a URB and data buffer
|
||||
|
@ -1,16 +1,20 @@
|
||||
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.esp32p4
|
||||
@pytest.mark.temp_skip_ci(targets=['esp32s2'], reason='lack of runners with usb_host_flash_disk tag')
|
||||
CONFIGS = [
|
||||
pytest.param('default', marks=[pytest.mark.esp32s2, pytest.mark.esp32s3, pytest.mark.esp32p4]),
|
||||
pytest.param('esp32p4_psram', marks=[pytest.mark.esp32p4])
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.usb_host_flash_disk
|
||||
@pytest.mark.temp_skip_ci(targets=['esp32s2'], reason='lack of runners with usb_host_flash_disk tag')
|
||||
@pytest.mark.parametrize('config', CONFIGS, indirect=True)
|
||||
def test_usb_hcd(dut: Dut) -> None:
|
||||
if (dut.target == 'esp32s3'):
|
||||
dut.run_all_single_board_cases(group='full_speed')
|
||||
if dut.target == 'esp32p4':
|
||||
dut.run_all_single_board_cases(group='high_speed', reset=True)
|
||||
else:
|
||||
dut.run_all_single_board_cases(group='high_speed')
|
||||
dut.run_all_single_board_cases(group='full_speed', reset=True)
|
||||
|
0
components/usb/test_apps/hcd/sdkconfig.ci
Normal file
0
components/usb/test_apps/hcd/sdkconfig.ci
Normal file
12
components/usb/test_apps/hcd/sdkconfig.ci.esp32p4_psram
Normal file
12
components/usb/test_apps/hcd/sdkconfig.ci.esp32p4_psram
Normal file
@ -0,0 +1,12 @@
|
||||
CONFIG_IDF_TARGET="esp32p4"
|
||||
|
||||
# Enable experimental features, to set PSRAM frequency to 200 MHz
|
||||
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
||||
|
||||
# Enable PSRAM and set PSRAM frequency
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_SPEED_200M=y
|
||||
# CONFIG_SPIRAM_MEMTEST is not set
|
||||
|
||||
# Allocate USB_DWC DMA capable memory in PSRAM
|
||||
CONFIG_USB_HOST_DWC_DMA_CAP_MEMORY_IN_PSRAM=y
|
@ -3,9 +3,10 @@
|
||||
|
||||
# USB: Host test application
|
||||
|
||||
There are two sets of tests in this application:
|
||||
There are three sets of tests in this application:
|
||||
1. Low-speed: Expects low-speed USB mouse with interrupt endpoint to be connected
|
||||
2. Full-speed: Expects full-speed USB flash disk with 2 bulk endpoints to be connected
|
||||
3. High-speed: Expects high-speed USB flash disk with 2 bulk endpoints to be connected
|
||||
|
||||
For running these tests locally, you will have to update device definitions (VID, PID, ...) in [mock_msc.h](../common/mock_msc.h) and [mock_hid.h](../common/mock_hid.h).
|
||||
For running these tests locally, you will have to update device definitions (VID, PID, ...) in [dev_msc.c](../common/dev_msc.c) and [dev_hid.c](../common/dev_hid.c).
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
@ -10,7 +10,7 @@ from pytest_embedded import Dut
|
||||
@pytest.mark.temp_skip_ci(targets=['esp32s2'], reason='lack of runners with usb_host_flash_disk tag')
|
||||
@pytest.mark.usb_host_flash_disk
|
||||
def test_usb_host(dut: Dut) -> None:
|
||||
if (dut.target == 'esp32s3'):
|
||||
dut.run_all_single_board_cases(group='full_speed')
|
||||
if dut.target == 'esp32p4':
|
||||
dut.run_all_single_board_cases(group='high_speed', reset=True)
|
||||
else:
|
||||
dut.run_all_single_board_cases(group='high_speed')
|
||||
dut.run_all_single_board_cases(group='full_speed', reset=True)
|
||||
|
@ -9,11 +9,20 @@
|
||||
#include "usb_private.h"
|
||||
#include "usb/usb_types_ch9.h"
|
||||
|
||||
// ----------------------------------------------------- Macros --------------------------------------------------------
|
||||
|
||||
#if !CONFIG_IDF_TARGET_LINUX
|
||||
#include "esp_private/esp_cache_private.h"
|
||||
#define ALIGN_UP(num, align) ((align) == 0 ? (num) : (((num) + ((align) - 1)) & ~((align) - 1)))
|
||||
#endif
|
||||
#define DATA_BUFFER_CAPS (MALLOC_CAP_DMA | MALLOC_CAP_CACHE_ALIGNED)
|
||||
|
||||
// ----------------------- Configs -------------------------
|
||||
|
||||
#ifdef CONFIG_USB_HOST_DWC_DMA_CAP_MEMORY_IN_PSRAM // In esp32p4, the USB-DWC internal DMA can access external RAM
|
||||
#define DATA_BUFFER_CAPS (MALLOC_CAP_DMA | MALLOC_CAP_CACHE_ALIGNED | MALLOC_CAP_SPIRAM)
|
||||
#else
|
||||
#define DATA_BUFFER_CAPS (MALLOC_CAP_DMA | MALLOC_CAP_CACHE_ALIGNED | MALLOC_CAP_INTERNAL)
|
||||
#endif
|
||||
|
||||
urb_t *urb_alloc(size_t data_buffer_size, int num_isoc_packets)
|
||||
{
|
||||
|
@ -1,15 +1,22 @@
|
||||
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.esp32p4
|
||||
@pytest.mark.temp_skip_ci(targets=['esp32s2'], reason='lack of runners with usb_host_flash_disk tag')
|
||||
CONFIGS = [
|
||||
pytest.param('default', marks=[pytest.mark.esp32s2, pytest.mark.esp32s3, pytest.mark.esp32p4]),
|
||||
pytest.param('esp32p4_psram', marks=[pytest.mark.esp32p4])
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.usb_host_flash_disk
|
||||
@pytest.mark.temp_skip_ci(targets=['esp32s2'], reason='lack of runners with usb_host_flash_disk tag')
|
||||
@pytest.mark.parametrize('config', CONFIGS, indirect=True)
|
||||
def test_usb_host_msc_example(dut: Dut) -> None:
|
||||
# Check whether the USB-DWC DMA capable memory is allocated in PSRAM
|
||||
usb_dwc_in_psram = bool(dut.app.sdkconfig.get('USB_HOST_DWC_DMA_CAP_MEMORY_IN_PSRAM'))
|
||||
|
||||
# Get wMaxPacketSize to get USB device speed
|
||||
max_packet_size = int(dut.expect(r'wMaxPacketSize (\d{2,3})')[1].decode())
|
||||
|
||||
@ -21,14 +28,14 @@ def test_usb_host_msc_example(dut: Dut) -> None:
|
||||
read_throughput = float(dut.expect(r'example: Read speed ([0-9]*[.]?[0-9]+) MiB')[1].decode())
|
||||
|
||||
# Set write and read throughput limits
|
||||
if (max_packet_size == 512): # wMaxPacketSize = 512 for HS
|
||||
write_throughput_limit = 4.9
|
||||
read_throughput_limit = 11.5
|
||||
else: # wMaxPacketSize = 64 for FS
|
||||
if max_packet_size == 512: # wMaxPacketSize = 512 for HS
|
||||
write_throughput_limit = 3.5 if usb_dwc_in_psram else 4.9
|
||||
read_throughput_limit = 9.9 if usb_dwc_in_psram else 11.5
|
||||
else: # wMaxPacketSize = 64 for FS
|
||||
write_throughput_limit = 0.9
|
||||
read_throughput_limit = 1.0
|
||||
|
||||
# These values should be updated for HS targets
|
||||
# Evaluate the speed measurements
|
||||
if write_throughput > write_throughput_limit:
|
||||
print('Write throughput put OK')
|
||||
else:
|
||||
|
0
examples/peripherals/usb/host/msc/sdkconfig.ci
Normal file
0
examples/peripherals/usb/host/msc/sdkconfig.ci
Normal file
11
examples/peripherals/usb/host/msc/sdkconfig.ci.esp32p4_psram
Normal file
11
examples/peripherals/usb/host/msc/sdkconfig.ci.esp32p4_psram
Normal file
@ -0,0 +1,11 @@
|
||||
CONFIG_IDF_TARGET="esp32p4"
|
||||
|
||||
# Enable experimental features, to set PSRAM frequency to 200 MHz
|
||||
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
||||
|
||||
# Enable PSRAM and set PSRAM frequency
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_SPEED_200M=y
|
||||
|
||||
# Allocate USB_DWC DMA capable memory in PSRAM
|
||||
CONFIG_USB_HOST_DWC_DMA_CAP_MEMORY_IN_PSRAM=y
|
Reference in New Issue
Block a user