Solution for function calls not working on baremetal c code for cortex m0 with gnu toolchain
is Given Below:
I am building a custom soc based on the arm cortex m0 design start. The SOC now has a cortex mo core attached to a AHB lite bus with some ram on it and a gpio module. I am currently struggling to write and compiling code for it using the gnu tool chain. The function calls are not working. I tried many things but nothing seems to work. The interesting part is if i compile the same code with keil, it works. I guess the problem is somewhere in how i am setting up the stack and/or the liker script or I am missing some compiler/linker options.
This works
#include<stdint.h>
volatile uint8_t* led_reg;
int main() {
led_reg=(uint8_t*)0x50000000;
while (1) {
*led_reg=0x8;
for(int i=0;i<=0x2FFFFF;i++);
*led_reg=0x4;
for(int i=0;i<=0x2FFFFF;i++);
*led_reg=0x2;
for(int i=0;i<=0x2FFFFF;i++);
*led_reg=0x1;
for(int i=0;i<=0x2FFFFF;i++);
}
}
But this doesn’t
#include<stdint.h>
volatile uint8_t* led_reg;
void wait(void){
for(int i=0;i<=0x2FFFFF;i++);
}
void on(uint8_t pat){
*led_reg=pat;
}
int main() {
led_reg=(uint8_t*)0x50000000;
while (1){
on(0x8);
wait();
on(0x4);
wait();
on(0x2);
wait();
on(0x1);
wait();
}
}
This is the linker script
ENTRY(Reset_Handler)
STACK_SIZE = 0x1000;
SECTIONS
{
. = 0x00000000;
.ram :
{
. = ALIGN(4);
_stext = .;
KEEP(*(.vectors .vectors.*))
*(.text .text.*)
*(.rodata .rodata*)
. = ALIGN(4);
_sbss = . ;
*(.bss .bss.*)
*(COMMON)
. = ALIGN(4);
_ebss = . ;
. = ALIGN(4);
_sdata = .;
*(.data .data.*);
. = ALIGN(4);
_edata = .;
. = ALIGN(8);
_sstack = .;
. = . + STACK_SIZE;
. = ALIGN(8);
_estack = .;
. = ALIGN(4);
_end = . ;
}
}
Relevant portion of the startup code
/* Exception Table */
__attribute__ ((section(".vectors")))
const DeviceVectors exception_table = {
/* Configure Initial Stack Pointer, using linker-generated symbols */
.pvStack = (void*) (&_estack),
.pfnReset_Handler = (void*) Reset_Handler,
.pfnNMI_Handler = (void*) NMI_Handler,
.pfnHardFault_Handler = (void*) HardFault_Handler,
Here is the compiling and linking command examples
arm-none-eabi-gcc -Wall -Werror -g -O0 -std=c99 -ffreestanding -ffunction-sections -fdata-sections -mcpu=cortex-m0 -mfloat-abi=soft -march=armv6-m -mthumb -Wall -Iinclude/headers -o build/main.o -c src/main.c
arm-none-eabi-gcc -Wall -Werror -g -O0 -std=c99 -ffreestanding -ffunction-sections -fdata-sections -mcpu=cortex-m0 -mfloat-abi=soft -march=armv6-m -mthumb -Wall -Iinclude/headers -Wl,-Map=build/firmware.map,--gc-sections -T vcoresoc.lds --specs=nano.specs build/main.o build/startup_vcoresoc.o build/syscall.o -o build/firmware.elf
You have several smaller bugs in this code. It is likely that gcc optimizes the code better than Keil and therefore the function could simply be removed. In some cases you are missing volatile
which may break the code:
led_reg=(uint8_t*)0x50000000;
should beled_reg=(volatile uint8_t*)0x50000000u;
, see How to access a hardware register from firmware?void wait(void){ for(int i=0;i<=0x2FFFFF;i++); }
should bevolatile
as well or the loop will just get removed.
Additionally, I don’t know if you wrote the startup code or if the tool vendor did, but it has bugs. Namely .pvStack = (void*) (&_estack)
invokes undefined behavior, since C doesn’t allow conversions from function pointers to object pointers. Instead of void*
you need to use an unsigned integer or a function pointer type. As mentioned in comments, putting some alignment keyword on a Cortex M vector table is fishy – it should be at address 0 so how can it be misaligned? It would be interesting to see the definition of DeviceVectors
.
Also, bare metal microcontroller systems do not return from main()
. You should not use int main()
, since that is likely to cause a bit of stack overhead needlessly. Use an implementation-defined form such as void main (void)
then compile for embedded (“freestanding”) systems. With gcc that is -ffreestanding
.