Skip to content
12 changes: 12 additions & 0 deletions locale/circuitpython.pot
Original file line number Diff line number Diff line change
Expand Up @@ -1240,6 +1240,10 @@ msgstr ""
msgid "Invalid multicast MAC address"
msgstr ""

#: ports/espressif/common-hal/espulp/ULP.c
msgid "Invalid parameters"
msgstr ""

#: ports/espressif/common-hal/espidf/__init__.c
msgid "Invalid size"
msgstr ""
Expand Down Expand Up @@ -1280,6 +1284,10 @@ msgstr ""
msgid "Layer must be a Group or TileGrid subclass"
msgstr ""

#: ports/espressif/common-hal/espulp/ULP.c
msgid "Load binary failed"
msgstr ""

#: ports/espressif/common-hal/espidf/__init__.c
msgid "MAC address was invalid"
msgstr ""
Expand Down Expand Up @@ -1826,6 +1834,10 @@ msgstr ""
msgid "Right format but not supported"
msgstr ""

#: ports/espressif/common-hal/espulp/ULP.c
msgid "Run binary failed"
msgstr ""

#: main.c
msgid "Running in safe mode! Not running saved code.\n"
msgstr ""
Expand Down
67 changes: 52 additions & 15 deletions ports/espressif/bindings/espulp/ULP.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
//| Raises an exception if another ULP has been instantiated. This
//| ensures that is is only used by one piece of code at a time.
//|
//| :param Architecture arch: The ulp arch"""
//| :param Architecture arch: The ulp arch. Only `FSM` architecture is currently supported.
//| """
//| ...
static mp_obj_t espulp_ulp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_arch };
Expand Down Expand Up @@ -70,21 +71,50 @@ static mp_obj_t espulp_ulp_obj___exit__(size_t n_args, const mp_obj_t *args) {
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(espulp_ulp___exit___obj, 4, 4, espulp_ulp_obj___exit__);

//| def set_wakeup_period(self, period_index: int, period_us: int) -> None:
//| """Sets the wakeup period for the ULP.
//|
//| :param int period_index: = 0..4. Up to 5 different wakeup periods can be stored
//| and used by the wakeup timer.
//| By default, the value stored in period index 0 is used.
//| :param int period_us: The wakeup period given in microseconds."""
//| ...
static mp_obj_t espulp_ulp_set_wakeup_period(mp_obj_t self_in, mp_obj_t period_index, mp_obj_t period_us) {
espulp_ulp_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);

// period_index should be between 0 and 4 but bounds checking happens in esp-idf, so no need to do that here
common_hal_espulp_ulp_set_wakeup_period(self, mp_obj_get_int(period_index), mp_obj_get_int(period_us));

return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_3(espulp_ulp_set_wakeup_period_obj, espulp_ulp_set_wakeup_period);

//| def run(
//| self, program: ReadableBuffer, *, pins: Sequence[microcontroller.Pin] = ()
//| self,
//| program: ReadableBuffer,
//| *,
//| entrypoint: int = 0,
//| pins: Sequence[microcontroller.Pin] = ()
//| ) -> None:
//| """Loads the program into ULP memory and then runs the program. The given pins are
//| claimed and not reset until `halt()` is called.
//| """Loads the program into ULP memory and then runs the program.
//|
//| The program will continue to run even when the running Python is halted."""
//| The program will continue to run even Python is halted or in deep-sleep.
//|
//| :param ReadableBuffer program: the ULP binary.
//| :param int entrypoint: Specifies the offset (in bytes) of the first instruction
//| from the start of the program.
//| :param Sequence[microcontroller.Pin] pins: Pins made available to the ULP.
//| The pins are claimed and not reset until `halt()` is called."""
//| ...
static mp_obj_t espulp_ulp_run(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
espulp_ulp_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
check_for_deinit(self);

enum { ARG_program, ARG_pins };
enum { ARG_program, ARG_entrypoint, ARG_pins };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_program, MP_ARG_REQUIRED | MP_ARG_OBJ},
{ MP_QSTR_entrypoint, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0}},
{ MP_QSTR_pins, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_empty_tuple} },
};

