Espressif ESP32-P4

Tags: chip:esp32p4 arch:risc-v vendor:espressif

The ESP32-P4 is a high-performance, highly integrated SoC featuring RISC-V processors, rich multimedia capabilities, and abundant peripherals. It targets applications that demand efficient power usage, compact design, security, high performance, and reliability.

Key highlights include:

  • RISC-V 32-bit processors * Dual-core High Performance (HP) running at 360 MHz * Low Power core (LP) at 40 MHz

  • Powerful image and voice processing capability

  • In-package PSRAM (16 MB or 32 MB depending on the chip variant)

  • 55 GPIOs and rich peripheral set

  • Additional multimedia and I/O features commonly used on ESP32-P4 based designs, such as USB 2.0, MIPI-CSI/DSI, and H.264 encoder

Typical application scenarios include smart home, industrial automation, audio devices, IoT sensor hubs, data loggers, video streaming cameras, USB devices, and speech/image recognition systems.

References: see the ESP Hardware Design Guidelines for ESP32-P4 and the Technical Reference Manual for detailed SoC architecture, memory map, and peripheral descriptions.

ESP32-P4 Toolchain

A generic RISC-V toolchain can be used to build ESP32-P4 projects. It’s recommended to use the same toolchain version used by NuttX CI for RISC-V. Please refer to the Docker container and check for the current compiler version being used. For instance:

###############################################################################
# Build image for tool required by RISCV builds
###############################################################################
FROM nuttx-toolchain-base AS nuttx-toolchain-riscv
# Download the latest RISCV GCC toolchain prebuilt by xPack
RUN mkdir -p riscv-none-elf-gcc && \
  curl -s -L "https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v14.2.0-3/xpack-riscv-none-elf-gcc-14.2.0-3-linux-x64.tar.gz" \
  | tar -C riscv-none-elf-gcc --strip-components 1 -xz

Installing

First, create a directory to hold the toolchain:

$ mkdir -p /path/to/your/toolchain/riscv-none-elf-gcc

Download and extract the recommended toolchain (example uses xPack GCC):

$ curl -s -L "https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v14.2.0-3/xpack-riscv-none-elf-gcc-14.2.0-3-linux-x64.tar.gz" \
| tar -C /path/to/your/toolchain/riscv-none-elf-gcc --strip-components 1 -xz

Add the toolchain to your PATH:

$ echo "export PATH=/path/to/your/toolchain/riscv-none-elf-gcc/bin:$PATH" >> ~/.bashrc

You can edit your shell’s rc files if you don’t use bash.

Building and flashing NuttX

Installing esptool

First, make sure that esptool.py is installed and up-to-date. This tool is used to convert the ELF to an ESP image and to flash the image into the board.

It can be installed with: pip install esptool.

Warning

Installing esptool.py may require a Python virtual environment on newer systems. If the pip install command is blocked by an externally-managed environment policy, consider using a virtual environment (python3 -m venv) or a tool like pipx.

Bootloader and partitions

NuttX supports booting ESP SoCs directly using “Simple Boot”. Depending on future requirements, an externally-built 2nd stage bootloader (e.g., MCUboot) may also be used. Refer to board-specific documentation for the selected boot method and partitioning.

Building and Flashing

Building and flashing are board-specific. Once an ESP32-P4 board is selected via ./tools/configure.sh, in general you can build and flash with:

$ make -j$(nproc)
$ make -j$(nproc) flash ESPTOOL_PORT=<port> ESPTOOL_BINDIR=./

Where <port> is the serial/USB port connected to your board.

Please check Supported Boards for the actual commands.

Building with CMake

General CMake usage (out-of-tree build, menuconfig target, and so on) is described in Compiling with CMake. The ESP32-P4 arch uses the shared Espressif RISC-V integration, which enables post-build steps that produce nuttx.bin (and related images) under the CMake binary directory; the build log also prints suggested esptool.py command lines for your layout (including the ESP32-P4 image offset when applicable).

Example (NuttX shell defconfig, Ninja generator):

$ cd nuttx
$ cmake -B build -DBOARD_CONFIG=esp32p4-function-ev-board:nsh -GNinja
$ cmake --build build

To reconfigure the tree after changing options (same as other NuttX CMake boards):

$ cmake --build build -t menuconfig
$ cmake --build build

Persistent HAL cache (NXTMPDIR)

