[esptool] segments with load 0x00000000

michprev
Posts: 92
Joined: Fri Aug 04, 2017 8:57 pm

[esptool] segments with load 0x00000000

Postby michprev » Wed Jun 06, 2018 12:01 pm

Hi,
after running

Code: Select all

esptool.py --chip esp32 --no-stub elf2image app.elf
esptool.py --chip esp32 --no-stub image_info app.bin
I am getting (with a few debug statements in esptool.py)

Code: Select all

flash segments
[.drom.rodata len 0x00050 load 0x3f400018, .irom.text len 0x00a10 load 0x400c2000]
ram segments
[.dram.data len 0x00024 load 0x3ffb0000, .iram.text len 0x00854 load 0x40080400]

Image version: 1
Entry point: 400805f4
6 segments
Segment 1: len 0x00024 load 0x3ffb0000 file_offs 0x00000018
Segment 2: len 0x00854 load 0x40080400 file_offs 0x00000044
Segment 3: len 0x0f768 load 0x00000000 file_offs 0x000008a0
Segment 4: len 0x00050 load 0x3f400018 file_offs 0x00010010
Segment 5: len 0x11f88 load 0x00000000 file_offs 0x00010068
Segment 6: len 0x00a10 load 0x400c2000 file_offs 0x00021ff8
Checksum: 8a (valid)
I guess that 0x00000000 segments are the reason why my application does not get loaded by first stage bootloader.

Code: Select all

rst:0x10 (RTCWDT_RTC_RESET),boot:0x3e (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3ffb0000,len:36
load:0x40080400,len:2132
load:0x00000000,len:63336
ets Jun  8 2016 00:22:57
There is probably problem in my linker script:

Code: Select all

MEMORY
{
    IRAM (RX) : ORIGIN = 0x40080000, LENGTH = 128K

    /* SRAM 1 will be used as DATA RAM */
    /* first 8 KB of SRAM 1 are used by bootloader */
    DRAM (RW) : ORIGIN = 0x3ffB0000, LENGTH = 320K

    /* esptool adds 24 bytes to the begin of the image */

    IROM (RX) : ORIGIN = 0x400C2000, LENGTH = 11512K - 24B

    DROM (R) : ORIGIN = 0x3F400018, LENGTH = 4M - 24B

    /* TODO RTC memory */
}

ENTRY(bootloader_start);

SECTIONS
{
  .vectors :
  {
    . = 0x0;
    KEEP(*(.WindowVectors.text));
    . = 0x180;
    KEEP(*(.Level2InterruptVector.text));
    . = 0x1c0;
    KEEP(*(.Level3InterruptVector.text));
    . = 0x200;
    KEEP(*(.Level4InterruptVector.text));
    . = 0x240;
    KEEP(*(.Level5InterruptVector.text));
    . = 0x280;
    KEEP(*(.DebugExceptionVector.text));
    . = 0x2c0;
    KEEP(*(.NMIExceptionVector.text));
    . = 0x300;
    KEEP(*(.KernelExceptionVector.text));
    . = 0x340;
    KEEP(*(.UserExceptionVector.text));
    . = 0x3C0;
    KEEP(*(.DoubleExceptionVector.text));
    . = 0x400;
    *(.*Vector.literal)
  } > IRAM

  .iram.text :
  {
    . = ALIGN (16);
    *(.entry.text)
    *(.init.literal)
    *(.init)
    *(.iram .iram.*)
    *libheap.a:multi_heap.o(.literal .text .literal.* .text.*)
    *libheap.a:multi_heap_poisoning.o(.literal .text .literal.* .text.*)
    *libesp32.a:core_dump.o(.literal .text .literal.* .text.*)
    *libapp_trace.a:(.literal .text .literal.* .text.*)
    *libxtensa-debug-module.a:eri.o(.literal .text .literal.* .text.*)
    *libsoc.a:(.literal .text .literal.* .text.*)
    *libhal.a:(.literal .text .literal.* .text.*)
    *libgcc.a:lib2funcs.o(.literal .text .literal.* .text.*)
    *libspi_flash.a:spi_flash_rom_patch.o(.literal .text .literal.* .text.*)
    *libgcov.a:(.literal .text .literal.* .text.*)
    INCLUDE esp32.spiram.rom-functions-iram.ld
  } > IRAM

  .dram.data :
  {
    *(.data)
    *(.data.*)
    *(.gnu.linkonce.d.*)
    *(.data1)
    *(.sdata)
    *(.sdata.*)
    *(.gnu.linkonce.s.*)
    *(.sdata2)
    *(.sdata2.*)
    *(.gnu.linkonce.s2.*)
    *(.jcr)
    *(.dram .dram.*)

    *libesp32.a:panic.o(.rodata .rodata.*)
    *libphy.a:(.rodata .rodata.*)
    *libsoc.a:rtc_clk.o(.rodata .rodata.*)
    *libapp_trace.a:(.rodata .rodata.*)
    *libgcov.a:(.rodata .rodata.*)
    *libheap.a:multi_heap.o(.rodata .rodata.*)
    *libheap.a:multi_heap_poisoning.o(.rodata .rodata.*)
    INCLUDE esp32.spiram.rom-functions-dram.ld
    . = ALIGN(4);
  } > DRAM

  .dram.bss (NOLOAD) :
  {
    . = ALIGN (8);
    _bss_start = ABSOLUTE(.);
    *(.dynsbss)
    *(.sbss)
    *(.sbss.*)
    *(.gnu.linkonce.sb.*)
    *(.scommon)
    *(.sbss2)
    *(.sbss2.*)
    *(.gnu.linkonce.sb2.*)
    *(.dynbss)
    *(.bss)
    *(.bss.*)
    *(.gnu.linkonce.b.*)
    *(COMMON)
    . = ALIGN (8);
    _bss_end = ABSOLUTE(.);
  } > DRAM

  .drom.rodata :
  {
    _rodata_start = ABSOLUTE(.);
    *(.rodata)
    *(.rodata.*)
    *(.gnu.linkonce.r.*)
    *(.rodata1)
    __XT_EXCEPTION_TABLE_ = ABSOLUTE(.);
    *(.xt_except_table)
    *(.gcc_except_table .gcc_except_table.*)
    *(.gnu.linkonce.e.*)
    *(.gnu.version_r)
    . = (. + 3) & ~ 3;
    __eh_frame = ABSOLUTE(.);
    KEEP(*(.eh_frame))
    . = (. + 7) & ~ 3;
    /*  C++ constructor and destructor tables, properly ordered:  */
    __init_array_start = ABSOLUTE(.);
    KEEP (*crtbegin.o(.ctors))
    KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
    __init_array_end = 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)
    _rodata_end = ABSOLUTE(.);
    /* Literals are also RO data. */
    _lit4_start = ABSOLUTE(.);
    *(*.lit4)
    *(.lit4.*)
    *(.gnu.linkonce.lit4.*)
    _lit4_end = ABSOLUTE(.);
    . = ALIGN(4);
    _thread_local_start = ABSOLUTE(.);
    *(.tdata)
    *(.tdata.*)
    *(.tbss)
    *(.tbss.*)
    _thread_local_end = ABSOLUTE(.);
    . = ALIGN(4);
  } > DROM

  .irom.text :
  {
    *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
    *(.fini.literal)
    *(.fini)
    *(.gnu.version)
  } > IROM

}

