From 5a916ce12656cb7b171ea2c1262679ae1eb3519c Mon Sep 17 00:00:00 2001 From: Roland Dobai Date: Mon, 22 Jul 2019 16:04:03 +0200 Subject: [PATCH] Support ELF files loadable with gdb --- .gitlab-ci.yml | 2 +- Kconfig | 77 +++++++++++ components/bootloader/CMakeLists.txt | 2 +- components/bootloader/project_include.cmake | 4 +- components/esp32/Kconfig | 5 + components/esp32/clk.c | 4 +- components/esp32/cpu_start.c | 45 ++++-- components/esp32/ld/esp32.ld | 16 +++ components/esp32/ld/esp32.project.ld.in | 28 +++- components/esp32/ld/esp32_fragments.lf | 8 +- components/esp32/panic.c | 2 +- .../esp_rom/esp32/ld/esp32.rom.spiflash.ld | 1 + components/esptool_py/CMakeLists.txt | 2 +- components/esptool_py/Makefile.projbuild | 4 + components/esptool_py/project_include.cmake | 28 ++-- components/soc/src/memory_layout_utils.c | 6 +- examples/get-started/hello_world/.gdbinit.ci | 13 ++ .../hello_world/loadable_elf_example_test.py | 129 ++++++++++++++++++ examples/get-started/hello_world/sdkconfig.ci | 6 + make/project.mk | 10 ++ tools/ci/config/target-test.yml | 13 ++ tools/idf.py | 4 + tools/tiny-test-fw/IDF/IDFApp.py | 25 ++-- .../components/test_utils/test_runner.c | 8 ++ 24 files changed, 387 insertions(+), 55 deletions(-) create mode 100644 examples/get-started/hello_world/.gdbinit.ci create mode 100644 examples/get-started/hello_world/loadable_elf_example_test.py create mode 100644 examples/get-started/hello_world/sdkconfig.ci diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5bf113b946..5d94e62310 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -66,7 +66,7 @@ variables: rm -rf "$CUSTOM_TOOLCHAIN_PATH" .setup_tools_unless_target_test: &setup_tools_unless_target_test | - if [ "$CI_JOB_STAGE" != "target_test" ]; then + if [[ "$SETUP_TOOLS" == "1" || "$CI_JOB_STAGE" != "target_test" ]]; then tools/idf_tools.py --non-interactive install && eval "$(tools/idf_tools.py --non-interactive export)" || exit 1 fi diff --git a/Kconfig b/Kconfig index 129a0abc69..92c76c38d5 100644 --- a/Kconfig +++ b/Kconfig @@ -68,6 +68,83 @@ mainmenu "Espressif IoT Development Framework Configuration" endmenu # SDK tool configuration + menu "Build type" + + choice APP_BUILD_TYPE + prompt "Application build type" + default APP_BUILD_TYPE_APP_2NDBOOT + help + Select the way the application is built. + + By default, the application is built as a binary file in a format compatible with + the ESP32 bootloader. In addition to this application, 2nd stage bootloader is + also built. Application and bootloader binaries can be written into flash and + loaded/executed from there. + + Another option, useful for only very small and limited applications, is to only link + the .elf file of the application, such that it can be loaded directly into RAM over + JTAG. Note that since IRAM and DRAM sizes are very limited, it is not possible to + build any complex application this way. However for kinds of testing and debugging, + this option may provide faster iterations, since the application does not need to be + written into flash. + Note that at the moment, ESP-IDF does not contain all the startup code required to + initialize the CPUs and ROM memory (data/bss). Therefore it is necessary to execute + a bit of ROM code prior to executing the application. A gdbinit file may look as follows: + + # Connect to a running instance of OpenOCD + target remote :3333 + # Reset and halt the target + mon reset halt + # Run to a specific point in ROM code, + # where most of initialization is complete. + thb *0x40007901 + c + # Load the application into RAM + load + # Run till app_main + tb app_main + c + + Execute this gdbinit file as follows: + + xtensa-esp32-elf-gdb build/app-name.elf -x gdbinit + + Recommended sdkconfig.defaults for building loadable ELF files is as follows. + CONFIG_APP_BUILD_TYPE_ELF_RAM is required, other options help reduce application + memory footprint. + + CONFIG_APP_BUILD_TYPE_ELF_RAM=y + CONFIG_VFS_SUPPORT_TERMIOS= + CONFIG_NEWLIB_NANO_FORMAT=y + CONFIG_ESP32_PANIC_PRINT_HALT=y + CONFIG_ESP32_DEBUG_STUBS_ENABLE= + CONFIG_ESP_ERR_TO_NAME_LOOKUP= + + + config APP_BUILD_TYPE_APP_2NDBOOT + bool + prompt "Default (binary application + 2nd stage bootloader)" + select APP_BUILD_GENERATE_BINARIES + select APP_BUILD_BOOTLOADER + select APP_BUILD_USE_FLASH_SECTIONS + + config APP_BUILD_TYPE_ELF_RAM + bool + prompt "ELF file, loadable into RAM (EXPERIMENTAL))" + endchoice # APP_BUILD_TYPE + + # Hidden options, set according to the choice above + config APP_BUILD_GENERATE_BINARIES + bool # Whether to generate .bin files or not + + config APP_BUILD_BOOTLOADER + bool # Whether to build the bootloader + + config APP_BUILD_USE_FLASH_SECTIONS + bool # Whether to place code/data into memory-mapped flash sections + + endmenu # Build type + source "$COMPONENT_KCONFIGS_PROJBUILD" menu "Compiler options" diff --git a/components/bootloader/CMakeLists.txt b/components/bootloader/CMakeLists.txt index afe1ad639b..bc80cbbd94 100644 --- a/components/bootloader/CMakeLists.txt +++ b/components/bootloader/CMakeLists.txt @@ -1,7 +1,7 @@ idf_component_register(PRIV_REQUIRES partition_table) # Do not generate flash file when building bootloader or is in early expansion of the build -if(BOOTLOADER_BUILD) +if(BOOTLOADER_BUILD OR NOT CONFIG_APP_BUILD_BOOTLOADER) return() endif() diff --git a/components/bootloader/project_include.cmake b/components/bootloader/project_include.cmake index fa26fd097f..d883a234e8 100644 --- a/components/bootloader/project_include.cmake +++ b/components/bootloader/project_include.cmake @@ -1,7 +1,7 @@ set(BOOTLOADER_OFFSET 0x1000) # Do not generate flash file when building bootloader -if(BOOTLOADER_BUILD) +if(BOOTLOADER_BUILD OR NOT CONFIG_APP_BUILD_BOOTLOADER) return() endif() @@ -125,4 +125,4 @@ endif() # So for now we just have the top-level build remove the final build products... set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES - ${bootloader_binary_files}) \ No newline at end of file + ${bootloader_binary_files}) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 5a512f5627..b58e5cfe69 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -738,6 +738,11 @@ menu "ESP32-specific" Enabling this setting adds approximately 1KB to the app's IRAM usage. + config ESP32_APP_INIT_CLK + bool + default y if ESP32_COMPATIBLE_PRE_V2_1_BOOTLOADERS + default y if APP_BUILD_TYPE_ELF_RAM + config ESP32_RTCDATA_IN_FAST_MEM bool "Place RTC_DATA_ATTR and RTC_RODATA_ATTR variables into RTC fast memory segment" default n diff --git a/components/esp32/clk.c b/components/esp32/clk.c index fd5c477618..28bcbec7fe 100644 --- a/components/esp32/clk.c +++ b/components/esp32/clk.c @@ -75,7 +75,7 @@ void esp_clk_init(void) rtc_config_t cfg = RTC_CONFIG_DEFAULT(); rtc_init(cfg); -#ifdef CONFIG_ESP32_COMPATIBLE_PRE_V2_1_BOOTLOADERS +#if (CONFIG_ESP32_COMPATIBLE_PRE_V2_1_BOOTLOADERS || CONFIG_ESP32_APP_INIT_CLK) /* Check the bootloader set the XTAL frequency. Bootloaders pre-v2.1 don't do this. @@ -293,6 +293,8 @@ void esp_perip_clk_init(void) DPORT_I2S1_CLK_EN | DPORT_SPI_DMA_CLK_EN; + common_perip_clk &= ~DPORT_SPI01_CLK_EN; + #if CONFIG_SPIRAM_SPEED_80M //80MHz SPIRAM uses SPI2/SPI3 as well; it's initialized before this is called. Because it is used in //a weird mode where clock to the peripheral is disabled but reset is also disabled, it 'hangs' diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index e3a4bc11ed..ebea01b62b 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -72,6 +72,11 @@ #include "esp_efuse.h" #include "bootloader_flash_config.h" +#ifdef CONFIG_APP_BUILD_TYPE_ELF_RAM +#include "esp32/rom/efuse.h" +#include "esp32/rom/spi_flash.h" +#endif // CONFIG_APP_BUILD_TYPE_ELF_RAM + #define STRINGIFY(s) STRINGIFY2(s) #define STRINGIFY2(s) #s @@ -391,6 +396,32 @@ void start_cpu0_default(void) #ifndef CONFIG_FREERTOS_UNICORE esp_dport_access_int_init(); #endif + + bootloader_flash_update_id(); +#if !CONFIG_SPIRAM_BOOT_INIT + // Read the application binary image header. This will also decrypt the header if the image is encrypted. + esp_image_header_t fhdr = {0}; +#ifdef CONFIG_APP_BUILD_TYPE_ELF_RAM + fhdr.spi_mode = ESP_IMAGE_SPI_MODE_DIO; + fhdr.spi_speed = ESP_IMAGE_SPI_SPEED_40M; + fhdr.spi_size = ESP_IMAGE_FLASH_SIZE_4MB; + + extern void esp_rom_spiflash_attach(uint32_t, bool); + esp_rom_spiflash_attach(ets_efuse_get_spiconfig(), false); + esp_rom_spiflash_unlock(); +#else + // This assumes that DROM is the first segment in the application binary, i.e. that we can read + // the binary header through cache by accessing SOC_DROM_LOW address. + memcpy(&fhdr, (void*) SOC_DROM_LOW, sizeof(fhdr)); +#endif // CONFIG_APP_BUILD_TYPE_ELF_RAM + + // If psram is uninitialized, we need to improve some flash configuration. + bootloader_flash_clock_config(&fhdr); + bootloader_flash_gpio_config(&fhdr); + bootloader_flash_dummy_config(&fhdr); + bootloader_flash_cs_timing_config(); +#endif //!CONFIG_SPIRAM_BOOT_INIT + spi_flash_init(); /* init default OS-aware flash access critical section */ spi_flash_guard_set(&g_flash_guard_default_ops); @@ -424,20 +455,6 @@ void start_cpu0_default(void) esp_coex_adapter_register(&g_coex_adapter_funcs); #endif - bootloader_flash_update_id(); -#if !CONFIG_SPIRAM_BOOT_INIT - // Read the application binary image header. This will also decrypt the header if the image is encrypted. - esp_image_header_t fhdr = {0}; - // This assumes that DROM is the first segment in the application binary, i.e. that we can read - // the binary header through cache by accessing SOC_DROM_LOW address. - memcpy(&fhdr, (void*) SOC_DROM_LOW, sizeof(fhdr)); - // If psram is uninitialized, we need to improve some flash configuration. - bootloader_flash_clock_config(&fhdr); - bootloader_flash_gpio_config(&fhdr); - bootloader_flash_dummy_config(&fhdr); - bootloader_flash_cs_timing_config(); -#endif - portBASE_TYPE res = xTaskCreatePinnedToCore(&main_task, "main", ESP_TASK_MAIN_STACK, NULL, ESP_TASK_MAIN_PRIO, NULL, 0); diff --git a/components/esp32/ld/esp32.ld b/components/esp32/ld/esp32.ld index a52b56f90d..0400fc2ab3 100644 --- a/components/esp32/ld/esp32.ld +++ b/components/esp32/ld/esp32.ld @@ -49,6 +49,7 @@ MEMORY /* IRAM for PRO cpu. Not sure if happy with this, this is MMU area... */ iram0_0_seg (RX) : org = 0x40080000, len = 0x20000 +#ifdef CONFIG_APP_BUILD_USE_FLASH_SECTIONS /* Even though the segment name is iram, it is actually mapped to flash */ iram0_2_seg (RX) : org = 0x400D0018, len = 0x330000-0x18 @@ -58,6 +59,7 @@ MEMORY which is flashed to the chip has a 0x18 byte file header. Setting this offset makes it simple to meet the flash cache MMU's constraint that (paddr % 64KB == vaddr % 64KB).) */ +#endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS /* Shared data RAM, excluding memory reserved for ROM bss/data/stack. @@ -72,10 +74,12 @@ MEMORY dram0_0_seg (RW) : org = 0x3FFB0000 + CONFIG_BT_RESERVE_DRAM, len = DRAM0_0_SEG_LEN - CONFIG_BT_RESERVE_DRAM +#ifdef CONFIG_APP_BUILD_USE_FLASH_SECTIONS /* Flash mapped constant data */ drom0_0_seg (R) : org = 0x3F400018, len = 0x400000-0x18 /* (See iram0_2_seg for meaning of 0x18 offset in the above.) */ +#endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS /* RTC fast memory (executable). Persists over deep sleep. */ @@ -116,3 +120,15 @@ REGION_ALIAS("rtc_data_location", rtc_slow_seg ); #else REGION_ALIAS("rtc_data_location", rtc_data_seg ); #endif + +#ifdef CONFIG_APP_BUILD_USE_FLASH_SECTIONS + REGION_ALIAS("default_code_seg", iram0_2_seg); +#else + REGION_ALIAS("default_code_seg", iram0_0_seg); +#endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS + +#ifdef CONFIG_APP_BUILD_USE_FLASH_SECTIONS + REGION_ALIAS("default_rodata_seg", drom0_0_seg); +#else + REGION_ALIAS("default_rodata_seg", dram0_0_seg); +#endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS diff --git a/components/esp32/ld/esp32.project.ld.in b/components/esp32/ld/esp32.project.ld.in index 872cecd330..b8e710e071 100644 --- a/components/esp32/ld/esp32.project.ld.in +++ b/components/esp32/ld/esp32.project.ld.in @@ -161,12 +161,8 @@ SECTIONS mapping[iram0_text] _iram_text_end = ABSOLUTE(.); - _iram_end = ABSOLUTE(.); } > iram0_0_seg - ASSERT(((_iram_text_end - ORIGIN(iram0_0_seg)) <= LENGTH(iram0_0_seg)), - "IRAM0 segment data does not fit.") - .dram0.data : { _data_start = ABSOLUTE(.); @@ -312,7 +308,7 @@ SECTIONS *(.tbss.*) _thread_local_end = ABSOLUTE(.); . = ALIGN(4); - } >drom0_0_seg + } >default_rodata_seg .flash.text : { @@ -334,5 +330,25 @@ SECTIONS the flash.text segment. */ _flash_cache_start = ABSOLUTE(0); - } >iram0_2_seg + } >default_code_seg + + /* Marks the end of IRAM code segment */ + .iram0.text_end (NOLOAD) : + { + . = ALIGN (4); + _iram_end = ABSOLUTE(.); + } > iram0_0_seg + + /* Marks the end of data, bss and possibly rodata */ + .dram0.heap_start (NOLOAD) : + { + . = ALIGN (8); + _heap_start = ABSOLUTE(.); + } > dram0_0_seg } + +ASSERT(((_iram_text_end - ORIGIN(iram0_0_seg)) <= LENGTH(iram0_0_seg)), + "IRAM0 segment data does not fit.") + +ASSERT(((_heap_start - ORIGIN(dram0_0_seg)) <= LENGTH(dram0_0_seg)), + "DRAM segment data does not fit.") diff --git a/components/esp32/ld/esp32_fragments.lf b/components/esp32/ld/esp32_fragments.lf index 932197caf0..abf8fa8bf7 100644 --- a/components/esp32/ld/esp32_fragments.lf +++ b/components/esp32/ld/esp32_fragments.lf @@ -50,8 +50,12 @@ entries: [scheme:default] entries: - text -> flash_text - rodata -> flash_rodata + if APP_BUILD_USE_FLASH_SECTIONS = y: + text -> flash_text + rodata -> flash_rodata + else: + text -> iram0_text + rodata -> dram0_data data -> dram0_data bss -> dram0_bss common -> dram0_bss diff --git a/components/esp32/panic.c b/components/esp32/panic.c index d84ec1ecb4..5a27f26d0d 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -450,7 +450,7 @@ static void esp_panic_dig_reset(void) ; } } -#endif +#endif // CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT static void putEntry(uint32_t pc, uint32_t sp) { diff --git a/components/esp_rom/esp32/ld/esp32.rom.spiflash.ld b/components/esp_rom/esp32/ld/esp32.rom.spiflash.ld index 709b358114..56497f1407 100644 --- a/components/esp_rom/esp32/ld/esp32.rom.spiflash.ld +++ b/components/esp_rom/esp32/ld/esp32.rom.spiflash.ld @@ -8,6 +8,7 @@ PROVIDE ( esp_rom_spiflash_erase_area = 0x400631ac ); PROVIDE ( esp_rom_spiflash_erase_block = 0x40062c4c ); PROVIDE ( esp_rom_spiflash_erase_chip = 0x40062c14 ); PROVIDE ( esp_rom_spiflash_erase_sector = 0x40062ccc ); +PROVIDE ( esp_rom_spiflash_attach = 0x40062a6c ); PROVIDE ( esp_rom_spiflash_lock = 0x400628f0 ); PROVIDE ( esp_rom_spiflash_read = 0x40062ed8 ); PROVIDE ( esp_rom_spiflash_config_readmode = 0x40062b64 ); /* SPIMasterReadModeCnfig */ diff --git a/components/esptool_py/CMakeLists.txt b/components/esptool_py/CMakeLists.txt index d30210531f..8fae3c6eb2 100644 --- a/components/esptool_py/CMakeLists.txt +++ b/components/esptool_py/CMakeLists.txt @@ -1,6 +1,6 @@ idf_component_register(REQUIRES bootloader) -if(NOT BOOTLOADER_BUILD) +if(NOT BOOTLOADER_BUILD AND CONFIG_APP_BUILD_GENERATE_BINARIES) string(REPLACE ";" " " ESPTOOLPY_FLASH_PROJECT_OPTIONS "${ESPTOOLPY_FLASH_OPTIONS}") set(ESPTOOLPY_FLASH_PROJECT_OPTIONS "${ESPTOOLPY_FLASH_PROJECT_OPTIONS}" diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index a351e8282f..9c00c4e3ff 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -68,7 +68,11 @@ endif APP_BIN_UNSIGNED ?= $(APP_BIN) $(APP_BIN_UNSIGNED): $(APP_ELF) $(ESPTOOLPY_SRC) | check_python_dependencies +ifeq ("$(CONFIG_APP_BUILD_GENERATE_BINARIES)","y") $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) $(ESPTOOL_ELF2IMAGE_OPTIONS) -o $@ $< +else + @echo "Skipping the BIN generation" +endif ifdef CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT encrypted-flash: all_binaries $(ESPTOOLPY_SRC) $(call prereq_if_explicit,erase_flash) partition_table_get_info | check_python_dependencies diff --git a/components/esptool_py/project_include.cmake b/components/esptool_py/project_include.cmake index af676a626b..f0433d8424 100644 --- a/components/esptool_py/project_include.cmake +++ b/components/esptool_py/project_include.cmake @@ -78,24 +78,28 @@ set(PROJECT_BIN "${elf_name}.bin") # # Add 'app.bin' target - generates with elf2image # -add_custom_command(OUTPUT "${build_dir}/.bin_timestamp" - COMMAND ${ESPTOOLPY} elf2image ${ESPTOOLPY_FLASH_OPTIONS} ${ESPTOOLPY_ELF2IMAGE_OPTIONS} - -o "${build_dir}/${unsigned_project_binary}" "${elf}" - COMMAND ${CMAKE_COMMAND} -E echo "Generated ${build_dir}/${unsigned_project_binary}" - COMMAND ${CMAKE_COMMAND} -E md5sum "${build_dir}/${unsigned_project_binary}" > "${build_dir}/.bin_timestamp" - DEPENDS ${elf} - VERBATIM - WORKING_DIRECTORY ${build_dir} - COMMENT "Generating binary image from built executable" - ) -add_custom_target(gen_project_binary DEPENDS "${build_dir}/.bin_timestamp") +if(CONFIG_APP_BUILD_GENERATE_BINARIES) + add_custom_command(OUTPUT "${build_dir}/.bin_timestamp" + COMMAND ${ESPTOOLPY} elf2image ${ESPTOOLPY_FLASH_OPTIONS} ${ESPTOOLPY_ELF2IMAGE_OPTIONS} + -o "${build_dir}/${unsigned_project_binary}" "${elf}" + COMMAND ${CMAKE_COMMAND} -E echo "Generated ${build_dir}/${unsigned_project_binary}" + COMMAND ${CMAKE_COMMAND} -E md5sum "${build_dir}/${unsigned_project_binary}" > "${build_dir}/.bin_timestamp" + DEPENDS ${elf} + VERBATIM + WORKING_DIRECTORY ${build_dir} + COMMENT "Generating binary image from built executable" + ) + add_custom_target(gen_project_binary DEPENDS "${build_dir}/.bin_timestamp") +endif() set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${build_dir}/${unsigned_project_binary}" ) -add_custom_target(app ALL DEPENDS gen_project_binary) +if(CONFIG_APP_BUILD_GENERATE_BINARIES) + add_custom_target(app ALL DEPENDS gen_project_binary) +endif() if(NOT BOOTLOADER_BUILD AND CONFIG_SECURE_SIGNED_APPS) if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES) diff --git a/components/soc/src/memory_layout_utils.c b/components/soc/src/memory_layout_utils.c index 29f752b9da..2f4acb5c2e 100644 --- a/components/soc/src/memory_layout_utils.c +++ b/components/soc/src/memory_layout_utils.c @@ -29,7 +29,7 @@ extern soc_reserved_region_t soc_reserved_memory_region_end; These variables have the start and end of the data and static IRAM area used by the program. Defined in the linker script. */ -extern int _data_start, _static_data_end, _iram_start, _iram_end; +extern int _data_start, _heap_start, _iram_start, _iram_end; /* static DRAM & IRAM chunks */ static const size_t EXTRA_RESERVED_REGIONS = 2; @@ -67,8 +67,8 @@ static void s_prepare_reserved_regions(soc_reserved_region_t *reserved, size_t c (count - EXTRA_RESERVED_REGIONS) * sizeof(soc_reserved_region_t)); /* Add the EXTRA_RESERVED_REGIONS at the beginning */ - reserved[0].start = (intptr_t)&_data_start; /* DRAM used by data+bss */ - reserved[0].end = (intptr_t)&_static_data_end; + reserved[0].start = (intptr_t)&_data_start; /* DRAM used by data+bss and possibly rodata */ + reserved[0].end = (intptr_t)&_heap_start; reserved[1].start = (intptr_t)&_iram_start; /* IRAM used by code */ reserved[1].end = (intptr_t)&_iram_end; diff --git a/examples/get-started/hello_world/.gdbinit.ci b/examples/get-started/hello_world/.gdbinit.ci new file mode 100644 index 0000000000..a7fb13506e --- /dev/null +++ b/examples/get-started/hello_world/.gdbinit.ci @@ -0,0 +1,13 @@ +# Connect to a running instance of OpenOCD +target remote 127.0.0.1:3333 +# Reset and halt the target +mon reset halt +# Run to a specific point in ROM code, +# where most of initialization is complete. +thb *0x40007901 +c +# Load the application into RAM +load +# Run till app_main +tb app_main +c diff --git a/examples/get-started/hello_world/loadable_elf_example_test.py b/examples/get-started/hello_world/loadable_elf_example_test.py new file mode 100644 index 0000000000..c6f9e2462f --- /dev/null +++ b/examples/get-started/hello_world/loadable_elf_example_test.py @@ -0,0 +1,129 @@ +import os +import pexpect +import serial +import sys +import threading +import time + +try: + import IDF +except ImportError: + test_fw_path = os.getenv('TEST_FW_PATH') + if test_fw_path and test_fw_path not in sys.path: + sys.path.insert(0, test_fw_path) + import IDF + +import Utility + + +class CustomProcess(object): + def __init__(self, cmd, logfile): + self.f = open(logfile, 'wb') + self.p = pexpect.spawn(cmd, timeout=60, logfile=self.f) + + def __enter__(self): + return self + + def close(self): + self.p.terminate(force=True) + + def __exit__(self, type, value, traceback): + self.close() + self.f.close() + + +class OCDProcess(CustomProcess): + def __init__(self, proj_path): + cmd = 'openocd -f interface/ftdi/esp32_devkitj_v1.cfg -f board/esp-wroom-32.cfg' + log_file = os.path.join(proj_path, 'openocd.log') + super(OCDProcess, self).__init__(cmd, log_file) + i = self.p.expect_exact(['Info : Listening on port 3333 for gdb connections', 'Error:']) + if i == 0: + Utility.console_log('openocd is listening for gdb connections') + else: + raise RuntimeError('openocd initialization has failed') + + def close(self): + try: + self.p.sendcontrol('c') + self.p.expect_exact('shutdown command invoked') + except Exception: + Utility.console_log('openocd needs to be killed', 'O') + super(OCDProcess, self).close() + + +class GDBProcess(CustomProcess): + def __init__(self, proj_path, elf_path): + cmd = 'xtensa-esp32-elf-gdb -x {} --directory={} {}'.format(os.path.join(proj_path, '.gdbinit.ci'), + os.path.join(proj_path, 'main'), + elf_path) + log_file = os.path.join(proj_path, 'gdb.log') + super(GDBProcess, self).__init__(cmd, log_file) + self.p.sendline('') # it is for "---Type to continue, or q to quit---" + i = self.p.expect_exact(['Thread 1 hit Temporary breakpoint 2, app_main ()', + 'Load failed']) + if i == 0: + Utility.console_log('gdb is at breakpoint') + else: + raise RuntimeError('Load failed: probably the ELF file was not built for loading with gdb') + self.p.expect_exact('(gdb)') + + def close(self): + try: + self.p.sendline('q') + self.p.expect_exact('Quit anyway? (y or n)') + self.p.sendline('y') + self.p.expect_exact('Ending remote debugging.') + except Exception: + Utility.console_log('gdb needs to be killed', 'O') + super(GDBProcess, self).close() + + def break_till_end(self): + self.p.sendline('b esp_restart') + self.p.sendline('c') + self.p.expect_exact('Thread 1 hit Breakpoint 3, esp_restart ()') + + +class SerialThread(object): + def run(self, log_path, exit_event): + with serial.Serial('/dev/ttyUSB1', 115200) as ser, open(log_path, 'wb') as f: + while True: + f.write(ser.read(ser.in_waiting)) + if exit_event.is_set(): + break + time.sleep(1) + + def __init__(self, log_path): + self.exit_event = threading.Event() + self.t = threading.Thread(target=self.run, args=(log_path, self.exit_event,)) + self.t.start() + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.exit_event.set() + self.t.join(60) + if self.t.is_alive(): + Utility.console_log('The pyserial thread is still alive', 'O') + + +@IDF.idf_example_test(env_tag="test_jtag_arm") +def test_examples_loadable_elf(env, extra_data): + + idf_path = os.environ['IDF_PATH'] + rel_project_path = os.path.join('examples', 'get-started', 'hello_world') + proj_path = os.path.join(idf_path, rel_project_path) + elf_path = os.path.join(IDF.Example(rel_project_path).get_binary_path(rel_project_path), 'hello-world.elf') + esp_log_path = os.path.join(proj_path, 'esp.log') + + with SerialThread(esp_log_path): + with OCDProcess(proj_path), GDBProcess(proj_path, elf_path) as gdb: + gdb.break_till_end() + + if pexpect.run('grep "Restarting now." {}'.format(esp_log_path), withexitstatus=True)[1]: + raise RuntimeError('Expected output from ESP was not received') + + +if __name__ == '__main__': + test_examples_loadable_elf() diff --git a/examples/get-started/hello_world/sdkconfig.ci b/examples/get-started/hello_world/sdkconfig.ci new file mode 100644 index 0000000000..c8b9cae75b --- /dev/null +++ b/examples/get-started/hello_world/sdkconfig.ci @@ -0,0 +1,6 @@ +CONFIG_APP_BUILD_TYPE_ELF_RAM=y +CONFIG_VFS_SUPPORT_TERMIOS= +CONFIG_NEWLIB_NANO_FORMAT=y +CONFIG_ESP32_PANIC_PRINT_HALT=y +CONFIG_ESP32_DEBUG_STUBS_ENABLE= +CONFIG_ESP_ERR_TO_NAME_LOOKUP= diff --git a/make/project.mk b/make/project.mk index 4c5935b07f..a8ec59f405 100644 --- a/make/project.mk +++ b/make/project.mk @@ -320,9 +320,15 @@ ifndef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES endif @echo "To flash app & partition table, run 'make flash' or:" else +ifdef CONFIG_APP_BUILD_GENERATE_BINARIES @echo "To flash all build output, run 'make flash' or:" endif +endif +ifdef CONFIG_APP_BUILD_GENERATE_BINARIES @echo $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) +else + @echo "Binary is not available for flashing" +endif # If we have `version.txt` then prefer that for extracting IDF version @@ -533,6 +539,7 @@ $(APP_ELF): $(foreach libcomp,$(COMPONENT_LIBRARIES),$(BUILD_DIR_BASE)/$(libcomp $(CC) $(LDFLAGS) -o $@ -Wl,-Map=$(APP_MAP) app: $(APP_BIN) partition_table_get_info +ifeq ("$(CONFIG_APP_BUILD_GENERATE_BINARIES)","y") ifeq ("$(CONFIG_SECURE_BOOT_ENABLED)$(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES)","y") # secure boot enabled, but remote sign app image @echo "App built but not signed. Signing step via espsecure.py:" @echo "espsecure.py sign_data --keyfile KEYFILE $(APP_BIN)" @@ -542,6 +549,9 @@ else @echo "App built. Default flash app command is:" @echo $(ESPTOOLPY_WRITE_FLASH) $(APP_OFFSET) $(APP_BIN) endif +else + @echo "Application in not built and cannot be flashed." +endif all_binaries: $(APP_BIN) diff --git a/tools/ci/config/target-test.yml b/tools/ci/config/target-test.yml index 4609f10be1..aeb627941e 100644 --- a/tools/ci/config/target-test.yml +++ b/tools/ci/config/target-test.yml @@ -202,6 +202,19 @@ example_test_008: - ESP32 - Example_Flash_Encryption +example_test_009: + extends: .example_test_template + tags: + - ESP32 + - test_jtag_arm + artifacts: + when: always + paths: + - $CI_PROJECT_DIR/examples/get-started/hello_world/*.log + expire_in: 1 week + variables: + SETUP_TOOLS: "1" + UT_001: extends: .unit_test_template parallel: 50 diff --git a/tools/idf.py b/tools/idf.py index b35b60be82..406b45a3d1 100755 --- a/tools/idf.py +++ b/tools/idf.py @@ -924,6 +924,10 @@ def init_cli(verbose_output=None): print("Done") return + if not os.path.exists(os.path.join(args.build_dir, "flasher_args.json")): + print("Done") + return + # Otherwise, if we built any binaries print a message about # how to flash them def print_flashing_message(title, key): diff --git a/tools/tiny-test-fw/IDF/IDFApp.py b/tools/tiny-test-fw/IDF/IDFApp.py index 82f42b0c35..6324071f57 100644 --- a/tools/tiny-test-fw/IDF/IDFApp.py +++ b/tools/tiny-test-fw/IDF/IDFApp.py @@ -35,18 +35,21 @@ class IDFApp(App.BaseApp): self.binary_path = self.get_binary_path(app_path) self.elf_file = self._get_elf_file_path(self.binary_path) assert os.path.exists(self.binary_path) - if self.IDF_DOWNLOAD_CONFIG_FILE not in os.listdir(self.binary_path): - if self.IDF_FLASH_ARGS_FILE not in os.listdir(self.binary_path): - msg = ("Neither {} nor {} exists. " - "Try to run 'make print_flash_cmd | tail -n 1 > {}/{}' " - "or 'idf.py build' " - "for resolving the issue." - "").format(self.IDF_DOWNLOAD_CONFIG_FILE, self.IDF_FLASH_ARGS_FILE, - self.binary_path, self.IDF_DOWNLOAD_CONFIG_FILE) - raise AssertionError(msg) + sdkconfig_dict = self.get_sdkconfig() + if "CONFIG_APP_BUILD_GENERATE_BINARIES" in sdkconfig_dict: + # There are no flashing targets available when no binaries where generated. + if self.IDF_DOWNLOAD_CONFIG_FILE not in os.listdir(self.binary_path): + if self.IDF_FLASH_ARGS_FILE not in os.listdir(self.binary_path): + msg = ("Neither {} nor {} exists. " + "Try to run 'make print_flash_cmd | tail -n 1 > {}/{}' " + "or 'idf.py build' " + "for resolving the issue." + "").format(self.IDF_DOWNLOAD_CONFIG_FILE, self.IDF_FLASH_ARGS_FILE, + self.binary_path, self.IDF_DOWNLOAD_CONFIG_FILE) + raise AssertionError(msg) - self.flash_files, self.flash_settings = self._parse_flash_download_config() - self.partition_table = self._parse_partition_table() + self.flash_files, self.flash_settings = self._parse_flash_download_config() + self.partition_table = self._parse_partition_table() @classmethod def get_sdk_path(cls): diff --git a/tools/unit-test-app/components/test_utils/test_runner.c b/tools/unit-test-app/components/test_utils/test_runner.c index 1f00a0168b..fc09d5a450 100644 --- a/tools/unit-test-app/components/test_utils/test_runner.c +++ b/tools/unit-test-app/components/test_utils/test_runner.c @@ -70,7 +70,15 @@ void setUp(void) #endif printf("%s", ""); /* sneakily lazy-allocate the reent structure for this test task */ + +#ifdef CONFIG_APP_BUILD_USE_FLASH_SECTIONS + /* TODO: add sufficient startup code in case of building an ELF file, so that + * flash cache is initialized and can work in such mode. + * For now this is disabled to allow running unit tests which don't require + * flash cache related operations. + */ get_test_data_partition(); /* allocate persistent partition table structures */ +#endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS unity_reset_leak_checks(); test_utils_set_leak_level(CONFIG_UNITY_CRITICAL_LEAK_LEVEL_GENERAL, TYPE_LEAK_CRITICAL, COMP_LEAK_GENERAL);