2
\$\begingroup\$

I just recently started working with a bare STM32F103C8T6 (an ARM Cortex M3) and ran into some difficulties.

I have set up a SysTick_Handler to switch the current context of the application. The handler invokes a PendSV_Handler in which I want to save the registers of the CPU to a datastructure. This is the simplified code:

typedef struct Registers {
    int r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12;
    int sp, lr, pc, xPSR;
} Registers;

void PendSV_Handler()
{
    Registers reg;

    register int r0 asm("r0");
    reg.r0 = r0;
    /* and so on with all other registers */
}

However while testing the inline asm I ran across an issue. I adapted my handler like this:

void PendSV_Handler()
{
    asm volatile(
      "movw r0 100"  // This should store the integer 100 in register r0
    );

    register int temp asm("r0");  // This should store the value in
                                  // register r0 into temp

    toggleLED();     // Toggles an LED to visualise the behavior
    delay_ms(temp);  // Delays the execution by [temp] ms, known to work
    toggleLED();
}

Now the problem is that the LED to be toggled only switches on once and then stays stuck. Also in the SysTick_Handler another LED is toggled with SysTick-frequency, which also stops toggling after the first PendSV call. I know that they have the same priority, which is why I conclude, that the Tick-LED is stuck, because the delay is way too long and this lets me conclude, that the value from the register is not stored correctly. Did I miss out on how to store data from CPU registers or is there another flaw in my conclusions?

\$\endgroup\$
  • \$\begingroup\$ 100 ms is a very short time. \$\endgroup\$ – Eugene Sh. Jun 5 '17 at 17:22
  • \$\begingroup\$ You may want to show toggleLED(). Are you using any optimizations? (ie: did you forget to assign an LED state variable as "volatile" or possibly as "static" if declared within the function?) \$\endgroup\$ – Tut Jun 5 '17 at 17:23
  • \$\begingroup\$ @StainlessSteelRat Not a random value, doesn't the assignment of the value 100 work how I thought it would (i.e. being stored in register r0)? \$\endgroup\$ – flashingx Jun 5 '17 at 17:25
  • \$\begingroup\$ @Tut The problem lies not within the toggleLED function. I am able to see it if I manually enter 100 as an argument. The whole program is much more complicated, but I am sure it is the asm part, that doesn't work as I expect. \$\endgroup\$ – flashingx Jun 5 '17 at 17:27
  • \$\begingroup\$ Have you tried disassembling the compiled code? (Try the -S compiler option if gcc) \$\endgroup\$ – pjc50 Jun 5 '17 at 18:08
4
\$\begingroup\$

I am not sure if you are aware the logic already stores the state of the machine during an event/interrupt. If you want to use this to task switch you need to remove/re-build the stack to account for the switch. You may want to pull that information and save it for the switch back sure.

Take a look at Atomthreads or FreeRTOS or others to see how to do a complete task switch in a Cortex-M (it is not like other processors where you take control of the registers and change the return address. For starters, the "return address" in LR from an event/interrupt is not a valid address -- "Cortex-M3 Devices Generic User Guide: Exception entry and return").

\$\endgroup\$
  • \$\begingroup\$ I know that the Cortex-family stores r0-r4 plus some other registers on the stack, so I have to store the others like this. I also know, that I have to pop said registers from the stack and push others to be loaded by the exception return. Thank you for the Wikipedia link, I found one to have a really good documentation. \$\endgroup\$ – flashingx Jun 6 '17 at 7:56
2
\$\begingroup\$

in the SysTick_Handler another LED is toggled with SysTick-frequency, which also stops toggling after the first PendSV call. I know that they have the same priority

If SysTick and PendSV have the same priority, then SysTick_Handler will not be invoked again until PendSV_Handler returns. That's a problem because delay_ms() usually depends on SysTick_Handler updating a counter. That will never happen, therefore your program will hang there.

Using delay_ms() in an interrupt handler is a bad idea, but if you'd like to have it there for testing purposes, then make sure that SysTick_Handler has higher priority than any interrupt using delay_ms(). (Or modify delay_ms() to use a hardware timer.)

\$\endgroup\$
  • \$\begingroup\$ The delay method is not using timers, but what you mentioned is what I concluded. As the PendSV takes longer than until the next SysTick interrupt is due, the SysTick_Handler is not invoked. \$\endgroup\$ – flashingx Jun 6 '17 at 8:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.