Page 1 of 1

RISC-V ULP interrupt context saving

Posted: Tue Nov 19, 2024 1:58 pm
by Baoshi
In ESP32-S3 RISC-V ULP interrupt code (ulp/ulp_riscv/ulp_core/ulp_riscv_vector.S), there is a note saying:

Code: Select all

Note: We don't save the callee-saved s0-s11 registers to save space
This causes some issues that the riscv32-esp-elf-gcc generated code do used s registers for function local variables. If I manually add saving/restore of s0-s11 then the issue is solved.

Is there an "elegant" way to solve this without touch the esp-idf code base?

Thanks

Baoshi

Re: RISC-V ULP interrupt context saving

Posted: Tue Nov 19, 2024 4:45 pm
by MicroController
Looks fine to me:

Code: Select all

static volatile uint32_t dummy;

void __attribute__((noinline)) testFunc() {
    // Force the compiler to create and hold a lot of local variables:
    uint32_t a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p;
    uint32_t z;
    a = dummy;
    b = dummy;
    c = dummy;
    d = dummy;
    e = dummy;
    f = dummy;
    g = dummy;
    h = dummy;
    i = dummy;
    j = dummy;
    k = dummy;
    l = dummy;
    m = dummy;
    n = dummy;
    o = dummy;
    p = dummy;

    z = dummy;
    dummy = (a==z) && (b==z) && (c==z) && (d==z) && (e==z) && (f==z) && (g==z) && (h==z) && (i==z) && (j==z) &&
        (k==z) && (l==z) && (m==z) && (n==z) && (o==z) && (p==z);
}
yields

Code: Select all

<testFunc()>:
addi sp,sp,-16
sw s0,12(sp)
sw s1,8(sp)
sw s2,4(sp)
sw s3,0(sp)
lw s3,788(zero) # 314 <dummy>

  ...
 
lw s0,12(sp)
sw a3,788(zero) # 314 <dummy>
lw s1,8(sp)
lw s2,4(sp)
lw s3,0(sp)
addi sp,sp,16
ret

Re: RISC-V ULP interrupt context saving

Posted: Wed Nov 20, 2024 12:38 am
by Baoshi
This is exactly where the problem is: A function can use s0-s11 for local variables, but when interrupt happens, sx registers are not preserved so when returning from ISR, variable values may change.

Re: RISC-V ULP interrupt context saving

Posted: Wed Nov 20, 2024 8:18 am
by MicroController
sx registers are not preserved so when returning from ISR, variable values may change.
No. Gcc does the right thing and makes the callee, i.e. the actual ISR function, save&restore the callee-saved registers. Saving the callee-saved registers before calling a function would be rather pointless.

Re: RISC-V ULP interrupt context saving

Posted: Mon Nov 25, 2024 1:25 am
by Baoshi
I think I found the reason of my problem:

In ulp_riscv_vectors.S, saving of registers are implemented as:

Code: Select all

  .equ SAVE_REGS, 17
    .equ CONTEXT_SIZE, (SAVE_REGS * 4)

/* Macro which first allocates space on the stack to save general
 * purpose registers, and then save them. GP register is excluded.
 * The default size allocated on the stack is CONTEXT_SIZE, but it
 * can be overridden.
 *
 * Note: We don't save the callee-saved s0-s11 registers to save space
 */
.macro save_general_regs cxt_size=CONTEXT_SIZE
    addi sp, sp, -\cxt_size
    sw   ra, RV_STK_RA(sp)
    sw   tp, RV_STK_TP(sp)
    sw   t0, RV_STK_T0(sp)
    sw   t1, RV_STK_T1(sp)
    sw   t2, RV_STK_T2(sp)
    sw   a0, RV_STK_A0(sp)
    sw   a1, RV_STK_A1(sp)
    sw   a2, RV_STK_A2(sp)
    sw   a3, RV_STK_A3(sp)
    sw   a4, RV_STK_A4(sp)
    sw   a5, RV_STK_A5(sp)
    sw   a6, RV_STK_A6(sp)
    sw   a7, RV_STK_A7(sp)
    sw   t3, RV_STK_T3(sp)
    sw   t4, RV_STK_T4(sp)
    sw   t5, RV_STK_T5(sp)
    sw   t6, RV_STK_T6(sp)
.endm
When this code is compiled, it generates the instructions below:

Code: Select all

00000010 <irq_vector>:
  10:	fbc10113          	add	sp,sp,-68
  14:	c206                	sw	ra,4(sp)
  16:	c812                	sw	tp,16(sp)
  18:	ca16                	sw	t0,20(sp)
  1a:	cc1a                	sw	t1,24(sp)
  1c:	ce1e                	sw	t2,28(sp)
  1e:	d42a                	sw	a0,40(sp)
  20:	d62e                	sw	a1,44(sp)
  22:	d832                	sw	a2,48(sp)
  24:	da36                	sw	a3,52(sp)
  26:	dc3a                	sw	a4,56(sp)
  28:	de3e                	sw	a5,60(sp)
  2a:	c0c2                	sw	a6,64(sp)
  2c:	c2c6                	sw	a7,68(sp)
  2e:	d8f2                	sw	t3,112(sp)
  30:	daf6                	sw	t4,116(sp)
  32:	dcfa                	sw	t5,120(sp)
  34:	defe                	sw	t6,124(sp)
It is clear they allocated 68 bytes in the stack to save 17 registers, but save actual registers to sp-124, that causes the problem.

Re: RISC-V ULP interrupt context saving

Posted: Mon Nov 25, 2024 9:17 am
by MicroController

Code: Select all

add	sp,sp,-68
...
sw	a7,68(sp)
sw	t3,112(sp)
sw	t4,116(sp)
sw	t5,120(sp)
sw	t6,124(sp)
:o Now that is actually a problem!
Apparently the RISC-V frame definitions RV_STK_... are way out of whack there.

Re: RISC-V ULP interrupt context saving

Posted: Mon Nov 25, 2024 9:27 am
by MicroController
riscv/rvruntime-frames.h - Yep. The definitions relate to a full RvExcFrame, which the ULP code doesn't allocate. That's a bug in the IDF.
Good find!

Was about to suggest you open an issue... but you're way ahead :D