Separate user code from system code to decrease ota binary size

user4_esp32
Posts: 21
Joined: Sun Nov 26, 2017 4:48 pm

Separate user code from system code to decrease ota binary size

Postby user4_esp32 » Sun Jun 03, 2018 4:20 pm

Hello,

Is there a method in the ESP32 IDF to "partition" the flash memory space to store the user-coded code separately from the ESP32 "system" code (e.g. all the code in esp-idf/components), and realize the capability to ota the user code and system code separately?

My code is about 1.2 MB currently, which pushes the limits of flash memory space when using a factory partition with two ota partitions.

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

Re: Separate user code from system code to decrease ota binary size

Postby kolban » Sun Jun 03, 2018 4:51 pm

Howdy,
First realize that the size of the partitions available can be changed. If you have 4 MBytes of flash, then you could use just two OTA partitions of about 2MB each.

But back to your question.

I'm working on a project right now which needs a constant firmware but the "user code" will be dynamically loaded at runtime. The user code needs to be much smaller than a completely linked ESP32 binary.

We have prototyped a technique which seems to be working. We have examined our user applications and come up with a relatively small set of unresolved APIs that are needed from ESP-IDF. In our firmware app, we then create a "jump table" starting at a fixed memory location.

As an example, imagine the ESP-IDF exposes a function called "foo()" and we want to write a user app that calls "foo()". In our system firmware, we create an entry in the jump table at a given index (eg. 12) ... that basically is a pointer to the ESP-IDF function foo(). Now if we compile our user app, we will get an unresolved of foo(). To solve this problem, we have created a small piece of assembler language code that exposes itself as a function called "foo". This assembler is about 10 instructions that perform a lookup of the jump table and branch to it. This assembler code is then linked with the user app. At runtime, the user app is coded to call "foo()" which then is resolved to our assembler which looks up the the jump table and we end up in the real "foo()". So far all is working just great for our needs. If anyone can think of better recipes or techniques, I'm all ears and would be delighted to participate in discussions / thinking on this topic.
Free book on ESP32 available here: https://leanpub.com/kolban-ESP32

user4_esp32
Posts: 21
Joined: Sun Nov 26, 2017 4:48 pm

Re: Separate user code from system code to decrease ota binary size

Postby user4_esp32 » Tue Jun 05, 2018 12:28 pm

Thanks much, kolban. As of now, I do plan to downsize my factory app and factory app partition to less than the size of the ota apps and ota partitions, to give me more room in flash for the larger ota apps.

Decreasing the size of the ota apps in order to decrease ota time in the field is also a priority for me, making the user code approach appealing.

The "user code" implementation you are developing sounds very interesting. Do you plan to post an example on github?

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

Re: Separate user code from system code to decrease ota binary size

Postby kolban » Tue Jun 05, 2018 2:05 pm

The project I am working on is on behalf of a company that releases their code base as open source. IF it comes to fruition then I would anticipate the whole code base being placed on Github for community examination and download. Please don't hold your breath on availability. It could be many months before that happens ... if it at all. However, the concept proved to be technically sound and seems to work ... what this means is that from a pure design (rather than implementation) perspective, there would be nothing to prevent others from "having a go" at the same notion.
Free book on ESP32 available here: https://leanpub.com/kolban-ESP32

jcsbanks
Posts: 300
Joined: Tue Mar 28, 2017 8:03 pm

Re: Separate user code from system code to decrease ota binary size

Postby jcsbanks » Tue Jun 05, 2018 4:08 pm

I haven't started with partitions/OTA on ESP32 yet, but do a lot of related tricks on automotive engine control units running Tricore microcontrollers using GNU C and linker scripts with bits of assembly where necessary to bypass, replace or augment existing compiled functions, including calling existing functions from new functions.

For this application could you define function pointers and then simply call the function in the normal way instead of using an indexed table of addresses? End result would be similar but easier to debug/maintain?

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

Re: Separate user code from system code to decrease ota binary size

Postby kolban » Tue Jun 05, 2018 4:42 pm

When we compile C code that refers to functions found elsewhere, these become "undefineds" in the resulting object code. At link time, the undefineds are normally resolved to the address of the functions that service them. One solution to separating user code from system code is to compile the system code and generate linker definitions for each of the exposed symbols in the system code. When we then link the user code, we can resolve the user code undefined with the symbols generated from the compilation/linkage of the system code. This is indeed simpler and easier to maintain. However it has a down-side. It introduces tight coupling between the system code and the user code. If the system code changes and is recompiled, previously compiled user applications will now fail in interesting and spectacular ways as the addresses that they thought were good are now likely not going to be good anymore.

In the project I'm working on, the designers wanted to preserve the ability to recompile EITHER the system code or the user code without impact to the other ... and hence the indirection through a jump table.

For many projects however, what mr jcsbanks suggests may be just perfect. Just remember to recompile user apps should the system code addresses changes.
Free book on ESP32 available here: https://leanpub.com/kolban-ESP32

jcsbanks
Posts: 300
Joined: Tue Mar 28, 2017 8:03 pm

Re: Separate user code from system code to decrease ota binary size

Postby jcsbanks » Tue Jun 05, 2018 4:53 pm

ISWYM. Some of my build steps use symbol information from other parts of the build that are in a .map file. Works well as long as you keep the binary and the map file together. Have you found a better or alternative way to automatically populate your function pointer table?

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

Re: Separate user code from system code to decrease ota binary size

Postby kolban » Tue Jun 05, 2018 5:06 pm

In the project I am working on, the "user" application isn't going to call ESP-IDF APIs ... but instead a wealth of entry points in the system code. As such, we have a relatively small number of jump table entries.

We have developed a macro that looks similar to:

EXPOSE(index, function)

for example

EXPOSE(23, myFunc)

These macros are then used in a C source file that is compiled twice. Once in the context of the system code and once in the context of the user code.

In the system code, this would store the function pointer at the given jump table index.

In the user code, this would generate (in assembler) a callable function with the same name as the exposed function where the implementation of that function efficiently looks up the given jump table index and branches to it. Since the code generate in the user code is "code" (about 10 assembler instructions) this satisfies the local user code linker resulting in an ELF that is nearly ready to be loaded and run.
Free book on ESP32 available here: https://leanpub.com/kolban-ESP32

jcsbanks
Posts: 300
Joined: Tue Mar 28, 2017 8:03 pm

Re: Separate user code from system code to decrease ota binary size

Postby jcsbanks » Tue Jun 05, 2018 5:15 pm

Interesting. Do you also pass parameters to the functions?

user4_esp32
Posts: 21
Joined: Sun Nov 26, 2017 4:48 pm

Re: Separate user code from system code to decrease ota binary size

Postby user4_esp32 » Mon Feb 04, 2019 4:42 pm

Would it be possible to use linker script generation https://docs.espressif.com/projects/esp ... ation.html to place all of the esp-idf "system" in one portion of the memory, then place the user function code in another portion of the memory, and only perform OTAs on the user portion? (i.e. the vast majority of OTAs would be to the user code, with occassional OTAs to the esp-idf "system" code)

Who is online

Users browsing this forum: No registered users and 26 guests