WiFive
Posts: 3529
Joined: Tue Dec 01, 2015 7:35 am

Re: [esptool] segments with load 0x00000000

Postby WiFive » Wed Jun 06, 2018 12:58 pm


michprev
Posts: 92
Joined: Fri Aug 04, 2017 8:57 pm

Re: [esptool] segments with load 0x00000000

Postby michprev » Wed Jun 06, 2018 10:17 pm

So after first stage bootloader configures SPI flash it does something like this (lets assume we do not use secure boot):
  1. check image header at 0x1000 as described here https://github.com/espressif/esptool/wi ... ile-header (ESP32 probably has additional bytes in header)
  2. iterate over segments with their headers as described here https://github.com/espressif/esptool/wi ... at#segment; based on memory offset decide
    • if this is a RAM segment copy it to the corresponding RAM space
    • if this is a flash segment leave it right as it is
Because of this behavior flash segments need to be already aligned to 64 KB flash pages so that they can be mapped using MMU.
Am I right?
What are the address bounds to say that target segment is a RAM / flash segment? I noticed that esptool does not count 0x400C2000 <= address < 0x400D0000 as a flash segment address.

Thank you.

ESP_Angus
Posts: 2344
Joined: Sun May 08, 2016 4:11 am

Re: [esptool] segments with load 0x00000000

Postby ESP_Angus » Thu Jun 07, 2018 12:06 am

Hi michprev,
michprev wrote:So after first stage bootloader configures SPI flash it does something like this (lets assume we do not use secure boot):
  1. check image header at 0x1000 as described here https://github.com/espressif/esptool/wi ... ile-header (ESP32 probably has additional bytes in header)
Pretty much. ROM bootloader (ie first stage bootloader) actually uses this header to further configure the SPI flash (based on mode, flash frequency, flash size which are all encoded there).