Pass -DNXTMPDIR=ON at configure time to reuse a persistent clone of the esp-hal-3rdparty repository under nuttx/../nxtmpdir/esp-hal-3rdparty. CMake checks the expected revision; if it does not match, the cache directory is refreshed. This cuts repeat configure/build time when the HAL checkout would otherwise be re-fetched into the binary directory.

Example:

$ cmake -B build -DBOARD_CONFIG=esp32p4-function-ev-board:nsh -DNXTMPDIR=ON -GNinja
$ cmake --build build

MCUBoot: building the 2nd-stage bootloader (-t bootloader)

For configurations that use MCUboot, build the bootloader the same way as with Make, but via the CMake target:

$ cmake --build build -t bootloader

The image is installed as mcuboot-esp32p4.bin in the NuttX source directory (not inside build/).

Note

Flashing paths differ from the pure-Make flow: the application image is under your CMake build directory (for example build/nuttx.bin), while MCUboot binaries live next to nuttx sources. Use the esptool.py hints printed at the end of the build, or the same offsets as documented for make flash with ESPTOOL_BINDIR.

Target flashing (-t flash)

After a successful CMake build, you can flash the chip with the flash custom target. This is the CMake-side equivalent of the Make FLASH logic in tools/espressif/Config.mk.

Serial port: you must set ESPTOOL_PORT to a non-empty value (for example /dev/ttyUSB0). If it is unset or empty, the flash step fails.

Example:

$ export ESPTOOL_PORT=/dev/ttyUSB0
$ cmake --build build -t flash

Or for a single invocation:

$ ESPTOOL_PORT=/dev/ttyUSB0 cmake --build build -t flash

CMake limitations

The following is not supported when building with CMake yet; use the Make-based build instead:

  • ULP / LP core — Low-power coprocessor support (CONFIG_ESPRESSIF_USE_LP_CORE) is not wired up for CMake (ULP/LP integration is TODO).

Debugging

Debugging with openocd and gdb

Espressif uses a specific version of OpenOCD to support ESP chips: openocd-esp32. Please check Building OpenOCD from Sources for more information on how to build OpenOCD.

ESP32-P4 integrates a USB-to-JTAG adapter, so no external JTAG adapter is necessary.

OpenOCD can then be used:

openocd -s <tcl_scripts_path> -c 'set ESP_RTOS hwthread; set ESP_ONLYCPU 1' -f board/esp32p4-builtin.cfg -c 'init; reset halt; esp appimage_offset 0x2000'

Note

  • appimage_offset should be set to 0x2000 when Simple Boot is used.

  • -s <tcl_scripts_path> defines the path to the OpenOCD scripts. Usually set to tcl if running openocd from its source directory. It can be omitted if openocd-esp32 was installed in the system with sudo make install.

Once OpenOCD is running, GDB can be used to connect to the device and debug the running application:

riscv-none-elf-gdb -x gdbinit nuttx

whereas the content of the gdbinit file is:

target remote :3333
set remote hardware-watchpoint-limit 2
mon reset halt
flushregs
monitor reset halt
thb nsh_main
c

Note

nuttx is the ELF file generated by the build process. Please note that CONFIG_DEBUG_SYMBOLS must be enabled in the menuconfig.

Please refer to Debugging for more information about debugging techniques.

Stack Dump and Backtrace Dump

NuttX can dump the stack of a task and produce backtraces to aid debugging, especially when diagnosing exceptions and crashes.

Enable the following options to use this feature: CONFIG_SCHED_BACKTRACE and CONFIG_DEBUG_SYMBOLS; optionally enable CONFIG_ALLSYMS to have symbolized backtraces at runtime (at the expense of larger binary size).

When CONFIG_ALLSYMS is not enabled, use NuttX’s ./tools/btdecode.sh tool to translate addresses into symbols by providing the captured console log and the ELF file.

Example - Crash Dump

A typical crash dump, caused by an illegal load with CONFIG_SCHED_BACKTRACE and CONFIG_DEBUG_SYMBOLS enabled, is shown below:

