ulp: added support to building code for riscv ULP coprocessor

This commit is contained in:
Felipe Neves 2020-04-17 16:34:56 -03:00
parent 95b33895b8
commit b6dba84323
48 changed files with 1393 additions and 99 deletions

View File

@ -302,7 +302,7 @@ menu "ESP32S2-specific"
config ESP32S2_ULP_COPROC_RESERVE_MEM
int
prompt "RTC slow memory reserved for coprocessor" if ESP32S2_ULP_COPROC_ENABLED
default 512 if ESP32S2_ULP_COPROC_ENABLED
default 2048 if ESP32S2_ULP_COPROC_ENABLED
range 32 8192 if ESP32S2_ULP_COPROC_ENABLED
default 0 if !ESP32S2_ULP_COPROC_ENABLED
range 0 0 if !ESP32S2_ULP_COPROC_ENABLED
@ -311,6 +311,13 @@ menu "ESP32S2-specific"
Data is reserved at the beginning of RTC slow memory.
config ESP32S2_ULP_COPROC_RISCV
bool "Enable RISC-V as ULP coprocessor"
depends on ESP32S2_ULP_COPROC_ENABLED
default n
help
Set this to y to use the RISC-V coprocessor instead of the FSM-ULP.
config ESP32S2_DEBUG_OCDAWARE
bool "Make exception and panic handlers JTAG/OCD aware"
default y

View File

@ -55,16 +55,18 @@ typedef enum {
* @brief Sleep wakeup cause
*/
typedef enum {
ESP_SLEEP_WAKEUP_UNDEFINED, //!< In case of deep sleep, reset was not caused by exit from deep sleep
ESP_SLEEP_WAKEUP_ALL, //!< Not a wakeup cause, used to disable all wakeup sources with esp_sleep_disable_wakeup_source
ESP_SLEEP_WAKEUP_EXT0, //!< Wakeup caused by external signal using RTC_IO
ESP_SLEEP_WAKEUP_EXT1, //!< Wakeup caused by external signal using RTC_CNTL
ESP_SLEEP_WAKEUP_TIMER, //!< Wakeup caused by timer
ESP_SLEEP_WAKEUP_TOUCHPAD, //!< Wakeup caused by touchpad
ESP_SLEEP_WAKEUP_ULP, //!< Wakeup caused by ULP program
ESP_SLEEP_WAKEUP_GPIO, //!< Wakeup caused by GPIO (light sleep only)
ESP_SLEEP_WAKEUP_UART, //!< Wakeup caused by UART (light sleep only)
ESP_SLEEP_WAKEUP_WIFI, //!< Wakeup caused by WIFI (light sleep only)
ESP_SLEEP_WAKEUP_UNDEFINED, //!< In case of deep sleep, reset was not caused by exit from deep sleep
ESP_SLEEP_WAKEUP_ALL, //!< Not a wakeup cause, used to disable all wakeup sources with esp_sleep_disable_wakeup_source
ESP_SLEEP_WAKEUP_EXT0, //!< Wakeup caused by external signal using RTC_IO
ESP_SLEEP_WAKEUP_EXT1, //!< Wakeup caused by external signal using RTC_CNTL
ESP_SLEEP_WAKEUP_TIMER, //!< Wakeup caused by timer
ESP_SLEEP_WAKEUP_TOUCHPAD, //!< Wakeup caused by touchpad
ESP_SLEEP_WAKEUP_ULP, //!< Wakeup caused by ULP program
ESP_SLEEP_WAKEUP_GPIO, //!< Wakeup caused by GPIO (light sleep only)
ESP_SLEEP_WAKEUP_UART, //!< Wakeup caused by UART (light sleep only)
ESP_SLEEP_WAKEUP_WIFI, //!< Wakeup caused by WIFI (light sleep only)
ESP_SLEEP_WAKEUP_COCPU, //!< Wakeup caused by COCPU int
ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG, //!< Wakeup caused by COCPU crash
} esp_sleep_source_t;
/* Leave this type define for compatibility */
@ -90,14 +92,9 @@ esp_err_t esp_sleep_disable_wakeup_source(esp_sleep_source_t source);
/**
* @brief Enable wakeup by ULP coprocessor
* @note In revisions 0 and 1 of the ESP32, ULP wakeup source
* can not be used when RTC_PERIPH power domain is forced
* to be powered on (ESP_PD_OPTION_ON) or when ext0 wakeup
* source is used.
*
* @return
* - ESP_OK on success
* - ESP_ERR_NOT_SUPPORTED if additional current by touch (CONFIG_ESP32_RTC_EXT_CRYST_ADDIT_CURRENT) is enabled.
* - ESP_ERR_INVALID_STATE if ULP co-processor is not enabled or if wakeup triggers conflict
*/
esp_err_t esp_sleep_enable_ulp_wakeup(void);

View File

@ -68,7 +68,7 @@
typedef struct {
esp_sleep_pd_option_t pd_options[ESP_PD_DOMAIN_MAX];
uint64_t sleep_duration;
uint32_t wakeup_triggers : 11;
uint32_t wakeup_triggers : 15;
uint32_t ext1_trigger_mode : 1;
uint32_t ext1_rtc_gpio_mask : 18;
uint32_t ext0_trigger_level : 1;
@ -413,7 +413,8 @@ esp_err_t esp_sleep_disable_wakeup_source(esp_sleep_source_t source)
esp_err_t esp_sleep_enable_ulp_wakeup(void)
{
return ESP_ERR_NOT_SUPPORTED;
s_config.wakeup_triggers |= (RTC_ULP_TRIG_EN | RTC_COCPU_TRIG_EN | RTC_COCPU_TRAP_TRIG_EN);
return ESP_OK;
}
esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us)
@ -633,6 +634,10 @@ esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause(void)
return ESP_SLEEP_WAKEUP_UART;
} else if (wakeup_cause & RTC_WIFI_TRIG_EN) {
return ESP_SLEEP_WAKEUP_WIFI;
} else if (wakeup_cause & RTC_COCPU_TRIG_EN) {
return ESP_SLEEP_WAKEUP_ULP;
} else if (wakeup_cause & RTC_COCPU_TRAP_TRIG_EN) {
return ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG;
} else {
return ESP_SLEEP_WAKEUP_UNDEFINED;
}

View File

@ -12,5 +12,5 @@
#define SOC_TWAI_SUPPORTED 1
#define SOC_CAN_SUPPORTED SOC_TWAI_SUPPORTED
#define SOC_EMAC_SUPPORTED 1
#define SOC_CPU_CORES_NUM 2
#define SOC_RISCV_COPROC_SUPPORTED 0
#define SOC_CPU_CORES_NUM 2

View File

@ -93,6 +93,10 @@
#define REG_SPI_MEM_BASE(i) (DR_REG_SPI0_BASE - (i) * 0x1000)
#define REG_I2C_BASE(i) (DR_REG_I2C_EXT_BASE + (i) * 0x14000 )
//Convenient way to replace the register ops when ulp riscv projects
//consume this file
#ifndef ULP_RISCV_REGISTER_OPS
//Registers Operation {{
#define ETS_UNCACHED_ADDR(addr) (addr)
#define ETS_CACHED_ADDR(addr) (addr)
@ -229,6 +233,7 @@
#endif /* !__ASSEMBLER__ */
//}}
#endif /* !ULP_RISCV_REGISTER_OPS */
//Periheral Clock {{
#define APB_CLK_FREQ_ROM ( 40*1000000 )

View File

@ -8,3 +8,4 @@
#define SOC_TWAI_SUPPORTED 1
#define SOC_CPU_CORES_NUM 1
#define SOC_SUPPORTS_SECURE_DL_MODE 1
#define SOC_RISCV_COPROC_SUPPORTED 1

View File

