Interrupt handler in a single stack program

Technical questions regarding the XTC tools and programming with XMOS.
User avatar
rweickelt
Member++
Posts: 16
Joined: Tue Sep 02, 2014 8:38 pm

Interrupt handler in a single stack program

Post by rweickelt »

Hi,

I'm implementing an interrupt handler in assembler, which is able to call an arbitrary handler in C. Such a high-level function could then for example read from a channel end. Everything should operate on the program stack rather than a separate kernel stack. I've read the wiki article about interrupts, but from that example it is unclear to me, how to work with a single-stack program.

I came up with the following solution which seems to work:

Code: Select all

interruptHandler:
    stw r0, dp[tempR0]    # Store r0 into RAM
    ldw r0, sp[0]         # Store sp[0] into r0
    stw sp, sp[0]         # Prepare modification of ksp
    krestsp 0             # Modify ksp, ksp=sp+0, sp=ksp[0]
    stw r0, sp[0]         # Restore sp[0]
    ldw r0, dp[tempR0]    # Restore r0

    kentsp 10         # Switch to kernel stack and allocate 10 words

    stw spc, sp[1]    # Store caller-save and management registers
    stw ssr, sp[2]
    stw sed, sp[3]
    stw lr,  sp[4]
    stw r0,  sp[5]
    stw r1,  sp[6]
    stw r2,  sp[7]
    stw r3,  sp[8]
    stw r11, sp[9]

    get r11, ed       # Load handler id from the environment vector
    ldc r0, 0xff      # Load a mask
    and r0, r11, r0   # and mask the handler id in r0
    bl handle         # call C-function handle(id)

    ldw spc, sp[1]
    ldw ssr, sp[2]
    ldw sed, sp[3]
    ldw lr,  sp[4]
    ldw r0,  sp[5]
    ldw r1,  sp[6]
    ldw r2,  sp[7]
    ldw r3,  sp[8]
    ldw r11, sp[9]

    krestsp 10      # Contract the kernel stack and switch to user stack
                    # Sets ksp to point to sp[0] and restores sp from there
    kret            # Return from interrupt
handle() is a C-function which calls arbitrary functions from a lookup-table. The id is stored in the environment vector register when setting up the interrupts.

What I'm unsure about is, how to switch to kernel stack. I assume, that neither sp[0] nor any register must be touched during the epilogue. That's why I use dp[tempR0] as temporary variable.

Another odd thing is, that I have to mask the environment vector register because the high nibble is not correct when reading. I just use 1 Byte as handler id, so this is no problem. But am I missing something?

Do You think, that this is correct? Are there any other errors?

Thanks


User avatar
Bianco
XCore Expert
Posts: 754
Joined: Thu Dec 10, 2009 6:56 pm

Post by Bianco »

You need to setup the kernel stack pointer outside the interrupt routine.
This must be done before enabling interrupts and only needs to be done once.

You will want to set the ksp to some address of a memory region that is reserved for the kernel stack, (you can use malloc or an array). Setting it to the same value of the regular stack is fairly pointless.
User avatar
rweickelt
Member++
Posts: 16
Joined: Tue Sep 02, 2014 8:38 pm

Post by rweickelt »

Thank You for taking the time to answer. Isn't it possible, to work with a single stack for everything? Why?

What if the interrupt handler reenables interrupts? This would let the kernel stack size grow. But I don't want to gamble on the kernel stack size. That's why I want to avoid it completely.
User avatar
Bianco
XCore Expert
Posts: 754
Joined: Thu Dec 10, 2009 6:56 pm

Post by Bianco »

You can keep using the regular stack if you wish, but you need to keep in mind that the stack usage calculation made by the tools do not include the stack used by the interrupt routine. When running multiple cores on one tile, the stacks of the cores are stacked (:)), so it may not have room to grow until you are actually out of physical memory.
User avatar
rweickelt
Member++
Posts: 16
Joined: Tue Sep 02, 2014 8:38 pm

Post by rweickelt »

I'm still looking for an improvement to the above code. How to avoid the usage of an external scratch location in the prologue? As far as I understand, an interrupt handler must finish with the kret instruction in order to restore the service registers. Would it be possible to mix stack instructions somehow? Could I even work with entsp and set ksp right before returning?

An idea would be great. Thanks