riscv_exception: EXCEPTION: Store/AMO access fault. MCAUSE: 00000007, EPC: 40017174, MTVAL: 00000000
riscv_exception: PANIC!!! Exception = 00000007
dump_assert_info: Current Version: NuttX  10.4.0 baf7d9dd3b-dirty Oct 24 2025 15:54:40 risc-v
dump_assert_info: Assertion failed panic: at file: common/riscv_exception.c:134 task: backtrace process: backtrace 0x4001712e
up_dump_register: EPC: 40017174
up_dump_register: A0: 0000005a A1: 00000000 A2: 00000002 A3: 00000004
up_dump_register: A4: 7ffffffe A5: 00000000 A6: 7fffffff A7: 00000000
up_dump_register: T0: 4fc1a058 T1: 0000000f T2: ffffffff T3: 00000000
up_dump_register: T4: 00000000 T5: 00000000 T6: 00000000
up_dump_register: S0: 4ff0a776 S1: 4ff0a760 S2: 00000000 S3: 00000000
up_dump_register: S4: 00000000 S5: 00000000 S6: 00000000 S7: 00000000
up_dump_register: S8: 00000000 S9: 00000000 S10: 00000000 S11: 00000000
up_dump_register: SP: 4ff0b710 FP: 4ff0a776 TP: 00000000 RA: 40017174
dump_stackinfo: User Stack:
dump_stackinfo:   base: 0x4ff0a780
dump_stackinfo:   size: 00004048
dump_stackinfo:     sp: 0x4ff0b710
stack_dump: 0x4ff0b6f0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00001880
stack_dump: 0x4ff0b710: 00000000 4ff0a3b0 4001712e 400098f8 00000000 00000000 00000002 4ff0a760
stack_dump: 0x4ff0b730: 00000000 00000000 00000000 400070c4 00000000 00000000 00000000 00000000
stack_dump: 0x4ff0b750: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
sched_dumpstack: backtrace| 2: 0x40017174
dump_tasks:    PID GROUP PRI POLICY   TYPE    NPX STATE   EVENT      SIGMASK          STACKBASE  STACKSIZE   COMMAND
dump_tasks:   ----   --- --- -------- ------- --- ------- ---------- ---------------- 0x4ff066c0      2048   irq
dump_task:       0     0   0 FIFO     Kthread -   Ready              0000000000000000 0x4ff08984      2032   Idle_Task
dump_task:       1     1 100 RR       Task    -   Waiting Semaphore  0000000000000000 0x4ff09898      1992   nsh_main
dump_task:       2     2 255 RR       Task    -   Running            0000000000000000 0x4ff0a780      4048   backtrace task
sched_dumpstack: backtrace| 0: 0x4000bb20
sched_dumpstack: backtrace| 1: 0x4000c088
sched_dumpstack: backtrace| 2: 0x40017174

The lines starting with sched_dumpstack show the backtrace of the tasks. By checking it, it is possible to track the root cause of the crash. Saving this output to a file and using the btdecode.sh:

./tools/btdecode.sh esp32p4 backtrace.log
Backtrace for task 2:
0x40017174: assert_on_task at backtrace_main.c:168
(inlined by) backtrace_main at backtrace_main.c:204

Backtrace dump for all tasks:

Backtrace for task 2:
0x40017174: assert_on_task at backtrace_main.c:168
(inlined by) backtrace_main at backtrace_main.c:204

Backtrace for task 1:
0x4000c088: sys_call0 at syscall.h:161
(inlined by) up_switch_context at riscv_switchcontext.c:83

Backtrace for task 0:
0x4000bb20: up_idle at esp_idle.c:76

Peripheral Support

The following list indicates the state of peripherals’ support in NuttX on ESP32-P4 based on current Kconfig options and upstream support status. Refer to board documentation for what is enabled by default.

HP Peripherals

Peripheral

Support

NOTES

SPI

Yes

SPI2 master/slave; bitbang

I2C

Yes

I2S

Yes

ADC

Yes

ISP

No

PPA

No

GPIO

Yes

Dedicated GPIO supported

UART

Yes

Bit Scrambler

No

SD/MMC Host

No

H264 Encoder

No

2D-DMA

No

TWAI (CAN)

Yes

TWAI0/1

Pulse Counter

Yes

Implemented as Quadrature Encoder

RMT

Yes

USB Serial/JTAG

Yes

JPEG Codec

No

I3C

No

GDMA

Yes

SOC ETM

No

USB 2.0 OTG

No

Camera Interface

No

MIPI CSI

No

LED PWM

Yes

MCPWM

Yes

Motor control and capture

Parallel IO

No

LCD Interface

No

MIPI DSI

No

Timers

Yes

Watchdog

Yes

MWDT0/1 and RWDT

Ethernet

No

Brownout

