Skip to content

WFE function not LTO safe, MCU doesn't wake up properly #61

@maxgerhardt

Description

@maxgerhardt

Per Community-PIO-CH32V/framework-wch-noneos-sdk#14.

When compiling a project that uses deepsleep (and thus the WFE function) per

__attribute__( (section(".highcode")) ) void WFE(uint32_t t)
{
asm volatile ("wfi");
asm volatile ("1:addi a0, a0, -1\n\t" \
"bnez a0, 1b\n\t" \
);
}

with LTO enabled (-flto in compiler and linker), the function gets "optimized" incorrectly, by removing the instruction that writes the value of the argument to the function.

The function is written incorrectly and unsafely, since it does not reference the input parameter t inside the body of the function (which is usually passed in the a0 register), the input parameter can be removed from the function, yet the inline assembly inside the function expects a0 to be filled be holding the value of the input parameter t.

As a result, firmwares compiled with LTO and using WFE essentially never wake up from sleep, because a0 may hold a very large value, and that is used as the "number of cycles to delay after wakeup".

An alternative version

__attribute__( (section(".highcode")) ) void WFE(uint32_t t)
{
    /* Force t into RISC-V a0 so the delay loop works */
    register uint32_t a0 asm("a0") = t;

    asm volatile (
        "wfi\n"
        "1: addi a0, a0, -1\n"
        "   bnez a0, 1b\n"
        :
        : "r"(a0)
        : "memory"
    );
}

works correctly when compiled with LTO enabled.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions