Skip to content

esp32: Add the ability to disable ULP module using mpconfigport.h and ULP images built into custom builds #16469

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from

Conversation

purewack
Copy link

esp32: Add the ability to compile custom ULP image into the build by defining ulp_app_name, ulp_sources, and ulp_exp_dep_srcs

Summary

I think it should be configurable to disable the ULP module, so that experimentation with the RISCV ULP can be done without causing conflicts in the esp-idf build steps, since only one type of ULP can be enabled at a time (for those chips that do have 2 types)

Testing

I have tested this by writing my own test module which uses the RISCV ULP by firstly disabling the built in ULP module. I have done the test by creating a custom board definition for testing and defining ULP specific build steps in mpconfigboard.cmake as follows:

set(ulp_app_name ulp_test)
set(ulp_riscv_sources ${MICROPY_BOARD_DIR}/ulp/main.c)
set(ulp_exp_dep_srcs "${MICROPY_BOARD_DIR}/cmodules/modulptest.c ${MICROPY_BOARD_DIR}/board_init.c")

Add the ability to disable the built in ULP module
using mpconfigport.h
Add the ability to compile custom ULP image into
the build by defining the cmake variables:
    ulp_app_name
    ulp_sources
    ulp_exp_dep_srcs

Signed-off-by: Damian Nowacki (purewack) <bobimaster15@gmail.com>
@stephanelsmith
Copy link
Contributor

I'm SUPER interested in learning more about RISCV ULP. Do you have an example you'd be able to share (ulp/main.c, etc...) on how you were able to do it? I'm looking into low power ADC sampling of audio for my project (https://github.com/stephanelsmith/micro-aprs) and doubly interested in getting started on RISCV.

Appreciate you putting it on my radar, and hope this one gets merged!

@purewack
Copy link
Author

purewack commented Dec 30, 2024

I'm SUPER interested in learning more about RISCV ULP. Do you have an example you'd be able to share (ulp/main.c, etc...) on how you were able to do it? I'm looking into low power ADC sampling of audio for my project (https://github.com/stephanelsmith/micro-aprs) and doubly interested in getting started on RISCV.

Appreciate you putting it on my radar, and hope this one gets merged!

Sure! I will try and create an example in my fork later tonight or by tomorrow. I am using the RISCV ULP because it's simpler to understand the c code 🤣. There are similarities between the two types of ULP's but the ESP-IDF examples aren't very clear in my opinion. I had to cross reference multiple headers to find out how to use gpios in ULP c code for example.

Just to be clear, I have disabled the built in FSM module in micropython in favour of my own minimal module implementation, which acts sort of like a bridge between the micropython runtime and the user-defined variables exposed through the RTC ram. Essentially I had written a system monitor for battery voltage observation as well as a custom key input mechanism but that's beyond the scope of this PR

Edit:
@stephanelsmith
I may have been prematurely overoptimistic and in my blind faith I had misread the results when I was testing. It turns out (dont quote me on this) the current micropython builds for the esp32 use the legacy adc drivers, which does not play well with the ULP's expectations of being setup.
As a result, if it is not being done already, the esp32 port might benefit from re-writing the adc module to use the new adc drivers from esp-idf, resulting in a crash an the error: ADC: CONFLICT! driver_ng is not allowed to be used with the legacy driver.

I think I will investigate this more or perhaps see If I can adapt the adc module so that it works well with RISCV adc configuration, since the esp-idf adc-example and ulp-adc-example work well already.

As promised though, I have created an example in my own fork, where I modified the ESP32_GENERIC_S3 board to include an extra c module called ulp_adc but as mentioned above it suffers from the weird readings due to driver mismatch, although there is a big chance I've done something wrong :/

@stephanelsmith
Copy link
Contributor

I'm but a simple man, I get excited when something compiles! This is GREAT! Very much appreciated it, been thinking all day how cool it'd be to have a ulp enabled squelch. And thank you for the additional IDF examples as well. I'll definitely be digging in! In case someone hasn't already told it to you today, YOU ROCK! 👊

Possible override if you prefer to still use FSM ULP by specifying:
`set(PREFER_FSM_ULP ON)`

and appending:
`boards/sdkconfig.ulp_fsm`

to your `mpconfigboard.cmake` of your target board