No

Debug Probe

No

LP Peripherals

Peripheral

Support

NOTES

LP SPI

No

LP I2C

Yes

LP I2S

No

LP UART

Yes

LP GPIO

No

LP Timers

No

LP ADC

No

Temperature

Yes

Internal temperature sensor

Touch Sensor

No

eFuse

Yes

Virtual eFuses supported

Security

Peripheral

Support

NOTES

SHA

Yes

RSA

No

ECC

No

HMAC

No

TRNG

No

ECDSA

No

TEE

No

APM

No

AES

Yes

Digital Signature

No

Secure Boot

No

XTS_AES

No

4096-bit OTP

No

PMP/PMA

Mo

Note

The exact feature availability and default pin mapping depend on the board and configuration. Consult the board documentation and the arch/risc-v/src/common/espressif/Kconfig for feature flags and pin selections.

ULP LP Core Coprocessor

The ULP LP core (Low-power core) is a 32-bit RISC-V coprocessor integrated into the ESP32-P4 SoC. It is designed to run independently of the main high-performance (HP) core and is capable of executing lightweight tasks such as GPIO polling, simple peripheral control and I/O interactions.

This coprocessor benefits to offload simple tasks from HP core (e.g., GPIO polling , I2C operations, basic control logic) and frees the main CPU for higher-level processing

For more information about ULP LP Core Coprocessor check here.

Features of the ULP LP-Core

  • Processor Architecture
    • RV32I RISC-V core with IMAC extensions—Integer (I), Multiplication/Division (M), Atomic (A), and Compressed (C) instructions

    • Runs at 40 MHz

  • Memory
    • Access to 16 KB of low-power ROM (LP-ROM) any time

    • Access to 32 KB of low-power memory (LP-RAM) and LP-domain peripherals any time

    • Access to 768 KB of L2 SRAM any time with latency

    • Full access to all of the chip’s memory and peripherals when the HP core is active

  • Debugging
    • Built-in JTAG debug module for external debugging

    • Supports LP UART for logging from the ULP itself

    • Includes a panic handler capable of dumping register state via LP UART on exceptions

  • Peripheral support
    • LP domain peripherals (LP GPIO, LP I2C, LP UART, LP SPI, LP Mailbox, LP Timer and more)

    • Full access HP domain peripherals when when the HP core is active

Loading Binary into ULP LP-Core

There are two ways to load a binary into LP-Core:
  • Using a prebuilt binary

  • Using NuttX internal build system to build your own (bare-metal) application

When using a prebuilt binary, the already compiled output for the ULP system whether built from NuttX or the ESP-IDF environment can be leveraged. However, whenever the ULP code needs to be modified, it must be rebuilt separately, and the resulting .bin file has to be integrated into NuttX. This workflow, while compatible, can become tedious.

With NuttX internal build system, the ULP binary code can be built and flashed from a single location. It is more convenient but using build system has some dependencies on example side.

Both methods requires CONFIG_ESPRESSIF_USE_LP_CORE variable to enable ULP core and it can be set using make menuconfig or kconfig-tweak commands.

Additionally, a Makefile needs to be provided to specify the ULP application name, source path of the ULP application, and either the binary (for prebuilt) or the source files (for internal build). This Makefile must include the ULP makefile after the variable set process on arch/risc-v/src/common/espressif/esp_ulp.mk integration script. For more information please refer to ulp example Makefile.

Makefile Variables for ULP Core Build:

  • ULP_APP_NAME: Sets name for the ULP application. This variable also be used as prefix (e.g. ULP application bin variable name)

  • ULP_APP_FOLDER: Specifies the directory containing the ULP application’s source codes.

  • ULP_APP_BIN: Defines the path of the prebuilt ULP binary.

  • ULP_APP_C_SRCS: Lists all C source files (.c) that need to be compiled for the ULP application.

  • ULP_APP_ASM_SRCS: Lists all assembly source files (.S or .s) to be assembled.

  • ULP_APP_INCLUDES: Specifies additional include directories for the compiler and assembler.

Here is an Makefile example when using prebuilt binary for ULP core:

ULP_APP_NAME = esp_ulp
ULP_APP_FOLDER = $(TOPDIR)$(DELIM)arch$(DELIM)$(CONFIG_ARCH)$(DELIM)src$(DELIM)$(CHIP_SERIES)
ULP_APP_BIN = $(TOPDIR)$(DELIM)Documentation$(DELIM)platforms$(DELIM)$(CONFIG_ARCH)$(DELIM)$(CONFIG_ARCH_CHIP)$(DELIM)boards$(DELIM)$(CONFIG_ARCH_BOARD)$(DELIM)ulp_riscv_blink.bin