@ -142,6 +142,9 @@ uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp
/* Start entry into sleep mode */
SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_SLEEP_EN);
/* Set wait cycle for touch or COCPU after deep sleep. */
REG_SET_FIELD(RTC_CNTL_TIMER2_REG, RTC_CNTL_ULPCP_TOUCH_START_WAIT, 0xFF);
while (GET_PERI_REG_MASK(RTC_CNTL_INT_RAW_REG,
RTC_CNTL_SLP_REJECT_INT_RAW | RTC_CNTL_SLP_WAKEUP_INT_RAW) == 0) {
;

View File

@ -1,3 +1,9 @@
idf_component_register(SRCS "ulp.c"
"ulp_macro.c"
set(srcs "ulp.c"
"ulp_macro.c")
if(CONFIG_ESP32S2_ULP_COPROC_RISCV)
list(APPEND srcs "ulp_riscv.c")
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS include)

View File

@ -3,6 +3,8 @@ cmake_minimum_required(VERSION 3.5)
include(${IDF_PATH}/tools/cmake/utilities.cmake)
project(${ULP_APP_NAME} ASM C)
option(ULP_COCPU_IS_RISCV "Use RISC-V based ULP" OFF)
set(version_pattern "[a-z0-9\.-]+")
# Check assembler version
@ -14,23 +16,30 @@ execute_process(
string(REGEX MATCH "GNU assembler \\(GNU Binutils\\) (${version_pattern})" as_version ${as_output})
set(as_version ${CMAKE_MATCH_1})
message(STATUS "Building ULP app ${ULP_APP_NAME}")
message(STATUS "ULP assembler version: ${as_version}")
# Check the supported assembler version
file(STRINGS ${IDF_PATH}/components/ulp/toolchain_ulp_version.mk version_file_contents)
string(REGEX MATCH "SUPPORTED_ULP_ASSEMBLER_VERSION = (${version_pattern})" as_supported_version ${version_file_contents})
set(as_supported_version ${CMAKE_MATCH_1})
if(ULP_COCPU_IS_RISCV)
set(ULP_LD_TEMPLATE ${IDF_PATH}/components/ulp/ld/esp32s2.ulp.riscv.ld)
else()
message(STATUS "ULP assembler version: ${as_version}")
if(NOT as_version STREQUAL as_supported_version)
message(WARNING "WARNING: ULP assembler version ${as_version} is not supported. Expected to see version: \
${as_supported_version}. Please check ESP-IDF ULP setup instructions and update \
the toolchain, or proceed at your own risk.")
# Check the supported assembler version
file(STRINGS ${IDF_PATH}/components/ulp/toolchain_ulp_version.mk version_file_contents)
string(REGEX MATCH "SUPPORTED_ULP_ASSEMBLER_VERSION = (${version_pattern})" as_supported_version ${version_file_contents})
set(as_supported_version ${CMAKE_MATCH_1})
if(NOT as_version STREQUAL as_supported_version)
message(WARNING "WARNING: ULP assembler version ${as_version} is not supported. Expected to see version: \
${as_supported_version}. Please check ESP-IDF ULP setup instructions and update \
the toolchain, or proceed at your own risk.")
endif()
set(ULP_LD_TEMPLATE ${IDF_PATH}/components/ulp/ld/esp32.ulp.ld)
endif()
set(ULP_MAP_GEN ${PYTHON} ${IDF_PATH}/components/ulp/esp32ulp_mapgen.py)
set(ULP_LD_TEMPLATE ${IDF_PATH}/components/ulp/ld/esp32.ulp.ld)
set(ULP_MAP_GEN ${PYTHON} ${IDF_PATH}/components/ulp/esp32ulp_mapgen.py)
get_filename_component(sdkconfig_dir ${SDKCONFIG} DIRECTORY)
foreach(include ${COMPONENT_INCLUDES})
@ -41,7 +50,7 @@ list(APPEND ULP_PREPROCESSOR_ARGS ${component_includes})
list(APPEND ULP_PREPROCESSOR_ARGS -I${COMPONENT_DIR})
list(APPEND ULP_PREPROCESSOR_ARGS -I${sdkconfig_dir})
include_directories(${component_includes})
include_directories(${COMPONENT_INCLUDES})
list(APPEND ULP_PREPROCESSOR_ARGS -D__ASSEMBLER__)
@ -56,28 +65,62 @@ add_custom_target(${ULP_APP_NAME}_ld_script
DEPENDS ${ULP_LD_SCRIPT}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
foreach(ulp_s_source ${ULP_S_SOURCES})
get_filename_component(ulp_ps_source ${ulp_s_source} NAME_WE)
set(ulp_ps_output ${CMAKE_CURRENT_BINARY_DIR}/${ulp_ps_source}.ulp.S)
# Generate preprocessed assembly files.
add_custom_command( OUTPUT ${ulp_ps_output}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND ${CMAKE_C_COMPILER} -E -P -xc ${ULP_PREPROCESSOR_ARGS} -o ${ulp_ps_output} ${ulp_s_source}
DEPENDS ${ulp_s_source} ${ULP_LD_SCRIPT}
VERBATIM)
# During assembly file compilation, output listing files as well.
set_source_files_properties(${ulp_ps_output}
PROPERTIES COMPILE_FLAGS
"-al=${CMAKE_CURRENT_BINARY_DIR}/${ulp_ps_source}.lst")
list(APPEND ULP_PS_SOURCES ${ulp_ps_output})
endforeach()
if(ULP_COCPU_IS_RISCV)
#risc-v ulp uses extra files for building:
list(APPEND ULP_S_SOURCES
"${IDF_PATH}/components/ulp/ulp_riscv/start.S"
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_riscv_utils.c")
# Create an executable
add_executable(${ULP_APP_NAME} ${ULP_PS_SOURCES})
#dummy loop to force pre-processed linker file generation:
foreach(ulp_s_source ${ULP_S_SOURCES})
set(noop ${ulp_s_source})
add_custom_command(OUTPUT ${noop}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND cmake -E echo
DEPENDS ${ULP_LD_SCRIPT}
)
set_source_files_properties(${noop} PROPERTIES NOOP_PROPERTY ${ULP_LD_SCRIPT})
endforeach()
#creates the executable:
add_executable(${ULP_APP_NAME} ${ULP_S_SOURCES})
set(DUMP_SYMBOL_ARGS -g)
set(MAP_GEN_EXTRA_ARGS --riscv)
set(EXTRA_LINKER_ARGS "-nostartfiles -Wl,--gc-sections -Xlinker -Map=${CMAKE_CURRENT_BINARY_DIR}/${ULP_APP_NAME}.map")
#Makes the csr utillies for riscv visible:
target_include_directories(${ULP_APP_NAME} PRIVATE "${IDF_PATH}/components/ulp/ulp_riscv/include")
else()
foreach(ulp_s_source ${ULP_S_SOURCES})
get_filename_component(ulp_ps_source ${ulp_s_source} NAME_WE)
set(ulp_ps_output ${CMAKE_CURRENT_BINARY_DIR}/${ulp_ps_source}.ulp.S)
# Generate preprocessed assembly files.
add_custom_command( OUTPUT ${ulp_ps_output}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND ${CMAKE_C_COMPILER} -E -P -xc ${ULP_PREPROCESSOR_ARGS} -o ${ulp_ps_output} ${ulp_s_source}
DEPENDS ${ulp_s_source} ${ULP_LD_SCRIPT}
VERBATIM)
# During assembly file compilation, output listing files as well.
set_source_files_properties(${ulp_ps_output}
PROPERTIES COMPILE_FLAGS
"-al=${CMAKE_CURRENT_BINARY_DIR}/${ulp_ps_source}.lst")
list(APPEND ULP_PS_SOURCES ${ulp_ps_output})
endforeach()
# Create an executable
add_executable(${ULP_APP_NAME} ${ULP_PS_SOURCES})
set(DUMP_SYMBOL_ARGS -g -f posix)
set(MAP_GEN_EXTRA_ARGS .)
set(EXTRA_LINKER_ARGS "-Map=${CMAKE_CURRENT_BINARY_DIR}/${ULP_APP_NAME}.map")
endif()
# Dump the list of global symbols in a convenient format
add_custom_command( OUTPUT ${ULP_APP_NAME}.sym
COMMAND ${CMAKE_NM} -g -f posix $<TARGET_FILE:${ULP_APP_NAME}> > ${ULP_APP_NAME}.sym
COMMAND ${CMAKE_NM} ${DUMP_SYMBOL_ARGS} $<TARGET_FILE:${ULP_APP_NAME}> > ${ULP_APP_NAME}.sym
DEPENDS ${ULP_APP_NAME}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
@ -88,7 +131,7 @@ add_custom_command( OUTPUT ${ULP_APP_NAME}.bin
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
add_custom_command( OUTPUT ${ULP_APP_NAME}.ld ${ULP_APP_NAME}.h
COMMAND ${ULP_MAP_GEN} -s ${ULP_APP_NAME}.sym -o ${ULP_APP_NAME}
COMMAND ${ULP_MAP_GEN} ${MAP_GEN_EXTRA_ARGS} -s ${ULP_APP_NAME}.sym -o ${ULP_APP_NAME}
DEPENDS ${ULP_APP_NAME}.sym
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
@ -100,4 +143,4 @@ add_custom_target(build
${CMAKE_CURRENT_BINARY_DIR}/${ULP_APP_NAME}.h
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(${ULP_APP_NAME} -T${CMAKE_CURRENT_BINARY_DIR}/${ULP_LD_SCRIPT} -Map=${CMAKE_CURRENT_BINARY_DIR}/${ULP_APP_NAME}.map)
target_link_libraries(${ULP_APP_NAME} -T${CMAKE_CURRENT_BINARY_DIR}/${ULP_LD_SCRIPT} ${EXTRA_LINKER_ARGS})

View File

@ -0,0 +1,18 @@
# CMake toolchain file for ULP-RISC-V
set(CMAKE_SYSTEM_NAME Generic)
# Not best solution, needs to figure why the compiler detection
# fails in CI
set(CMAKE_C_COMPILER_FORCED TRUE)
set(CMAKE_CXX_COMPILER_FORCED TRUE)
set(CMAKE_C_COMPILER "riscv-none-embed-gcc")
set(CMAKE_ASM_COMPILER "riscv-none-embed-gcc")
if(NOT ASM_DIALECT)
set(ASM_DIALECT "")
endif()
set(CMAKE_C_FLAGS "-Os -march=rv32imc -mdiv -fdata-sections -ffunction-sections")
set(CMAKE_ASM_FLAGS "-march=rv32imc -mdiv -x assembler-with-cpp -fdata-sections -ffunction-sections")
set(CMAKE_EXE_LINKER_FLAGS "-march=rv32imc --specs=nano.specs --specs=nosys.specs")

View File

@ -0,0 +1 @@
COMPONENT_OBJEXCLUDE := ulp_riscv.o

View File

@ -19,7 +19,22 @@ def gen_ld_h_from_sym(f_sym, f_ld, f_h):
f_h.write("#pragma once\n\n")
for line in f_sym:
name, _, addr_str = line.split()
name, _, addr_str = line.split(" ", 2)
addr = int(addr_str, 16) + BASE_ADDR
f_h.write("extern uint32_t ulp_{0};\n".format(name))
f_ld.write("PROVIDE ( ulp_{0} = 0x{1:08x} );\n".format(name, addr))
def gen_ld_h_from_sym_riscv(f_sym, f_ld, f_h):
f_ld.write("/* Variable definitions for ESP32ULP linker\n")
f_ld.write(" * This file is generated automatically by esp32ulp_mapgen.py utility.\n")
f_ld.write(" */\n\n")
f_h.write("// Variable definitions for ESP32ULP\n")
f_h.write("// This file is generated automatically by esp32ulp_mapgen.py utility\n\n")
f_h.write("#pragma once\n\n")
for line in f_sym:
addr_str, _, name = line.split()
addr = int(addr_str, 16) + BASE_ADDR
f_h.write("extern uint32_t ulp_{0};\n".format(name))
f_ld.write("PROVIDE ( ulp_{0} = 0x{1:08x} );\n".format(name, addr))
@ -36,6 +51,8 @@ def main():
parser.add_option("-o", "--outputfile", dest="outputfile",
help="destination .h and .ld files name prefix", metavar="OUTFILE")
parser.add_option("--riscv", action="store_true", help="use format for ulp riscv .sym file")
(options, args) = parser.parse_args()
if options.symfile is None:
parser.print_help()
@ -45,6 +62,11 @@ def main():
parser.print_help()
return 1
if options.riscv:
with open(options.outputfile + ".h", 'w') as f_h, open(options.outputfile + ".ld", 'w') as f_ld, open(options.symfile) as f_sym:
gen_ld_h_from_sym_riscv(f_sym, f_ld, f_h)
return 0
with open(options.outputfile + ".h", 'w') as f_h, open(options.outputfile + ".ld", 'w') as f_ld, open(options.symfile) as f_sym:
gen_ld_h_from_sym(f_sym, f_ld, f_h)
return 0

View File

@ -0,0 +1,44 @@
// Copyright 2010-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include "esp_err.h"
#include "soc/soc.h"
#include "ulp_common.h"
/**
* @brief Run the program loaded into RTC memory
* @return ESP_OK on success
*/
esp_err_t ulp_riscv_run(void);
/**
* @brief Load ULP-RISC-V program binary into RTC memory
*
* Different than ULP FSM, the binary program has no special format, it is the ELF
* file generated by RISC-V toolchain converted to binary format using objcopy.
*
* Linker script in components/ulp/ld/esp32.ulp.riscv.ld produces ELF files which
* correspond to this format. This linker script produces binaries with load_addr == 0.
*
* @param program_binary pointer to program binary
* @param program_size_bytes size of the program binary
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_SIZE if program_size_bytes is more than 8KiB
*/
esp_err_t ulp_riscv_load_binary(const uint8_t* program_binary, size_t program_size_bytes);

View File

@ -29,7 +29,7 @@ SECTIONS
. = ALIGN(4);
*(.bss)
} >ram
.header : AT(0)
{
LONG(ULP_BIN_MAGIC)

View File

@ -0,0 +1,36 @@
#include "sdkconfig.h"
ENTRY(reset_vector)
MEMORY
{
ram(RW) : ORIGIN = 0, LENGTH = CONFIG_ESP32S2_ULP_COPROC_RESERVE_MEM
}
SECTIONS
{
. = ORIGIN(ram);
.text :
{
*(.text)
*(.text*)
} >ram
.data ALIGN(4):
{
*(.data)
*(.data*)
*(.sdata)
*(.sdata*)
} > ram
.bss ALIGN(4) :
{
*(.bss)
*(.bss*)
*(.sbss)
*(.sbss*)
} >ram
__stack_top = ORIGIN(ram) + LENGTH(ram);
}

View File

@ -36,30 +36,39 @@ function(ulp_embed_binary app_name s_sources exp_dep_srcs)
if(IDF_TARGET STREQUAL "esp32")
set(TOOLCHAIN_FLAG ${idf_path}/components/ulp/cmake/toolchain-esp32-ulp.cmake)
set(ULP_IS_RISCV OFF)
endif()
if(IDF_TARGET STREQUAL "esp32s2")
set(TOOLCHAIN_FLAG ${idf_path}/components/ulp/cmake/toolchain-esp32s2-ulp.cmake)
if(CONFIG_ESP32S2_ULP_COPROC_RISCV STREQUAL "y")
set(TOOLCHAIN_FLAG ${idf_path}/components/ulp/cmake/toolchain-esp32s2-ulp-riscv.cmake)
set(ULP_IS_RISCV ON)
else()
set(TOOLCHAIN_FLAG ${idf_path}/components/ulp/cmake/toolchain-esp32s2-ulp.cmake)
set(ULP_IS_RISCV OFF)
endif()
endif()
externalproject_add(${app_name}
SOURCE_DIR ${idf_path}/components/ulp/cmake
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${app_name}
INSTALL_COMMAND ""
CMAKE_ARGS -DCMAKE_GENERATOR=${CMAKE_GENERATOR}
-DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FLAG}
-DULP_S_SOURCES=$<TARGET_PROPERTY:${app_name},ULP_SOURCES>
-DULP_APP_NAME=${app_name}
-DCOMPONENT_DIR=${COMPONENT_DIR}
-DCOMPONENT_INCLUDES=$<TARGET_PROPERTY:${COMPONENT_TARGET},INTERFACE_INCLUDE_DIRECTORIES>
-DIDF_PATH=${idf_path}
-DSDKCONFIG=${SDKCONFIG_HEADER}
-DPYTHON=${python}
${extra_cmake_args}
BUILD_COMMAND ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/${app_name} --target build
BUILD_BYPRODUCTS ${ulp_artifacts} ${ulp_artifacts_extras} ${ulp_ps_sources}
${CMAKE_CURRENT_BINARY_DIR}/${app_name}/${app_name}
BUILD_ALWAYS 1
)
SOURCE_DIR ${idf_path}/components/ulp/cmake
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${app_name}
INSTALL_COMMAND ""
CMAKE_ARGS -DCMAKE_GENERATOR=${CMAKE_GENERATOR}
-DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FLAG}
-DULP_S_SOURCES=$<TARGET_PROPERTY:${app_name},ULP_SOURCES>
-DULP_APP_NAME=${app_name}
-DCOMPONENT_DIR=${COMPONENT_DIR}
-DCOMPONENT_INCLUDES=$<TARGET_PROPERTY:${COMPONENT_TARGET},INTERFACE_INCLUDE_DIRECTORIES>
-DIDF_PATH=${idf_path}
-DSDKCONFIG=${SDKCONFIG_HEADER}
-DPYTHON=${python}
-DULP_COCPU_IS_RISCV=${ULP_IS_RISCV}
${extra_cmake_args}
BUILD_COMMAND ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/${app_name} --target build
BUILD_BYPRODUCTS ${ulp_artifacts} ${ulp_artifacts_extras} ${ulp_ps_sources}
${CMAKE_CURRENT_BINARY_DIR}/${app_name}/${app_name}
BUILD_ALWAYS 1
)
set_property(TARGET ${app_name} PROPERTY ULP_SOURCES "${sources}")

View File

@ -1,10 +1,13 @@
if(IDF_TARGET STREQUAL "esp32")
idf_component_register(SRC_DIRS esp32
PRIV_INCLUDE_DIRS .
PRIV_REQUIRES unity ulp soc esp_common)
idf_component_register(SRC_DIRS ${IDF_TARGET}
PRIV_INCLUDE_DIRS .
PRIV_REQUIRES unity ulp soc esp_common)
set(ulp_app_name ulp_test_app)
set(ulp_s_sources "ulp/test_jumps_esp32.S")
set(ulp_exp_dep_srcs "esp32/test_ulp_as.c")
ulp_embed_binary(${ulp_app_name} "${ulp_s_sources}" "${ulp_exp_dep_srcs}")
if(IDF_TARGET STREQUAL "esp32")
set(ulp_sources "ulp/test_jumps_esp32.S")
elseif(IDF_TARGET STREQUAL "esp32s2")
set(ulp_sources "ulp_riscv/test_main.c")
endif()
set(ulp_app_name ulp_test_app)
set(ulp_exp_dep_srcs ${IDF_TARGET})
ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}")

View File

@ -0,0 +1,93 @@
// Copyright 2010-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <string.h>
#include "esp_sleep.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/sens_reg.h"
#include "soc/rtc_periph.h"
#include "esp32s2/ulp.h"
#include "esp32s2/ulp_riscv.h"
#include "ulp_test_app.h"
#include "unity.h"
typedef enum{
RISCV_READ_WRITE_TEST = 1,
RISCV_DEEP_SLEEP_WAKEUP_TEST,
RISCV_LIGHT_SLEEP_WAKEUP_TEST,
RISCV_NO_COMMAND,
} riscv_test_commands_t;
typedef enum {
RISCV_COMMAND_OK = 1,
RISCV_COMMAND_NOK,
} riscv_test_command_reply_t;
#define XOR_MASK 0xDEADBEEF
extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_test_app_bin_start");
extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_test_app_bin_end");
static bool firmware_loaded = false;
static void load_and_start_ulp_firmware(void)
{
if (!firmware_loaded) {
TEST_ASSERT(ulp_riscv_load_binary(ulp_main_bin_start,
(ulp_main_bin_end - ulp_main_bin_start)) == ESP_OK);
TEST_ASSERT(ulp_set_wakeup_period(0, 1000000) == ESP_OK);
TEST_ASSERT(ulp_riscv_run() == ESP_OK);
firmware_loaded = true;
}
}
TEST_CASE("ULP-RISC-V and main CPU are able to exchange data", "[ulp][ignore]")
{
const uint32_t test_data = 0x12345678;
load_and_start_ulp_firmware();
TEST_ASSERT(esp_sleep_enable_ulp_wakeup() == ESP_OK);
ulp_riscv_test_data_in = test_data ^ XOR_MASK;
ulp_main_cpu_command = RISCV_READ_WRITE_TEST;
TEST_ASSERT(esp_light_sleep_start() == ESP_OK);
TEST_ASSERT(esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_ULP);
TEST_ASSERT(ulp_main_cpu_reply == RISCV_COMMAND_OK);
printf("data out: 0x%X, expected: 0x%X \n", ulp_riscv_test_data_out, test_data);
TEST_ASSERT(test_data == ulp_riscv_test_data_out);
}
TEST_CASE("ULP-RISC-V is able to wakeup main CPU from light sleep", "[ulp][ignore]")
{
load_and_start_ulp_firmware();
TEST_ASSERT(esp_sleep_enable_ulp_wakeup() == ESP_OK);
ulp_main_cpu_command = RISCV_LIGHT_SLEEP_WAKEUP_TEST;
TEST_ASSERT(esp_light_sleep_start() == ESP_OK);
TEST_ASSERT(esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_ULP);
TEST_ASSERT(ulp_main_cpu_reply == RISCV_COMMAND_OK);
}
TEST_CASE("ULP-RISC-V is able to wakeup main CPU from deep sleep", "[ulp][reset=SW_CPU_RESET][ignore]")
{
load_and_start_ulp_firmware();
TEST_ASSERT(esp_sleep_enable_ulp_wakeup() == ESP_OK);
ulp_main_cpu_command = RISCV_DEEP_SLEEP_WAKEUP_TEST;
esp_deep_sleep_start();
UNITY_TEST_FAIL(__LINE__, "Should not get here!");
}

View File

@ -0,0 +1,70 @@
// Copyright 2010-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "ulp_riscv/ulp_riscv.h"
#include "ulp_riscv/ulp_riscv_utils.h"
typedef enum{
RISCV_READ_WRITE_TEST = 1,
RISCV_DEEP_SLEEP_WAKEUP_TEST,
RISCV_LIGHT_SLEEP_WAKEUP_TEST,
RISCV_NO_COMMAND,
} riscv_test_commands_t;
typedef enum {
RISCV_COMMAND_OK = 1,
RISCV_COMMAND_NOK,
RISCV_COMMAND_INVALID,
} riscv_test_command_reply_t;
#define XOR_MASK 0xDEADBEEF
volatile riscv_test_commands_t main_cpu_command = RISCV_NO_COMMAND;
volatile riscv_test_command_reply_t main_cpu_reply = RISCV_COMMAND_INVALID;
volatile uint32_t riscv_test_data_in = 0;
volatile uint32_t riscv_test_data_out = 0;
void handle_commands(riscv_test_commands_t cmd)
{
switch (cmd) {
case RISCV_READ_WRITE_TEST:
riscv_test_data_out =riscv_test_data_in ^ XOR_MASK;
main_cpu_reply = RISCV_COMMAND_OK;
break;
case RISCV_DEEP_SLEEP_WAKEUP_TEST:
case RISCV_LIGHT_SLEEP_WAKEUP_TEST:
main_cpu_reply = RISCV_COMMAND_OK;
break;
default:
main_cpu_reply = RISCV_COMMAND_NOK;
break;
}
ulp_riscv_wakeup_main_processor();
}
int main (void)
{
while (1) {
handle_commands(main_cpu_command);
break;
}
/* ulp_riscv_shutdown() is called automatically when main exits */
return 0;
}

View File

@ -72,15 +72,15 @@ esp_err_t ulp_run(uint32_t entry_point)
ets_delay_us(10);
// set entry point
REG_SET_FIELD(RTC_CNTL_ULP_CP_TIMER_REG, RTC_CNTL_ULP_CP_PC_INIT, entry_point);
SET_PERI_REG_MASK(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_SEL); // Select ULP_TIMER trigger target for ULP.
CLEAR_PERI_REG_MASK(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_DONE_FORCE); // Select the value for ULP_TIMER sleep. 1: REG_COCPU_DONE;0: ULP END value.
// start ULP clock gate.
SET_PERI_REG_MASK(RTC_CNTL_ULP_CP_CTRL_REG ,RTC_CNTL_ULP_CP_CLK_FO);
// ULP FSM sends the DONE signal.
CLEAR_PERI_REG_MASK(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_DONE_FORCE);
/* Set the number of cycles of ULP_TIMER sleep, the wait time required to start ULP */
REG_SET_FIELD(RTC_CNTL_ULP_CP_TIMER_REG, RTC_CNTL_ULP_CP_TIMER_SLP_CYCLE, 100);
/* Clear interrupt COCPU status */
REG_WRITE(RTC_CNTL_INT_CLR_REG, RTC_CNTL_COCPU_INT_CLR | RTC_CNTL_COCPU_TRAP_INT_CLR | RTC_CNTL_ULP_CP_INT_CLR);
// start ULP clock gate.
SET_PERI_REG_MASK(RTC_CNTL_ULP_CP_CTRL_REG ,RTC_CNTL_ULP_CP_CLK_FO);
// 1: start with timer. wait ULP_TIMER cnt timer.
CLEAR_PERI_REG_MASK(RTC_CNTL_ULP_CP_CTRL_REG, RTC_CNTL_ULP_CP_FORCE_START_TOP); // Select ULP_TIMER timer as COCPU trigger source
SET_PERI_REG_MASK(RTC_CNTL_ULP_CP_TIMER_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); // Software to turn on the ULP_TIMER timer
@ -154,9 +154,22 @@ esp_err_t ulp_set_wakeup_period(size_t period_index, uint32_t period_us)
if (period_index > 4) {
return ESP_ERR_INVALID_ARG;
}
uint64_t period_us_64 = period_us;
uint64_t period_cycles = (period_us_64 * 1000) / 90; //COCPU sleep clock is 90KHZ.
REG_SET_FIELD(RTC_CNTL_ULP_CP_TIMER_REG, RTC_CNTL_ULP_CP_TIMER_SLP_CYCLE, period_cycles);
rtc_slow_freq_t slow_clk_freq = rtc_clk_slow_freq_get();
rtc_slow_freq_t rtc_slow_freq_x32k = RTC_SLOW_FREQ_32K_XTAL;
rtc_slow_freq_t rtc_slow_freq_8MD256 = RTC_SLOW_FREQ_8MD256;
rtc_cal_sel_t cal_clk = RTC_CAL_RTC_MUX;
if (slow_clk_freq == (rtc_slow_freq_x32k)) {
cal_clk = RTC_CAL_32K_XTAL;
} else if (slow_clk_freq == rtc_slow_freq_8MD256) {
cal_clk = RTC_CAL_8MD256;
}
uint32_t slow_clk_period = rtc_clk_cal(cal_clk, 100);
uint64_t period_cycles = rtc_time_us_to_slowclk(period_us_64, slow_clk_period);
REG_SET_FIELD(RTC_CNTL_ULP_CP_TIMER_1_REG, RTC_CNTL_ULP_CP_TIMER_SLP_CYCLE, ((uint32_t)period_cycles));
#endif
return ESP_OK;
}

View File

@ -0,0 +1,72 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp32s2/clk.h"
#include "esp32s2/ulp.h"
#include "esp32s2/ulp_riscv.h"
#include "soc/soc.h"
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/sens_reg.h"
#include "ulp_private.h"
esp_err_t ulp_riscv_run(void)
{
/* Reset COCPU when power on. */
SET_PERI_REG_MASK(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_CLK_FO);
SET_PERI_REG_MASK(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_SHUT_RESET_EN);
ets_delay_us(20);
CLEAR_PERI_REG_MASK(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_CLK_FO);
CLEAR_PERI_REG_MASK(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_SHUT_RESET_EN);
/* Disable ULP timer */
CLEAR_PERI_REG_MASK(RTC_CNTL_ULP_CP_TIMER_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN);
/* wait for at least 1 RTC_SLOW_CLK cycle */
ets_delay_us(20);
/* Select RISC-V as the ULP_TIMER trigger target. */
CLEAR_PERI_REG_MASK(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_SEL);
/* Select ULP_TIMER sleep trigger source. 1: REG_COCPU_DONE; 0: ULP END.*/
SET_PERI_REG_MASK(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_DONE);
/* start ULP_TIMER */
CLEAR_PERI_REG_MASK(RTC_CNTL_ULP_CP_CTRL_REG, RTC_CNTL_ULP_CP_FORCE_START_TOP);
SET_PERI_REG_MASK(RTC_CNTL_ULP_CP_TIMER_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN);
return ESP_OK;
}
esp_err_t ulp_riscv_load_binary(const uint8_t* program_binary, size_t program_size_bytes)
{
if (program_binary == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (program_size_bytes > ULP_RESERVE_MEM) {
return ESP_ERR_INVALID_SIZE;
}
uint8_t* base = (uint8_t*) RTC_SLOW_MEM;
//Start by clearing memory reserved with zeros, this will also will initialize the bss:
memset(base, 0, ULP_RESERVE_MEM);
memcpy(base, program_binary, program_size_bytes);
return ESP_OK;
}

View File

@ -0,0 +1,27 @@
// Copyright 2010-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "ulp_riscv_register_ops.h"
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/soc_ulp.h"
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,148 @@
// Copyright 2010-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#define ULP_RISCV_REGISTER_OPS
#ifdef __cplusplus
extern "C" {
#endif
//Registers Operation {{
/*
* When COCPU accesses the RTC register, it needs to convert the access address.
* When COCPU accesses the RTC memory, dont need to convert the access address.
*/
#define WRITE_RTC_MEM(addr, val) (*((volatile int*)(addr))) = (int) (val)
#define READ_RTC_MEM(addr) (*(volatile int*)(addr))
/*
* When COCPU accesses the RTC register, it needs to convert the access address.
* When COCPU accesses the RTC memory, dont need to convert the access address.
*/
#define RISCV_REG_CONV(addr) (((addr&0xffff)<<3 & 0xe000) | (addr & 0x1fff) | 0x8000)
#define ETS_UNCACHED_ADDR(addr) (RISCV_REG_CONV(addr))
#ifndef __ASSEMBLER__
#define BIT(nr) (1UL << (nr))
#else
#define BIT(nr) (1 << (nr))
#endif
//write value to register
#define REG_WRITE(_r, _v) ({ \
(*(volatile uint32_t *)RISCV_REG_CONV(_r)) = (_v); \
})
//read value from register
#define REG_READ(_r) ({ \
(*(volatile uint32_t *)RISCV_REG_CONV(_r)); \
})
//get bit or get bits from register
#define REG_GET_BIT(_r, _b) ({ \
(*(volatile uint32_t*)RISCV_REG_CONV(_r) & (_b)); \
})
//set bit or set bits to register
#define REG_SET_BIT(_r, _b) ({ \
(*(volatile uint32_t*)RISCV_REG_CONV(_r) |= (_b)); \
})
//clear bit or clear bits of register
#define REG_CLR_BIT(_r, _b) ({ \
(*(volatile uint32_t*)RISCV_REG_CONV(_r) &= ~(_b)); \
})
//set bits of register controlled by mask
#define REG_SET_BITS(_r, _b, _m) ({ \
(*(volatile uint32_t*)RISCV_REG_CONV(_r) = (*(volatile uint32_t*)RISCV_REG_CONV(_r) & ~(_m)) | ((_b) & (_m))); \
})
//get field from register, uses field _S & _V to determine mask
#define REG_GET_FIELD(_r, _f) ({ \
((REG_READ(_r) >> (_f##_S)) & (_f##_V)); \
})
//set field of a register from variable, uses field _S & _V to determine mask
#define REG_SET_FIELD(_r, _f, _v) ({ \
(REG_WRITE((_r),((REG_READ(_r) & ~((_f##_V) << (_f##_S)))|(((_v) & (_f##_V))<<(_f##_S))))); \
})
//get field value from a variable, used when _f is not left shifted by _f##_S
#define VALUE_GET_FIELD(_r, _f) (((_r) >> (_f##_S)) & (_f))
//get field value from a variable, used when _f is left shifted by _f##_S
#define VALUE_GET_FIELD2(_r, _f) (((_r) & (_f))>> (_f##_S))
//set field value to a variable, used when _f is not left shifted by _f##_S
#define VALUE_SET_FIELD(_r, _f, _v) ((_r)=(((_r) & ~((_f) << (_f##_S)))|((_v)<<(_f##_S))))
//set field value to a variable, used when _f is left shifted by _f##_S
#define VALUE_SET_FIELD2(_r, _f, _v) ((_r)=(((_r) & ~(_f))|((_v)<<(_f##_S))))
//generate a value from a field value, used when _f is not left shifted by _f##_S
#define FIELD_TO_VALUE(_f, _v) (((_v)&(_f))<<_f##_S)
//generate a value from a field value, used when _f is left shifted by _f##_S
#define FIELD_TO_VALUE2(_f, _v) (((_v)<<_f##_S) & (_f))
//read value from register
#define READ_PERI_REG(addr) ({ \
(*((volatile uint32_t *)ETS_UNCACHED_ADDR(addr))); \
})
//write value to register
#define WRITE_PERI_REG(addr, val) ({ \
(*((volatile uint32_t *)ETS_UNCACHED_ADDR(addr))) = (uint32_t)(val); \
})
//clear bits of register controlled by mask
#define CLEAR_PERI_REG_MASK(reg, mask) ({ \
WRITE_PERI_REG((reg), (READ_PERI_REG(reg)&(~(mask)))); \
})
//set bits of register controlled by mask
#define SET_PERI_REG_MASK(reg, mask) ({ \
WRITE_PERI_REG((reg), (READ_PERI_REG(reg)|(mask))); \
})
//get bits of register controlled by mask
#define GET_PERI_REG_MASK(reg, mask) ({ \
(READ_PERI_REG(reg) & (mask)); \
})
//get bits of register controlled by highest bit and lowest bit
// #define GET_PERI_REG_BITS(reg, hipos,lowpos) ({
// ASSERT_IF_DPORT_REG((reg), GET_PERI_REG_BITS);
// ((READ_PERI_REG(reg)>>(lowpos))&((1UL<<((hipos)-(lowpos)+1))-1));
// })
#define GET_PERI_REG_BITS(reg, bit_map, shift) ((READ_PERI_REG(reg))&((bit_map)<<(shift)))>>shift
//set bits of register controlled by mask and shift
#define SET_PERI_REG_BITS(reg,bit_map,value,shift) ({ \
(WRITE_PERI_REG((reg),(READ_PERI_REG(reg)&(~((bit_map)<<(shift))))|(((value) & bit_map)<<(shift)) )); \
})
//get field of register
#define GET_PERI_REG_BITS2(reg, mask,shift) ({ \
((READ_PERI_REG(reg)>>(shift))&(mask)); \
})
//}}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,63 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Wakeup main CPU from sleep or deep sleep.
*
* This raises a software interrupt signal, if the
* main CPU is configured the ULP as a wakeup source
* calling this function will make the main CPU to
* exit from sleep or deep sleep.
*/
void ulp_riscv_wakeup_main_processor(void);
/**
* @brief Rescues the cpu from monitor mode
*
* This function cancels the low power mode
* of the ULP-RISC-V, should be called
* every time the co-processor starts.
*
* @note by convenience this function is
* automatically called in startup code.
*/
void ulp_riscv_rescue_from_monitor(void);
/**
* @brief Finishes the ULP program and powers down the ULP
* until next wakeup.
*
* @note This function does not return. After called it will
* fully reset the ULP.
*
* @note Returning from main() in the ULP program results on
* calling this function.
*
* This function should be called after the ULP program Finishes
* its processing, it will trigger the timer for the next wakeup,
* put the ULP in monitor mode and triggers a reset.
*
*/
void __attribute__((noreturn)) ulp_riscv_shutdown(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,21 @@
.section .text
.global irq_vector
.global reset_vector
/* The reset vector, jumps to startup code */
reset_vector:
j __start
/* Interrupt handler */
.balign 16
irq_vector:
ret
__start:
/* setup the stack pointer */
la sp, __stack_top
call ulp_riscv_rescue_from_monitor
call main
call ulp_riscv_shutdown
loop:
j loop

View File

@ -0,0 +1,41 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ulp_riscv/ulp_riscv.h"
#include "ulp_riscv/ulp_riscv_utils.h"
void ulp_riscv_rescue_from_monitor(void)
{
/* Rescue RISCV from monitor state. */
CLEAR_PERI_REG_MASK(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_DONE | RTC_CNTL_COCPU_SHUT_RESET_EN);
}
void ulp_riscv_wakeup_main_processor(void)
{
SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_SW_CPU_INT);
}
void ulp_riscv_shutdown(void)
{
/* Setting the delay time after RISCV recv `DONE` signal, Ensure that action `RESET` can be executed in time. */
REG_SET_FIELD(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_SHUT_2_CLK_DIS, 0x3F);
/* suspends the ulp operation*/
SET_PERI_REG_MASK(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_DONE);
/* Resets the processor */
SET_PERI_REG_MASK(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_SHUT_RESET_EN);
while(1);
}