No RTC pin configuration yet, RISCV can configure its own RTC pins.
FSM - I have not found a way of doing so yet without explicitly setting
each pin required using
```
rtc_gpio_init(pin);
rtc_gpio_set_direction(pin, RTC_GPIO_MODE_OUTPUT_ONLY);
rtc_gpio_pulldown_dis(pin);
rtc_gpio_pullup_dis(pin);
```
Unified interface for the ULP.
`ULP.run(buffer)` will load & run the binary. (`ULP.run(entry, buffer)` on esp32)

`ULP.pause()` and `ULP.resume()` functions added to start and stop the *wakeup* timer.
If your ULP code is in an infinite loop, it will not be stopped on FSM as
there is no ulp_halt() function.

`ULP.read(address)` and `ULP.write(address,value)`  will allow interacting with variables
if an embedded application is used, variable addresses will abtomatically be saved
and added to the ULP class, meaning you can do
`ulp.read(ulp.VAR_TEST)`
and
`ulp.write(ulp.VAR_TEST, 12345)`
addresses are relative from RTC_SLOW_MEM, but you can use the full address too.
Allow initialization of ADC and RTC pins using
`ULP.rtc_init(pin)`
`ULP.rtc_deinit(pin)`
`ULP.adc_init(unit,channel)`

During testing, it turns out that this needs to be done,
so that the FSM can utilize IO.
RISCV is already able to do this from its own code, however,
you can still use this if you do not configure RISCV pins explicitly in the ulp code.
esp32/adc: Updated the module to use new driver (esp_adc/adc_oneshot.h).
	Tested and confired working state on several pins using:
	- esp32
	- esp32 C3
	- esp32 S2
	- esp32 S3
	I am unable to test any more hardware as I do not own it yet.

esp32/ulp: Enable RISCV ULP by default on targets that support it
	(esp32 S2 & S3) with an option of reverting back to FSM in a custom build.
	To use FSM instead, add the line
	`set(PREFER_FSM_ULP ON)` in your board's mpconfigboard.cmake
	and add
	`boards/sdkconfig.ulp_fsm` to the end of SDKCONFIG_DEFAULTS like so:
	```
	set(SDKCONFIG_DEFAULTS
	    boards/sdkconfig.base
	    boards/sdkconfig.usb
	    boards/sdkconfig.ble
	    boards/sdkconfig.spiram_sx
	    boards/ESP32_GENERIC_S3/sdkconfig.board
	    boards/sdkconfig.ulp_fsm
	)
	```

	To embed ULP code, define the sources and dependant files using the generated `ulp_embedded.h` file
	The app will always be called `ulp_embedded` internally
	An example board definition that uses custom ulp code located at `boards/${board}/ulp/main.c`
	has the following mpconfigboard.cmake

	```
	set(MICROPY_SOURCE_BOARD
	    ${MICROPY_SOURCE_BOARD}
	    ${MICROPY_BOARD_DIR}/cmodules/modtest.c
	)

	set(ulp_embedded_sources ${MICROPY_BOARD_DIR}/ulp/main_pin.c)
	set(ulp_dependants ${MICROPY_BOARD_DIR}/cmodules/modtest.c)
	```
	The `ulp_dependants` is only needed if you have custom c modules that want to reference ULP variables directly.

	You can also read and write the embedded ULP variables using `ULP.read(address)` and `ULP.write(address, value)`
	By default, when you embed a ULP app, all c variables with the prefix `var_` will be added as constant address offsets to the ULP class,
	so you can call `ulp.read(ulp.VAR_TEST)` to get a value that way.

	I have confirmed that it *is* possible to load the binary ULP code after flashing a built in version.
@purewack
Copy link
Author

purewack commented Jan 3, 2025

I'm but a simple man, I get excited when something compiles! This is GREAT! Very much appreciated it, been thinking all day how cool it'd be to have a ulp enabled squelch. And thank you for the additional IDF examples as well. I'll definitely be digging in! In case someone hasn't already told it to you today, YOU ROCK! 👊

The new PR #16521 might be what you were after ;)

@stephanelsmith
Copy link
Contributor

stephanelsmith commented Jan 7, 2025

🤯 🤯 🤯 🤯 Indeed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants