From 7b02eae9e6b886fc5abb832a35086a88df79c90e Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 16 Dec 2016 20:01:15 +0800 Subject: [PATCH 1/3] ulp: add RD_REG, WR_REG, END instruction --- components/ulp/README.rst | 3 ++ components/ulp/include/esp32/ulp.h | 65 ++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/components/ulp/README.rst b/components/ulp/README.rst index 14c67d7109..875a01ef88 100644 --- a/components/ulp/README.rst +++ b/components/ulp/README.rst @@ -83,8 +83,11 @@ ULP coprocessor instruction defines .. doxygendefine:: I_DELAY .. doxygendefine:: I_HALT +.. doxygendefine:: I_END .. doxygendefine:: I_ST .. doxygendefine:: I_LD +.. doxygendefine:: I_WR_REG +.. doxygendefine:: I_RD_REG .. doxygendefine:: I_BL .. doxygendefine:: I_BGE .. doxygendefine:: I_BXR diff --git a/components/ulp/include/esp32/ulp.h b/components/ulp/include/esp32/ulp.h index c23d9cf9d1..18ad41ca6d 100644 --- a/components/ulp/include/esp32/ulp.h +++ b/components/ulp/include/esp32/ulp.h @@ -47,6 +47,10 @@ extern "C" { #define OPCODE_RD_REG 2 /*!< Instruction: read peripheral register (RTC_CNTL/RTC_IO/SARADC) (not implemented yet) */ +#define RD_REG_PERIPH_RTC_CNTL 0 /*!< Identifier of RTC_CNTL peripheral for RD_REG and WR_REG instructions */ +#define RD_REG_PERIPH_RTC_IO 1 /*!< Identifier of RTC_IO peripheral for RD_REG and WR_REG instructions */ +#define RD_REG_PERIPH_SARADC 2 /*!< Identifier of SARADC peripheral for RD_REG and WR_REG instructions */ + #define OPCODE_I2C 3 /*!< Instruction: read/write I2C (not implemented yet) */ #define OPCODE_DELAY 4 /*!< Instruction: delay (nop) for a given number of cycles */ @@ -191,8 +195,8 @@ typedef union { uint32_t addr : 8; /*!< Address within either RTC_CNTL, RTC_IO, or SARADC */ uint32_t periph_sel : 2; /*!< Select peripheral: RTC_CNTL (0), RTC_IO(1), SARADC(2) */ uint32_t data : 8; /*!< 8 bits of data to write */ - uint32_t high : 5; /*!< High bit */ uint32_t low : 5; /*!< Low bit */ + uint32_t high : 5; /*!< High bit */ uint32_t opcode : 4; /*!< Opcode (OPCODE_WR_REG) */ } wr_reg; /*!< Format of WR_REG instruction */ @@ -200,10 +204,10 @@ typedef union { uint32_t addr : 8; /*!< Address within either RTC_CNTL, RTC_IO, or SARADC */ uint32_t periph_sel : 2; /*!< Select peripheral: RTC_CNTL (0), RTC_IO(1), SARADC(2) */ uint32_t unused : 8; /*!< Unused */ - uint32_t high : 5; /*!< High bit */ uint32_t low : 5; /*!< Low bit */ + uint32_t high : 5; /*!< High bit */ uint32_t opcode : 4; /*!< Opcode (OPCODE_WR_REG) */ - } rd_reg; /*!< Format of WR_REG instruction */ + } rd_reg; /*!< Format of RD_REG instruction */ struct { uint32_t dreg : 2; /*!< Register where to store ADC result */ @@ -256,6 +260,8 @@ typedef union { } ulp_insn_t; +_Static_assert(sizeof(ulp_insn_t) == 4, "ULP coprocessor instruction size should be 4 bytes"); + /** * Delay (nop) for a given number of cycles */ @@ -272,6 +278,59 @@ typedef union { .opcode = OPCODE_HALT } } +static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { + uint32_t ret = 3; + if (reg < DR_REG_RTCCNTL_BASE) { + assert(0 && "invalid register base"); + } else if (reg < DR_REG_RTCIO_BASE) { + ret = 0; + } else if (reg < DR_REG_SENS_BASE) { + ret = 1; + } else if (reg < DR_REG_RTCMEM0_BASE){ + ret = 2; + } else { + assert(0 && "invalid register base"); + } + return ret; +} + +/** + * Write literal value to a peripheral register + * + * reg[high_bit : low_bit] = val + */ +#define I_WR_REG(reg, low_bit, high_bit, val) {.wr_reg = {\ + .addr = reg & 0xff, \ + .periph_sel = SOC_REG_TO_ULP_PERIPH_SEL(reg), \ + .data = val, \ + .low = low_bit, \ + .high = high_bit, \ + .opcode = OPCODE_WR_REG } } + +/** + * Read from peripheral register into R0 + * + * R0 = reg[high_bit : low_bit] + */ +#define I_RD_REG(reg, low_bit, high_bit, val) {.wr_reg = {\ + .addr = reg & 0xff, \ + .periph_sel = SOC_REG_TO_ULP_PERIPH_SEL(reg), \ + .unused = 0, \ + .low = low_bit, \ + .high = high_bit, \ + .opcode = OPCODE_RD_REG } } + +/** + * End program. + * + * If wake == 1, wake up main CPU. + */ +#define I_END(wake) { .end = { \ + .wakeup = wake, \ + .unused = 0, \ + .sub_opcode = SUB_OPCODE_END, \ + .opcode = OPCODE_END } } + /** * Store value from register reg_val into RTC memory. * From 7a527896dcfbe7dc93752d9ef6c7ee8f3ce26f2a Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 16 Dec 2016 20:25:38 +0800 Subject: [PATCH 2/3] ulp: use timer to start ULP, fix I_ANDI bug, add tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Starting the ULP using SENS_SAR_START_FORCE_REG doesn’t disable clock gating of RTC fast clock. When SoC goes into deep sleep mode, RTC fast clock gets gated, so ULP can no longer run. Instead, it has to be started using the timer (RTC_CNTL_ULP_CP_SLP_TIMER_EN bit). When ULP is enabled by the timer, clock also gets enabled. --- components/ulp/include/esp32/ulp.h | 2 +- components/ulp/test/test_ulp.c | 76 ++++++++++++++++++++++++++++++ components/ulp/ulp.c | 11 ++++- 3 files changed, 86 insertions(+), 3 deletions(-) diff --git a/components/ulp/include/esp32/ulp.h b/components/ulp/include/esp32/ulp.h index 18ad41ca6d..a0903824c0 100644 --- a/components/ulp/include/esp32/ulp.h +++ b/components/ulp/include/esp32/ulp.h @@ -600,7 +600,7 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { #define I_ANDI(reg_dest, reg_src, imm_) { .alu_imm = { \ .dreg = reg_dest, \ .sreg = reg_src, \ - .imm = reg_imm_, \ + .imm = imm_, \ .unused = 0, \ .sel = ALU_SEL_AND, \ .sub_opcode = SUB_OPCODE_ALU_IMM, \ diff --git a/components/ulp/test/test_ulp.c b/components/ulp/test/test_ulp.c index f0000e1aa2..854eb3ee20 100644 --- a/components/ulp/test/test_ulp.c +++ b/components/ulp/test/test_ulp.c @@ -22,12 +22,14 @@ #include "esp_attr.h" #include "esp_err.h" #include "esp_log.h" +#include "esp_deep_sleep.h" #include "esp32/ulp.h" #include "soc/soc.h" #include "soc/rtc_cntl_reg.h" #include "soc/sens_reg.h" +#include "driver/rtc_io.h" #include "sdkconfig.h" @@ -92,3 +94,77 @@ TEST_CASE("ulp branch test", "[ulp]") } TEST_ASSERT_EQUAL(0, RTC_SLOW_MEM[64]); } + +TEST_CASE("ulp wakeup test", "[ulp]") +{ + assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); + memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM); + const ulp_insn_t program[] = { + I_MOVI(R1, 1024), + M_LABEL(1), + I_DELAY(32000), + I_SUBI(R1, R1, 1), + M_BXZ(3), + I_RSHI(R3, R1, 5), // R3 = R1 / 32 + I_ST(R1, R3, 16), + M_BX(1), + M_LABEL(3), + I_MOVI(R2, 42), + I_MOVI(R3, 15), + I_ST(R2, R3, 0), + I_END(1) + }; + size_t size = sizeof(program)/sizeof(ulp_insn_t); + ulp_process_macros_and_load(0, program, &size); + ulp_run(0); + esp_deep_sleep_enable_ulp_wakeup(); + esp_deep_sleep_start(); +} + +TEST_CASE("ulp controls RTC_IO", "[ulp]") +{ + assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); + memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM); + const ulp_insn_t program[] = { + I_MOVI(R0, 0), // R0 is LED state + I_MOVI(R2, 16), // loop R2 from 16 down to 0 + M_LABEL(4), + I_SUBI(R2, R2, 1), + M_BXZ(6), + I_ADDI(R0, R0, 1), // R0 = (R0 + 1) % 2 + I_ANDI(R0, R0, 0x1), + M_BL(0, 1), // if R0 < 1 goto 0 + M_LABEL(1), + I_WR_REG(RTC_GPIO_OUT_REG, 26, 27, 1), // RTC_GPIO12 = 1 + M_BX(2), // goto 2 + M_LABEL(0), // 0: + I_WR_REG(RTC_GPIO_OUT_REG, 26, 27, 0), // RTC_GPIO12 = 0 + M_LABEL(2), // 2: + I_MOVI(R1, 100), // loop R1 from 100 down to 0 + M_LABEL(3), + I_SUBI(R1, R1, 1), + M_BXZ(5), + I_DELAY(32000), // delay for a while + M_BX(3), + M_LABEL(5), + M_BX(4), + M_LABEL(6), + I_END(1) // wake up the SoC + }; + const gpio_num_t led_gpios[] = { + GPIO_NUM_2, + GPIO_NUM_0, + GPIO_NUM_4 + }; + for (size_t i = 0; i < sizeof(led_gpios)/sizeof(led_gpios[0]); ++i) { + rtc_gpio_init(led_gpios[i]); + rtc_gpio_set_direction(led_gpios[i], RTC_GPIO_MODE_OUTPUT_ONLY); + rtc_gpio_set_level(led_gpios[i], 0); + } + size_t size = sizeof(program)/sizeof(ulp_insn_t); + ulp_process_macros_and_load(0, program, &size); + ulp_run(0); + esp_deep_sleep_enable_ulp_wakeup(); + esp_deep_sleep_start(); +} + diff --git a/components/ulp/ulp.c b/components/ulp/ulp.c index 228e3ff16d..60fa292f7c 100644 --- a/components/ulp/ulp.c +++ b/components/ulp/ulp.c @@ -263,8 +263,15 @@ esp_err_t ulp_process_macros_and_load(uint32_t load_addr, const ulp_insn_t* prog esp_err_t ulp_run(uint32_t entry_point) { - SET_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_FORCE_START_TOP_M); + // disable ULP timer + CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); + // set entry point SET_PERI_REG_BITS(SENS_SAR_START_FORCE_REG, SENS_PC_INIT_V, entry_point, SENS_PC_INIT_S); - SET_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_START_TOP_M); + // disable force start + CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_FORCE_START_TOP_M); + // make sure voltage is raised when RTC 8MCLK is enabled + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FOLW_8M); + // enable ULP timer + SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); return ESP_OK; } From b62f8b42d44edca43ae86119e6d1cfe898d84d8a Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 16 Dec 2016 20:32:34 +0800 Subject: [PATCH 3/3] ulp: document peripherals accessible using RD_REG and WR_REG --- components/ulp/include/esp32/ulp.h | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/components/ulp/include/esp32/ulp.h b/components/ulp/include/esp32/ulp.h index a0903824c0..f4e37e924a 100644 --- a/components/ulp/include/esp32/ulp.h +++ b/components/ulp/include/esp32/ulp.h @@ -49,7 +49,7 @@ extern "C" { #define RD_REG_PERIPH_RTC_CNTL 0 /*!< Identifier of RTC_CNTL peripheral for RD_REG and WR_REG instructions */ #define RD_REG_PERIPH_RTC_IO 1 /*!< Identifier of RTC_IO peripheral for RD_REG and WR_REG instructions */ -#define RD_REG_PERIPH_SARADC 2 /*!< Identifier of SARADC peripheral for RD_REG and WR_REG instructions */ +#define RD_REG_PERIPH_SENS 2 /*!< Identifier of SARADC peripheral for RD_REG and WR_REG instructions */ #define OPCODE_I2C 3 /*!< Instruction: read/write I2C (not implemented yet) */ @@ -277,17 +277,23 @@ _Static_assert(sizeof(ulp_insn_t) == 4, "ULP coprocessor instruction size should .unused = 0, \ .opcode = OPCODE_HALT } } - +/** + * Map SoC peripheral register to periph_sel field of RD_REG and WR_REG + * instructions. + * + * @param reg peripheral register in RTC_CNTL_, RTC_IO_, SENS_ peripherals. + * @return periph_sel value for the peripheral to which this register belongs. + */ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { uint32_t ret = 3; if (reg < DR_REG_RTCCNTL_BASE) { assert(0 && "invalid register base"); } else if (reg < DR_REG_RTCIO_BASE) { - ret = 0; + ret = RD_REG_PERIPH_RTC_CNTL; } else if (reg < DR_REG_SENS_BASE) { - ret = 1; + ret = RD_REG_PERIPH_RTC_IO; } else if (reg < DR_REG_RTCMEM0_BASE){ - ret = 2; + ret = RD_REG_PERIPH_SENS; } else { assert(0 && "invalid register base"); } @@ -298,6 +304,7 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { * Write literal value to a peripheral register * * reg[high_bit : low_bit] = val + * This instruction can access RTC_CNTL_, RTC_IO_, and SENS_ peripheral registers. */ #define I_WR_REG(reg, low_bit, high_bit, val) {.wr_reg = {\ .addr = reg & 0xff, \ @@ -311,6 +318,7 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { * Read from peripheral register into R0 * * R0 = reg[high_bit : low_bit] + * This instruction can access RTC_CNTL_, RTC_IO_, and SENS_ peripheral registers. */ #define I_RD_REG(reg, low_bit, high_bit, val) {.wr_reg = {\ .addr = reg & 0xff, \