View File

@ -257,7 +257,8 @@ INPUT = \
##
## NOTE: for line below header_file.inc is not used
$(IDF_PATH)/components/ulp/include/$(IDF_TARGET)/ulp.h \
$(IDF_PATH)/components/ulp/include/ulp_common.h \
$(IDF_PATH)/components/ulp/include/esp32s2/ulp_riscv.h \
$(IDF_PATH)/components/ulp/include/ulp_common.h \
##
## Application Level Tracing - API Reference
##

View File

@ -164,6 +164,7 @@ ESP32S2_DOCS = ['esp32s2.rst',
'api-guides/ulps2_instruction_set.rst',
'api-guides/dfu.rst',
'api-guides/usb-console.rst',
'api-guides/ulp-risc-v.rst',
'api-reference/peripherals/hmac.rst',
'api-reference/peripherals/ds.rst',
'api-reference/peripherals/spi_slave_hd.rst',

View File

@ -36,6 +36,7 @@ API Guides
Tools <tools/index>
ULP Coprocessor <ulp>
:esp32: ULP Coprocessor (Legacy GNU Make) <ulp-legacy>
:esp32s2: ULP-RISC-V Coprocessor <ulp-risc-v>
Unit Testing <unit-tests>
:esp32: Unit Testing (Legacy GNU Make) <unit-tests-legacy>
:esp32s2: USB Console <usb-console>

View File

@ -20,6 +20,11 @@
.. tool-esp32s2ulp-elf-notes
---
.. tool-riscv-none-embed-gcc-notes
---
.. tool-openocd-esp32-notes

View File

@ -0,0 +1,151 @@
ULP-RISC-V Coprocessor programming
==================================
.. toctree::
:maxdepth: 1
The ULP-RISC-V coprocessor is a variant of the ULP, present in ESP32-S2. Similar to ULP, ULP RISC-V coprocessor can perform tasks such as sensor readings while the main CPU stays in low power modes. The main difference from the FSM ULP is this variant can be programmed in C using standard GNU tools. The ULP-RISC-V coprocessor can access the RTC_SLOW_MEM memory region, and registers in RTC_CNTL, RTC_IO, and SARADC peripherals. The RISC-V processor is a 32-bit, fixed point machine. Its instruction set is based on RV32IMC which includes hardware multiplication and division, and compressed code.
Installing the ULP-RISC-V Toolchain
-----------------------------------
The ULP-RISC-V coprocessor code is written in C (assembly is also possible) and compiled using the `riscv-embedded toolchain`_.
If you have already set up ESP-IDF with CMake build system according to the :doc:`Getting Started Guide <../../get-started/index>`, then you need to perform an extra step to get the ULP-RISC-V toolchain installed, execute the command below inside the IDF folder::
$ idf_tools.py install riscv-none-embed-gcc
Compiling the ULP-RISC-V Code
-----------------------------
To compile the ULP-RISC-V code as part of the component, the following steps must be taken:
1. The ULP-RISC-V code, written in C or assembly (must use the `.S` extension), must be placed into a separate directory inside the component directory, for instance `ulp/`.
.. note: When registering the component (via ``idf_component_register``), this directory should not be added to the ``SRC_DIRS`` argument as it is currently done for the FSM ULP. See the step below for how to properly add ULP source files
2. Call ``ulp_embed_binary`` from the component CMakeLists.txt after registration. For example::
...
idf_component_register()
set(ulp_app_name ulp_${COMPONENT_NAME})
set(ulp_sources "ulp/ulp_c_source_file.c" "ulp/ulp_assembly_source_file.S")
set(ulp_exp_dep_srcs "ulp_c_source_file.c")
ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}")
The first argument to ``ulp_embed_binary`` specifies the ULP binary name. The name specified here will also be used by other generated artifacts
such as the ELF file, map file, header file and linker export file. The second argument specifies the ULP source files.
Finally, the third argument specifies the list of component source files which include the header file to be generated.
This list is needed to build the dependencies correctly and ensure that the generated header file will be created before any of these files are compiled.
See section below for the concept of generated header files for ULP applications.
3. Build the application as usual (e.g. `idf.py app`)
Inside, the build system will take the following steps to build ULP program:
1. **Run each source file through the C compiler and assembler.** This step generates the object files (.obj.c or .obj.S depending of source file processed) in the component build directory.
2. **Run the linker script template through the C preprocessor.** The template is located in ``components/ulp/ld`` directory.
4. **Link the object files into an output ELF file** (``ulp_app_name.elf``). The Map file (``ulp_app_name.map``) generated at this stage may be useful for debugging purposes.
5. **Dump the contents of the ELF file into a binary** (``ulp_app_name.bin``) which can then be embedded into the application.
6. **Generate a list of global symbols** (``ulp_app_name.sym``) in the ELF file using ``riscv-none-embed-nm``.
7. **Create an LD export script and header file** (``ulp_app_name.ld`` and ``ulp_app_name.h``) containing the symbols from ``ulp_app_name.sym``. This is done using the ``esp32ulp_mapgen.py`` utility.
8. **Add the generated binary to the list of binary files** to be embedded into the application.
Accessing the ULP-RISC-V Program Variables
------------------------------------------
Global symbols defined in the ULP-RISC-V program may be used inside the main program.
For example, the ULP-RISC-V program may define a variable ``measurement_count`` which will define the number of ADC measurements the program needs to make before waking up the chip from deep sleep
.. code-block:: c
volatile int measurement_count;
int some_function()
{
//read the measurement count for use it later.
int temp = measurement_count;
...do something.
}
The main program can access the global ULP-RISC-V program variables, the build system makes this possible by generating the ``${ULP_APP_NAME}.h`` and ``${ULP_APP_NAME}.ld`` files which define the global symbols present in the ULP program. Each global symbol defined in the ULP program is included in these files and are prefixed with ``ulp_``.
The header file contains the declaration of the symbol
.. code-block:: c
extern uint32_t ulp_measurement_count;
Note that all symbols (variables, arrays, functions) are declared as ``uint32_t``. For functions and arrays, take the address of the symbol and cast it to the appropriate type.
The generated linker script file defines the locations of symbols in RTC_SLOW_MEM::
PROVIDE ( ulp_measurement_count = 0x50000060 );
To access the ULP-RISC-V program variables from the main program, the generated header file should be included using an ``include`` statement. This will allow the ULP program variables to be accessed as regular variables
.. code-block:: c
#include "ulp_app_name.h"
void init_ulp_vars() {
ulp_measurement_count = 64;
}
Starting the ULP-RISC-V Program
-------------------------------
To run a ULP-RISC-V program, the main application needs to load the ULP program into RTC memory using the :cpp:func:`ulp_riscv_load_binary` function, and then start it using the :cpp:func:`ulp_riscv_run` function.
Note that `CONFIG_ESP32S2_ULP_COPROC_ENABLED` and `CONFIG_ESP32S2_ULP_COPROC_RISCV` options must be enabled in menuconfig to reserve memory for the ULP. "RTC slow memory reserved for coprocessor" option must be set to a value sufficient to store ULP code and data. If the application components contain multiple ULP programs, then the size of the RTC memory must be sufficient to hold the largest one.
Each ULP-RISC-V program is embedded into the ESP-IDF application as a binary blob. The application can reference this blob and load it in the following way (suppose ULP_APP_NAME was defined to ``ulp_app_name``)
.. code-block:: c
extern const uint8_t bin_start[] asm("_binary_ulp_app_name_bin_start");
extern const uint8_t bin_end[] asm("_binary_ulp_app_name_bin_end");
void start_ulp_program() {
ESP_ERROR_CHECK( ulp_riscv_load_binary( bin_start,
(bin_end - bin_start)) );
}
.. doxygenfunction:: ulp_riscv_load_binary()
Once the program is loaded into RTC memory, the application can start it, calling the :cpp:func:`ulp_riscv_run` function
.. code-block:: c
ESP_ERROR_CHECK( ulp_riscv_run() );
.. doxygenfunction:: ulp_riscv_run()
ULP-RISC-V Program Flow
-----------------------
The ULP-RISC-V coprocessor is started by a timer. The timer is started once :cpp:func:`ulp_riscv_run` is called. The timer counts the number of RTC_SLOW_CLK ticks (by default, produced by an internal 150 kHz RC oscillator). The number of ticks is set using ``RTC_CNTL_ULP_CP_TIMER_1_REG`` register. When starting the ULP, ``RTC_CNTL_ULP_CP_TIMER_1_REG`` will be used to set the number of timer ticks.
The application can set ULP timer period values (RTC_CNTL_ULP_CP_TIMER_1_REG) using the :cpp:func:`ulp_set_wakeup_period` function.
Once the timer counts the number of ticks set in the ``RTC_CNTL_ULP_CP_TIMER_1_REG`` register, the ULP coprocessor will power up and start running the program from the entry point set in the call to :cpp:func:`ulp_riscv_run`.
The program runs until the field ``RTC_CNTL_COCPU_DONE`` in register ``RTC_CNTL_COCPU_CTRL_REG`` gets written or when a trap occurs due to illegal processor state. Once the program halts, the ULP coprocessor will power down, and the timer will be started again.
To disable the timer (effectively preventing the ULP program from running again), please clear the ``RTC_CNTL_ULP_CP_SLP_TIMER_EN`` bit in the ``RTC_CNTL_STATE0_REG`` register. This can be done both from the ULP code and from the main program.
.. _riscv-embedded toolchain: https://xpack.github.io/riscv-none-embed-gcc/

