ESP32 Core v2: Add configuration to supporting linking NuttX for execution out of IRAM.

This commit is contained in:
Gregory Nutt 2016-11-14 17:51:50 -06:00
parent 96e7d1c310
commit 5dfc5f1da5
6 changed files with 282 additions and 34 deletions

View File

@ -16,4 +16,15 @@ config ESP32CORE_XTAL_26MHz
bool "26MHz"
endchoice # On-board Crystal Frequency
config ESP32CORE_RUN_IRAM
bool "Run from IRAM"
default n
---help---
The default configuration is set up run from IRAM. However, the
current (2016-11-14) OpenOCD for ESP32 does not support writing to
FLASH. This option sets up the liner scripts to support execution
from IRAM. In this case, OpenOCD can be used to load directly into
IRAM.
endif # ARCH_BOARD_ESP32CORE

View File

@ -23,7 +23,8 @@ Contents
o Serial Console
o Buttons and LEDs
o SMP
o Debug Issues
o OpenOCD for the ESP32
o Executing and Debugging from FLASH and IRAM
o Configurations
o Things to Do
@ -222,8 +223,8 @@ SMP
3. Assertions. On a fatal assertions, other CPUs need to be stopped.
Debug Issues
============
OpenOCD for the ESP32
=====================
First you in need some debug environment which would be a JTAG emulator
and the ESP32 OpenOCD software which is available here:
@ -335,6 +336,16 @@ Debug Issues
This should give you a gdb prompt.
Breakpoints
-----------
You can set up to 2 hardware breakpoints, which can be anywhere in the
address space. Also 2 hardware watchpoints.
The openocd esp32.cfg file currently forces gdb to use hardware
breakpoints, I believe because software breakpoints (or, at least, the
memory map for automatically choosing them) aren't implemented yet
(as of 2016-11-14).
JTAG Emulator
-------------
The documentation indicates that you need to use an external JTAG
@ -397,49 +408,74 @@ Debug Issues
20 GND N/A GND
------------ ----------
Executing and Debugging from FLASH and IRAM
===========================================
FLASH
-----
OpenOCD currently doesn't have a FLASH driver for ESP32, so you can load
code into IRAM only via JTAG. FLASH-resident sections like .FLASH.rodata
will fail to load. The bootloader in ROM doesn't parse ELF, so any imag
which is bootloaded from FLASH has to be converted into a custom image
format first.
The tool esp-idf uses for flashing is a command line Python tool called
"esptool.py" which talks to a serial bootloader in ROM. A version is
supplied in the esp-idf codebase in components/esptool_py/esptool, the
"upstream" for that tool is here:
https://github.com/espressif/esptool/pull/121
The master branch for esptool.py is currently ESP8266-only (as of 2016-11-14),
this PR has the ESP32 support which still needs some final tidying up before
it's
merged.
To FLASH an ELF via the command line is a two step process, something like
this:
esptool.py --chip esp32 elf2image --flash_mode dio --flash_size 4MB -o ./nuttx.bin nuttx.elf
esptool.py --chip esp32 --port COMx write_flash 0x1000 bootloader.bin 0x4000 partition_table.bin 0x10000 nuttx.bin
The first step converts an ELF image into an ESP32-compatible binary
image format, and the second step flashes it (along with bootloader image and
partition table binary.)
To put the ESP32 into serial flashing mode, it needs to be reset with IO0 held
low. On the Core boards this can be accomplished by holding the button marked
"Boot" and pressing then releasing the button marked "EN". Actually, esptool.py
can enter bootloader mode automatically (via RTS/DTR control lines), but
unfortunately a timing interaction between the Windows CP2012 driver and the
hardware means this doesn't currently work on Windows.
Secondary Boot Loader / Partition Table
---------------------------------------
I need to understand how to use the secondary bootloader. My
understanding is that it will configure hardware, read a partition
table at address 0x5000, and then load code into memory. I do need to
download and build the bootloader?
Do I need to create a partition table at 0x5000? Should this be part
of the NuttX build?
See https://github.com/espressif/esp-idf/tree/master/components/bootloader
and https://github.com/espressif/esp-idf/tree/master/components/partition_table.
I suppose some of what I need is in there, but I am not sure what I am
looking at right now.
Running from IRAM
-----------------
Running from IRAM is a good debug option. You should be able to load the ELF directly via JTAG in this case, and you may not need the bootloader. The one "gotcha" for needing the bootloader is disabling the initial watchdog, there is code in bootloader_start.c that does this.
It is possible to skip the secondary bootloader and run out of IRAM using
only the primary bootloader if your application of small enough (< 128KiB code,
<180KiB data), then you can simplify initial bring-up by avoiding second stage
bootloader. Your application will be loaded into IRAM using first stage
bootloader present in ESP32 ROM. To achieve this, you need two things:
1. Have a linker script which places all code into IRAM and all data into DRAM
1. Have a linker script which places all code into IRAM and all data into
IRAM/DRAM
2. Use "esptool.py" utility found in ESP-IDF to convert application .elf file
into binary format which can be loaded by first stage bootloader.
2. Use "esptool.py" utility found in ESP-IDF to convert application .elf
file into binary format which can be loaded by first stage bootloader.
The default linker script in ESP-IDF places most code into memory-mapped flash:
https://github.com/espressif/esp-idf/blob/master/components/esp32/ld/esp32.common.ld#L178-L186
NuttX supports a configuration option, CONFIG_ESP32CORE_RUN_IRAM, that may be
selected for execution from IRAM. This option simply selects the correct
linker script for IRAM execution.
You would need to remove this section and move its contents into the end of .iram0.text section:
https://github.com/espressif/esp-idf/blob/master/components/esp32/ld/esp32.common.ld#L85
Same with constant data: move contents of .flash.rodata section:
https://github.com/espressif/esp-idf/blob/master/components/esp32/ld/esp32.common.ld#L134-L173
into the end of .dram0.data section (before _heap_start):
https://github.com/espressif/esp-idf/blob/master/components/esp32/ld/esp32.common.ld#L128
With these modifications, all code and data should be moved into IRAM/DRAM. Next, you would
need to link the ELF file and convert it to binary format suitable for flashing into the
board. The xommand should to convert ELF file to binary image looks as follows:
Again you would need to link the ELF file and convert it to binary format suitable
for flashing into the board. The command should to convert ELF file to binary
image looks as follows:
python esp-idf/components/esptool_py/esptool/esptool.py --chip esp32 elf2image --flash_mode "dio" --flash_freq "40m" --flash_size "2MB" -o app.bin app.elf
@ -554,4 +590,4 @@ Things to Do
5. See SMP-related issues above
6. See Debug Issues above
6. See OpenOCD for the ESP32 above