include $(TOPDIR)$(DELIM)arch$(DELIM)$(CONFIG_ARCH)$(DELIM)src$(DELIM)common$(DELIM)espressif$(DELIM)esp_ulp.mk

Here is an example for enabling ULP and using the prebuilt test binary for ULP core:

make distclean
./tools/configure.sh esp32p4-function-ev-board:nsh
kconfig-tweak -e CONFIG_ESPRESSIF_USE_LP_CORE
kconfig-tweak -e CONFIG_ESPRESSIF_ULP_USE_TEST_BIN
make olddefconfig
make -j

Creating an ULP LP-Core Application

To use NuttX’s internal build system to compile the bare-metal LP binary, check the following instructions.

First, create a folder for the ULP source and header files into your NuttX example. This folder is just for ULP project and it is an independent project. Therefore, the NuttX example guide should not be followed for ULP example (folder location is irrelevant. It can be the same of the nuttx-apps repository, for instance). To include the ULP folder in the build system, don’t forget to include the ULP Makefile in the NuttX example Makefile. Lastly, configuration variables needed to enable ULP core instructions can be found above.

NuttX’s internal functions or POSIX calls are not supported.

Here is an example:

  • ULP UART Snippet:

#include <stdint.h>
#include "ulp_lp_core_print.h"
#include "ulp_lp_core_utils.h"
#include "ulp_lp_core_uart.h"
#include "ulp_lp_core_gpio.h"

#define nop() __asm__ __volatile__ ("nop")

int main (void)
{
  while(1)
  {

    lp_core_printf("Hello from the LP core!!\r\n");
    for (int i = 0; i < 10000; i++)
      {
        nop();
      }
  }

  return 0;
}

For more information about ULP Core Coprocessor examples check here. After these settings follow the same steps as for any other configuration to build NuttX. Build system checks ULP project path, adds every source and header file into project and builds it.

To sum up, here is an example. ulp_example/ulp (../ulp_example/ulp) folder selected as example to create a subfolder for ULP but folder that includes ULP source code can be anywhere. For more information about custom apps, please follow NuttX Custom Apps How-to guide, this example will demonstrate how to add ULP code into a custom application:

  • Tree view:

nuttxspace/
├── nuttx/
└── apps/
└── ulp_example/
    └── Makefile
    └── Kconfig
    └── ulp_example.c
    └── ulp/
        └── Makefile
        └── ulp_main.c
  • Contents in Makefile:

include $(APPDIR)/Make.defs

PROGNAME  = $(CONFIG_EXAMPLES_ULP_EXAMPLE_PROGNAME)
PRIORITY  = $(CONFIG_EXAMPLES_ULP_EXAMPLE_PRIORITY)
STACKSIZE = $(CONFIG_EXAMPLES_ULP_EXAMPLE_STACKSIZE)
MODULE    = $(CONFIG_EXAMPLES_ULP_EXAMPLE)

MAINSRC = ulp_example.c

include $(APPDIR)/Application.mk

include ulp/Makefile
  • Contents in Kconfig:

config EXAMPLES_ULP_EXAMPLE
  bool "ULP Example"
  default n
  • Contents in ulp_example.c:

#include <nuttx/config.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>

#include "ulp/ulp/ulp_main.h"
/* Files that holds ULP binary header */

#include "ulp/ulp/ulp_code.h"

int main (void)
 {
   int fd;
   fd = open("/dev/ulp", O_WRONLY);
   if (fd < 0)
     {
       printf("Failed to open ULP: %d\n", errno);
       return -1;
     }
   /* ulp_example is the prefix which can be changed with ULP_APP_NAME makefile
    * variable to access ULP binary code variable */
   write(fd, ulp_example_bin, ulp_example_bin_len);
   return 0;
 }
  • Contents in ulp/Makefile:

ULP_APP_NAME = ulp_example
ULP_APP_FOLDER = $(APPDIR)$(DELIM)ulp_example$(DELIM)ulp
ULP_APP_C_SRCS = ulp_main.c