View File

@ -16,12 +16,13 @@
# sphinx==1.8.4
# breathe==4.11.1
#
ulp-legacy.rst:line: WARNING: Duplicate declaration, esp_err_t ulp_load_binary(uint32_t load_addr, const uint8_t * program_binary, size_t program_size)
ulp-legacy.rst:line: WARNING: Duplicate declaration, esp_err_t ulp_run(uint32_t entry_point)
ulp-legacy.rst:line: WARNING: Duplicate declaration, esp_err_t ulp_set_wakeup_period(size_t period_index, uint32_t period_us)
ulp.rst:line: WARNING: Duplicate declaration, esp_err_t ulp_load_binary(uint32_t load_addr, const uint8_t * program_binary, size_t program_size)
ulp.rst:line: WARNING: Duplicate declaration, esp_err_t ulp_run(uint32_t entry_point)
ulp.rst:line: WARNING: Duplicate declaration, esp_err_t ulp_set_wakeup_period(size_t period_index, uint32_t period_us)
ulp-legacy.rst:line: WARNING: Duplicate declaration, esp_err_t ulp_load_binary(uint32_t load_addr, const uint8_t * program_binary, size_t program_size)
ulp-legacy.rst:line: WARNING: Duplicate declaration, esp_err_t ulp_run(uint32_t entry_point)
ulp-legacy.rst:line: WARNING: Duplicate declaration, esp_err_t ulp_set_wakeup_period(size_t period_index, uint32_t period_us)
ulp-risc-v.rst:line: WARNING: Duplicate declaration, esp_err_t ulp_set_wakeup_period(size_t period_index, uint32_t period_us)
README.rst:line: WARNING: Duplicate declaration, esp_err_t ulp_run(uint32_t entry_point)