There's an additional 16 bytes of header for ESP32, for a total of 24. Most of this extra header is unused (see the load_extended_header function in esptool.py if you're interested).
michprev wrote: [*] iterate over segments with their headers as described here https://github.com/espressif/esptool/wi ... at#segment; based on memory offset decide
  • if this is a RAM segment copy it to the corresponding RAM space
  • if this is a flash segment leave it right as it is
[/list]
For the ROM bootloader, it actually tries to copy every segment to the specified load address. If the load address is in a flash cache mapped space then I believe what you'll get is essentially a lot of no-op writes as it reads from flash and discards the output by writing it into a black hole.

(This is one of the reasons the ESP-IDF software bootloader runs 100% from IRAM/DRAM.)

This is also why any padding segments (mapped at address 0x0) cause it to crash (memory protection is applied for 0x0)..
michprev wrote: Because of this behavior flash segments need to be already aligned to 64 KB flash pages so that they can be mapped using MMU.
Am I right?
Pretty much. Specifically, the flash cache only operates on 64KB pages (64KB aligned in the virtual address space, and on the flash). So the constraint which must be true in the flash contents is that (vaddr % 65536) == (paddr % 65536), where vaddr is the "virtual" address in the address space, and paddr is the offset on the flash. esptool.py elf2image ensures this is true[*] by adding padding segments. It will try to pad by splitting up segments which are loaded to RAM and using them to fill gaps, to minimise the overall .bin size, but if it runs out of these segments then it adds empty segments with load address 0x0.

[*] It's true provided the .bin file itself is aligned at a 64KB boundary. ESP-IDF says all app partitions should be 64KB aligned. So the constraint works for apps in app partitions but it's not true for the software bootloader image, which is written at offset 0x1000.
michprev wrote: What are the address bounds to say that target segment is a RAM / flash segment?
The ESP-IDF software bootloader (second stage) calls a static function should_map(), which returns true for any load address in this range:

Code: Select all

#define SOC_IROM_LOW    0x400D0000
#define SOC_IROM_HIGH   0x40400000
#define SOC_DROM_LOW    0x3F400000
#define SOC_DROM_HIGH   0x3F800000
esptool.py uses the same range (variables are IROM_MAP_START, IROM_MAP_END, DROM_MAP_START, DROM_MAP_END).
michprev wrote: I noticed that esptool does not count 0x400C2000 <= address < 0x400D0000 as a flash segment address.
At the moment IDF doesn't have any code to map this range as it overlaps with 8KB of fast RTC memory at the start of the page (0x400C0000 - 0x400C1FFF). So only 56KB of this 64KB page is available, compared to the other 51 full pages. It could be supported if necessary, but noone (AFAIK) is getting near having >3MB of IROM that needs to be mapped to instruction space at a single time yet.

I'm a little confused about what you're aiming to achieve here. It seems like you want to build an IDF app that boots directly, without a software bootloader. Can you fill in some more details, please?

michprev
Posts: 92
Joined: Fri Aug 04, 2017 8:57 pm

Re: [esptool] segments with load 0x00000000

Postby michprev » Thu Jun 07, 2018 6:42 pm

Thank you very much for such an exhausting answer, I really appreciate that.

I am interested in building quadcopters based on ESP32 and its followers. I have successfully built a flying quad with ESP-IDF but it seems to me too heavy for this. I would like to build a custom lightweigth SDK (without FreeRTOS) to more utilize CPU power.

I think that it would be more straightforward (and easier to understand) not to use any second bootloader and that is why I would like to run an application straight from the first stage bootloader. With info you have provided I think that I will find a way how to do that.

Perhaps I could insert 'fake' flash segments instead of padding 0x00000000 segments so that the first stage bootloader will not fail and after that in an app code recognize the true flash segments from the fake ones (somehow?) to setup memory cache.

Another option might be to use fake RAM segments instead of 0x00000000 ones and insert true RAM segments at the end of image (in case that bootloader takes segments one by one from the begin of image to the end).

ESP_Angus
Posts: 2344
Joined: Sun May 08, 2016 4:11 am

Re: [esptool] segments with load 0x00000000

Postby ESP_Angus » Sun Jun 10, 2018 3:13 am

michprev wrote:Thank you very much for such an exhausting answer, I really appreciate that.

I am interested in building quadcopters based on ESP32 and its followers. I have successfully built a flying quad with ESP-IDF but it seems to me too heavy for this. I would like to build a custom lightweigth SDK (without FreeRTOS) to more utilize CPU power.
I see. I'd be interested to know what aspects of ESP-IDF made it "too heavy". There might be other suggestions we can make.