View File

@ -38,10 +38,15 @@ include ${TOPDIR}/tools/Config.mk
include ${TOPDIR}/arch/xtensa/src/lx6/Toolchain.defs
LDSCRIPT1 = $(TOPDIR)/configs/$(CONFIG_ARCH_BOARD)/scripts/esp32_out.ld
LDSCRIPT2 = $(TOPDIR)/configs/$(CONFIG_ARCH_BOARD)/scripts/esp32_common.ld
LDSCRIPT3 = $(TOPDIR)/configs/$(CONFIG_ARCH_BOARD)/scripts/esp32_rom.ld
LDSCRIPT4 = $(TOPDIR)/configs/$(CONFIG_ARCH_BOARD)/scripts/esp32_peripherals.ld
ifeq ($(CONFIG_ESP32CORE_RUN_IRAM),y)
LDSCRIPT2 = $(TOPDIR)/configs/$(CONFIG_ARCH_BOARD)/scripts/esp32_iram.ld
else
LDSCRIPT2 = $(TOPDIR)/configs/$(CONFIG_ARCH_BOARD)/scripts/esp32_flash.ld
endif
ifeq ($(WINTOOL),y)
# Windows-native toolchains
DIRLINK = $(TOPDIR)/tools/copydir.sh

View File

@ -1,5 +1,5 @@
/****************************************************************************
* configs/elf32-core/scripts/esp32_common.ld
* configs/elf32-core/scripts/esp32_flash.ld
****************************************************************************/
/* Default entry point: */

View File