View File

@ -26,6 +26,7 @@ API 指南
:esp32: Secure Boot V2 <../security/secure-boot-v2>
ULP 协处理器 <ulp>
:esp32: ULP (传统 GNU Make) <ulp-legacy>
:esp32s2: ULP-RISC-V Coprocessor <ulp-risc-v>
单元测试 <unit-tests>
:esp32: 单元测试 (传统 GNU Make) <unit-tests-legacy>
应用层跟踪 <app_trace>

View File

@ -22,6 +22,11 @@
.. tool-esp32s2ulp-elf-notes
---
.. tool-riscv-none-embed-gcc-notes
---
.. tool-openocd-esp32-notes

View File

@ -0,0 +1 @@
.. include:: ../../en/api-guides/ulp-risc-v.rst

View File

@ -6,4 +6,3 @@
PROJECT_NAME := ulp-example
include $(IDF_PATH)/make/project.mk

View File

@ -1,5 +1,3 @@
#
# ULP support additions to component makefile.
#
@ -11,7 +9,8 @@ ULP_APP_NAME ?= ulp_$(COMPONENT_NAME)
# Files should be placed into a separate directory (in this case, ulp/),
# which should not be added to COMPONENT_SRCDIRS.
ULP_S_SOURCES = $(addprefix $(COMPONENT_PATH)/ulp/, \
pulse_cnt.S wake_up.S\
pulse_cnt.S \
wake_up.S \
)
#
# 3. List all the component object files which include automatically