Expand All @@ -94,6 +124,8 @@ static mp_obj_t espulp_ulp_run(size_t n_args, const mp_obj_t *pos_args, mp_map_t
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[ARG_program].u_obj, &bufinfo, MP_BUFFER_READ);

mp_uint_t entrypoint = args[ARG_entrypoint].u_int;

mp_obj_t pins_in = args[ARG_pins].u_obj;
const size_t num_pins = (size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(pins_in));

Expand All @@ -103,21 +135,25 @@ static mp_obj_t espulp_ulp_run(size_t n_args, const mp_obj_t *pos_args, mp_map_t

for (mp_uint_t i = 0; i < num_pins; i++) {
mp_obj_t pin_obj = mp_obj_subscr(pins_in, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL);
validate_obj_is_free_pin(pin_obj, MP_QSTR_pin);
// common-hal checks that pin is free (that way a possible "ULP already running" error
// is triggered before a possible "Pin in use" error, if ulp.run is called twice with the same pins).
validate_obj_is_pin(pin_obj, MP_QSTR_pin);
const mcu_pin_obj_t *pin = ((const mcu_pin_obj_t *)pin_obj);
if (pin->number >= 32) {
raise_ValueError_invalid_pin();
}
pin_mask |= 1 << pin->number;
}

common_hal_espulp_ulp_run(self, bufinfo.buf, bufinfo.len, pin_mask);
common_hal_espulp_ulp_run(self, bufinfo.buf, bufinfo.len, entrypoint, pin_mask);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(espulp_ulp_run_obj, 2, espulp_ulp_run);

//| def halt(self) -> None:
//| """Halts the running program and releases the pins given in `run()`."""
//| """Halts the running program and releases the pins given in `run()`.
//| Note: for the FSM ULP, a running ULP program is not actually interrupted.
//| Instead, only the wakeup timer is stopped."""
//| ...
static mp_obj_t espulp_ulp_halt(mp_obj_t self_in) {
espulp_ulp_obj_t *self = MP_OBJ_TO_PTR(self_in);
Expand All @@ -143,12 +179,13 @@ MP_PROPERTY_GETTER(espulp_ulp_arch_obj,
(mp_obj_t)&espulp_ulp_get_arch_obj);

static const mp_rom_map_elem_t espulp_ulp_locals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&espulp_ulp_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) },
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&espulp_ulp___exit___obj) },
{ MP_ROM_QSTR(MP_QSTR_run), MP_ROM_PTR(&espulp_ulp_run_obj) },
{ MP_ROM_QSTR(MP_QSTR_halt), MP_ROM_PTR(&espulp_ulp_halt_obj) },
{ MP_ROM_QSTR(MP_QSTR_arch), MP_ROM_PTR(&espulp_ulp_arch_obj) },
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&espulp_ulp_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) },
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&espulp_ulp___exit___obj) },
{ MP_ROM_QSTR(MP_QSTR_set_wakeup_period), MP_ROM_PTR(&espulp_ulp_set_wakeup_period_obj) },
{ MP_ROM_QSTR(MP_QSTR_run), MP_ROM_PTR(&espulp_ulp_run_obj) },
{ MP_ROM_QSTR(MP_QSTR_halt), MP_ROM_PTR(&espulp_ulp_halt_obj) },
{ MP_ROM_QSTR(MP_QSTR_arch), MP_ROM_PTR(&espulp_ulp_arch_obj) },
};
static MP_DEFINE_CONST_DICT(espulp_ulp_locals_dict, espulp_ulp_locals_table);

