2

In order to combine .c and assembly, I want to pass start address of my .c code, and program microcontroller to know that its program starts at that address. As I am writing my startup file in assembly, I need to pass .c code starting address to assembly, and then to write this address to the specific memory region of microcontroller ( so the microcontroller can start execution on this address after RESET)

Trying to create a project for stm32f103 in Keil with this structure:

Some .c file, for example main.c (for the main part of the program).
Startup file in assembly language. Which gets the adress of entry to the function written in some .c file, to be passed to Reset_Handler
Scatter file, written in this way:

LR_IROM1 0x08000000 0x00010000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00010000  {  ; load address = execution address
   *.o (RESET, +First)   ; RESET is code section with I.V.T.
   * (InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 0x00005000  {  ; RW data
   .ANY (+RW +ZI)
  }
}

The problem is passing the entry point to the .c function. Reset_Handler, which needs .c entry point(starting adress) passed by __main, looks like this:

Reset_Handler PROC

    EXPORT Reset_Handler [WEAK]
    IMPORT __main
    LDR R0, =__main
    BX R0

    ENDP

bout entry point __main, as a answer for one assembly raleted question was written:

__main() is the compiler supplied entry point for your C code. It is not the main() function you write, but performs initialisation for the standard library, static data, the heap before calling your `main()' function.

So, how to get this entry point in my assembly file?

Edit>> If somebody is interested in solution for KEIL, here it is, its all that simple!

Simple assembly startup.s file:

        AREA STACK, NOINIT, READWRITE
        SPACE 0x400       
Stack_top                 

        AREA RESET, DATA, READONLY
        dcd Stack_top     
        dcd Reset_Handler



        EXPORT _InitMC
        IMPORT notmain

        AREA PROGRAM, CODE, READONLY

Reset_Handler PROC
        bl notmain
        ENDP

_InitMC   PROC          ;start of the assembly procedure
Loop
        b Loop          ;infinite loop
        ENDP

        END

Simple c file:

extern int _InitMC();
int notmain(void) {
    _InitMC();
    return 0;
}

Linker is the same as the one mentioned above. Project build was successful.

Vaso
  • 811
  • 6
  • 12
  • What's your problem exactly? Do you get some error? If so, what? – Jester Mar 18 '19 at 19:15
  • Hey @Jester. I just dont know how to pass it. When I compile everything, compiler puts .c code at some address, in this particular case for stm32f103c8 it will be something like 0x2000 xxxx. But I dont know that in advance, so that i can write this address to 0x0800 0004 (this is the address for RESET vector)- it means that on RESET microcontroller will start execution from that address. – Vaso Mar 18 '19 at 19:20
  • address orred with 1 to be exact, and from power up there is no code in sram so you certainly cannot have "c" code there. back up, and explain what you are trying to do. make a very very simple program main ( ) { return 5; } or something like that with a minimal linker script, show the disassembly, etc. and then what you dont like about that and want to change... – old_timer Mar 18 '19 at 19:34
  • you put the address of your C entry point if you are writing the bootstrap code then you can call it pickle() if you want it doesnt have to be main or __main() or anything else. understand that you might be losing library support though. from the program I described above examine the bootstrap code provided by the toolchain you are using if you have one that can build for this target. that should answer all of your questions, but if they dont it makes a good talking point if we can see it. – old_timer Mar 18 '19 at 19:36
  • you want the toolchain to do the orr with 1 btw... – old_timer Mar 18 '19 at 19:58
  • Hey @old_timer. Thanks for an answer. Yeah, you are absolutely right. Of course c code wont be at 0x2000 xxxx. I will write now simple program and try to show it to you. – Vaso Mar 18 '19 at 20:13

1 Answers1

2

Using the gnu toolchain for example:

Bootstrap:

.cpu cortex-m0
.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word loop
.word loop
.word loop

.thumb_func
reset:
    bl notmain
    b loop
.thumb_func
loop:   b .

.align

.thumb_func
.globl fun
fun:
    bx lr

.end

C entry point (function name is not relevant, sometimes using main() adds garbage, depends on the compiler/toolchain)

void fun ( unsigned int );
int notmain ( void )
{
    unsigned int ra;
    for(ra=0;ra<1000;ra++) fun(ra);
    return(0);
}

Linker script

MEMORY
{
    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > rom
    .rodata : { *(.rodata*) } > rom
    .bss : { *(.bss*) } > ram
}

Build

arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding  -mthumb -mcpu=cortex-m0 -march=armv6-m -c so.c -o so.thumb.o
arm-none-eabi-ld -o so.thumb.elf -T flash.ld flash.o so.thumb.o
arm-none-eabi-objdump -D so.thumb.elf > so.thumb.list
arm-none-eabi-objcopy so.thumb.elf so.thumb.bin -O binary
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding  -mthumb -mcpu=cortex-m3 -march=armv7-m -c so.c -o so.thumb2.o
arm-none-eabi-ld -o so.thumb2.elf -T flash.ld flash.o so.thumb2.o
arm-none-eabi-objdump -D so.thumb2.elf > so.thumb2.list
arm-none-eabi-objcopy so.thumb2.elf so.thumb2.bin -O binary

Result (all thumb versions)

Disassembly of section .text:

08000000 <_start>:
 8000000:   20001000
 8000004:   08000015
 8000008:   0800001b
 800000c:   0800001b
 8000010:   0800001b

08000014 <reset>:
 8000014:   f000 f804   bl  8000020 <notmain>
 8000018:   e7ff        b.n 800001a <loop>

0800001a <loop>:
 800001a:   e7fe        b.n 800001a <loop>

0800001c <fun>:
 800001c:   4770        bx  lr
 800001e:   46c0        nop         ; (mov r8, r8)

08000020 <notmain>:
 8000020:   b570        push    {r4, r5, r6, lr}
 8000022:   25fa        movs    r5, #250    ; 0xfa
 8000024:   2400        movs    r4, #0
 8000026:   00ad        lsls    r5, r5, #2
 8000028:   0020        movs    r0, r4
 800002a:   3401        adds    r4, #1
 800002c:   f7ff fff6   bl  800001c <fun>
 8000030:   42ac        cmp r4, r5
 8000032:   d1f9        bne.n   8000028 <notmain+0x8>
 8000034:   2000        movs    r0, #0
 8000036:   bd70        pop {r4, r5, r6, pc}

Of course this has to be placed in flash at the right place with some tool.

The vector table is mapped by logic to 0x00000000 in the stm32 family.

08000000 <_start>:
 8000000:   20001000
 8000004:   08000015 <---- reset ORR 1

And in this minimal code the reset handler calls the C code the C code messes around and returns. Technically a fully functional program for most stm32s (change the stack init to a smaller value for those with less ram say 0x20000400 and it should work anywhere by using -mthumb by itself (armv4t) or adding the cortex-m0. well okay not the armv8ms they can technically not support all of armv6m but the one in the field I know about does.

I don't have Kiel so don't know how to translate to that, but it shouldn't be much of a stretch, just syntax.

halfer
  • 19,824
  • 17
  • 99
  • 186
old_timer
  • 69,149
  • 8
  • 89
  • 168
  • Thank you @old_timer. Your answer is really something more then just answering to my question. I will edit my answer to show the syntaxis for keil. Can you please suggest me some books regarding the gnu toolchain. I am trying to get to know stm32 from very basis. I am using keil for now as it provides really easy interface for debug and compilation. – Vaso Mar 18 '19 at 21:47
  • dont know about books for gnu, it has its own documentation. high percentage of folks have experience. more likely to find gnu toolchain help here on SO and elsewhere than other toolchains due to the percentage of folks with experience relative to other toolchains. as with baremetal itself you have to just put some time in and hack/tinker/play whatever your word is... – old_timer Mar 19 '19 at 05:57
  • the only thing really special in that code is thumb_func, the rest is obvious, thumb_func tells the toolchain that the next label that comes along is considered a function address, so that when used as ".word reset" the toolchain orrs the one on. try it without the thumb_func in front of the reset: label and you will see the orr with one goes away and your mcu will fault instead of boot, and likely your fault hander will fault as well. – old_timer Mar 19 '19 at 06:00
  • pre-built gnu cross compilers for arm are easy to come by if you dont want to build your own. – old_timer Mar 19 '19 at 06:01