View File

@ -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.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ulp-riscv-example)

View File

@ -0,0 +1,31 @@
| Supported Targets | ESP32-S2 |
| ----------------- | -------- |
# ULP-RISC-V simple example with GPIO Polling:
This example demonstrates how to program the ULP-RISC-V coprocessor to poll a gpio and wakeup the main CPU when it changes its state;
ULP program written in C can be found across `ulp/main.c`. The build system compiles and links this program, converts it into binary format, and embeds it into the .rodata section of the ESP-IDF application.
At runtime, the application running inside the main CPU loads ULP program into the `RTC_SLOW_MEM` memory region using `ulp_riscv_load_binary` function. The main code then configures the ULP wakeup period and starts the coprocessor by using `ulp_riscv_run`. Once the ULP program is started, it runs periodically, with the period set by the main program. The main program enables ULP wakeup source and puts the chip into deep sleep mode.
When the ULP program finds an state changing in the pin, it saves the current state and sends a wakeup signal to the main CPU.
Upon wakeup, the main program prints the current level of the measured gpio and go back to the deep sleep.
In this example the input signal is connected to GPIO0. Note that this pin was chosen because most development boards have a button connected to it, so the pulses to be counted can be generated by pressing the button. For real world applications this is not a good choice of a pin, because GPIO0 also acts as a bootstrapping pin. To change the pin number, check the ESP32-S2 Chip Pin List document and adjust `gpio_num` and `ulp_io_number` variables in main.c.
## Example output
```
Not a ULP wakeup, initializing it!
Entering in deep sleep
...
ULP-RISC-V woke up the main CPU!
ULP-RISC-V read changes in GPIO_0 current is: High
Entering in deep sleep
```

