Page 1 of 1

ULP RISC-V calling assembly from C not working

Posted: Mon May 03, 2021 10:10 pm
by bitmandu
According to the manual, the ULP-RISC-V co-processor on the esp32-s2 can be programmed with both C and assembly. I'm having trouble getting a trivial example using both C and assembly to work.

My code (on GitHub) builds and runs, but doesn't return the correct result from a function implemented in assembly.

This is the code running on the ULP-RISC-V:

Code: Select all

#include "ulp_riscv/ulp_riscv.h"
#include "ulp_riscv/ulp_riscv_utils.h"

extern int add(int x, int y);

volatile int result_s;
volatile int result_c;

int main(void)
{
    result_s = add(1, 2);
    result_c = 1 + 2;
}
My main application prints out both ulp_result_s and ulp_result_c. Instead of 3, both are 0.

If I comment out the call to the external assembly function add()

Code: Select all

    //result_s = add(1, 2);
    result_c = 1 + 2;
the C-only part works and ulp_result_c is correct (3).

The add function is implemented as add.S:

Code: Select all

.section .text

.global add
.type add, @function

add:
	add a0, a0, a1
	ret
I don't think it can get any simpler, but I am by no means an expert in assembly.

Everything builds and flashes to the chip fine. Any help getting this to work is appreciated!

Re: ULP RISC-V calling assembly from C not working

Posted: Wed May 05, 2021 7:39 am
by ESP_Angus
Hi bitmandu,

Thanks for the very clear test case! After scratching my head about this for a bit, I can confirm this is a bug in the ESP-IDF linker script that we will fix ASAP. In the meantime you can make your program work by changing the name of the assembly section that holds the add function, like this:

Code: Select all

.section .text.add

.global add
.type add, @function

add:
	add a0, a0, a1
	ret
(The section names for assembly files can be ".text.anything", anything but ".text")

Why does this fix the problem? Address 0x0 in the ULP binary has to be the ULP CPU's reset vector that starts program execution. This currently happens in most cases only because the reset vector is the only function in the ".text" section and this section is placed first by the linker script (any C files are compiled with -ffunction-sections so they all have section names like .text.something).

Because "add.S" was sorted first in the linker order, the add() function was placed at address 0x0 in the ULP binary and executed first as if it was the reset vector. Causing the whole program not to run!

Thanks again for helping us find and fix this. Sorry for the time it's probably cost you.

Re: ULP RISC-V calling assembly from C not working

Posted: Thu May 06, 2021 1:12 am
by bitmandu
Thanks for the help ESP_Angus! Your explanation (and solution) really made things clear.