@ -0,0 +1,191 @@
/****************************************************************************
* configs/elf32-core/scripts/esp32_iram.ld
****************************************************************************/
/* Default entry point: */
ENTRY(__start);
SECTIONS
{
/* Send .iram0 code to iram */
.iram0.vectors :
{
/* Vectors go to IRAM */
_init_start = ABSOLUTE(.);
/* Vectors according to builds/RF-2015.2-win32/esp108_v1_2_s5_512int_2/config.html */
. = 0x0;
KEEP(*(.window_vectors.text));
. = 0x180;
KEEP(*(.xtensa_level2_vector.text));
. = 0x1c0;
KEEP(*(.xtensa_level3_vector.text));
. = 0x200;
KEEP(*(.xtensa_level4_vector.text));
. = 0x240;
KEEP(*(.xtensa_level5_vector.text));
. = 0x280;
KEEP(*(.debug_exception_vector.text));
. = 0x2c0;
KEEP(*(.nmi_vector.text));
. = 0x300;
KEEP(*(.kernel_exception_vector.text));
. = 0x340;
KEEP(*(.user_exception_vector.text));
. = 0x3c0;
KEEP(*(.double_exception_vector.text));
. = 0x400;
*(.*_vector.literal)
. = ALIGN (16);
*(.entry.text)
*(.init.literal)
*(.init)
_init_end = ABSOLUTE(.);
} > iram0_0_seg
.iram0.text :
{
/* Code marked as runnning out of IRAM */
_iram_text_start = ABSOLUTE(.);
*(.iram1 .iram1.*)
*libphy.a:(.literal .text .literal.* .text.*)
*librtc.a:(.literal .text .literal.* .text.*)
*libpp.a:(.literal .text .literal.* .text.*)
*libhal.a:(.literal .text .literal.* .text.*)
_iram_text_end = ABSOLUTE(.);
_stext = .;
_text_start = ABSOLUTE(.);
*(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
*(.irom0.text) /* catch stray ICACHE_RODATA_ATTR */
*(.fini.literal)
*(.fini)
*(.gnu.version)
_text_end = ABSOLUTE(.);
_etext = .;
} > iram0_0_seg
/* Shared RAM */
.dram0.bss (NOLOAD) :
{
/* .bss initialized on power-up */
. = ALIGN (8);
_sbss = ABSOLUTE(.);
*(.dynsbss)
*(.sbss)
*(.sbss.*)
*(.gnu.linkonce.sb.*)
*(.scommon)
*(.sbss2)
*(.sbss2.*)
*(.gnu.linkonce.sb2.*)
*(.dynbss)
KEEP(*(.bss))
*(.bss.*)
*(.share.mem)
*(.gnu.linkonce.b.*)
*(COMMON)
. = ALIGN (8);
_ebss = ABSOLUTE(.);
/* Uninitialized .bss */
*(.noinit)
} >dram0_0_seg
.dram0.data :
{
/* .data initialized on power-up in ROMed configurations. */
_sdata = ABSOLUTE(.);
KEEP(*(.data))
KEEP(*(.data.*))
KEEP(*(.gnu.linkonce.d.*))
KEEP(*(.data1))
KEEP(*(.sdata))
KEEP(*(.sdata.*))
KEEP(*(.gnu.linkonce.s.*))
KEEP(*(.sdata2))
KEEP(*(.sdata2.*))
KEEP(*(.gnu.linkonce.s2.*))
KEEP(*(.jcr))
*(.dram1 .dram1.*)
_edata = ABSOLUTE(.);
. = ALIGN(4);
_srodata = ABSOLUTE(.);
*(.rodata)
*(.rodata.*)
*(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */
*(.gnu.linkonce.r.*)
*(.rodata1)
__XT_EXCEPTION_TABLE_ = ABSOLUTE(.);
*(.xt_except_table)
*(.gcc_except_table)
*(.gnu.linkonce.e.*)
*(.gnu.version_r)
*(.eh_frame)
. = (. + 3) & ~ 3;
/* C++ constructor and destructor tables, properly ordered: */
_sinit = ABSOLUTE(.);
KEEP (*crtbegin.o(.ctors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
_einit = ABSOLUTE(.);
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
/* C++ exception handlers table: */
__XT_EXCEPTION_DESCS_ = ABSOLUTE(.);
*(.xt_except_desc)
*(.gnu.linkonce.h.*)
__XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.);
*(.xt_except_desc_end)
*(.dynamic)
*(.gnu.version_d)
_erodata = ABSOLUTE(.);
/* Literals are also RO data. */
_lit4_start = ABSOLUTE(.);
*(*.lit4)
*(.lit4.*)
*(.gnu.linkonce.lit4.*)
_lit4_end = ABSOLUTE(.);
. = ALIGN(4);
/* Heap starts at the end of .data */
_sheap = ABSOLUTE(.);
} >dram0_0_seg
.flash.rodata :
{
} >drom0_0_seg
.rtc.text :
{
. = ALIGN(4);
*(.rtc.literal .rtc.text)
} >rtc_iram_seg
.rtc.data :
{
*(.rtc.data)
*(.rtc.rodata)
} > rtc_slow_seg
}

View File

@ -38,10 +38,15 @@ include ${TOPDIR}/tools/Config.mk
include ${TOPDIR}/arch/xtensa/src/lx6/Toolchain.defs
LDSCRIPT1 = $(TOPDIR)/configs/$(CONFIG_ARCH_BOARD)/scripts/esp32_out.ld
LDSCRIPT2 = $(TOPDIR)/configs/$(CONFIG_ARCH_BOARD)/scripts/esp32_common.ld
LDSCRIPT3 = $(TOPDIR)/configs/$(CONFIG_ARCH_BOARD)/scripts/esp32_rom.ld
LDSCRIPT4 = $(TOPDIR)/configs/$(CONFIG_ARCH_BOARD)/scripts/esp32_peripherals.ld
ifeq ($(CONFIG_ESP32CORE_RUN_IRAM),y)
LDSCRIPT2 = $(TOPDIR)/configs/$(CONFIG_ARCH_BOARD)/scripts/esp32_iram.ld
else
LDSCRIPT2 = $(TOPDIR)/configs/$(CONFIG_ARCH_BOARD)/scripts/esp32_flash.ld
endif
ifeq ($(WINTOOL),y)
# Windows-native toolchains
DIRLINK = $(TOPDIR)/tools/copydir.sh