Do you plan to use WiFi or any other RF features in your OS-less project?
michprev wrote:I think that it would be more straightforward (and easier to understand) not to use any second bootloader and that is why I would like to run an application straight from the first stage bootloader. With info you have provided I think that I will find a way how to do that.
If I were in your situation, I would probably use the IDF bootloader & partition table, at least to begin with. They can be shipped as .bin files and more or less ignored after that. Running from the IDF bootloader gives a more consistent initial runtime configuration (flash cache mapped correctly, hardware initialized, etc). Allows use of esptool.py elf2image without modifications, etc.

Once I was done implementing everything else, I could always go back and reimplement the bootloader functionality. But if my goal wasn't to reimplement the IDF bootloader, then I'd probably skip reimplemeting the IDF bootloader and move on to something more interesting/rewarding. ;)

Your priorities may differ, of course.

michprev
Posts: 92
Joined: Fri Aug 04, 2017 8:57 pm

Re: [esptool] segments with load 0x00000000

Postby michprev » Mon Jun 11, 2018 5:21 pm

ESP_Angus wrote: I see. I'd be interested to know what aspects of ESP-IDF made it "too heavy". There might be other suggestions we can make.
The biggest problem is FreeRTOS. We want to reach at least 200 Hz update loop (during which it is needed to read sensor data over SPI, compute and send the result to motors) to be able to fly. 200 Hz should be acheivable with Arduino. For ESP32 I would like to reach 8 kHz or even 32 kHz. Until now I've been running WiFi driver on core 0 and flight control loop on core 1.

The update loop looks like this:

Code: Select all

while (true)
{
	if (data_available) // at 8 kHz or 32 kHz
	{
		read_data_over_SPI(); // at 8 MHz or 20 MHz
		compute_crazy_things(); // FPU will have to do a job
		send_signal_to_motors(); // via MCPWM or RMT
	}
}
To reach this I think it will be needed not to run FreeRTOS (scheduler takes some time), write my own drivers (even with FreeRTOS I had to use custom driver for MCPWM) and optimize FPU.

I am aware of option running FreeRTOS only on the first core but I still need to communicate between both cores (=> I need locks), I need to register interrupts on the second core and I also have to implement my own drivers (mainly SPI, I2C, UART ...). It would be also nice to use newlib on the second core too.

So what I am trying to do is to reimplement basic functionalities without FreeRTOS and then decide if I will run FreeRTOS on the first core or not. I guess that there might be problems with tasks related hooks of wifi os adapter.

Once I will have at least interrupts & newlib & basic drivers working I will certainly make it open source.
From what I've read in this thread viewtopic.php?f=2&t=2772 I am sure that Espressif will continue to use Xtensa architecture so that it should not be a problem to port my work to any ESP32 successors.

I believe that ESP32 (or its successors) could easily beat ARM flight controllers.
ESP_Angus wrote: If I were in your situation, I would probably use the IDF bootloader & partition table, at least to begin with. They can be shipped as .bin files and more or less ignored after that. Running from the IDF bootloader gives a more consistent initial runtime configuration (flash cache mapped correctly, hardware initialized, etc). Allows use of esptool.py elf2image without modifications, etc.

Once I was done implementing everything else, I could always go back and reimplement the bootloader functionality. But if my goal wasn't to reimplement the IDF bootloader, then I'd probably skip reimplemeting the IDF bootloader and move on to something more interesting/rewarding. ;)
You are right. Bootloader in not the top most priority for me now. I was just wondering if I can just skip the second stage bootloader. For now I can even stay with using just RAM.
I have already finished implementing mutexes and critical sections. After reviewing and editing heap component I will have newlib working. The next will be interrupts.

ESP_Angus
Posts: 2344
Joined: Sun May 08, 2016 4:11 am

Re: [esptool] segments with load 0x00000000

Postby ESP_Angus » Mon Jun 11, 2018 11:41 pm

michprev wrote: To reach this I think it will be needed not to run FreeRTOS (scheduler takes some time), write my own drivers (even with FreeRTOS I had to use custom driver for MCPWM) and optimize FPU.
I don't want to discourage you from going your own way, but for what it's worth if you only have a single task running on CPU1 then the overhead from FreeRTOS should be pretty low (there is a periodic tick interrupt but that's about it).
michprev wrote: I believe that ESP32 (or its successors) could easily beat ARM flight controllers.
Great! I agree "bang for buck" should be pretty good. :)

michprev
Posts: 92
Joined: Fri Aug 04, 2017 8:57 pm

Re: [esptool] segments with load 0x00000000

