Use Custom Toolchain (ARMCC instead of GCC)

After some very crude hacking I got it to compile + link successfully.

Since I don’t have ARMCC (v5 I guess?) I can’t do much here.

1 Like

Nice. This helped me realize that using -M option prevents creation of output files, apparently. Now, with -M removed, output files get generated under the .pio workspace folder and .elf file is finally generated as well.

Another difference between your project and mine is that I have a custom linker .ld file. But for some reason PIO is trying to access the built-in linker file it has for this specific STM target. I get the following error:

Fatal error: L6002U: Could not open file C:\Users\gacnik\.platformio\platforms\ststm32\ldscripts: Permission denied

Again, not sure why it is trying to open default linker file (and also what prevents it from opening, since multiple .ld files are located in that folder) when I have defined the path to my own linker file (from CubeMX) inside platform.ini file like so:

board_build.ldscript = "src\STM32F103VCTx_FLASH.ld"

Any more ideas? :slight_smile:

EDIT: Actually, .elf file doesn’t get generated. Last step before error occurs is linking. And of course if linker file cannot be accessed for some reason, .elf can’t be created.

I’ve had this error when there were -LC:\Users\gacnik\.platformio\platforms\ststm32\ldscript flags present, I had to clear that out using

1 Like

Okay, this has resolved my last issue I had (in addition to OBJCOPY additional options to generate .bin file). I have yet to try this on HW and see if additionals modifications are needed.

Otherwise I would just like to know what is the purpose of the following? For sake of completeness.

SIZEPROGREGEXP=r"^(?:ER_IROM1)\s+(\d+).*",
SIZEDATAREGEXP=r"^(?:RW_IRAM1)\s+(\d+).*",

I built my project without this and it builds okay. I would say these are some linker file specific since I see ROM and RAM and RW (read write?).

Additionally, why you omitted putting -o $TARGET in your project’s script? Didn’t you say it’s bad if you leave it out? On the other hand, if you check env object’s contents you will see that -o is included during build by default. So, IMO, adding this from script would only add redundant -o options (or overwritten existing ones).

P.S.: Can you just delete the comments with absolute path and name of my projects from your github project? Thanks :laughing:

That’s so that the “Flash X% […] X Bytes” display is correct. PlatformIO will use arm-none-size-eabi -A -d <firmware.elf> to print all sections (and their sizes) of the .elf file. The armlink in conjuction with the used scatter file basically creates these two main sections, one for flash (“ROM”) and one for data (RAM). Without that, PlatformIO would report the flash and RAM usage as both 0 bytes since it’s default-setup for section names generated by the GNU compiler and the linker scripts.

I looked in the verbose output and saw PlatformIO / SCons added it automatically in the invocation.

Done.

1 Like

I see that size information doesn’t work for me and displays 0 bytes for each RAM and FLASH. I used your modification of SIZEPROGREGEXP and SIZEDATAREGEXP. I also see the there is no size tool under the MDK-ARM (ARMCC). Should it still be able to generate this information if there is no size tool?
This is what I get in the verbose output:

'SIZECHECKCMD': '$SIZETOOL -A -d $SOURCES',
'SIZEDATAREGEXP': '^(?:RW_IRAM1)\\s+(\\d+).*',
'SIZEPRINTCMD': '$SIZETOOL -B -d $SOURCES',
'SIZEPROGREGEXP': '^(?:ER_IROM1)\\s+(\\d+).*',
'SIZETOOL': 'arm-none-eabi-size',

This is trivial. But what I’m more worried about is when I’m trying to load .elf on target device, I get this:

** Programming Started **
Warn : no flash bank found for address 0x00008000
** Programming Finished **
** Verify Started **
Error: checksum mismatch - attempting binary compare
embedded:startup.tcl:1070: Error: ** Verify Failed **
in procedure 'program' 
in procedure 'program_error' called at file "embedded:startup.tcl", line 1131
at file "embedded:startup.tcl", line 1070
*** [upload] Error 1

This are address bases that are defined in my linker .ld script. Looks like FLASH base from the error log is not the same as what linker file says. Maybe I can tweak these upload-related settings in PIO as well?

/* Specify the memory areas */
MEMORY
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 48K
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 256K
}

It seems my linker file isn’t used. I may have found the problem but no solution. This is ARMCC specific and not PIO. If one uses linker file .ld (which is normally used in combination with GCC linker) additional options like “–sysv” and “–ld_script=linker_script.ld” need to be used (or “–scatter=scatter_file.sct” which is a default ARM memory layout file). However the problem is that none of mentioned options is recognized by the ARMLINK although these options are in fact valid for this version of ARMLINK. This also happens for other kind of options. Only the most commonly used ones like “-g”, “-c”, “–c99”, “-cpu=Cortex-M3”, etc. are recognized. For most others I get this kind of error:

Fatal error: C3900U: Unrecognized option '--split_section'.

Maybe I should ask this elsewhere as this discussion is reaching out of the scope of initial problem I had to solve.

@maxgerhardt How can I provide linker options so that when ARMLINK is run something like this will be executed?

--scatter file1.o file2.o file3.o ... -o .pio\build\genericSTM32F103VC\proj_name.axf

