mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(ulp): add option for routing LP-printf to HP console
This commit is contained in:
parent
42bcb84013
commit
74a04c140c
9
components/esp_rom/esp32p4/ld/esp32p4lp.rom.api.ld
Normal file
9
components/esp_rom/esp32p4/ld/esp32p4lp.rom.api.ld
Normal file
@ -0,0 +1,9 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
/** ROM APIs
|
||||
*/
|
||||
|
||||
PROVIDE ( esp_rom_output_putc = uart_tx_one_char );
|
@ -106,6 +106,25 @@ menu "Ultra Low Power (ULP) Co-processor"
|
||||
Disabling this option will reduce the LP core binary size by not
|
||||
linking in panic handler functionality.
|
||||
|
||||
config ULP_HP_UART_CONSOLE_PRINT
|
||||
depends on ULP_COPROC_TYPE_LP_CORE
|
||||
bool
|
||||
prompt "Route lp_core_printf to the console HP-UART"
|
||||
help
|
||||
Set this option to route lp_core_printf to the console HP-UART.
|
||||
This allows you to easily view print outputs from the LP core, without
|
||||
having to connect to the LP-UART. This option comes with the following
|
||||
limitations:
|
||||
|
||||
1. There is no mutual exclusion between the HP-Core and the LP-Core accessing
|
||||
the HP-UART, which means that if both cores are logging heavily the output
|
||||
strings might get mangled together.
|
||||
2. The HP-UART can only work while the HP-Core is running, which means that
|
||||
if the HP-Core is in deep sleep, the LP-Core will not be able to print to the
|
||||
console HP-UART.
|
||||
|
||||
Due to these limitations it is only recommended to use this option for easy debugging.
|
||||
For more serious use-cases you should use the LP-UART.
|
||||
endmenu
|
||||
|
||||
endmenu # Ultra Low Power (ULP) Co-processor
|
||||
|
@ -135,6 +135,8 @@ elseif(CONFIG_ULP_COPROC_TYPE_LP_CORE)
|
||||
PRIVATE SHELL:-T ${IDF_PATH}/components/esp_rom/${IDF_TARGET}/ld/${IDF_TARGET}lp.rom.newlib.ld)
|
||||
target_link_options(${ULP_APP_NAME}
|
||||
PRIVATE SHELL:-T ${IDF_PATH}/components/esp_rom/${IDF_TARGET}/ld/${IDF_TARGET}lp.rom.version.ld)
|
||||
target_link_options(${ULP_APP_NAME}
|
||||
PRIVATE SHELL:-T ${IDF_PATH}/components/esp_rom/${IDF_TARGET}/ld/${IDF_TARGET}lp.rom.api.ld)
|
||||
endif()
|
||||
|
||||
target_sources(${ULP_APP_NAME} PRIVATE ${ULP_S_SOURCES})
|
||||
|
@ -37,3 +37,10 @@ void lp_core_printf(const char* format, ...);
|
||||
extern void ets_install_uart_printf(void);
|
||||
#define lp_core_install_uart_print ets_install_uart_printf
|
||||
#endif /* CONFIG_ULP_ROM_PRINT_ENABLE */
|
||||
|
||||
/**
|
||||
* @brief Print a single character from the LP core
|
||||
*
|
||||
* @param c character to be printed
|
||||
*/
|
||||
void lp_core_print_char(char c);
|
||||
|
@ -6,25 +6,47 @@
|
||||
#include <stdarg.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "ulp_lp_core_uart.h"
|
||||
|
||||
#if !CONFIG_ULP_ROM_PRINT_ENABLE
|
||||
#include "hal/uart_hal.h"
|
||||
#include "esp_rom_uart.h"
|
||||
|
||||
#define LP_UART_PORT_NUM LP_UART_NUM_0
|
||||
#define BINARY_SUPPORT 1
|
||||
|
||||
#define is_digit(c) ((c >= '0') && (c <= '9'))
|
||||
|
||||
#if CONFIG_ULP_HP_UART_CONSOLE_PRINT
|
||||
void __attribute__((alias("hp_uart_send_char"))) lp_core_print_char(char c);
|
||||
|
||||
static void hp_uart_send_char(char t)
|
||||
{
|
||||
uart_dev_t *uart = (uart_dev_t *)UART_LL_GET_HW(CONFIG_ESP_CONSOLE_UART_NUM);
|
||||
|
||||
while (uart_ll_get_txfifo_len(uart) < 2) {
|
||||
;
|
||||
}
|
||||
uart_ll_write_txfifo(uart, &t, 1);
|
||||
}
|
||||
#elif !CONFIG_ULP_ROM_PRINT_ENABLE
|
||||
void __attribute__((alias("lp_uart_send_char"))) lp_core_print_char(char c);
|
||||
static void lp_uart_send_char(char c)
|
||||
{
|
||||
int tx_len = 0;
|
||||
int loop_cnt = 0;
|
||||
|
||||
/* Write one byte to LP UART. Break after few iterations if we are stuck for any reason. */
|
||||
while (tx_len != 1 && loop_cnt < 1000) {
|
||||
tx_len = lp_core_uart_tx_chars(LP_UART_PORT_NUM, (const void *)&c, 1);
|
||||
loop_cnt++;
|
||||
}
|
||||
}
|
||||
#else
|
||||
void __attribute__((alias("lp_rom_send_char"))) lp_core_print_char(char c);
|
||||
static void lp_rom_send_char(char c)
|
||||
{
|
||||
esp_rom_output_putc(c);
|
||||
}
|
||||
#endif // CONFIG_ULP_HP_UART_CONSOLE_PRINT
|
||||
|
||||
#if !CONFIG_ULP_ROM_PRINT_ENABLE
|
||||
|
||||
// Ported over ROM function _cvt()
|
||||
static int lp_core_cvt(unsigned long long val, char *buf, long radix, char *digits)
|
||||
@ -268,7 +290,7 @@ int lp_core_printf(const char* format, ...)
|
||||
va_start(ap, format);
|
||||
|
||||
/* Pass the input string and the argument list to ets_vprintf() */
|
||||
int ret = lp_core_ets_vprintf(lp_uart_send_char, format, ap);
|
||||
int ret = lp_core_ets_vprintf(lp_core_print_char, format, ap);
|
||||
|
||||
va_end(ap);
|
||||
|
||||
|
@ -1,18 +1,27 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_rom_caps.h"
|
||||
#include "rom/ets_sys.h"
|
||||
#include "ulp_lp_core_utils.h"
|
||||
#include "ulp_lp_core_lp_timer_shared.h"
|
||||
#include "ulp_lp_core_memory_shared.h"
|
||||
#include "ulp_lp_core_print.h"
|
||||
|
||||
extern void main();
|
||||
|
||||
/* Initialize lp core related system functions before calling user's main*/
|
||||
void lp_core_startup()
|
||||
{
|
||||
|
||||
#if CONFIG_ULP_HP_UART_CONSOLE_PRINT && ESP_ROM_HAS_LP_ROM
|
||||
ets_install_putc1(lp_core_print_char);
|
||||
#endif
|
||||
|
||||
ulp_lp_core_update_wakeup_cause();
|
||||
|
||||
main();
|
||||
|
@ -11,4 +11,4 @@ set(EXTRA_COMPONENT_DIRS
|
||||
set(COMPONENTS main)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(lp_core_test)
|
||||
project(lp_core_basic_tests)
|
@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
@ -0,0 +1,14 @@
|
||||
# This is the project CMakeLists.txt file for the test subproject
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
list(PREPEND SDKCONFIG_DEFAULTS "$ENV{IDF_PATH}/tools/test_apps/configs/sdkconfig.debug_helpers" "sdkconfig.defaults")
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS
|
||||
"$ENV{IDF_PATH}/tools/unit-test-app/components"
|
||||
)
|
||||
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
set(COMPONENTS main)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(lp_core_hp_uart_test)
|
@ -0,0 +1,3 @@
|
||||
| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 |
|
||||
| ----------------- | -------- | -------- | -------- |
|
||||
|
@ -0,0 +1,11 @@
|
||||
set(app_sources "test_app_main.c" "test_lp_core.c")
|
||||
set(lp_core_sources "lp_core/test_hello_main.c")
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "lp_core"
|
||||
REQUIRES ulp unity esp_timer test_utils
|
||||
WHOLE_ARCHIVE)
|
||||
|
||||
set(lp_core_exp_dep_srcs ${app_sources})
|
||||
|
||||
ulp_embed_binary(lp_core_test_app "${lp_core_sources}" "${lp_core_exp_dep_srcs}")
|
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "ulp_lp_core_print.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
lp_core_printf("Hello, World!\n");
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
// Some resources are lazy allocated in the sleep code, the threshold is left for that case
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (-500)
|
||||
|
||||
static size_t before_free_8bit;
|
||||
static size_t before_free_32bit;
|
||||
|
||||
static void check_leak(size_t before_free, size_t after_free, const char *type)
|
||||
{
|
||||
ssize_t delta = after_free - before_free;
|
||||
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
|
||||
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
|
||||
}
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
check_leak(before_free_8bit, after_free_8bit, "8BIT");
|
||||
check_leak(before_free_32bit, after_free_32bit, "32BIT");
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
unity_run_menu();
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/time.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "unity.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_rom_caps.h"
|
||||
#include "lp_core_test_app.h"
|
||||
#include "ulp_lp_core.h"
|
||||
|
||||
extern const uint8_t lp_core_main_bin_start[] asm("_binary_lp_core_test_app_bin_start");
|
||||
extern const uint8_t lp_core_main_bin_end[] asm("_binary_lp_core_test_app_bin_end");
|
||||
|
||||
static void load_and_start_lp_core_firmware(ulp_lp_core_cfg_t* cfg, const uint8_t* firmware_start, const uint8_t* firmware_end)
|
||||
{
|
||||
TEST_ASSERT(ulp_lp_core_load_binary(firmware_start,
|
||||
(firmware_end - firmware_start)) == ESP_OK);
|
||||
|
||||
TEST_ASSERT(ulp_lp_core_run(cfg) == ESP_OK);
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("lp-print can output to hp-uart", "[lp_core]")
|
||||
{
|
||||
/* Load ULP firmware and start the coprocessor */
|
||||
ulp_lp_core_cfg_t cfg = {
|
||||
.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU,
|
||||
};
|
||||
|
||||
load_and_start_lp_core_firmware(&cfg, lp_core_main_bin_start, lp_core_main_bin_end);
|
||||
|
||||
// Actual test output on UART is checked by pytest, not unity test-case
|
||||
// We simply wait to allow the lp-core to run once
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@pytest.mark.esp32c5
|
||||
@pytest.mark.esp32c6
|
||||
@pytest.mark.esp32p4
|
||||
@pytest.mark.generic
|
||||
def test_lp_core_hp_uart_print(dut: Dut) -> None:
|
||||
dut.expect_exact('Press ENTER to see the list of tests')
|
||||
dut.write('"lp-print can output to hp-uart"')
|
||||
dut.expect_exact('Hello, World!')
|
@ -0,0 +1,7 @@
|
||||
CONFIG_ESP_TASK_WDT_INIT=n
|
||||
|
||||
CONFIG_ULP_COPROC_ENABLED=y
|
||||
CONFIG_ULP_COPROC_TYPE_LP_CORE=y
|
||||
CONFIG_ULP_COPROC_RESERVE_MEM=12000
|
||||
CONFIG_ULP_PANIC_OUTPUT_ENABLE=y
|
||||
CONFIG_ULP_HP_UART_CONSOLE_PRINT=y
|
@ -212,6 +212,8 @@ When programming the LP-Core, it can sometimes be challenging to figure out why
|
||||
|
||||
* Use the LP-UART to print: the LP-Core has access to the LP-UART peripheral, which can be used for printing information independently of the main CPU sleep state. See :example:`system/ulp/lp_core/lp_uart/lp_uart_print` for an example of how to use this driver.
|
||||
|
||||
* Routing :cpp:func:`lp_core_printf` to the HP-Core console UART with :ref:`CONFIG_ULP_HP_UART_CONSOLE_PRINT`. This allows you to easily print LP-Core information to the already connected HP-Core console UART. The drawback of this approach is that it requires the main CPU to be awake and since there is no synchronization between the LP and HP cores, the output may be interleaved.
|
||||
|
||||
* Share program state through shared variables: as described in :ref:`ulp-lp-core-access-variables`, both the main CPU and the ULP core can easily access global variables in RTC memory. Writing state information to such a variable from the ULP and reading it from the main CPU can help you discern what is happening on the ULP core. The downside of this approach is that it requires the main CPU to be awake, which will not always be the case. Keeping the main CPU awake might even, in some cases, mask problems, as some issues may only occur when certain power domains are powered down.
|
||||
|
||||
* Panic handler: the LP-Core has a panic handler that can dump the state of the LP-Core registers by the LP-UART when an exception is detected. To enable the panic handler, set the :ref:`CONFIG_ULP_PANIC_OUTPUT_ENABLE` option to ``y``. This option can be kept disabled to reduce LP-RAM usage by the LP-Core application. To recover a backtrace from the panic dump, it is possible to use esp-idf-monitor_., e.g.:
|
||||
|
Loading…
Reference in New Issue
Block a user