View File

@ -0,0 +1,27 @@
# Set usual component variables
set(COMPONENT_SRCS "ulp_riscv_example_main.c")
set(COMPONENT_ADD_INCLUDEDIRS "")
set(COMPONENT_REQUIRES soc nvs_flash ulp driver)
register_component()
#
# ULP support additions to component CMakeLists.txt.
#
# 1. The ULP app name must be unique (if multiple components use ULP).
set(ulp_app_name ulp_${COMPONENT_NAME})
#
# 2. Specify all C and Assembly source files.
# Files should be placed into a separate directory (in this case, ulp/),
# which should not be added to COMPONENT_SRCS.
set(ulp_riscv_sources "ulp/main.c")
#
# 3. List all the component source files which include automatically
# generated ULP export file, ${ulp_app_name}.h:
set(ulp_exp_dep_srcs "ulp_riscv_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_riscv_sources}" "${ulp_exp_dep_srcs}")

View File

@ -0,0 +1,126 @@
// Copyright 2010-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "ulp_riscv/ulp_riscv.h"
#include "soc/rtc_io_reg.h"
typedef enum {
GPIO_NUM_0 = 0, /*!< GPIO0, input and output */
GPIO_NUM_1 = 1, /*!< GPIO1, input and output */
GPIO_NUM_2 = 2, /*!< GPIO2, input and output */
GPIO_NUM_3 = 3, /*!< GPIO3, input and output */
GPIO_NUM_4 = 4, /*!< GPIO4, input and output */
GPIO_NUM_5 = 5, /*!< GPIO5, input and output */
GPIO_NUM_6 = 6, /*!< GPIO6, input and output */
GPIO_NUM_7 = 7, /*!< GPIO7, input and output */
GPIO_NUM_8 = 8, /*!< GPIO8, input and output */
GPIO_NUM_9 = 9, /*!< GPIO9, input and output */
GPIO_NUM_10 = 10, /*!< GPIO10, input and output */
GPIO_NUM_11 = 11, /*!< GPIO11, input and output */
GPIO_NUM_12 = 12, /*!< GPIO12, input and output */
GPIO_NUM_13 = 13, /*!< GPIO13, input and output */
GPIO_NUM_14 = 14, /*!< GPIO14, input and output */
GPIO_NUM_15 = 15, /*!< GPIO15, input and output */
GPIO_NUM_16 = 16, /*!< GPIO16, input and output */
GPIO_NUM_17 = 17, /*!< GPIO17, input and output */
GPIO_NUM_18 = 18, /*!< GPIO18, input and output */
GPIO_NUM_19 = 19, /*!< GPIO19, input and output */
GPIO_NUM_20 = 20,
GPIO_NUM_21 = 21, /*!< GPIO21, input and output */
} gpio_num_t;
typedef enum {
RTCIO_MODE_OUTPUT = 0,
RTCIO_MODE_OUTPUT_OD = 1,
} rtc_io_out_mode_t;
static inline void example_ulp_gpio_init(gpio_num_t gpio_num)
{
SET_PERI_REG_MASK(RTC_IO_TOUCH_PAD0_REG + gpio_num*4, RTC_IO_TOUCH_PAD0_MUX_SEL);
REG_SET_FIELD(RTC_IO_TOUCH_PAD0_REG + gpio_num*4, RTC_IO_TOUCH_PAD0_FUN_SEL, 0);
}
static inline void example_ulp_gpio_deinit(gpio_num_t gpio_num)
{
CLEAR_PERI_REG_MASK(RTC_IO_TOUCH_PAD0_REG + gpio_num*4, RTC_IO_TOUCH_PAD0_MUX_SEL);
}
static inline void example_ulp_gpio_output_enable(gpio_num_t gpio_num)
{
REG_SET_FIELD(RTC_GPIO_ENABLE_W1TS_REG, RTC_GPIO_ENABLE_W1TS, BIT(gpio_num));
}
static inline void example_ulp_gpio_output_disable(gpio_num_t gpio_num)
{
REG_SET_FIELD(RTC_GPIO_ENABLE_W1TC_REG, RTC_GPIO_ENABLE_W1TC, BIT(gpio_num));
}
static inline void example_ulp_gpio_input_enable(gpio_num_t gpio_num)
{
SET_PERI_REG_MASK(RTC_IO_TOUCH_PAD0_REG + gpio_num*4, RTC_IO_TOUCH_PAD0_FUN_IE);
}
static inline void example_ulp_gpio_input_disable(gpio_num_t gpio_num)
{
CLEAR_PERI_REG_MASK(RTC_IO_TOUCH_PAD0_REG + gpio_num*4, RTC_IO_TOUCH_PAD0_FUN_IE);
}
static inline void example_ulp_gpio_output_level(gpio_num_t gpio_num, uint8_t level)
{
if (level) {
REG_SET_FIELD(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS, BIT(gpio_num));
} else {
REG_SET_FIELD(RTC_GPIO_OUT_W1TC_REG, RTC_GPIO_OUT_DATA_W1TS, BIT(gpio_num));
}
}
static inline uint8_t example_ulp_gpio_get_level(gpio_num_t gpio_num)
{
return (uint8_t)((REG_GET_FIELD(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT) & BIT(gpio_num)) ? 1 : 0);
}
static inline void example_ulp_gpio_set_output_mode(gpio_num_t gpio_num, rtc_io_out_mode_t mode)
{
REG_SET_FIELD(RTC_IO_TOUCH_PAD0_REG + gpio_num*4, RTC_IO_TOUCH_PAD0_DRV, mode);
}
static inline void example_ulp_gpio_pullup(gpio_num_t gpio_num)
{
SET_PERI_REG_MASK(RTC_IO_TOUCH_PAD0_REG + gpio_num*4, RTC_IO_TOUCH_PAD0_RUE);
}
static inline void example_ulp_gpio_pullup_disable(gpio_num_t gpio_num)
{
CLEAR_PERI_REG_MASK(RTC_IO_TOUCH_PAD0_REG + gpio_num*4, RTC_IO_TOUCH_PAD0_RUE);
}
static inline void example_ulp_gpio_pulldown(gpio_num_t gpio_num)
{
SET_PERI_REG_MASK(RTC_IO_TOUCH_PAD0_REG + gpio_num*4, RTC_IO_TOUCH_PAD0_RDE);
}
static inline void example_ulp_gpio_pulldown_disable(gpio_num_t gpio_num)
{
CLEAR_PERI_REG_MASK(RTC_IO_TOUCH_PAD0_REG + gpio_num*4, RTC_IO_TOUCH_PAD0_RDE);
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,42 @@
/* ULP-RISC-V example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
This code runs on ULP-RISC-V coprocessor
*/
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "ulp_riscv/ulp_riscv.h"
#include "ulp_riscv/ulp_riscv_utils.h"
#include "example_ulp_gpio.h"
static bool gpio_level = false;
/* this variable will be exported as a public symbol, visible from main CPU: */
bool gpio_level_previous = false;
int main (void)
{
gpio_level = (bool)example_ulp_gpio_get_level(GPIO_NUM_0);
gpio_level_previous = gpio_level;
while(1) {
gpio_level = (bool)example_ulp_gpio_get_level(GPIO_NUM_0);
/* Wakes up the main CPU if pin changed its state */
if(gpio_level != gpio_level_previous) {
gpio_level_previous = gpio_level;
ulp_riscv_wakeup_main_processor();
break;
}
}
/* ulp_riscv_shutdown() is called automatically when main exits */
return 0;
}

View File

@ -0,0 +1,75 @@
/* ULP riscv example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.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 "esp32s2/ulp.h"
#include "esp32s2/ulp_riscv.h"
#include "ulp_main.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end");
static void init_ulp_program(void);
void app_main(void)
{
/* Initialize selected GPIO as RTC IO, enable input, disable pullup and pulldown */
rtc_gpio_init(GPIO_NUM_0);
rtc_gpio_set_direction(GPIO_NUM_0, RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_pulldown_dis(GPIO_NUM_0);
rtc_gpio_pullup_dis(GPIO_NUM_0);
rtc_gpio_hold_en(GPIO_NUM_0);
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
/* not a wakeup from ULP, load the firmware */
if (cause != ESP_SLEEP_WAKEUP_ULP) {
printf("Not a ULP-RISC-V wakeup, initializing it! \n");
init_ulp_program();
}
/* ULP Risc-V read and detected a change in GPIO_0, prints */
if (cause == ESP_SLEEP_WAKEUP_ULP) {
printf("ULP-RISC-V woke up the main CPU! \n");
printf("ULP-RISC-V read changes in GPIO_0 current is: %s \n",
(bool)(ulp_gpio_level_previous == 0) ? "Low" : "High" );
}
/* Go back to sleep, only the ULP Risc-V will run */
printf("Entering in deep sleep\n\n");
/* Small delay to ensure the messages are printed */
vTaskDelay(100);
ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup());
esp_deep_sleep_start();
}
static void init_ulp_program(void)
{
esp_err_t err = ulp_riscv_load_binary(ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start));
ESP_ERROR_CHECK(err);
/* The first argument is the period index, which is not used by the ULP-RISC-V timer
* The second argument is the period in microseconds, which gives a wakeup time period of: 20ms
*/
ulp_set_wakeup_period(0, 20000);
/* Start the program */
err = ulp_riscv_run();
ESP_ERROR_CHECK(err);
}

