Home > database >  STM32F103C8 bare metal with linker scripts GCC
STM32F103C8 bare metal with linker scripts GCC

Time:12-23

I am trying to write bare metal code in assembly and compile link it using GCC toolchain. As I know the proper steps is to follow the following steps:

  1. After restart - MCU has to check vector table and execute reset handler, where I initialize stack pointer.
  2. Execute main code. In order to complete this task I also need to have respective linker script. When i am trying to execute linker it throws syntax error. Please advice:
  3. What has to be corrected in linker script
  4. Correct vtable and handler execution sequence.

Code:

stack_size      =       0x400
stack_start     =       0x20000000 stack_size
gpiob_base      =       0x40010C00
rcc_base        =       0x40021000
rcc_apb2enr     =       rcc_base 0x18
gpio_crl        =       gpiob_base
gpiob_odr       =       gpiob_base 0x0C

                .syntax unified
                .cpu cortex-m3
                .thumb
                .global main
                .global vtable
main:
                LDR R0, =rcc_apb2enr
                LDR R1, [R0]
                LDR R2, =0x8 // Activate 3rd bit in registry
                ORR R1, R2
                STR R1, [R0]

                // Configure GPIO_CRL
                LDR R0, =gpio_crl
                LDR R1, [R0]
                LDR R2, =0xFFFFFF00
                AND R1,R1,R2
                ORR R1, R1, #0x20
                STR R1, [R0] // Reset register

                //Configure GPIOB_ODR
                LDR R0, =gpiob_odr
                LDR R1, [R0]
                ORR R1, #0x2
                STR R1, [R0]
                B .
vtable:
        .word   stack_start
        .word   reset_handler

reset_handler:
                B main

Linker script:

/* - STM32F103C8T6 - Medium Density device
 * - RAM: 20K, Flash:64K CPU: 72MHz
*/
ENTRY(reset_handler);
MEMORY {
        RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
        FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K
}

SECTIONS
{
        /* Section that stores program instructions (code) */
        .text : {
                . = ALIGN(4);
                KEEP(*(.vtable))
                *(.text)
                *(.text*)
                *(.rodata)
                *(.rodata*)
                . = ALIGN(4);
        } > FLASH

        _data_flash = .;

        // Section that store initialized data - variables
        .data : AT(_data_flash){
                . = ALIGN(4);
                _data_begin = .;
                *(.data)
                *(.data*)
                . = ALIGN(4);
                _data_end = .;
        }  > RAM

        /* Section that stores uninitialized data */
        .bss :{
                _bss_begin = .;
                _bss_start_ = _bss_begin;
                *(.bss)
                *(.bss*)
                *(COMMON)
                . = ALIGN(4);
                _bss_end = .;
                _bss_end_ = _bss_end;
        } > RAM

        /* Here we define stack */
        _stack_size = 1024;
        _stack_end = ORIGIN(RAM) LENGTH(RAM);
        _stack_begin = _stack_end - _stack_size;
        . = _stack_begin;
    ._stack :{
                . = .   _stack_size;
        } > RAM
}



._stack :{
                . = .   _stack_size;
        } > RAM
}

Disassembly of program:

pi@mylab:~/assembly $ arm-none-eabi-objdump --disassemble bp.o

bp.o:     file format elf32-littlearm


Disassembly of section .text:

