0

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 
Clifford
  • 88,407
  • 13
  • 85
  • 165
varun
  • 341
  • 2
  • 17
  • 2
    How do you know it is not calling the functions? Have you stepped it in a debugger or are you just observing the LED behaviour? Does `for( volatile int i=0;i<=0x2FFFFF;i++);` work? The loop may be optimised out without `volatile` (although perhaps not with `-O0`, but even so). Better in any event to use SYSTICK for timing than empty loops. – Clifford Aug 01 '21 at 12:35
  • You need to use a debugger to investigate. Are you possibly ending up in the `HardFault_Handler()`? – Clifford Aug 01 '21 at 12:41
  • 1
    As the function does not have any observable side effect, the compiler might have removed it for optimization. Maybe reduce optimization level. At least check generated code to see what really happens. – Gerhardh Aug 01 '21 at 12:43
  • the core does expose a jtag interface for debugging but at present i don't have a jtag debugger with me..I dont really know exactly if the functions are not being called or whether they are getting called but are not working properly..any other way to may be debug..!? – varun Aug 01 '21 at 12:44
  • Does your compiler come with something like `__NOP()` that you could use instead of a blank `;`? – Gerhardh Aug 01 '21 at 12:44
  • i am using the -O0 compiler flag..does the optimisations still apply..!? – varun Aug 01 '21 at 12:46
  • @varun, if you are compiling with `-O0` to disable optimization then that detail should be mentioned in the question. – John Bollinger Aug 01 '21 at 12:48
  • -O0 means no optimization. what chip is this, what is at 0x50000000? you have logic that completes the bus transaction? – old_timer Aug 01 '21 at 12:49
  • 1
    obviously you should never put an align in front of the exception vectors, if you have a need to align your whole design is broken. – old_timer Aug 01 '21 at 12:49
  • you have not shown the disassembly of your binary, the vector table, etc. – old_timer Aug 01 '21 at 12:49
  • 1
    looks like the problem is simply the lack of a volatile in the wait loop count variable. – old_timer Aug 01 '21 at 12:52
  • @JohnBollinger I have mentioned the compiler and linker commands in the question. – varun Aug 01 '21 at 12:52
  • 1
    well swd not really jtag. the core certainly exposes it, whether or not you bring that out in the soc design or not. if you dont ultimately expose the swd interface by the end, you will very much regret it. – old_timer Aug 01 '21 at 12:53
  • you are not going to get very far with this until you work on basic code generation and debugging skills. – old_timer Aug 01 '21 at 12:54
  • ok will try out the suggestions and get back – varun Aug 01 '21 at 12:54
  • The first code does not need a call stack but the second does. Are you sure your startup code is setting up the stack pointer? – stark Aug 01 '21 at 13:11
  • the stack pointer is setup in the vector table and 0x1000 bytes allocated. – old_timer Aug 01 '21 at 14:17
  • I was able to figure out that the hard fault handler was being hit using some code to turn on the leds in some specific pattern inside the HF handler. – varun Aug 02 '21 at 04:56
  • I also came across one post that uses stack push to align the stack pointer and just remembered that i have seen something like this in the disassembly of my code. It was in a function called init reference by __libc_init_array() called in the startup script before main. removed this call and adjusted the compiler/linker options to compile with no stdlib support and everything worked fine. Exact reason for the error, i don't know yet. Will update the post when i get a hands on a proper debugger. – varun Aug 02 '21 at 04:56
  • @varun : If you figure it out, do not update the question, post an answer and accept it. You can (and should) do that for your own questions if you find the answer independently. If you remove `__libc_init_array()` your C run-time will not be correctly set up; and may cause errors in more complex code. – Clifford Aug 02 '21 at 15:38
  • I suspect that your are linking the 32bit ARM version of Newlib rather then the Thumb version. Cortex-M devices generate an exception if you attempt to use the 32bit instruction set. That would explain why it woks in Keil. It does not explain why having function calls causes an issue, unless there is some stack checking code inserted that is from 32-bit ARM. See Paul Wood's answer at https://electronics.stackexchange.com/questions/344625/why-does-libc-init-array-cause-an-exception – Clifford Aug 02 '21 at 15:40
  • How much RAM have you got? – Lundin Aug 03 '21 at 09:06

1 Answers1

0

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 be led_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 be volatile 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.

Lundin
  • 195,001
  • 40
  • 254
  • 396