View File

@ -0,0 +1,10 @@
# Enable ULP
CONFIG_ESP32S2_ULP_COPROC_ENABLED=y
CONFIG_ESP32S2_ULP_COPROC_RISCV=y
CONFIG_ESP32S2_ULP_COPROC_RESERVE_MEM=4096
# Set log level to Warning to produce clean output
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
CONFIG_BOOTLOADER_LOG_LEVEL=2
CONFIG_LOG_DEFAULT_LEVEL_WARN=y
CONFIG_LOG_DEFAULT_LEVEL=2

View File

@ -144,6 +144,8 @@ components/fatfs/src/diskio.h
components/fatfs/diskio/diskio_sdmmc.h
components/openssl/include/openssl/ssl.h
components/ulp/include/ulp_common.h
components/ulp/include/esp32s2/ulp_riscv.h
components/lwip/include/apps/sntp/sntp.h
components/mbedtls/esp_crt_bundle/include/esp_crt_bundle.h
components/wifi_provisioning/include/wifi_provisioning/scheme_softap.h

View File

@ -59,6 +59,9 @@ build_ssc_esp32s2:
variables:
LOG_PATH: "$CI_PROJECT_DIR/log_ut_cmake"
script:
# RISC-V toolchain is optional but ULP may need it, so install:
- $IDF_PATH/tools/idf_tools.py install riscv-none-embed-gcc
- . $IDF_PATH/export.sh
- export EXTRA_CFLAGS=${PEDANTIC_CFLAGS}
- export EXTRA_CXXFLAGS=${PEDANTIC_CXXFLAGS}
- mkdir -p ${LOG_PATH}
@ -93,6 +96,9 @@ build_esp_idf_tests_cmake_esp32s2:
- $BOT_LABEL_REGULAR_TEST
- $BOT_LABEL_WEEKEND_TEST
script:
# RISC-V toolchain is optional but ULP may need it, so install:
- $IDF_PATH/tools/idf_tools.py install riscv-none-embed-gcc
- . $IDF_PATH/export.sh
# it's not possible to build 100% out-of-tree and have the "artifacts"
# mechanism work, but this is the next best thing
- mkdir ${BUILD_PATH}

View File

@ -112,6 +112,59 @@
}
]
},
{
"description": "Toolchain for RISC-V",
"export_paths": [
[
"xPacks",
"riscv-none-embed-gcc",
"8.2.0-3.1",
"bin"
]
],
"export_vars": {},
"info_url": "https://github.com/xpack-dev-tools/riscv-none-embed-gcc-xpack",
"install": "on_request",
"license": "MIT",
"name": "riscv-none-embed-gcc",
"version_cmd": [
"riscv-none-embed-gcc",
"--version"
],
"version_regex": "(riscv-none-embed-gcc) \\(xPack GNU RISC-V Embedded GCC, 64-bit\\) (8.2.0)",
"version_regex_replace": "\\1-\\2",
"versions": [
{
"linux-amd64": {
"sha256": "3d40fab50ebad8424ff85748f25d2eaee50f86a5d5222abd7a45a2e490f1e4f5",
"size": 216042047,
"url": "https://github.com/xpack-dev-tools/riscv-none-embed-gcc-xpack/releases/download/v8.2.0-3.1/xpack-riscv-none-embed-gcc-8.2.0-3.1-linux-x64.tgz"
},
"linux-i686": {
"sha256": "2e856ee33ef544a2405183366cdf299da2cca697e8cc57627dfaad8ab4460a99",
"size": 219854326,
"url": "https://github.com/xpack-dev-tools/riscv-none-embed-gcc-xpack/releases/download/v8.2.0-3.1/xpack-riscv-none-embed-gcc-8.2.0-3.1-linux-x32.tgz"
},
"macos": {
"sha256": "48ece50b7272a8e49e56e37f54f9962f93dce4ca44f1bb4c8113fab0230fbdf1",
"size": 214715632,
"url": "https://github.com/xpack-dev-tools/riscv-none-embed-gcc-xpack/releases/download/v8.2.0-3.1/xpack-riscv-none-embed-gcc-8.2.0-3.1-darwin-x64.tgz"
},
"name": "riscv-none-embed-gcc-8.2.0",
"status": "recommended",
"win32": {
"sha256": "78bcd52f7b404133de3b2f9568e7101c1de747f98db3a7b0d6251b75a1754867",
"size": 232387519,
"url": "https://github.com/xpack-dev-tools/riscv-none-embed-gcc-xpack/releases/download/v8.2.0-3.1/xpack-riscv-none-embed-gcc-8.2.0-3.1-win32-x32.zip"
},
"win64": {
"sha256": "98b60720607f1400081806d60d70796b30399f8b426e2c790a4abb0bffb9e5ec",
"size": 241927614,
"url": "https://github.com/xpack-dev-tools/riscv-none-embed-gcc-xpack/releases/download/v8.2.0-3.1/xpack-riscv-none-embed-gcc-8.2.0-3.1-win32-x64.zip"
}
}
]
},
{
"description": "Toolchain for ESP32 ULP coprocessor",
"export_paths": [

View File

@ -1,3 +1,6 @@
CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y
CONFIG_ESP32S2_MEMPROT_FEATURE=n
CONFIG_ESP32S2_ULP_COPROC_ENABLED=y
CONFIG_ESP32S2_MEMPROT_FEATURE=n
CONFIG_ESP32S2_ULP_COPROC_RISCV=y
CONFIG_ESP32S2_ULP_COPROC_RESERVE_MEM=4096