mirror of
https://github.com/apache/nuttx.git
synced 2026-05-28 03:45:50 +08:00
328ffdb297
Add UF2 file format docs for RISC-V based Espressif devices Signed-off-by: Eren Terzioglu <eren.terzioglu@espressif.com>
1208 lines
48 KiB
ReStructuredText
1208 lines
48 KiB
ReStructuredText
==================
|
|
Espressif ESP32-C6
|
|
==================
|
|
|
|
The ESP32-C6 is an ultra-low-power and highly integrated SoC with a RISC-V
|
|
core and supports 2.4 GHz Wi-Fi 6, Bluetooth 5 (LE) and the 802.15.4 protocol.
|
|
|
|
* Address Space
|
|
- 800 KB of internal memory address space accessed from the instruction bus
|
|
- 560 KB of internal memory address space accessed from the data bus
|
|
- 1016 KB of peripheral address space
|
|
- 8 MB of external memory virtual address space accessed from the instruction bus
|
|
- 8 MB of external memory virtual address space accessed from the data bus
|
|
- 480 KB of internal DMA address space
|
|
* Internal Memory
|
|
- 320 KB ROM
|
|
- 512 KB SRAM (16 KB can be configured as Cache)
|
|
- 16 KB of SRAM in RTC
|
|
* External Memory
|
|
- Up to 16 MB of external flash
|
|
* Peripherals
|
|
- 35 peripherals
|
|
* GDMA
|
|
- 7 modules are capable of DMA operations.
|
|
|
|
ESP32-C6 Toolchain
|
|
==================
|
|
|
|
A generic RISC-V toolchain can be used to build ESP32-C6 projects. It's recommended to use the same
|
|
toolchain used by NuttX CI. Please refer to the Docker
|
|
`container <https://github.com/apache/nuttx/tree/master/tools/ci/docker/linux/Dockerfile>`_ and
|
|
check for the current compiler version being used. For instance:
|
|
|
|
.. code-block::
|
|
|
|
###############################################################################
|
|
# 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 riscv-none-elf-gcc && \
|
|
curl -s -L "https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v13.2.0-2/xpack-riscv-none-elf-gcc-13.2.0-2-linux-x64.tar.gz" \
|
|
| tar -C riscv-none-elf-gcc --strip-components 1 -xz
|
|
|
|
It uses the xPack's prebuilt toolchain based on GCC 13.2.0-2.
|
|
|
|
Installing
|
|
----------
|
|
|
|
First, create a directory to hold the toolchain:
|
|
|
|
.. code-block:: console
|
|
|
|
$ mkdir -p /path/to/your/toolchain/riscv-none-elf-gcc
|
|
|
|
Download and extract toolchain:
|
|
|
|
.. code-block:: console
|
|
|
|
$ curl -s -L "https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v13.2.0-2/xpack-riscv-none-elf-gcc-13.2.0-2-linux-x64.tar.gz" \
|
|
| tar -C /path/to/your/toolchain/riscv-none-elf-gcc --strip-components 1 -xz
|
|
|
|
Add the toolchain to your `PATH`:
|
|
|
|
.. code-block:: console
|
|
|
|
$ 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 a compatible ESP32-C6 image and to flash the image into the board.
|
|
|
|
It can be installed with: ``pip install esptool>=4.8.1``.
|
|
|
|
.. warning::
|
|
Installing ``esptool.py`` may required a Python virtual environment on newer systems.
|
|
This will be the case if the ``pip install`` command throws an error such as:
|
|
``error: externally-managed-environment``.
|
|
|
|
If you are not familiar with virtual environments, refer to `Managing esptool on virtual environment`_ for instructions on how to install ``esptool.py``.
|
|
|
|
|
|
Bootloader and partitions
|
|
-------------------------
|
|
|
|
NuttX can boot the ESP32-C6 directly using the so-called "Simple Boot".
|
|
An externally-built 2nd stage bootloader is not required in this case as all
|
|
functions required to boot the device are built within NuttX. Simple boot does not
|
|
require any specific configuration (it is selectable by default if no other
|
|
2nd stage bootloader is used). For compatibility among other SoCs and future options
|
|
of 2nd stage bootloaders, the commands ``make bootloader`` and the ``ESPTOOL_BINDIR``
|
|
option (for the ``make flash``) are kept (and ignored if Simple Boot is used).
|
|
|
|
If features like `Flash Encryption`_ are required, an externally-built 2nd stage bootloader is needed.
|
|
The MCUBoot bootloader is built using the ``make bootloader`` command. This command generates
|
|
the firmware in the ``nuttx`` folder. The ``ESPTOOL_BINDIR`` is used in the
|
|
``make flash`` command to specify the path to the bootloader. For compatibility
|
|
among other SoCs and future options of 2nd stage bootloaders, the commands
|
|
``make bootloader`` and the ``ESPTOOL_BINDIR`` option (for the ``make flash``)
|
|
can be used even if no externally-built 2nd stage bootloader
|
|
is being built (they will be ignored if Simple Boot is used, for instance)::
|
|
|
|
$ make bootloader
|
|
|
|
.. note:: It is recommended that if this is the first time you are using the board with NuttX to
|
|
perform a complete SPI FLASH erase.
|
|
|
|
.. code-block:: console
|
|
|
|
$ esptool.py erase_flash
|
|
|
|
Building and Flashing
|
|
---------------------
|
|
|
|
This is a two-step process where the first step converts the ELF file into an ESP32-C6 compatible binary
|
|
and the second step flashes it to the board. These steps are included in the build system and it is
|
|
possible to build and flash the NuttX firmware simply by running::
|
|
|
|
$ make flash ESPTOOL_PORT=<port> ESPTOOL_BINDIR=./
|
|
|
|
where:
|
|
|
|
* ``ESPTOOL_PORT`` is typically ``/dev/ttyUSB0`` or similar.
|
|
* ``ESPTOOL_BINDIR=./`` is the path of the externally-built 2nd stage bootloader and the partition table (if applicable): when built using the ``make bootloader``, these files are placed into ``nuttx`` folder.
|
|
* ``ESPTOOL_BAUD`` is able to change the flash baud rate if desired.
|
|
|
|
To create and flash with UF2 (USB Flashing Format) binary, ``UF2=1`` option needs to be set during build phase
|
|
(e.g ``make UF2=1 -j8``). This flag will create UF2 format file addition to binary. This output can be used to
|
|
flash the device with `ESP USB Bridge <https://github.com/espressif/esp-usb-bridge>`__.
|
|
To flash using ESP USB Bridge, either drag and drop the generated UF2 file onto the flasher's
|
|
mass storage device, or use the ``UF2=1`` flag during flashing (e.g. ``make flash ESPTOOL_PORT=<port> ESPTOOL_BINDIR=./ UF2=1``)
|
|
|
|
Flashing NSH Example
|
|
--------------------
|
|
|
|
This example shows how to build and flash the ``nsh`` defconfig for the ESP32-C6-DevKitC-1 board::
|
|
|
|
$ cd nuttx
|
|
$ make distclean
|
|
$ ./tools/configure.sh esp32c6-devkitc:nsh
|
|
$ make -j$(nproc)
|
|
|
|
When the build is complete, the firmware can be flashed to the board using the command::
|
|
|
|
$ make -j$(nproc) flash ESPTOOL_PORT=<port> ESPTOOL_BINDIR=./
|
|
|
|
where ``<port>`` is the serial port where the board is connected::
|
|
|
|
$ make flash ESPTOOL_PORT=/dev/ttyUSB0 ESPTOOL_BINDIR=./
|
|
CP: nuttx.hex
|
|
MKIMAGE: NuttX binary
|
|
esptool.py -c esp32c6 elf2image --ram-only-header -fs 4MB -fm dio -ff 80m -o nuttx.bin nuttx
|
|
esptool.py v4.8.1
|
|
Creating esp32c6 image...
|
|
Image has only RAM segments visible. ROM segments are hidden and SHA256 digest is not appended.
|
|
Merged 1 ELF section
|
|
Successfully created esp32c6 image.
|
|
Generated: nuttx.bin
|
|
esptool.py -c esp32c6 -p /dev/ttyUSB0 -b 921600 write_flash -fs 4MB -fm dio -ff 80m 0x0000 nuttx.bin
|
|
esptool.py v4.8.1
|
|
Serial port /dev/ttyUSB0
|
|
Connecting....
|
|
Chip is ESP32-C6 (QFN40) (revision v0.0)
|
|
[...]
|
|
Flash will be erased from 0x00000000 to 0x0003cfff...
|
|
Compressed 248628 bytes to 106757...
|
|
Wrote 248628 bytes (106757 compressed) at 0x00000000 in 2.5 seconds (effective 805.6 kbit/s)...
|
|
Hash of data verified.
|
|
|
|
Leaving...
|
|
Hard resetting via RTS pin...
|
|
|
|
Now opening the serial port with a terminal emulator should show the NuttX console::
|
|
|
|
$ picocom -b 115200 /dev/ttyUSB0
|
|
NuttShell (NSH) NuttX-12.8.0
|
|
nsh> uname -a
|
|
NuttX 12.8.0 759d37b97c-dirty Mar 5 2025 19:42:41 risc-v esp32c6-devkitc
|
|
|
|
Building with CMake
|
|
-------------------
|
|
|
|
General CMake usage (out-of-tree build, ``menuconfig`` target, and so on) is described in
|
|
:doc:`/quickstart/compiling_cmake`. The ESP32-C6 common arch 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.
|
|
|
|
Example (NuttX shell defconfig, Ninja generator)::
|
|
|
|
$ cd nuttx
|
|
$ cmake -B build -DBOARD_CONFIG=esp32c6-devkitc: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=esp32c6-devkitc: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-esp32c6.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
|
|
|
|
.. _esp32c6_debug:
|
|
|
|
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
|
|
=========
|
|
|
|
This section describes debugging techniques for the ESP32-C6.
|
|
|
|
Debugging with ``openocd`` and ``gdb``
|
|
--------------------------------------
|
|
|
|
Espressif uses a specific version of OpenOCD to support ESP32-C6: `openocd-esp32 <https://github.com/espressif/>`_.
|
|
|
|
Please check `Building OpenOCD from Sources <https://docs.espressif.com/projects/esp-idf/en/release-v5.1/esp32c6/api-guides/jtag-debugging/index.html#jtag-debugging-building-openocd>`_
|
|
for more information on how to build OpenOCD for ESP32-C6.
|
|
|
|
You do not need an external JTAG to debug, the ESP32-C6 integrates a
|
|
USB-to-JTAG adapter.
|
|
|
|
.. note:: One must configure the USB drivers to enable JTAG communication. Please check
|
|
`Configure USB Drivers <https://docs.espressif.com/projects/esp-idf/en/release-v5.1/esp32c6/api-guides/jtag-debugging/configure-builtin-jtag.html#configure-usb-drivers>`_
|
|
for more information.
|
|
|
|
OpenOCD can then be used::
|
|
|
|
openocd -s <tcl_scripts_path> -c 'set ESP_RTOS hwthread' -f board/esp32c6-builtin.cfg -c 'init; reset halt; esp appimage_offset 0x0'
|
|
|
|
.. note::
|
|
- ``appimage_offset`` should be set to ``0x0`` when ``Simple Boot`` is used. For MCUboot, this value should be set to
|
|
``CONFIG_ESPRESSIF_OTA_PRIMARY_SLOT_OFFSET`` value (``0x10000`` by default).
|
|
- ``-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` were installed in the system with `sudo make install`.
|
|
|
|
If you want to debug with an external JTAG adapter it can
|
|
be connected as follows:
|
|
|
|
============ ===========
|
|
ESP32-C6 Pin JTAG Signal
|
|
============ ===========
|
|
GPIO4 TMS
|
|
GPIO5 TDI
|
|
GPIO6 TCK
|
|
GPIO7 TDO
|
|
============ ===========
|
|
|
|
Furthermore, an efuse needs to be burnt to be able to debug::
|
|
|
|
espefuse.py -p <port> burn_efuse DIS_USB_JTAG
|
|
|
|
.. warning:: Burning eFuses is an irreversible operation, so please
|
|
consider the above option before starting the process.
|
|
|
|
OpenOCD can then be used::
|
|
|
|
openocd -c 'set ESP_RTOS hwtread; set ESP_FLASH_SIZE 0' -f board/esp32c6-ftdi.cfg
|
|
|
|
Once OpenOCD is running, you can use GDB to connect to it and debug your 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 :doc:`/quickstart/debugging` for more information about debugging techniques.
|
|
|
|
Stack Dump and Backtrace Dump
|
|
-----------------------------
|
|
|
|
NuttX has a feature to dump the stack of a task and to dump the backtrace of it (and of all
|
|
the other tasks). This feature is useful to debug the system when it is not behaving as expected,
|
|
especially when it is crashing.
|
|
|
|
In order to enable this feature, the following options must be enabled in the NuttX configuration:
|
|
``CONFIG_SCHED_BACKTRACE``, ``CONFIG_DEBUG_SYMBOLS`` and, optionally, ``CONFIG_ALLSYMS``.
|
|
|
|
.. note::
|
|
The first two options enable the backtrace dump. The third option enables the backtrace dump
|
|
with the associated symbols, but increases the size of the generated NuttX binary.
|
|
|
|
Espressif also provides a tool to translate the backtrace dump into a human-readable format.
|
|
This tool is called ``btdecode.sh`` and is available at ``tools/espressif/btdecode.sh`` of NuttX
|
|
repository.
|
|
|
|
.. note::
|
|
This tool is not necessary if ``CONFIG_ALLSYMS`` is enabled. In this case, the backtrace dump
|
|
contains the function names.
|
|
|
|
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: 420168ac, MT0
|
|
riscv_exception: PANIC!!! Exception = 00000007
|
|
_assert: Current Version: NuttX 10.4.0 2ae3246e40-dirty Sep 19 2024 14:47:41 risc-v
|
|
_assert: Assertion failed panic: at file: :0 task: backtrace process: backtrace 0x42016866
|
|
up_dump_register: EPC: 420168ac
|
|
up_dump_register: A0: 0000005a A1: 40809fc4 A2: 00000001 A3: 00000088
|
|
up_dump_register: A4: 00007fff A5: 00000001 A6: 00000000 A7: 00000000
|
|
up_dump_register: T0: 00000000 T1: 00000000 T2: ffffffff T3: 00000000
|
|
up_dump_register: T4: 00000000 T5: 00000000 T6: 00000000
|
|
up_dump_register: S0: 4080908e S1: 40809078 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: 4080a020 FP: 4080908e TP: 00000000 RA: 420168ac
|
|
dump_stack: User Stack:
|
|
dump_stack: base: 0x40809098
|
|
dump_stack: size: 00004040
|
|
dump_stack: sp: 0x4080a020
|
|
stack_dump: 0x4080a000: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00001880
|
|
stack_dump: 0x4080a020: 00000000 40808c90 42016866 42006e06 00000000 00000000 40809078 00000002
|
|
stack_dump: 0x4080a040: 00000000 00000000 00000000 42004d72 00000000 00000000 00000000 00000000
|
|
stack_dump: 0x4080a060: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
|
sched_dumpstack: backtrace| 2: 0x420168ac
|
|
dump_tasks: PID GROUP PRI POLICY TYPE NPX STATE EVENT SIGMASK STACKBASE STACKSIZE COMMAND
|
|
dump_tasks: ---- --- --- -------- ------- --- ------- ---------- ---------------- 0x40805a90 2048 irq
|
|
dump_task: 0 0 0 FIFO Kthread - Ready 0000000000000000 0x40807290 2032 Idle_Task
|
|
dump_task: 1 1 100 RR Task - Waiting Semaphore 0000000000000000 0x408081a8 1992 nsh_main
|
|
dump_task: 2 2 255 RR Task - Running 0000000000000000 0x40809098 4040 backtrace task
|
|
sched_dumpstack: backtrace| 0: 0x42008420
|
|
sched_dumpstack: backtrace| 1: 0x420089a2
|
|
sched_dumpstack: backtrace| 2: 0x420168ac
|
|
|
|
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 esp32c6 /tmp/backtrace.txt
|
|
Backtrace for task 2:
|
|
0x420168ac: assert_on_task at backtrace_main.c:158
|
|
(inlined by) backtrace_main at backtrace_main.c:194
|
|
|
|
Backtrace dump for all tasks:
|
|
|
|
Backtrace for task 2:
|
|
0x420168ac: assert_on_task at backtrace_main.c:158
|
|
(inlined by) backtrace_main at backtrace_main.c:194
|
|
|
|
Backtrace for task 1:
|
|
0x420089a2: sys_call2 at syscall.h:227
|
|
(inlined by) up_switch_context at riscv_switchcontext.c:95
|
|
|
|
Backtrace for task 0:
|
|
0x42008420: up_idle at esp_idle.c:74
|
|
|
|
Peripheral Support
|
|
==================
|
|
|
|
The following list indicates the state of peripherals' support in NuttX:
|
|
|
|
============== ======= ====================
|
|
Peripheral Support NOTES
|
|
============== ======= ====================
|
|
ADC Yes Oneshot and internal temperature sensor
|
|
AES Yes
|
|
Bluetooth No
|
|
CAN/TWAI Yes
|
|
DMA Yes
|
|
ECC No
|
|
eFuse Yes
|
|
GPIO Yes Dedicated GPIO supported
|
|
HMAC No
|
|
I2C Yes Master and Slave mode also LPI2C supported
|
|
I2S Yes
|
|
LED/PWM Yes
|
|
MCPWM Yes
|
|
Pulse Counter Yes
|
|
RMT Yes
|
|
RNG Yes
|
|
RSA No
|
|
RTC Yes
|
|
SDIO No
|
|
SHA Yes
|
|
SPI Yes
|
|
SPIFLASH Yes
|
|
SPIRAM No
|
|
Temp. Sensor No
|
|
Timers Yes
|
|
UART Yes LPUART supported
|
|
USB Serial Yes
|
|
Watchdog Yes
|
|
Wi-Fi Yes
|
|
XTS No
|
|
============== ======= ====================
|
|
|
|
Analog-to-digital converter (ADC)
|
|
---------------------------------
|
|
|
|
One ADC unit is available for the ESP32-C6, with 7 channels.
|
|
|
|
During bringup, GPIOs for selected channels are configured automatically to be used as ADC inputs.
|
|
If available, ADC calibration is automatically applied (see
|
|
`this page <https://docs.espressif.com/projects/esp-idf/en/v5.1/esp32c6/api-reference/peripherals/adc_calibration.html>`__ for more details).
|
|
Otherwise, a simple conversion is applied based on the attenuation and resolution.
|
|
|
|
The ADC unit is accessible using the ADC character driver, which returns data for the enabled channels.
|
|
|
|
The ADC unit can be enabled in the menu :menuselection:`System Type --> Peripheral Support --> Analog-to-digital converter (ADC)`.
|
|
|
|
Then, it can be customized in the menu :menuselection:`System Type --> ADC Configuration`, which includes operating mode, gain and channels.
|
|
|
|
========== ===========
|
|
Channel ADC1 GPIO
|
|
========== ===========
|
|
0 0
|
|
1 1
|
|
2 2
|
|
3 3
|
|
4 4
|
|
5 5
|
|
6 6
|
|
========== ===========
|
|
|
|
.. _MCUBoot and OTA Update C6:
|
|
|
|
MCUBoot and OTA Update
|
|
======================
|
|
|
|
The ESP32-C6 supports over-the-air (OTA) updates using MCUBoot.
|
|
|
|
Read more about the MCUBoot for Espressif devices `here <https://docs.mcuboot.com/readme-espressif.html>`__.
|
|
|
|
Executing OTA Update
|
|
--------------------
|
|
|
|
This section describes how to execute OTA update using MCUBoot.
|
|
|
|
1. First build the default ``mcuboot_update_agent`` config. This image defaults to the primary slot and already comes with Wi-Fi settings enabled::
|
|
|
|
./tools/configure.sh esp32c6-devkitc:mcuboot_update_agent
|
|
|
|
2. Build the MCUBoot bootloader::
|
|
|
|
make bootloader
|
|
|
|
3. Finally, build the application image::
|
|
|
|
make
|
|
|
|
Flash the image to the board and verify it boots ok.
|
|
It should show the message "This is MCUBoot Update Agent image" before NuttShell is ready.
|
|
|
|
At this point, the board should be able to connect to Wi-Fi so we can download a new binary from our network::
|
|
|
|
NuttShell (NSH) NuttX-12.4.0
|
|
This is MCUBoot Update Agent image
|
|
nsh>
|
|
nsh> wapi psk wlan0 <wifi_ssid> 3
|
|
nsh> wapi essid wlan0 <wifi_password> 1
|
|
nsh> renew wlan0
|
|
|
|
Now, keep the board as is and execute the following commands to **change the MCUBoot target slot to the 2nd slot**
|
|
and modify the message of the day (MOTD) as a mean to verify the new image is being used.
|
|
|
|
1. Change the MCUBoot target slot to the 2nd slot::
|
|
|
|
kconfig-tweak -d CONFIG_ESPRESSIF_ESPTOOL_TARGET_PRIMARY
|
|
kconfig-tweak -e CONFIG_ESPRESSIF_ESPTOOL_TARGET_SECONDARY
|
|
kconfig-tweak --set-str CONFIG_NSH_MOTD_STRING "This is MCUBoot UPDATED image!"
|
|
make olddefconfig
|
|
|
|
.. note::
|
|
The same changes can be accomplished through ``menuconfig`` in :menuselection:`System Type --> Bootloader and Image Configuration --> Target slot for image flashing`
|
|
for MCUBoot target slot and in :menuselection:`System Type --> Bootloader and Image Configuration --> Search (motd) --> NSH Library --> Message of the Day` for the MOTD.
|
|
|
|
2. Rebuild the application image::
|
|
|
|
make
|
|
|
|
At this point the board is already connected to Wi-Fi and has the primary image flashed.
|
|
The new image configured for the 2nd slot is ready to be downloaded.
|
|
|
|
To execute OTA, create a simple HTTP server on the NuttX directory so we can access the binary remotely::
|
|
|
|
cd nuttxspace/nuttx
|
|
python3 -m http.server
|
|
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
|
|
|
|
On the board, execute the update agent, setting the IP address to the one on the host machine. Wait until image is transferred and the board should reboot automatically::
|
|
|
|
nsh> mcuboot_agent http://10.42.0.1:8000/nuttx.bin
|
|
MCUboot Update Agent example
|
|
Downloading from http://10.42.0.1:8000/nuttx.bin
|
|
Firmware Update size: 1048576 bytes
|
|
Received: 512 of 1048576 bytes [0%]
|
|
Received: 1024 of 1048576 bytes [0%]
|
|
Received: 1536 of 1048576 bytes [0%]
|
|
[.....]
|
|
Received: 1048576 of 1048576 bytes [100%]
|
|
Application Image successfully downloaded!
|
|
Requested update for next boot. Restarting...
|
|
|
|
NuttShell should now show the new MOTD, meaning the new image is being used::
|
|
|
|
NuttShell (NSH) NuttX-12.4.0
|
|
This is MCUBoot UPDATED image!
|
|
nsh>
|
|
|
|
Finally, the image is loaded but not confirmed.
|
|
To make sure it won't rollback to the previous image, you must confirm with ``mcuboot_confirm`` and reboot the board.
|
|
The OTA is now complete.
|
|
|
|
Flash Encryption
|
|
----------------
|
|
|
|
Flash encryption is intended for encrypting the contents of the ESP32-C6's off-chip flash memory. Once this feature is enabled,
|
|
firmware is flashed as plaintext, and then the data is encrypted in place on the first boot. As a result, physical readout
|
|
of flash will not be sufficient to recover most flash contents.
|
|
|
|
The current state of flash encryption for ESP32-C6 allows the use of Virtual E-Fuses and development mode, which permit users to evaluate and test the firmware before making definitive changes such as burning E-Fuses.
|
|
|
|
Flash encryption supports the following features:
|
|
|
|
.. list-table::
|
|
:header-rows: 1
|
|
|
|
* - Feature
|
|
- Description
|
|
* - **Flash Encryption with Virtual E-Fuses**
|
|
- Use flash encryption without burning E-Fuses. Default selection when flash encryption is enabled.
|
|
* - **Flash Encryption in Development mode**
|
|
- Allows reflashing an encrypted device by appending the ``--encrypt`` argument to the ``esptool.py write_flash`` command. This is done automatically if ``ESPRESSIF_SECURE_FLASH_ENC_FLASH_DEVICE_ENCRYPTED`` is set.
|
|
* - **Flash Encryption in Release mode**
|
|
- Does not allow reflashing the device. This is a permanent setting.
|
|
* - **Flash Encryption key**
|
|
- A user-generated key is required by default. Alternatively, a device-generated key is possible, but it will not be recoverable by the user (not recommended). See ``ESPRESSIF_SECURE_FLASH_ENC_USE_HOST_KEY``.
|
|
* - **Encrypted MTD Partition**
|
|
- If SPI Flash is enabled, an empty user MTD partition will be automatically encrypted on first flash.
|
|
|
|
.. note::
|
|
|
|
It is **strongly suggested** to read the following before working on flash encryption:
|
|
|
|
- `MCUBoot Flash Encryption <https://docs.mcuboot.com/readme-espressif.html#flash-encryption>`_
|
|
- `General E-Fuse documentation <https://docs.espressif.com/projects/esp-idf/en/latest/esp32c6/api-reference/system/efuse.html>`_
|
|
- `Flash Encryption Relevant E-Fuses <https://docs.espressif.com/projects/esp-idf/en/latest/esp32c6/security/flash-encryption.html#relevant-efuses>`_
|
|
|
|
Flash Encryption Requirements
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Flash encryption requires burning E-Fuses to enable it on chip. This is not a reversible operation and should be done with caution.
|
|
There is, however, a way to test the flash encryption by simulating them on flash. Both paths are described below.
|
|
|
|
Build System Features
|
|
'''''''''''''''''''''
|
|
|
|
The build system contains some safeguards to avoid accidentally burning E-Fuses and automations for convenience. Those are summarized below:
|
|
|
|
1. A yellow warning will show up during build alerting that flash encryption is enabled (same for Virtual E-Fuses).
|
|
2. If ``ESPRESSIF_SECURE_FLASH_ENC_USE_HOST_KEY`` is set, build will fail if the flash encryption key is not found.
|
|
3. If SPI Flash is enabled, the user MTD partition is automatically encrypted with the provided encryption key.
|
|
4. ``make flash`` command will prompt the user for confirmation before burning the E-Fuse, if Virtual E-Fuses are disabled.
|
|
|
|
|
|
Simulating Flash Encryption with Virtual E-Fuses
|
|
'''''''''''''''''''''''''''''''''''''''''''''''''
|
|
|
|
It is highly recommended to use this method for testing the flash encryption before actually burning the E-Fuses.
|
|
The E-Fuses are stored in flash and persist between reboots. No real E-Fuses are changed.
|
|
|
|
To enable virtual E-Fuses for flash encryption testing, open ``menuconfig`` and:
|
|
1. Enable flash encryption on boot on: :menuselection:`System Type --> Bootloader and Image Configuration`
|
|
2. Verify Virtual E-Fuses are enabled (this is done by default): :menuselection:`System Type --> Peripheral Support --> E-Fuse support`
|
|
|
|
Now build the bootloader and the firmware. Flashing the device will trigger the following:
|
|
1. On the first boot, the bootloader will encrypt the flash::
|
|
|
|
...
|
|
[esp32c6] [WRN] eFuse virtual mode is enabled. If Secure boot or Flash encryption is enabled then it does not provide any security. FOR TESTING ONLY!
|
|
[esp32c6] [WRN] [efuse] [Virtual] try loading efuses from flash: 0x10000 (offset)
|
|
...
|
|
[esp32c6] [INF] [flash_encrypt] Encrypting bootloader...
|
|
[esp32c6] [INF] [flash_encrypt] Bootloader encrypted successfully
|
|
[esp32c6] [INF] [flash_encrypt] Encrypting primary slot...
|
|
[esp32c6] [INF] [flash_encrypt] Encrypting remaining flash...
|
|
[esp32c6] [INF] [flash_encrypt] Flash encryption completed
|
|
...
|
|
[esp32c6] [INF] Resetting with flash encryption enabled...
|
|
|
|
2. Device will reset and it should be now operating similar to an actual encrypted device::
|
|
|
|
...
|
|
[esp32c6] [INF] Checking flash encryption...
|
|
[esp32c6] [INF] [flash_encrypt] flash encryption is enabled (1 plaintext flashes left)
|
|
[esp32c6] [INF] Disabling RNG early entropy source...
|
|
[esp32c6] [INF] br_image_off = 0x20000
|
|
[esp32c6] [INF] ih_hdr_size = 0x20
|
|
[esp32c6] [INF] Loading image 0 - slot 0 from flash, area id: 1
|
|
...
|
|
NuttShell (NSH) NuttX-12.8.0
|
|
nsh>
|
|
|
|
Actual encryption and burning E-Fuses
|
|
'''''''''''''''''''''''''''''''''''''
|
|
|
|
E-Fuses are burned by esptool and the bootloader on the first boot after flashing with encryption enabled.
|
|
This process is automated on NuttX build system.
|
|
|
|
.. warning:: Burning E-Fuses is NOT a reversible operation and should be done with caution.
|
|
|
|
To build a firmware with E-Fuse support and flash encryption enabled, open ``menuconfig`` and:
|
|
1. Enable flash encryption on boot on: :menuselection:`System Type --> Bootloader and Image Configuration`
|
|
2. Disable Virtual E-Fuses :menuselection:`System Type --> Peripheral Support --> E-Fuse support`
|
|
3. Check usage mode is Development (this allows reflashing, while Release mode does not).
|
|
|
|
.. note:: If using development mode of flash encryption (see menuconfig and documentation above), it is still possible to re-flash the device with esptool by
|
|
setting ``ESPRESSIF_SECURE_FLASH_ENC_FLASH_DEVICE_ENCRYPTED`` which adds ``--encrypt`` argument to the ``esptool.py write_flash`` command.
|
|
This will apply the burned encryption key to the image while flashing.
|
|
|
|
Flash Allocation for MCUBoot
|
|
----------------------------
|
|
|
|
When MCUBoot is enabled on ESP32-C6, the flash memory is organized as follows
|
|
based on the default KConfig values:
|
|
|
|
**Flash Layout (MCUBoot Enabled)**
|
|
|
|
.. list-table::
|
|
:header-rows: 1
|
|
:widths: 40 20 20
|
|
:align: left
|
|
|
|
* - Region
|
|
- Offset
|
|
- Size
|
|
* - Bootloader
|
|
- 0x000000
|
|
- 64KB
|
|
* - E-Fuse Virtual (see Note)
|
|
- 0x010000
|
|
- 64KB
|
|
* - Primary Application Slot (/dev/ota0)
|
|
- 0x020000
|
|
- 1.4MB
|
|
* - Secondary Application Slot (/dev/ota1)
|
|
- 0x170000
|
|
- 1.4MB
|
|
* - Scratch Partition (/dev/otascratch)
|
|
- 0x2C0000
|
|
- 256KB
|
|
* - Storage MTD (optional)
|
|
- 0x300000
|
|
- 1MB
|
|
* - Available Flash
|
|
- 0x400000+
|
|
- Remaining
|
|
|
|
.. raw:: html
|
|
|
|
<div style="clear: both"></div>
|
|
|
|
|
|
**Note**: The E-Fuse Virtual region is optional and only used when
|
|
``ESPRESSIF_EFUSE_VIRTUAL_KEEP_IN_FLASH`` is enabled. However, this 64KB
|
|
location is always allocated in the memory layout to prevent accidental
|
|
erasure during board flashing operations, ensuring data preservation if
|
|
virtual E-Fuses are later enabled.
|
|
|
|
.. code-block:: text
|
|
|
|
Memory Map (Addresses in hex):
|
|
|
|
0x000000 ┌─────────────────────────────┐
|
|
│ │
|
|
│ MCUBoot Bootloader │
|
|
│ (64KB) │
|
|
│ │
|
|
0x010000 ├─────────────────────────────┤
|
|
│ E-Fuse Virtual │
|
|
│ (64KB) │
|
|
0x020000 ├─────────────────────────────┤
|
|
│ │
|
|
│ Primary App Slot │
|
|
│ (1.4MB) │
|
|
│ /dev/ota0 │
|
|
│ │
|
|
0x170000 ├─────────────────────────────┤
|
|
│ │
|
|
│ Secondary App Slot │
|
|
│ (1.4MB) │
|
|
│ /dev/ota1 │
|
|
│ │
|
|
0x2C0000 ├─────────────────────────────┤
|
|
│ │
|
|
│ Scratch Partition │
|
|
│ (256KB) │
|
|
│ /dev/otascratch │
|
|
│ │
|
|
0x300000 ├─────────────────────────────┤
|
|
│ │
|
|
│ Storage MTD (optional) │
|
|
│ (1MB) │
|
|
│ │
|
|
0x400000 ├─────────────────────────────┤
|
|
│ │
|
|
│ Available Flash │
|
|
│ (Remaining) │
|
|
│ │
|
|
└─────────────────────────────┘
|
|
|
|
The key KConfig options that control this layout:
|
|
|
|
- ``ESPRESSIF_OTA_PRIMARY_SLOT_OFFSET`` (default: 0x20000)
|
|
- ``ESPRESSIF_OTA_SECONDARY_SLOT_OFFSET`` (default: 0x170000)
|
|
- ``ESPRESSIF_OTA_SLOT_SIZE`` (default: 0x150000)
|
|
- ``ESPRESSIF_OTA_SCRATCH_OFFSET`` (default: 0x2C0000)
|
|
- ``ESPRESSIF_OTA_SCRATCH_SIZE`` (default: 0x40000)
|
|
- ``ESPRESSIF_STORAGE_MTD_OFFSET`` (default: 0x300000 when MCUBoot enabled)
|
|
- ``ESPRESSIF_STORAGE_MTD_SIZE`` (default: 0x100000)
|
|
|
|
For MCUBoot operation:
|
|
|
|
- The **Primary Slot** contains the currently running application
|
|
- The **Secondary Slot** receives OTA updates
|
|
- The **Scratch Partition** is used by MCUBoot for image swapping during updates
|
|
- MCUBoot manages image validation, confirmation, and rollback functionality
|
|
|
|
.. _esp32c6_ulp:
|
|
|
|
ULP LP Core Coprocessor
|
|
=======================
|
|
|
|
The ULP LP core (Low-power core) is a 32-bit RISC-V coprocessor integrated into the ESP32-C6 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 <https://docs.espressif.com/projects/esp-idf/en/stable/esp32c6/api-reference/system/ulp-lp-core.html>`__.
|
|
|
|
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 20 MHz
|
|
* Memory
|
|
- Access to 16 KB of low-power memory (LP-RAM) and LP-domain peripherals any time
|
|
- Full access to all of the chip's memory and peripherals when 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 and LP Timer)
|
|
- 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 :ref:`ulp example Makefile. <ulp_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:
|
|
|
|
.. code-block:: console
|
|
|
|
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 esp32c6-devkitc: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:
|
|
|
|
.. code-block:: C
|
|
|
|
#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 <https://github.com/espressif/esp-idf/tree/master/examples/system/ulp/lp_core>`__.
|
|
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 <https://nuttx.apache.org/docs/latest/guides/customapps.html#custom-apps-how-to>`__ guide,
|
|
this example will demonstrate how to add ULP code into a custom application:
|
|
|
|
- Tree view:
|
|
|
|
.. code-block:: text
|
|
|
|
nuttxspace/
|
|
├── nuttx/
|
|
└── apps/
|
|
└── ulp_example/
|
|
└── Makefile
|
|
└── Kconfig
|
|
└── ulp_example.c
|
|
└── ulp/
|
|
└── Makefile
|
|
└── ulp_main.c
|
|
|
|
|
|
- Contents in Makefile:
|
|
|
|
.. code-block:: console
|
|
|
|
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:
|
|
|
|
.. code-block:: console
|
|
|
|
config EXAMPLES_ULP_EXAMPLE
|
|
bool "ULP Example"
|
|
default n
|
|
|
|
- Contents in ulp_example.c:
|
|
|
|
.. code-block:: 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;
|
|
}
|
|
|
|
.. _ulp_makefile:
|
|
|
|
- Contents in ulp/Makefile:
|
|
|
|
.. code-block:: console
|
|
|
|
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:
|
|
|
|
.. code-block:: 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 esp32c6-devkitc: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:
|
|
|
|
.. code-block:: C
|
|
|
|
#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;
|
|
}
|
|
|
|
ULP LP-Core Wakeup Configuration
|
|
--------------------------------
|
|
|
|
By default, ULP LP-Core is woken up by HP core but other wakeup sources can be selected.
|
|
|
|
The available wakeup sources are:
|
|
|
|
* ``CONFIG_ESPRESSIF_ULP_WAKEUP_HP_CPU``: Wakeup by HP core
|
|
* ``CONFIG_ESPRESSIF_ULP_WAKEUP_LP_TIMER``: Wakeup by LP timer
|
|
* ``CONFIG_ESPRESSIF_ULP_WAKEUP_LP_UART``: Wakeup by LP UART activity
|
|
* ``CONFIG_ESPRESSIF_ULP_WAKEUP_LP_IO``: Wakeup by LP IO
|
|
|
|
Debugging ULP LP-Core
|
|
---------------------
|
|
|
|
To debug ULP LP-Core please first refer to :ref:`Debugging section. <esp32c6_debug>`
|
|
Debugging ULP core consist same steps with some small differences. First of all, configuration file
|
|
needs to be changed from ``board/esp32c6-builtin.cfg`` or ``board/esp32c6-ftdi.cfg`` to
|
|
``board/esp32c6-lpcore-builtin.cfg`` or ``board/esp32c6-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.
|
|
|
|
_`Managing esptool on virtual environment`
|
|
==========================================
|
|
|
|
This section describes how to install ``esptool``, ``imgtool`` or any other Python packages in a
|
|
proper environment.
|
|
|
|
Normally, a Linux-based OS would already have Python 3 installed by default. Up to a few years ago,
|
|
you could simply call ``pip install`` to install packages globally. However, this is no longer recommended
|
|
as it can lead to conflicts between packages and versions. The recommended way to install Python packages
|
|
is to use a virtual environment.
|
|
|
|
A virtual environment is a self-contained directory that contains a Python installation for a particular
|
|
version of Python, plus a number of additional packages. You can create a virtual environment for each
|
|
project you are working on, and install the required packages in that environment.
|
|
|
|
Two alternatives are explained below, you can select any one of those.
|
|
|
|
Using pipx (recommended)
|
|
------------------------
|
|
|
|
``pipx`` is a tool that makes it easy to install Python packages in a virtual environment. To install
|
|
``pipx``, you can run the following command (using apt as example)::
|
|
|
|
$ apt install pipx
|
|
|
|
Once you have installed ``pipx``, you can use it to install Python packages in a virtual environment. For
|
|
example, to install the ``esptool`` package, you can run the following command::
|
|
|
|
$ pipx install esptool
|
|
|
|
This will create a new virtual environment in the ``~/.local/pipx/venvs`` directory, which contains the
|
|
``esptool`` package. You can now use the ``esptool`` command as normal, and so will the build system.
|
|
|
|
Make sure to run ``pipx ensurepath`` to add the ``~/.local/bin`` directory to your ``PATH``. This will
|
|
allow you to run the ``esptool`` command from any directory.
|
|
|
|
Using venv (alternative)
|
|
------------------------
|
|
To create a virtual environment, you can use the ``venv`` module, which is included in the Python standard
|
|
library. To create a virtual environment, you can run the following command::
|
|
|
|
$ python3 -m venv myenv
|
|
|
|
This will create a new directory called ``myenv`` in the current directory, which contains a Python
|
|
installation and a copy of the Python standard library. To activate the virtual environment, you can run
|
|
the following command::
|
|
|
|
$ source myenv/bin/activate
|
|
|
|
This will change your shell prompt to indicate that you are now working in the virtual environment. You can
|
|
now install packages using ``pip``. For example, to install the ``esptool`` package, you can run the following
|
|
command::
|
|
|
|
$ pip install esptool
|
|
|
|
This will install the ``esptool`` package in the virtual environment. You can now use the ``esptool`` command as
|
|
normal. When you are finished working in the virtual environment, you can deactivate it by running the following
|
|
command::
|
|
|
|
$ deactivate
|
|
|
|
This will return your shell prompt to its normal state. You can reactivate the virtual environment at any time by
|
|
running the ``source myenv/bin/activate`` command again. You can also delete the virtual environment by deleting
|
|
the directory that contains it.
|
|
|
|
Supported Boards
|
|
================
|
|
|
|
.. toctree::
|
|
:glob:
|
|
:maxdepth: 1
|
|
|
|
boards/*/*
|