| /* |
| * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include "pico.h" |
| #include "hardware/irq.h" |
| #include "pico/asm_helper.S" |
| |
| pico_default_asm_setup |
| |
| #if !PICO_DISABLE_SHARED_IRQ_HANDLERS |
| |
| .data |
| .align 2 |
| |
| .global irq_handler_chain_slots |
| |
| .global irq_handler_chain_first_slot |
| .global irq_handler_chain_remove_tail |
| |
| // |
| // These Slots make up the code and structure of the handler chains; the only external information are the VTABLE entries |
| // (obviously one set per core) and a free list head. Each individual handler chain starts with the VTABLE entry I |
| // pointing at the address of slot S (with thumb bit set). Thus each slot which is part of a chain is executble. |
| // |
| // The execution jumps (via branch instruction) from one slot to the other, then jumps to the end of chain handler. |
| // The entirety of the state needed to traverse the chain is contained within the slots of the chain, which is why |
| // a VTABLE entry is all that is needed per chain (rather than requiring a separarte set of head pointers) |
| // |
| |
| irq_handler_chain_slots: |
| .set next_slot_number, 1 |
| .rept PICO_MAX_SHARED_IRQ_HANDLERS |
| // a slot is executable and is always 3 instructions long. |
| .hword 0 // inst1 (either: ldr r0, [pc, #4] or for the FIRST slot : add r1, pc, #0 ) |
| .hword 0 // inst2 ( blx r0 b irq_handler_chain_first_slot ) |
| |
| .hword 0 // inst3 (either: b next_slot or for the LAST pop {pc} ) |
| |
| // next is a single byte index of next slot in chain (or -1 to end) |
| .if next_slot_number == PICO_MAX_SHARED_IRQ_HANDLERS |
| .byte 0xff |
| .else |
| .byte next_slot_number |
| .endif |
| // next is the 8 bit unsigned priority |
| .byte 0x00 |
| 1: |
| // and finally the handler function pointer |
| .word 0x00000000 |
| .set next_slot_number, next_slot_number + 1 |
| .endr |
| |
| irq_handler_chain_first_slot: |
| push {r0, lr} // Save EXC_RETURN token, so `pop {r0, pc}` will return from interrupt |
| // Note that r0 does not NEED to be saved, however we must maintain |
| // an 8 byte stack alignment, and this is the cheapest way to do so |
| ldr r0, [r1, #4] // Get `handler` field of irq_handler_chain_slot |
| adds r1, #1 // r1 points to `inst3` field of slot struct. Set Thumb bit on r1, |
| mov lr, r1 // and copy to lr, so `inst3` is executed on return from handler |
| bx r0 // Enter handler |
| |
| irq_handler_chain_remove_tail: |
| mov r0, lr // Get start of struct. This function was called by a bl at offset +4, |
| subs r0, #9 // so lr points to offset +8. Note also lr has its Thumb bit set! |
| ldr r1, =irq_add_tail_to_free_list |
| blx r1 |
| pop {r0, pc} // Top of stack is EXC_RETURN |
| |
| |
| #endif |