mirror of
https://github.com/espressif/esp-idf.git
synced 2025-07-30 18:57:19 +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
|
# 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
|
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)
|
if(CONFIG_SOC_USB_OTG_SUPPORTED)
|
||||||
list(APPEND srcs "hcd_dwc.c"
|
list(APPEND srcs "hcd_dwc.c"
|
||||||
"enum.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
|
If enabled, the enumeration filter callback can be set via 'usb_host_config_t' when calling
|
||||||
'usb_host_install()'.
|
'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
|
# Hidden or compatibility options
|
||||||
config USB_OTG_SUPPORTED
|
config USB_OTG_SUPPORTED
|
||||||
# Invisible config kept for compatibility
|
# Invisible config kept for compatibility
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/queue.h>
|
#include <sys/queue.h>
|
||||||
|
#include "sdkconfig.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
@ -32,8 +33,6 @@
|
|||||||
|
|
||||||
// --------------------- Constants -------------------------
|
// --------------------- 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 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 DEBOUNCE_DELAY_MS CONFIG_USB_HOST_DEBOUNCE_DELAY_MS
|
||||||
#define RESET_HOLD_MS CONFIG_USB_HOST_RESET_HOLD_MS
|
#define RESET_HOLD_MS CONFIG_USB_HOST_RESET_HOLD_MS
|
||||||
@ -48,6 +47,12 @@
|
|||||||
|
|
||||||
// ----------------------- Configs -------------------------
|
// ----------------------- 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 FRAME_LIST_LEN USB_HAL_FRAME_LIST_LEN_32
|
||||||
#define NUM_BUFFERS 2
|
#define NUM_BUFFERS 2
|
||||||
|
|
||||||
@ -1514,7 +1519,7 @@ static dma_buffer_block_t *buffer_block_alloc(usb_transfer_type_t type)
|
|||||||
break;
|
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));
|
dma_buffer_block_t *buffer = calloc(1, sizeof(dma_buffer_block_t));
|
||||||
if (buffer == NULL) {
|
if (buffer == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1522,7 +1527,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
|
// 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);
|
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);
|
free(buffer);
|
||||||
heap_caps_free(xfer_desc_list);
|
heap_caps_free(xfer_desc_list);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -23,6 +23,10 @@
|
|||||||
#include "unity.h"
|
#include "unity.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
// ----------------------------------------------------- Macros --------------------------------------------------------
|
||||||
|
|
||||||
|
// --------------------- Constants -------------------------
|
||||||
|
|
||||||
#define PORT_NUM 1
|
#define PORT_NUM 1
|
||||||
#define EVENT_QUEUE_LEN 5
|
#define EVENT_QUEUE_LEN 5
|
||||||
#define ENUM_ADDR 1 // Device address to use for tests that enumerate the device
|
#define ENUM_ADDR 1 // Device address to use for tests that enumerate the device
|
||||||
@ -261,9 +265,15 @@ void test_hcd_pipe_free(hcd_pipe_handle_t pipe_hdl)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#include "esp_private/esp_cache_private.h"
|
#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)))
|
#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)
|
urb_t *test_hcd_alloc_urb(int num_isoc_packets, size_t data_buffer_size)
|
||||||
{
|
{
|
||||||
// Allocate a URB and data buffer
|
// Allocate a URB and data buffer
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
|
|
||||||
# USB: Host test application
|
# 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
|
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
|
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).
|
||||||
|
@ -7,9 +7,13 @@ from pytest_embedded_idf.utils import idf_parametrize
|
|||||||
|
|
||||||
@pytest.mark.temp_skip_ci(targets=['esp32s2'], reason='lack of runners with usb_host_flash_disk tag')
|
@pytest.mark.temp_skip_ci(targets=['esp32s2'], reason='lack of runners with usb_host_flash_disk tag')
|
||||||
@pytest.mark.usb_host_flash_disk
|
@pytest.mark.usb_host_flash_disk
|
||||||
@idf_parametrize('target', ['esp32s2', 'esp32s3', 'esp32p4'], indirect=['target'])
|
@idf_parametrize(
|
||||||
|
'config,target',
|
||||||
|
[('default', 'esp32s2'), ('default', 'esp32s3'), ('default', 'esp32p4'), ('esp32p4_psram', 'esp32p4')],
|
||||||
|
indirect=['config', 'target'],
|
||||||
|
)
|
||||||
def test_usb_hcd(dut: Dut) -> None:
|
def test_usb_hcd(dut: Dut) -> None:
|
||||||
if dut.target == 'esp32s3':
|
if dut.target == 'esp32p4':
|
||||||
dut.run_all_single_board_cases(group='full_speed', reset=True)
|
|
||||||
else:
|
|
||||||
dut.run_all_single_board_cases(group='high_speed', reset=True)
|
dut.run_all_single_board_cases(group='high_speed', reset=True)
|
||||||
|
else:
|
||||||
|
dut.run_all_single_board_cases(group='full_speed', reset=True)
|
||||||
|
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
|
# 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
|
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
|
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).
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ from pytest_embedded_idf.utils import idf_parametrize
|
|||||||
@pytest.mark.usb_host_flash_disk
|
@pytest.mark.usb_host_flash_disk
|
||||||
@idf_parametrize('target', ['esp32s2', 'esp32s3', 'esp32p4'], indirect=['target'])
|
@idf_parametrize('target', ['esp32s2', 'esp32s3', 'esp32p4'], indirect=['target'])
|
||||||
def test_usb_host(dut: Dut) -> None:
|
def test_usb_host(dut: Dut) -> None:
|
||||||
if dut.target == 'esp32s3':
|
if dut.target == 'esp32p4':
|
||||||
dut.run_all_single_board_cases(group='full_speed', reset=True)
|
|
||||||
else:
|
|
||||||
dut.run_all_single_board_cases(group='high_speed', reset=True)
|
dut.run_all_single_board_cases(group='high_speed', reset=True)
|
||||||
|
else:
|
||||||
|
dut.run_all_single_board_cases(group='full_speed', reset=True)
|
||||||
|
@ -9,11 +9,20 @@
|
|||||||
#include "usb_private.h"
|
#include "usb_private.h"
|
||||||
#include "usb/usb_types_ch9.h"
|
#include "usb/usb_types_ch9.h"
|
||||||
|
|
||||||
|
// ----------------------------------------------------- Macros --------------------------------------------------------
|
||||||
|
|
||||||
#if !CONFIG_IDF_TARGET_LINUX
|
#if !CONFIG_IDF_TARGET_LINUX
|
||||||
#include "esp_private/esp_cache_private.h"
|
#include "esp_private/esp_cache_private.h"
|
||||||
#define ALIGN_UP(num, align) ((align) == 0 ? (num) : (((num) + ((align) - 1)) & ~((align) - 1)))
|
#define ALIGN_UP(num, align) ((align) == 0 ? (num) : (((num) + ((align) - 1)) & ~((align) - 1)))
|
||||||
#endif
|
#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)
|
urb_t *urb_alloc(size_t data_buffer_size, int num_isoc_packets)
|
||||||
{
|
{
|
||||||
|
@ -7,8 +7,15 @@ from pytest_embedded_idf.utils import idf_parametrize
|
|||||||
|
|
||||||
@pytest.mark.temp_skip_ci(targets=['esp32s2'], reason='lack of runners with usb_host_flash_disk tag')
|
@pytest.mark.temp_skip_ci(targets=['esp32s2'], reason='lack of runners with usb_host_flash_disk tag')
|
||||||
@pytest.mark.usb_host_flash_disk
|
@pytest.mark.usb_host_flash_disk
|
||||||
@idf_parametrize('target', ['esp32s2', 'esp32s3', 'esp32p4'], indirect=['target'])
|
@idf_parametrize(
|
||||||
|
'config,target',
|
||||||
|
[('default', 'esp32s2'), ('default', 'esp32s3'), ('default', 'esp32p4'), ('esp32p4_psram', 'esp32p4')],
|
||||||
|
indirect=['config', 'target'],
|
||||||
|
)
|
||||||
def test_usb_host_msc_example(dut: Dut) -> None:
|
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
|
# Get wMaxPacketSize to get USB device speed
|
||||||
max_packet_size = int(dut.expect(r'wMaxPacketSize (\d{2,3})')[1].decode())
|
max_packet_size = int(dut.expect(r'wMaxPacketSize (\d{2,3})')[1].decode())
|
||||||
|
|
||||||
@ -21,13 +28,13 @@ def test_usb_host_msc_example(dut: Dut) -> None:
|
|||||||
|
|
||||||
# Set write and read throughput limits
|
# Set write and read throughput limits
|
||||||
if max_packet_size == 512: # wMaxPacketSize = 512 for HS
|
if max_packet_size == 512: # wMaxPacketSize = 512 for HS
|
||||||
write_throughput_limit = 4.9
|
write_throughput_limit = 3.5 if usb_dwc_in_psram else 4.9
|
||||||
read_throughput_limit = 11.5
|
read_throughput_limit = 9.9 if usb_dwc_in_psram else 11.5
|
||||||
else: # wMaxPacketSize = 64 for FS
|
else: # wMaxPacketSize = 64 for FS
|
||||||
write_throughput_limit = 0.9
|
write_throughput_limit = 0.9
|
||||||
read_throughput_limit = 1.0
|
read_throughput_limit = 1.0
|
||||||
|
|
||||||
# These values should be updated for HS targets
|
# Evaluate the speed measurements
|
||||||
if write_throughput > write_throughput_limit:
|
if write_throughput > write_throughput_limit:
|
||||||
print('Write throughput put OK')
|
print('Write throughput put OK')
|
||||||
else:
|
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