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:
- After restart - MCU has to check vector table and execute reset handler, where I initialize stack pointer.
- 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:
- What has to be corrected in linker script
- 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.