It seems just providing --scatter option would result in error since no input (the .o files) is provided as its argument.

The object files and -o <firmware.elf> are added automatically, you should not try to add them yourself. I added the --scatter option via linkflags.

Are you sure armlink is invoked? Unless I overrode env["LINKER"] = "armlink", it would still try to invoke the $CXX.

What’s the current full linker invocation? (pio run -v -j1)

The problem is that if I used the --scatter option in existing script under LINKFLAGS I got error Unrecognized option even though this is a valid option. Then I tried this manually from PIO CLI and --scatter was successfully recognized and produced .axf output. Which I then used to generate .elf and was able to successfully load firmware onto my board!

Maybe I am getting Unrecognized option when I use --scatter inside LINKFLAGS without this option being followed with arguments (the output .o files).

If I add this option directly to LINKFLAGS without providing any additionall argument I get the following error:

".pio\build\genericSTM32F103VC\src\Core\Src\fooFoo.o", line 1 (column 8): Error: L6226E: Missing base address for region \177ELF\001\001\001.
".pio\build\genericSTM32F103VC\src\Core\Src\fooFoo.o", line 1 (column 8): Error: L6228E: Expected '{', found ''.
Error: L6372E: Image needs at least one load region.

This is because --scatter remains under LINKFLAGS without any argument. Whereas a .sct file should first be provided, then all the output files as the following arguments. This is how it looked like when I provided a valid output from PIO CLI (just so you know what I have in mind):

C:\Keil_v5\ARM\ARMCC\bin\armlink --scatter PCV2-SES.sct
.pio\build\genericSTM32F103VC\src\Core\Src\main.o
.pio\build\genericSTM32F103VC\src\Core\Src\fooFoo.o
.pio\build\genericSTM32F103VC\src\Core\Src\stm32f1xx_hal_msp.o
.pio\build\genericSTM32F103VC\src\Core\Src\stm32f1xx_it.o
.pio\build\genericSTM32F103VC\src\Core\Src\system_stm32f1xx.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_adc_ex.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_adc.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_can.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_cortex.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_dac_ex.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_dac.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_dma.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_exti.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_flash_ex.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_flash.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_gpio_ex.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_gpio.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_i2c.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_pwr.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_rcc_ex.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_rcc.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_tim_ex.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_tim.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_uart.o
.pio\build\genericSTM32F103VC\src\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal.o
.pio\build\genericSTM32F103VC\src\startup_stm32f103xe.o
-o .pio\build\genericSTM32F103VC\firmware.axf

This generated .axf file from where I re-generated a valid .elf file (valid because it was generated based on valid memory layout) using the following set of options:

C:\Keil_v5\ARM\ARMCC\bin\fromelf --elf --output .pio\build\genericSTM32F103VC\firmware.elf .pio\build\genericSTM32F103VC\firmware.axf

And this is all I’m trying to automate using that python script. To recap: create .axf file from .sct file using --scatter and all the .o files in the project. Then use fromelf to create .elf from .axf.

My bad, I misremembered – actually by setting my scatter file as the board_build.ldscript = .. option in the platformio.ini and with PlatformIO / SCons automatically generating a -T <linker script> argument, I just go in and transform the -T flag to --scatter. The argument after it remains unchanged and correct.

--scatter is not added to LINKFLAGS.

1 Like

Ahh okay. But otherwise, intially I already had .sct path defined with board_build.ldscript.

Otherwise that replacement code where -T are replaced with --scatter, doesn’t this assume that you first built a project using GCC and then ARMCC? Or is the -T option put into LINKFLAGS by default when PIO is installed?

Otherwise, with this setup where -T are replaced with --scatter and proper .sct file is provided, this is now successfully uploaded to target. I was initially filtering out -T options and therefore this replacement didn’t work out as expected.

PIO appends this option if the LDSCRIPT_PATH is set (which it is with board_build.ldscript, for example)

1 Like

I would only like to know how to provide additional options to linker flags so that I would be able to generate additional files (like .map, .lst, .axf) in the same folder that .elf and .bin are located? What I mean is how to let the PIO script (main.py probably) insert the current project name, .pio folder, etc. so that these files are always located under the same folder.

As far as I understand, .axf = .elf. Map should be generatable by prepending to LINKFLAGS with the appropriate “–map”, “$BUILD_DIR/$PROGNAME.map” argument

https://developer.arm.com/documentation/dui0474/m/linker-command-line-options/--map----no-map

And .lst, not sure where the disassembler is, but you could add a post action to the $PROGNAME.elf file to call upon `arm-none-eabi-objdump -d firwmare.elf > disassembly.lst".

https://docs.platformio.org/en/latest/scripting/actions.html

1 Like

I tried but I get the following error:

*** [.pio\build\genericSTM32F103VC\firmware.elf] AttributeError `'str' object has no attribute 'map'' trying to evaluate `$PROGNAME.map'

Mh the env vars need to be wrapped in ${} then like in the platformio-build-esp32.py seen above.

This is the exact string it generates but I get error that --map doesn’t expects such argument. Shouldn’t actual path be created before storing this string to LINKFLAGS?

'--map="${BUILD_DIR}\\${PROGNAME}.map"'