Postby michprev » Tue Jun 12, 2018 1:59 pm

ESP_Angus wrote: I don't want to discourage you from going your own way, but for what it's worth if you only have a single task running on CPU1 then the overhead from FreeRTOS should be pretty low (there is a periodic tick interrupt but that's about it).
Are you sure about that? I have just run my app with 1 kHz sensors update frequency.

Code: Select all

    while (true)
    {
        if (imu.Data_Ready()) // is ready every 1 ms
        {
            // <-- measured here
            data_type = imu.Read_Data(accel, gyro);

            complementary.Compute(accel, gyro, complementary_euler);

            if (data_type == IMU::Read_Data_Type::ACCEL_GYRO)
            {
                logger.Log_Next(&accel, sizeof(accel));
                logger.Log_Next(&gyro, sizeof(gyro));

                motors_controller.Feed_Stab_PIDs(complementary_euler);
                motors_controller.Feed_Rate_PIDs(gyro);
            } else
                motors_controller.Feed_Rate_PIDs(gyro);
        }
    }
I have measured elapsed time between last run in if condition and current run. It should be around 1000 us. The result is very bad (measurement1.txt). To make sure that the delay is not added by any function called inside if (data ready) block I have also measured the time we are inside complementary.Compute() function. The result is in measurement2.txt file.

Code: Select all

void Complementary_Filter::Compute(IMU::Sensor_Data accel, IMU::Sensor_Data gyro, IMU::Euler_Angles &euler)
{
    float delta_t;

    if (this->last_time == 0)
    {
        delta_t = 0;

        this->last_time = esp_timer_get_time();
    } else
    {
        int64_t tmp = esp_timer_get_time();
        delta_t = (tmp - this->last_time) * 0.000001f;

        this->last_time = tmp;
    }

    float accel_roll = atan2f(accel.y, accel.z) * Math::RAD_TO_DEG;

    float accel_pitch =
            atan2f(-accel.x, sqrtf(accel.y * accel.y + accel.z * accel.z)) * Math::RAD_TO_DEG;

    this->euler.roll = this->COMPLEMENTARY_COEFFICIENT * (this->euler.roll + gyro.y * delta_t)
                       + (1 - this->COMPLEMENTARY_COEFFICIENT) * accel_roll;

    this->euler.pitch = this->COMPLEMENTARY_COEFFICIENT * (this->euler.pitch + gyro.x * delta_t)
                        + (1 - this->COMPLEMENTARY_COEFFICIENT) * accel_pitch;

    this->euler.yaw += gyro.z * delta_t;

    if (this->euler.yaw > 180)
        this->euler.yaw -= 360;
    else if (this->euler.yaw < -180)
        this->euler.yaw += 360;

    euler = this->euler;
}
As you can see I am not calling any FreeRTOS code inside the function.

These are the tasks I am running:

Code: Select all

my IMU task          1
my WiFi task         0
main                 0
IDLE                 1
IDLE                 0
tiT                  2147483647
Tmr Svc              0
eventTask            0
ipc1                 1
esp_timer            0
pmT                  2147483647
wifi                 0
ipc0                 0
Any suggestions?
Attachments
measurement2.txt
(6.85 KiB) Downloaded 509 times
measurement1.txt
(8.33 KiB) Downloaded 531 times

User avatar
kolban
Posts: 1683
Joined: Mon Nov 16, 2015 4:43 pm
Location: Texas, USA

Re: [esptool] segments with load 0x00000000

Postby kolban » Sun Jul 01, 2018 10:39 am

I think I understand that segments in flash need to be 64K aligned so that they can be mapped by the MMU component. For example, I may have:

Code: Select all

[Segment A: 10K]
[Padding:   54K]
[Segment B: 20K]
[Padding:   44K]
[Segment C: 30K]
This seems to result in an image bin file size of about 158K. However, if we look at the actual data content, it appears to be only 10K + 20K + 30K = 60K with the remainder being padding.

I fully understand that the image on flash has to have these padding parts so that the MMU can map correctly, but I'm not sensing why the out of the box image bin file needs them actually included in the file data? My understanding is that to flash an ESP32, the image bin file is transmitted by the uploader protocol over serial or OTA and the image bin file is written as-is to flash. Does this mean that the padding data is also transmitted and written into flash? Could the image bin not be reduced in size by specifying the length of the padding section? I think padding sections will always have a VMA of 0 and hence that could be used as the indicator that actual data in the incoming stream is not present. The ESP32 upload app could then reconstitute the data to be written to flash.
Free book on ESP32 available here: https://leanpub.com/kolban-ESP32

Who is online

Users browsing this forum: No registered users and 89 guests