00000000 <main>:
   0:   480d            ldr     r0, [pc, #52]   ; (38 <reset_handler 0x4>)
   2:   6801            ldr     r1, [r0, #0]
   4:   f04f 0208       mov.w   r2, #8
   8:   ea41 0102       orr.w   r1, r1, r2
   c:   6001            str     r1, [r0, #0]
   e:   480b            ldr     r0, [pc, #44]   ; (3c <reset_handler 0x8>)
  10:   6801            ldr     r1, [r0, #0]
  12:   f06f 02ff       mvn.w   r2, #255        ; 0xff
  16:   ea01 0102       and.w   r1, r1, r2
  1a:   f041 0120       orr.w   r1, r1, #32
  1e:   6001            str     r1, [r0, #0]
  20:   4807            ldr     r0, [pc, #28]   ; (40 <reset_handler 0xc>)
  22:   6801            ldr     r1, [r0, #0]
  24:   f041 0102       orr.w   r1, r1, #2
  28:   6001            str     r1, [r0, #0]
  2a:   e7fe            b.n     2a <main 0x2a>

0000002c <isr_vector>:
  2c:   20000400        .word   0x20000400
  30:   00000034        .word   0x00000034

00000034 <reset_handler>:
  34:   f7ff bffe       b.w     0 <main>
  38:   40021018        .word   0x40021018
  3c:   40010c00        .word   0x40010c00
  40:   40010c0c        .word   0x40010c0c

CodePudding user response:

For what you are doing you can start simpler.

flash.s

stack_size      =       0x400
stack_start     =       0x20000000 stack_size
gpiob_base      =       0x40010C00
rcc_base        =       0x40021000
rcc_apb2enr     =       rcc_base 0x18
gpio_crl        =       gpiob_base
gpiob_odr       =       gpiob_base 0x0C


.syntax unified
.cpu cortex-m3
.thumb

vtable:
    .word   stack_start
    .word   reset_handler


.thumb_func
reset_handler:
    B main

main:
    LDR R0, =rcc_apb2enr
    LDR R1, [R0]
    LDR R2, =0x8 // Activate 3rd bit in registry
    ORR R1, R2
    STR R1, [R0]

    // Configure GPIO_CRL
    LDR R0, =gpio_crl
    LDR R1, [R0]
    LDR R2, =0xFFFFFF00
    AND R1,R1,R2
    ORR R1, R1, #0x20
    STR R1, [R0] // Reset register

    //Configure GPIOB_ODR
    LDR R0, =gpiob_odr
    LDR R1, [R0]
    ORR R1, #0x2
    STR R1, [R0]
    B .

flash.ld

MEMORY
{
    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
}
SECTIONS
{
    .text   : { *(.text*)   } > rom
}

build

arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m3 flash.s -o flash.o
arm-none-eabi-ld -nostdlib -nostartfiles -T flash.ld flash.o -o so.elf
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy -O binary so.elf so.bin

Examine (should always do this for a new project)

so.elf:     file format elf32-littlearm


Disassembly of section .text:

08000000 <vtable>:
 8000000:   20000400    andcs   r0, r0, r0, lsl #8
 8000004:   08000009    stmdaeq r0, {r0, r3}

08000008 <reset_handler>:
 8000008:   e7ff        b.n 800000a <main>

0800000a <main>:
 800000a:   480b        ldr r0, [pc, #44]   ; (8000038 <main 0x2e>)
 800000c:   6801        ldr r1, [r0, #0]
 800000e:   f04f 0208   mov.w   r2, #8
 8000012:   ea41 0102   orr.w   r1, r1, r2
 8000016:   6001        str r1, [r0, #0]
...

So this has half a chance of working simply by having the vector table in the right place, and the reset handler address is correct (Address orred with one).

Can also see it is generating thumb2 code (that will bite you if you end up on a cortex-m0 and do not fix your build/code easiest to start with cortex-m0 which works on all of them then if needed select thumb2. YMMV)

now if you had other objects the one with the vector table, flash.o in this case, would need to be the first object on the command line to be the first .text to be parsed and placed in the binary.

You can complicate things to mark a special section for the vector tables (note you call sections whatever you want with some limitations, do not have to call it vectors or vector_table, etc unless you want). You will then need to add more linker stuff and more code, in the source, etc. Since you have to get the right stuff on the command line to start with and you have to put the work in to get the code right including directives. Why create more work that adds no value, just put the vector table in the right place, near the reset handler. YMMV.

So you have your vector table near the end of the code, not the first thing. Basically you asked for the table to be in the wrong place. And it is. You need it first to fit in with the rest of this code. Next since you are referencing the address to the reset handler in the table, you need the function address not just the address, you need to declare that label as a function for this to work for thumb/arm. For thumb functions you can use the shortcut .thumb_func somewhere before the label (does not have to be the line before) it will trigger the next label the gnu assembler finds as a thumb function. Because this is all a single file under a single .thumb near the top the branch to main is okay. Ideally you might want to declare that a function too, certainly if you are doing arm/thumb interwork for a non-cortex-m arm you need to do this in your global asm functions.

An alternate way that works both for arm and thumb gnu assembler is

.type functionname, %function
functionname:

Your linker script is over complicated for this task. Im sure you are working your way toward that complication.

You used the right address but you disassembled the object not the elf. But based on your code, it would have still built wrong.

You are declaring a stack in the linker script (I tend to hate this with a passion, but some folks like it, YMMV). But you are not using that linker script variable in your vector table, you are using a computed value. It is not broken for this program, but something to note.

The stack is being set up by the vector table so you can use it (can call a function). I recommend

bl main
b .

in the reset handler, and then main can choose to infinite loop or return and you are safe/fine.

If you had not set up the stack address properly in the vector table then you might get in trouble there.

Note odr is fine but you might want to look at the bsrr register. More flexible for individual port bits. And turning the bit "on" does not turn the led "on" on all boards, some the gpio is wired to the other end and you ground it to turn it on. So be aware of that and look at the schematic or documentation.

  • Related