mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
Merge branch 'refactor/enable_fsm_and_riscv_ulp_simultaneously' into 'master'
refactor(ulp): Allow both ULP-FSM and ULP-RISCV to enable at build time Closes IDFGH-11916 See merge request espressif/esp-idf!45751
This commit is contained in:
@@ -18,4 +18,4 @@ set(ulp_exp_dep_srcs "ulp_adc_example_main.c")
|
||||
#
|
||||
# 4. Call function to build ULP binary and embed in project using the argument
|
||||
# values above.
|
||||
ulp_embed_binary(${ulp_app_name} "${ulp_s_sources}" "${ulp_exp_dep_srcs}")
|
||||
ulp_embed_binary(${ulp_app_name} "${ulp_s_sources}" "${ulp_exp_dep_srcs}" TYPE fsm)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
examples/system/ulp/ulp_fsm_riscv_combined/counter:
|
||||
enable:
|
||||
- if: SOC_ULP_FSM_SUPPORTED == 1 and SOC_RISCV_COPROC_SUPPORTED == 1 and IDF_TARGET in ["esp32s2", "esp32s3"]
|
||||
temporary: false
|
||||
reason: Only ESP32-S2 and ESP32-S3 support both FSM and RISC-V ULP coprocessors
|
||||
depends_components:
|
||||
- ulp
|
||||
@@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(ulp_fsm_riscv_combined)
|
||||
@@ -0,0 +1,119 @@
|
||||
| Supported Targets | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | -------- | -------- |
|
||||
|
||||
# ULP FSM and RISC-V Combined Example
|
||||
|
||||
This example demonstrates how to use both ULP FSM and ULP RISC-V coprocessors sequentially in the same application.
|
||||
|
||||
## Overview
|
||||
|
||||
The example implements a counter that is incremented in three stages:
|
||||
|
||||
1. **ULP FSM Stage**: The FSM coprocessor increments the counter from 0 to 100
|
||||
2. **ULP RISC-V Stage**: The RISC-V coprocessor continues incrementing from 100 to 500
|
||||
3. **Main CPU Stage**: The main processor completes the count from 500 to 1500
|
||||
|
||||
This demonstrates:
|
||||
- Enabling both `CONFIG_ULP_COPROC_TYPE_FSM` and `CONFIG_ULP_COPROC_TYPE_RISCV` for sequential use
|
||||
- Using the `TYPE` parameter in `ulp_embed_binary()` to specify which toolchain to use for each ULP program
|
||||
- Coordinating execution between FSM ULP, RISC-V ULP, and the main CPU
|
||||
- Sharing data between different coprocessors and the main CPU
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* ESP32-S2 or ESP32-S3 development board
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT build flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
ULP FSM and RISC-V Combined Example
|
||||
====================================
|
||||
|
||||
Step 1: Starting ULP FSM to count from 0 to 100...
|
||||
HP core going to sleep, will be woken by ULP FSM when counting completes...
|
||||
HP core woken up by: ULP
|
||||
ULP FSM completed. Counter value: 100
|
||||
|
||||
Step 2: Starting ULP RISC-V to count from 100 to 500...
|
||||
HP core going to sleep, will be woken by ULP RISC-V when counting completes...
|
||||
HP core woken up by: ULP
|
||||
ULP RISC-V completed. Counter value: 500
|
||||
|
||||
Step 3: Main CPU counting from 500 to 1500...
|
||||
Main CPU completed. Final counter value: 1500
|
||||
|
||||
====================================
|
||||
All stages completed successfully!
|
||||
FSM: 0 -> 100
|
||||
RISC-V: 100 -> 500
|
||||
Main CPU: 500 -> 1500
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
* If you see an error about missing ULP type when building, ensure both `CONFIG_ULP_COPROC_TYPE_FSM` and `CONFIG_ULP_COPROC_TYPE_RISCV` are enabled in menuconfig.
|
||||
|
||||
* The `TYPE` parameter in `ulp_embed_binary()` is mandatory when both ULP types are enabled. Check `main/CMakeLists.txt` to see the correct syntax:
|
||||
```cmake
|
||||
ulp_embed_binary(ulp_fsm_main "${ulp_fsm_sources}" "" TYPE fsm)
|
||||
ulp_embed_binary(ulp_riscv_main "${ulp_riscv_sources}" "" TYPE riscv)
|
||||
```
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### CMakeLists.txt
|
||||
|
||||
The main component's CMakeLists.txt shows how to embed both FSM and RISC-V ULP binaries:
|
||||
|
||||
```cmake
|
||||
# ULP FSM
|
||||
set(ulp_fsm_app_name ulp_fsm_main)
|
||||
set(ulp_fsm_sources "ulp_fsm/main.S")
|
||||
ulp_embed_binary(${ulp_fsm_app_name} "${ulp_fsm_sources}" "" TYPE fsm)
|
||||
|
||||
# ULP RISC-V
|
||||
set(ulp_riscv_app_name ulp_riscv_main)
|
||||
set(ulp_riscv_sources "ulp_riscv/main.c")
|
||||
ulp_embed_binary(${ulp_riscv_app_name} "${ulp_riscv_sources}" "" TYPE riscv)
|
||||
```
|
||||
|
||||
The `TYPE` parameter is crucial - it tells the build system which toolchain to use for each ULP program.
|
||||
|
||||
### ULP FSM Program
|
||||
|
||||
The FSM program (`ulp_fsm/main.S`) is written in assembly and:
|
||||
- Maintains a counter in RTC slow memory
|
||||
- Increments it on each wakeup
|
||||
- Halts when reaching 100
|
||||
|
||||
### ULP RISC-V Program
|
||||
|
||||
The RISC-V program (`ulp_riscv/main.c`) is written in C and:
|
||||
- Continues from where FSM left off
|
||||
- Increments the counter on each wakeup
|
||||
- Halts when reaching 500
|
||||
|
||||
### Main Application
|
||||
|
||||
The main application coordinates the three stages:
|
||||
1. Loads and starts the FSM ULP program
|
||||
2. Waits for FSM to complete
|
||||
3. Transfers the counter value and starts the RISC-V ULP program
|
||||
4. Waits for RISC-V to complete
|
||||
5. Completes the final counting stage on the main CPU
|
||||
@@ -0,0 +1,20 @@
|
||||
idf_component_register(SRCS "ulp_fsm_riscv_combined_main.c"
|
||||
INCLUDE_DIRS "")
|
||||
|
||||
#
|
||||
# ULP FSM support additions to component CMakeLists.txt.
|
||||
#
|
||||
|
||||
set(ulp_fsm_app_name ulp_fsm_main)
|
||||
set(ulp_fsm_sources "ulp_fsm/main.S")
|
||||
|
||||
ulp_embed_binary(${ulp_fsm_app_name} "${ulp_fsm_sources}" "" TYPE fsm)
|
||||
|
||||
#
|
||||
# ULP RISC-V support additions to component CMakeLists.txt.
|
||||
#
|
||||
|
||||
set(ulp_riscv_app_name ulp_riscv_main)
|
||||
set(ulp_riscv_sources "ulp_riscv/main.c")
|
||||
|
||||
ulp_embed_binary(${ulp_riscv_app_name} "${ulp_riscv_sources}" "" TYPE riscv)
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
/* ULP FSM assembly program to increment counter */
|
||||
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/soc_ulp.h"
|
||||
|
||||
/* Define variables */
|
||||
.bss
|
||||
.global fsm_counter
|
||||
fsm_counter:
|
||||
.long 0
|
||||
|
||||
/* Code section */
|
||||
.text
|
||||
.global entry
|
||||
entry:
|
||||
/* Load counter address */
|
||||
move r1, fsm_counter
|
||||
|
||||
/* Load current counter value */
|
||||
ld r0, r1, 0
|
||||
|
||||
/* Check if counter >= 100 */
|
||||
jumpr done, 100, ge
|
||||
|
||||
/* Increment counter */
|
||||
add r0, r0, 1
|
||||
|
||||
/* Store incremented value */
|
||||
st r0, r1, 0
|
||||
|
||||
/* Halt until next wakeup */
|
||||
halt
|
||||
|
||||
done:
|
||||
/* Counter reached 100, wake up the main processor */
|
||||
wake
|
||||
/* Halt - task complete */
|
||||
halt
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "esp_sleep.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/sens_reg.h"
|
||||
#include "soc/rtc_periph.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/rtc_io.h"
|
||||
#include "ulp.h"
|
||||
#include "ulp_riscv.h"
|
||||
#include "ulp_fsm_main.h"
|
||||
#include "ulp_riscv_main.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
extern const uint8_t ulp_fsm_main_bin_start[] asm("_binary_ulp_fsm_main_bin_start");
|
||||
extern const uint8_t ulp_fsm_main_bin_end[] asm("_binary_ulp_fsm_main_bin_end");
|
||||
|
||||
extern const uint8_t ulp_riscv_main_bin_start[] asm("_binary_ulp_riscv_main_bin_start");
|
||||
extern const uint8_t ulp_riscv_main_bin_end[] asm("_binary_ulp_riscv_main_bin_end");
|
||||
|
||||
static void init_ulp_fsm_program(void);
|
||||
static void init_ulp_riscv_program(void);
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/* Initialize shared counter */
|
||||
ulp_fsm_counter = 0;
|
||||
ulp_riscv_counter = 0;
|
||||
|
||||
printf("ULP FSM and RISC-V Combined Example\n");
|
||||
printf("====================================\n\n");
|
||||
|
||||
/* Enable ULP wakeup */
|
||||
ESP_ERROR_CHECK(esp_sleep_enable_ulp_wakeup());
|
||||
|
||||
/* Step 1: Start ULP FSM to count from 0 to 100 */
|
||||
printf("Step 1: Starting ULP FSM to count from 0 to 100...\n");
|
||||
printf("HP core going to sleep, will be woken by ULP FSM when counting completes...\n");
|
||||
init_ulp_fsm_program();
|
||||
|
||||
/* Enter light sleep - ULP FSM will wake us when done */
|
||||
esp_light_sleep_start();
|
||||
|
||||
/* Woken up by ULP FSM */
|
||||
uint32_t wakeup_causes = esp_sleep_get_wakeup_causes();
|
||||
printf("HP core woken up by: %s\n", (wakeup_causes & (1U << ESP_SLEEP_WAKEUP_ULP)) ? "ULP" : "other");
|
||||
|
||||
/* Stop FSM ULP */
|
||||
ulp_timer_stop();
|
||||
uint32_t fsm_final_count = ulp_fsm_counter; // Save FSM result before RISC-V overwrites it
|
||||
printf("ULP FSM completed. Counter value: %" PRIu32 "\n\n", fsm_final_count);
|
||||
|
||||
/* Small delay to ensure FSM is fully stopped before starting RISC-V */
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
/* Step 2: Start ULP RISC-V to count from 100 to 500 */
|
||||
printf("Step 2: Starting ULP RISC-V to count from 100 to 500...\n");
|
||||
printf("HP core going to sleep, will be woken by ULP RISC-V when counting completes...\n");
|
||||
init_ulp_riscv_program();
|
||||
|
||||
/* Transfer counter value to RISC-V ULP (after loading binary, otherwise it will be overwritten) */
|
||||
ulp_riscv_counter = fsm_final_count;
|
||||
|
||||
/* Enter light sleep - ULP RISC-V will wake us when done */
|
||||
esp_light_sleep_start();
|
||||
|
||||
/* Woken up by ULP RISC-V */
|
||||
wakeup_causes = esp_sleep_get_wakeup_causes();
|
||||
printf("HP core woken up by: %s\n", (wakeup_causes & (1U << ESP_SLEEP_WAKEUP_ULP)) ? "ULP" : "other");
|
||||
|
||||
/* Stop RISC-V ULP */
|
||||
ulp_riscv_timer_stop();
|
||||
uint32_t riscv_final_count = ulp_riscv_counter; // Save RISC-V result
|
||||
printf("ULP RISC-V completed. Counter value: %" PRIu32 "\n\n", riscv_final_count);
|
||||
|
||||
/* Stage 3: Main CPU counts from 500 to 1500 */
|
||||
printf("Step 3: Main CPU counting from 500 to 1500...\n");
|
||||
uint32_t counter = riscv_final_count;
|
||||
while (counter < 1500) {
|
||||
counter++;
|
||||
}
|
||||
printf("Main CPU completed. Final counter value: %" PRIu32 "\n\n", counter);
|
||||
|
||||
printf("====================================\n");
|
||||
printf("All stages completed successfully!\n");
|
||||
printf("FSM: 0 -> %" PRIu32 "\n", fsm_final_count);
|
||||
printf("RISC-V: %" PRIu32 " -> %" PRIu32 "\n", fsm_final_count, riscv_final_count);
|
||||
printf("Main CPU: %" PRIu32 " -> %" PRIu32 "\n", riscv_final_count, counter);
|
||||
}
|
||||
|
||||
static void init_ulp_fsm_program(void)
|
||||
{
|
||||
esp_err_t err = ulp_load_binary(0, ulp_fsm_main_bin_start,
|
||||
(ulp_fsm_main_bin_end - ulp_fsm_main_bin_start) / sizeof(uint32_t));
|
||||
ESP_ERROR_CHECK(err);
|
||||
|
||||
/* Set ULP wake up period to 10ms */
|
||||
ulp_set_wakeup_period(0, 10000);
|
||||
|
||||
/* Start the ULP FSM program */
|
||||
err = ulp_run(&ulp_entry - RTC_SLOW_MEM);
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
||||
|
||||
static void init_ulp_riscv_program(void)
|
||||
{
|
||||
esp_err_t err = ulp_riscv_load_binary(ulp_riscv_main_bin_start,
|
||||
(ulp_riscv_main_bin_end - ulp_riscv_main_bin_start));
|
||||
ESP_ERROR_CHECK(err);
|
||||
|
||||
/* Set ULP wake up period to 1ms */
|
||||
ulp_set_wakeup_period(0, 1000);
|
||||
|
||||
/* Start the ULP RISC-V program */
|
||||
err = ulp_riscv_run();
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
/* ULP RISC-V program to increment counter */
|
||||
|
||||
#include <stdint.h>
|
||||
#include "ulp_riscv.h"
|
||||
#include "ulp_riscv_utils.h"
|
||||
|
||||
uint32_t riscv_counter = 0;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* Check if counting is complete */
|
||||
if (riscv_counter >= 500) {
|
||||
/* Wake up the main processor and halt */
|
||||
ulp_riscv_wakeup_main_processor();
|
||||
ulp_riscv_halt();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Increment counter once per wakeup */
|
||||
riscv_counter++;
|
||||
|
||||
/* Continue - will be woken up again by timer */
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
from pytest_embedded_idf.utils import idf_parametrize
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@idf_parametrize('target', ['esp32s2', 'esp32s3'], indirect=['target'])
|
||||
def test_ulp_fsm_riscv_combined(dut: Dut) -> None:
|
||||
dut.expect('ULP FSM and RISC-V Combined Example', timeout=10)
|
||||
|
||||
# Check Step 1: FSM counting
|
||||
dut.expect('Step 1: Starting ULP FSM to count from 0 to 100', timeout=5)
|
||||
dut.expect('HP core going to sleep, will be woken by ULP FSM when counting completes', timeout=5)
|
||||
dut.expect('HP core woken up by: ULP', timeout=5)
|
||||
dut.expect('ULP FSM completed. Counter value: 100', timeout=5)
|
||||
|
||||
# Check Step 2: RISC-V counting
|
||||
dut.expect('Step 2: Starting ULP RISC-V to count from 100 to 500', timeout=5)
|
||||
dut.expect('HP core going to sleep, will be woken by ULP RISC-V when counting completes', timeout=5)
|
||||
dut.expect('HP core woken up by: ULP', timeout=5)
|
||||
dut.expect('ULP RISC-V completed. Counter value: 500', timeout=5)
|
||||
|
||||
# Check Step 3: Main CPU counting
|
||||
dut.expect('Step 3: Main CPU counting from 500 to 1500', timeout=5)
|
||||
dut.expect('Main CPU completed. Final counter value: 1500', timeout=5)
|
||||
# Verify success message
|
||||
dut.expect('All stages completed successfully!', timeout=5)
|
||||
dut.expect('FSM: 0 -> 100', timeout=5)
|
||||
dut.expect('RISC-V: 100 -> 500', timeout=5)
|
||||
dut.expect('Main CPU: 500 -> 1500', timeout=5)
|
||||
@@ -0,0 +1,11 @@
|
||||
# Enable both ULP types
|
||||
CONFIG_ULP_COPROC_ENABLED=y
|
||||
CONFIG_ULP_COPROC_TYPE_FSM=y
|
||||
CONFIG_ULP_COPROC_TYPE_RISCV=y
|
||||
CONFIG_ULP_COPROC_RESERVE_MEM=4096
|
||||
|
||||
# Set log level to Info to see output
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL=3
|
||||
CONFIG_LOG_DEFAULT_LEVEL_INFO=y
|
||||
CONFIG_LOG_DEFAULT_LEVEL=3
|
||||
Reference in New Issue
Block a user