include $(TOPDIR)$(DELIM)arch$(DELIM)$(CONFIG_ARCH)$(DELIM)src$(DELIM)common$(DELIM)espressif$(DELIM)esp_ulp.mk
  • Contents in ulp_main.c:

#include <stdint.h>
#include <stdbool.h>
#include "ulp_lp_core_gpio.h"

#define GPIO_PIN 0

#define nop() __asm__ __volatile__ ("nop")

bool gpio_level_previous = true;

int main (void)
 {
    while (1)
        {
        ulp_lp_core_gpio_set_level(GPIO_PIN, gpio_level_previous);
        gpio_level_previous = !gpio_level_previous;
        for (int i = 0; i < 10000; i++)
          {
            nop();
          }
        }

    return 0;
 }
  • Command to build:

    make distclean
    ./tools/configure.sh esp32p4-function-ev-board:nsh
    kconfig-tweak -e CONFIG_ESPRESSIF_GPIO_IRQ
    kconfig-tweak -e CONFIG_DEV_GPIO
    kconfig-tweak -e CONFIG_ESPRESSIF_USE_LP_CORE
    kconfig-tweak -e CONFIG_EXAMPLES_ULP_EXAMPLE
    make olddefconfig
    make -j
    

Here is an example of a single ULP application. However, support is not limited to just one application. Multiple ULP applications are also supported. By following the same guideline, multiple ULP applications can be created and loaded using write POSIX call. Each NuttX application can build one ULP application. Therefore, to build multiple ULP applications, multiple NuttX applications are needed to create each ULP binary. This limitation only applies when using the NuttX build system to build multiple ULP applications; it does not affect the ability to load multiple ULP applications built by other means.

ULP binary can be included in NuttX application by adding #include "ulp/ulp/ulp_code.h" line. Then, the ULP binary is accessible by using the ULP application prefix (defined by the ULP_APP_NAME variable in the ULP application Makefile) with the bin keyword to access the binary data (e.g., if ULP_APP_NAME is ulp_test, the binary variable will be ulp_test_bin) and bin_len keyword to access its length (e.g., ulp_test_bin_len for ULP_APP_NAME is ulp_test).

Accessing the ULP LP-Core Program Variables

Global symbols defined in the ULP application are available to the HP core through a shared memory region. To read or write ULP variables, direct reading/writing to such memory positions are not allowed. POSIX calls are needed instead. To access the ULP variable through the HP core, consider that its name is defined by the ULP application prefix (defined by the ULP_APP_NAME variable in the ULP application Makefile) + the ULP application variable. For example if HP core tries to access a ULP application variable named result and ULP_APP_NAME in the ULP application Makefile set as ulp_app, required name for that variable will be ulp_app_result. FIONREAD or FIONWRITE ioctl calls are, then, performed with the address of a struct symtab_s previously defined with the name of the variable to be read or written.

Warning

Ensure that the related ULP application is running. Otherwise, another ULP application may interfere by using the same memory space for a different variables.

Here is a snippet for reading and writing to a ULP variable named var_test (assuming the ULP_APP_NAME is set to ulp) through the HP core:

#include <nuttx/config.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "nuttx/symtab.h"

int main (void)
 {
   uint32_t ulp_var;
   int fd;
   struct symtab_s sym =
   {
     .sym_name = "ulp_var_test",
     .sym_value = &ulp_var,
   };
   fd = open("/dev/ulp", O_RDWR);
   ioctl(fd, FIONREAD, &sym);
   if (ulp_var != 0)
     {
       ulp_var = 0;
       ioctl(fd, FIONWRITE, &sym);
     }

   return OK;
 }

Debugging ULP LP-Core

To debug ULP LP-Core please first refer to Debugging section. Debugging ULP core consist same steps with some small differences. First of all, configuration file needs to be changed from board/esp32p4-builtin.cfg or board/esp32p4-ftdi.cfg to board/esp32p4-lpcore-builtin.cfg or board/esp32p4-lpcore-ftdi.cfg depending on preferred debug adapter.

LP core supports limited set of HW exceptions, so, for example, writing at address 0x0 will not cause a panic as it would be for the code running on HP core. This can be overcome to some extent by enabling undefined behavior sanitizer for LP core application, so ubsan can help to catch some errors. But note that it will increase code size significantly and it can happen that application won’t fit into RTC RAM. To enable ubsan for ULP please add CONFIG_ESPRESSIF_ULP_ENABLE_UBSAN in menuconfig.

Supported Boards