Expand Down
3 changes: 2 additions & 1 deletion ports/espressif/bindings/espulp/ULP.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ void common_hal_espulp_ulp_construct(espulp_ulp_obj_t *self, espulp_architecture
bool common_hal_espulp_ulp_deinited(espulp_ulp_obj_t *self);
void common_hal_espulp_ulp_deinit(espulp_ulp_obj_t *self);

void common_hal_espulp_ulp_run(espulp_ulp_obj_t *self, uint32_t *program, size_t length, uint32_t pin_mask);
void common_hal_espulp_ulp_set_wakeup_period(espulp_ulp_obj_t *self, size_t period_index, uint32_t period_us);
void common_hal_espulp_ulp_run(espulp_ulp_obj_t *self, uint32_t *program, size_t length, uint32_t entry_point, uint32_t pin_mask);
void common_hal_espulp_ulp_halt(espulp_ulp_obj_t *self);
34 changes: 28 additions & 6 deletions ports/espressif/common-hal/espulp/ULP.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,20 @@
#include "py/runtime.h"
#include "shared-bindings/microcontroller/Pin.h"

#include "esp_sleep.h"

#if defined(CONFIG_IDF_TARGET_ESP32)
#include "esp32/ulp.h"
#define ULP_COPROC_RESERVE_MEM (CONFIG_ESP32_ULP_COPROC_RESERVE_MEM)
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
#include "esp32s2/ulp.h"
#include "ulp_riscv.h"
#define CONFIG_ULP_COPROC_TYPE_RISCV // Overwrite sdkconfig setting, due to Issue https://github.com/espressif/esp-idf/issues/12999
#define ULP_COPROC_RESERVE_MEM (CONFIG_ESP32S2_ULP_COPROC_RESERVE_MEM)
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
#include "esp32s3/ulp.h"
#include "ulp_riscv.h"
#define CONFIG_ULP_COPROC_TYPE_RISCV // Overwrite sdkconfig setting, due to Issue https://github.com/espressif/esp-idf/issues/12999
#define ULP_COPROC_RESERVE_MEM (CONFIG_ESP32S3_ULP_COPROC_RESERVE_MEM)
#endif

Expand All @@ -34,7 +38,14 @@ void espulp_reset(void) {
ulp_used = false;
}

void common_hal_espulp_ulp_run(espulp_ulp_obj_t *self, uint32_t *program, size_t length, uint32_t pin_mask) {
void common_hal_espulp_ulp_set_wakeup_period(espulp_ulp_obj_t *self, size_t period_index, uint32_t period_us) {
int _errno = ulp_set_wakeup_period(period_index, period_us);
if (_errno != ESP_OK) {
mp_raise_ValueError(MP_ERROR_TEXT("Invalid parameters"));
}
}

void common_hal_espulp_ulp_run(espulp_ulp_obj_t *self, uint32_t *program, size_t length, uint32_t entry_point, uint32_t pin_mask) {
if (length > CONFIG_ULP_COPROC_RESERVE_MEM) {
mp_raise_ValueError(MP_ERROR_TEXT("Program too long"));
}
Expand Down Expand Up @@ -67,13 +78,25 @@ void common_hal_espulp_ulp_run(espulp_ulp_obj_t *self, uint32_t *program, size_t
}
pins_used = pin_mask;

ulp_set_wakeup_period(0, 20000);
// Main purpose of ULP is to run while main cpu is in deep sleep, so
// ensure GPIO Power Domain remains enabled during deep sleep,
// if any GPIO were supplied here.
if (pins_used > 0) {
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
}

int _errno;
switch (self->arch) {
#ifdef CONFIG_ULP_COPROC_TYPE_FSM
case FSM:
ulp_load_binary(0, (const uint8_t *)program, length);
ulp_run(0);
_errno = ulp_load_binary(0, (const uint8_t *)program, length / sizeof(uint32_t));
if (_errno != ESP_OK) {
mp_raise_RuntimeError(MP_ERROR_TEXT("Load binary failed"));
}
_errno = ulp_run(entry_point / sizeof(uint32_t));
if (_errno != ESP_OK) {
mp_raise_RuntimeError(MP_ERROR_TEXT("Run binary failed"));
}
break;
#endif
#ifdef CONFIG_ULP_COPROC_TYPE_RISCV
Expand All @@ -90,12 +113,11 @@ void common_hal_espulp_ulp_run(espulp_ulp_obj_t *self, uint32_t *program, size_t

void common_hal_espulp_ulp_halt(espulp_ulp_obj_t *self) {
switch (self->arch) {
/*
#ifdef CONFIG_ULP_COPROC_TYPE_FSM
case FSM:
ulp_timer_stop();
break;
#endif
*/
#ifdef CONFIG_ULP_COPROC_TYPE_RISCV
case RISCV:
ulp_riscv_timer_stop();
Expand Down
Loading