platformio / platform-ststm8

ST STM8: development platform for PlatformIO
https://registry.platformio.org/platforms/platformio/ststm8
Apache License 2.0
41 stars 26 forks source link

Binary code generated by Platformio is too big #60

Open rtek1000 opened 1 year ago

rtek1000 commented 1 year ago

Hello,

Is it normal for the binary HEX/S19 file generated by Platformio/SDCC to be larger than the code generated by STVD/Cosmic?

The attached codes are complete projects for using serial port with interrupt reception. They are not exactly the same commands, but they are similar.

Platformio (4842 bytes): Large_code_Hex

STVD (2824 bytes): Small_code

Comparing to code generated by SDCC with Makefile (Direct access to registers), it is even smaller than Platformio.

About 8.8 times smaller.

Makefile/SDCC (552 bytes): Very_Small_code

Based in this source: https://github.com/rtek1000/W1209-firmware-modified/tree/master/w1209-firmware-modified/Src

W1209_Remote_Serial_SDCC_Makefile.tar.gz

spl-uart-loopback_Platformio.tar.gz

UART Interrupt_Cosmic.tar.gz

wmarkow commented 10 months ago

I'm also facing the issue of big binary code. I use the following board in platformio: board: stm8s003f3; platform: ststm8; framework: arduino in version 2.1.0

During my development I have reached the 99.% limit (used 8182 bytes from 8192 bytes) of the Flash. This is the piece of logfile during the build (to point the toolchain used by Platformio):

CONFIGURATION: https://docs.platformio.org/page/boards/ststm8/stm8s003f3.html
PLATFORM: ST STM8 (2.1.0) > ST STM8S003F3 chip
HARDWARE: STM8S003F3P6 16MHz, 1KB RAM, 8KB Flash
DEBUG: Current (stlink) External (stlink)
PACKAGES:
 - framework-arduinoststm8 @ 0.50.210317
 - tool-stm8binutils @ 0.230.0 (2.30)
 - toolchain-sdcc @ 1.30901.11242 (3.9.1)

I did some tests and: the code snippet of main.c below gives me 99.9% of Flash usage:

#include <Arduino.h>

void display_device_info();

void setup()
{
    ...
    display_device_info();
    ...
}

void display_device_info()
{
    // here the function definition
}

When I commented out the usage of display_device_info() (so this method is not used at all in the source code) I also have the 99.9% of the flash memory usage, while I expected the not used display_device_info() method to be not included into the binary :

#include <Arduino.h>

void display_device_info();

void setup()
{
    ...
    //display_device_info();
    ...
}

void display_device_info()
{
    // here the function definition
}

When I totally comment out the display_device_info() definition, then I have 97.6% of memory usage:

#include <Arduino.h>

//void display_device_info();

void setup()
{
    ...
    //display_device_info();
    ...
}

//void display_device_info()
//{
//    // here the function definition
//}

Maybe some additional switch need to be used during compiling/building, so some kind of optimization would be enabled? At the moment I have no idea.

@rtek1000, did you solve the issue somehow?

wmarkow commented 10 months ago

The following platform PLATFORM: ST STM8 (2.1.0) > ST STM8S003F3 chip that I use (keep in mind the version 2.1.0) has the following package definition inside of its platform.json:

"toolchain-sdcc": {
      "type": "toolchain",
      "owner": "platformio",
      "version": ">=1.40100.0,<1.40201.0",
      "optionalVersions": [
        "~1.30901.0"
      ]
    },

It says the toolchain is in version between >=1.40100.0 and <1.40201.0 however during compilation I see that 1.30901.0 is used. With a small update in my platformio.ini I have forced to use a different version of the toolchain:

[env:hc12]
board = stm8s003f3
board_build.mcu = stm8s003f3p6
platform_packages =
  toolchain-sdcc@>=1.40100.0,<1.40201.0

Then the version 1.40200.0 is used and the flash memory footprint has dropped to around 92% but anyway the not used methods are still linked in. I could be happy at the moment with that but unfortunately - after flashing the chip - the software doesn't work; the chip seems to be dead; at least there is no communication over serial port. It works again when I use the default (optional) toolchain. That may point me to the conclusion, that toolchain-sdcc version 1.40200.0 generates a buggy firmware.

wmarkow commented 10 months ago

It seems that SDCC toolchain doesn't optimize the code in the meaning of "not linking the unused functions". See the topics below: https://electronics.stackexchange.com/questions/604740/how-to-suppress-unused-c-library-functions-in-sdcc-compiler https://sourceforge.net/p/sdcc/feature-requests/452/

Especially a quotation from the first link:

It is not possible to eliminate the unused function while linking. I got the answer to the above question posted on the official forum of SDCC and it is as follows:

    >>The linker sdld does not support function-level linking. The standard library, therefore, uses one source file per function.

So we can make a header file which include all functions in that library and then make single c files containing single functions . Header file must be included in each C file so that we can call other functions of same library. Then compile all individual C files. SDCC can then eliminate the unused functions as they are in separate modules

If I understand correctly, the unused function may be not linked as long as its definition is in a separate compilation module (i.e. a C file).

wmarkow commented 10 months ago

I did more tests and splitting functions into separate compilation modules is not good enough. Then I have found this explanation. The linker will not put unused function to the output binary only if the functions are located inside of the *.lib file. This means that the optimization mechanism works only for Platformio library code. Possible solutions:

The project structure looks like on the picture below:

stm8_project_structure

Under lib there is a "fake" library called core. It requires library.json file, so Platformio can use it:

{
    "frameworks": "arduino",
    "name": "core",
    "platforms": "ststm8",
    "version": "1.0.0"
}

The main platformio.ini contains the following configuration:

[env]
platform = ststm8
framework = arduino
build_type = release
lib_extra_dirs = lib
lib_deps = core

where two last parameters are important:

In this way the source code under lib/core will be compiled into a single *.lib file and will be reused then in the main sources. With this setting the unused methods are not inside of the output binary and the binary - which previously hat 99.9% of flash - uses now 96.5% of flash.

I can imagine that it may not solve the issue reported by @rtek1000 , because the STVD/Cosmic compiler may still generate a smaller code (more optimized) but anyway it is good to now about SDCC that it doesn't skip unused functions as easily.

Link to my code, where you can check the solution: https://github.com/wmarkow/arduino-sandbox/tree/master/fox-hunting-stm8

rtek1000 commented 7 months ago

@rtek1000, did you solve the issue somehow?

Sorry I didn't answer, I just didn't notice the question.

I no longer used Platformio for STM8, only for other uC, Arduino, STM32, ESP32.

In fact, I thought the navigation part of vscode was good, to find the functions in the libraries, but I didn't like Platformio itself as much as the Arduino IDE, because you have to write commands in files instead of clicking on a menu, for example , select the serial monitor speed.

I also didn't try to change uC in an already finished project, which seems to be much easier with the Arduino IDE.

For STM32, I recommend STM32CubeIDE, the last version I used was 1.12.1, if I'm not mistaken the next versions had the compiler updated and this caused several complaints of errors when compiling.

For ESP32, what I recommend is using ESP-IDF, it seems that there is also a specific IDE for ESP32, I haven't used it yet, but it should be similar to the STM32 case, a dedicated IDE for ESP32 should be better too.

The Arduino platform is good for a first contact, but as it adapts the generic code to hardware, this ends up needing more resources as well.

Anyone who wants to make the most of uC needs to use a program made in Baremetal mode, manipulating only the necessary registers, without using libraries.

In the case of uC PIC, for example, using a compiler in Assembler is much smaller than compiling in XC8.