cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 CubeIDE assembly code + C issue

gambablue
Associate

Hello, I am working on C + Assembly and encountering an issue.

I am working on a simple program on an STM32F3 (Cortex-M4) that calls an assembly function (assembly_main) from main() in a loop. However, assembly_main()executes only once and does not return back to main() as expected. The only way to make it work continuously is by forcing an unconditional branch, which is not what I want.

I suspect the issue is related to stack pointer (SP) corruption or an incorrect return address being stored while executing main(), which prevents assembly_main() from properly returning.

Questions

  • I assume the system starts execution from Reset_Handler, jumps to main(), and then calls assembly_main()? Is this the correct flow?
  • However, something goes wrong when trying to return from assembly_main(). Why does bx lr fail to return back to main()?
    • It seems that LR (Link Register) is not holding a valid return address.
    • Is my stack pointer (SP) being overwritten?

Any insights or debugging pointers would be greatly appreciated!



/* I do have an STM32F303RETX_FLASH.ld file which initialize with: Entry Point

ENTRY(Reset_Handler)*/

/* Highest address of the user mode stack */

_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */

_Min_Heap_Size = 0x200; /* required amount of heap */

_Min_Stack_Size = 0x4000; /* required amount of stack */

// ... other lines of initializations device specific

// I have two simple codes; one is C as follows:
// C code just a looping call to blink led in assembly
extern void assembly_main(void);

__attribute__((noreturn)) int main(void) {

while (1) {

assembly_main();

}

}

 

@ the reset_handler and assembly are below 

.section .vectors

.global Reset_Handler

Reset_Handler:

ldr r0, =_estack @ Set MSP to top of RAM

msr msp, r0

mov sp, r0 @ Explicitly set SP

ldr r0, =main @ Load address of main()

bx r0 @ Jump to main()

.syntax unified

.cpu cortex-m4

.thumb

 

.global assembly_main

.section .text

assembly_main: //init device and blink

@ Enable GPIOA Clock (RCC_AHBENR)

ldr r0, =0x40021014 @ RCC_AHBENR register address

ldr r1, [r0] @ Load current value

orr r1, r1, #(1 << 17) @ Enable GPIOA clock (Bit 17)

str r1, [r0] @ Store back

 

@ Configure PA5 as output (GPIOA_MODER)

ldr r0, =0x48000000 @ GPIOA_MODER register address

ldr r1, [r0] @ Load current value

bic r1, r1, #(3 << (5 * 2)) @ Clear bits for PA5

orr r1, r1, #(1 << (5 * 2)) @ Set PA5 as output (01)

str r1, [r0] @ Store back

 

loop:

@ Turn LED ON (GPIOA_ODR)

ldr r0, =0x48000014 @ GPIOA_ODR register address

ldr r1, [r0] @ Load current value

orr r1, r1, #(1 << 5) @ Set PA5 high

str r1, [r0] @ Store back

bl delay @ Call delay function

 

@ Turn LED OFF (GPIOA_ODR)

ldr r1, [r0] @ Load current value

bic r1, r1, #(1 << 5) @ Set PA5 low

str r1, [r0] @ Store back

bl delay @ Call delay function

bx lr @this should return to main but it will not!

 

delay:

ldr r2, =200000 @ Adjust delay for visibility

delay_loop:

subs r2, #1

bne delay_loop

bx lr

 

5 REPLIES 5
gbm
Principal

From your assembly function you call delay routine without saving the lr beforehand. Delay returns to your function, and your function returns to itself - to the instruction after the last delay call, so it's stuck in an infinite loop.

Add push {lr, r1} as the first instruction and replace bx lr at the end with pop {r1, pc}

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
gambablue
Associate

Thank you for the reply! I somehow missed that and made that correction.

However, the problem persists. Even just to make sure, I unrolled the internal functions in the assembly (delay related) to make sure there are no stack related issues.  It blinks only once as usual and will not return to main unless the following is replaced with pop{r1,pc}

 

ldr r0, =main

mov pc, r0 @explicit return to main :(

which i wouldn't prefer to have.

 

It is probably a stack issue, where I think lr gets corrupted or main.c vs main.s stack initialization differences??
I tried to make sure stack is well defined and quite large in the FLASH.id file as follows:

/* Entry Point */

ENTRY(Reset_Handler)

/* Highest address of the user mode stack */

_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */

_Min_Heap_Size = 0x200; /* required amount of heap */

_Min_Stack_Size = 0x8000; /* required amount of stack */

/* Memories definition */

MEMORY

{

CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 16K

RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K

FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 512K

}

 

and the stack pointer in the Reset_Handler:

Reset_Handler:

ldr r0, =_estack @ Set MSP to top of RAM

msr msp, r0

mov sp, r0 @ Explicitly set SP

ldr r0, =main @ Load address of main()

bx r0 @ Jump to main()

 

The final code in assembly is:

Reset_Handler:

ldr r0, =_estack @ Set MSP to top of RAM

msr msp, r0

mov sp, r0 @ Explicitly set SP

ldr r0, =main @ Load address of main()

bx r0 @ Jump to main()

.syntax unified

.cpu cortex-m4

.thumb

 

.global assembly_main

.section .text

 

assembly_main:

push {r1, lr} @ Save return address (lr) and registers

 

@ Enable GPIOA Clock (RCC_AHBENR)

ldr r0, =0x40021014

ldr r1, [r0]

orr r1, r1, #(1 << 17)

str r1, [r0]

 

@ Configure PA5 as output (GPIOA_MODER)

ldr r0, =0x48000000

ldr r1, [r0]

bic r1, r1, #(3 << (5 * 2))

orr r1, r1, #(1 << (5 * 2))

str r1, [r0]

 

@ Turn LED ON (GPIOA_ODR)

ldr r0, =0x48000014

ldr r1, [r0]

orr r1, r1, #(1 << 5)

str r1, [r0]

 

ldr r2, =900000

delay_loop1: @unrolled

subs r2, #1

bne delay_loop1

 

@ Turn LED OFF (GPIOA_ODR)

ldr r1, [r0]

bic r1, r1, #(1 << 5)

str r1, [r0]

 

ldr r2, =900000

delay_loop2: @unrolled

subs r2, #1

bne delay_loop2

 

pop {r1, pc} @ Restore registers and return properly (not working)

 

I would appreciate if you can comment. Thank you.

 

TDK
Guru

Why do you think it's not working? What does it do when you step through the code at the assembly level?

The following function returns correctly for me:

assembly_main:
push {r1, lr}
pop {r1, pc}

 

If you feel a post has answered your question, please click "Accept as Solution".

It will not work a) it blinks only once and gets stuck in hardware b) on Debug it yields the error below.
I tried the same 2 line code as suggested, but it gives the following error regardless on Debug. It will not go further from that undefined location obviously and debug quits. 

gambablue_0-1741620647349.png

Inserting extra breakpoints doesn't work since it crashes due to an error looks like an exception (an undefined location reference). I assume it is not the coding but rather a configuration problem, a misconfiguration of the stack etc. Same code works fine on Keil by the way.  Thank you.

 

Check if the size of RAM specified in the linker script is correct. In CubeIDE it influences the stack location.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice