Implement IRQ software support for RISC-V.
Well, at least PicoRV32-specific. Turns out there is no RISC-V specification for simple microcontroller-like interrupts, so PicoRV32 implements its' own based on custom opcodes. It's somewhat esoteric, and for example doesn't offer a global interrupt enable/disable. For this we implement a thin wrapper in assembly and then expose it via a few helpers in irq.h.
This commit is contained in:
parent
2108c97b9b
commit
6daf3eabc5
|
@ -5,6 +5,27 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __riscv
|
||||||
|
// PicoRV32 has a very limited interrupt support, implemented via custom
|
||||||
|
// instructions. It also doesn't have a global interrupt enable/disable, so
|
||||||
|
// we have to emulate it via saving and restoring a mask and using 0/~1 as a
|
||||||
|
// hardware mask.
|
||||||
|
// Due to all this somewhat low-level mess, all of the glue is implementein
|
||||||
|
// the RiscV crt0, and this header is kept as a thin wrapper. Since interrupts
|
||||||
|
// managed by this layer, do not call interrupt instructions directly, as the
|
||||||
|
// state will go out of sync with the hardware.
|
||||||
|
|
||||||
|
// Read only.
|
||||||
|
extern unsigned int _irq_pending;
|
||||||
|
// Read only.
|
||||||
|
extern unsigned int _irq_mask;
|
||||||
|
// Read only.
|
||||||
|
extern unsigned int _irq_enabled;
|
||||||
|
extern void _irq_enable(void);
|
||||||
|
extern void _irq_disable(void);
|
||||||
|
extern void _irq_setmask(unsigned int);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __or1k__
|
#ifdef __or1k__
|
||||||
#include <system.h>
|
#include <system.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -17,9 +38,8 @@ static inline unsigned int irq_getie(void)
|
||||||
return ie;
|
return ie;
|
||||||
#elif defined (__or1k__)
|
#elif defined (__or1k__)
|
||||||
return !!(mfspr(SPR_SR) & SPR_SR_IEE);
|
return !!(mfspr(SPR_SR) & SPR_SR_IEE);
|
||||||
#elif defined (__riscv__)
|
#elif defined (__riscv)
|
||||||
/* FIXME */
|
return _irq_enabled != 0;
|
||||||
return 0;
|
|
||||||
#else
|
#else
|
||||||
#error Unsupported architecture
|
#error Unsupported architecture
|
||||||
#endif
|
#endif
|
||||||
|
@ -34,9 +54,11 @@ static inline void irq_setie(unsigned int ie)
|
||||||
mtspr(SPR_SR, mfspr(SPR_SR) | SPR_SR_IEE);
|
mtspr(SPR_SR, mfspr(SPR_SR) | SPR_SR_IEE);
|
||||||
else
|
else
|
||||||
mtspr(SPR_SR, mfspr(SPR_SR) & ~SPR_SR_IEE);
|
mtspr(SPR_SR, mfspr(SPR_SR) & ~SPR_SR_IEE);
|
||||||
#elif defined (__riscv__)
|
#elif defined (__riscv)
|
||||||
/* FIXME */
|
if (ie & 0x1)
|
||||||
return 0;
|
_irq_enable();
|
||||||
|
else
|
||||||
|
_irq_disable();
|
||||||
#else
|
#else
|
||||||
#error Unsupported architecture
|
#error Unsupported architecture
|
||||||
#endif
|
#endif
|
||||||
|
@ -50,9 +72,10 @@ static inline unsigned int irq_getmask(void)
|
||||||
return mask;
|
return mask;
|
||||||
#elif defined (__or1k__)
|
#elif defined (__or1k__)
|
||||||
return mfspr(SPR_PICMR);
|
return mfspr(SPR_PICMR);
|
||||||
#elif defined (__riscv__)
|
#elif defined (__riscv)
|
||||||
/* FIXME */
|
// PicoRV32 interrupt mask bits are high-disabled. This is the inverse of how
|
||||||
return 0;
|
// LiteX sees things.
|
||||||
|
return ~_irq_mask;
|
||||||
#else
|
#else
|
||||||
#error Unsupported architecture
|
#error Unsupported architecture
|
||||||
#endif
|
#endif
|
||||||
|
@ -64,9 +87,10 @@ static inline void irq_setmask(unsigned int mask)
|
||||||
__asm__ __volatile__("wcsr IM, %0" : : "r" (mask));
|
__asm__ __volatile__("wcsr IM, %0" : : "r" (mask));
|
||||||
#elif defined (__or1k__)
|
#elif defined (__or1k__)
|
||||||
mtspr(SPR_PICMR, mask);
|
mtspr(SPR_PICMR, mask);
|
||||||
#elif defined (__riscv__)
|
#elif defined (__riscv)
|
||||||
/* FIXME */
|
// PicoRV32 interrupt mask bits are high-disabled. This is the inverse of how
|
||||||
return 0;
|
// LiteX sees things.
|
||||||
|
_irq_setmask(~mask);
|
||||||
#else
|
#else
|
||||||
#error Unsupported architecture
|
#error Unsupported architecture
|
||||||
#endif
|
#endif
|
||||||
|
@ -80,9 +104,8 @@ static inline unsigned int irq_pending(void)
|
||||||
return pending;
|
return pending;
|
||||||
#elif defined (__or1k__)
|
#elif defined (__or1k__)
|
||||||
return mfspr(SPR_PICSR);
|
return mfspr(SPR_PICSR);
|
||||||
#elif defined (__riscv__)
|
#elif defined (__riscv)
|
||||||
/* FIXME */
|
return _irq_pending;
|
||||||
return 0;
|
|
||||||
#else
|
#else
|
||||||
#error Unsupported architecture
|
#error Unsupported architecture
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,145 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018, Serge Bazanski <serge@bazanski.pl>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "picorv32-extraops.S"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interrupt vector.
|
||||||
|
*/
|
||||||
.global _start
|
.global _start
|
||||||
_start:
|
_start:
|
||||||
|
|
||||||
|
.org 0x00000000 # Reset
|
||||||
|
j _crt0
|
||||||
|
|
||||||
|
.org 0x00000010 # IRQ
|
||||||
|
_irq_vector:
|
||||||
|
j _irq
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IRQ handler, branched to from the vector.
|
||||||
|
*/
|
||||||
|
_irq:
|
||||||
|
/* save x1/x2 to q1/q2 */
|
||||||
|
picorv32_setq_insn(q2, x1)
|
||||||
|
picorv32_setq_insn(q3, x2)
|
||||||
|
|
||||||
|
/* use x1 to index into irq_regs */
|
||||||
|
lui x1, %hi(irq_regs)
|
||||||
|
addi x1, x1, %lo(irq_regs)
|
||||||
|
|
||||||
|
/* use x2 as scratch space for saving registers */
|
||||||
|
|
||||||
|
/* q0 (== x1), q2(== x2), q3 */
|
||||||
|
picorv32_getq_insn(x2, q0)
|
||||||
|
sw x2, 0*4(x1)
|
||||||
|
picorv32_getq_insn(x2, q2)
|
||||||
|
sw x2, 1*4(x1)
|
||||||
|
picorv32_getq_insn(x2, q3)
|
||||||
|
sw x2, 2*4(x1)
|
||||||
|
|
||||||
|
/* save x3 - x31 */
|
||||||
|
sw x3, 3*4(x1)
|
||||||
|
sw x4, 4*4(x1)
|
||||||
|
sw x5, 5*4(x1)
|
||||||
|
sw x6, 6*4(x1)
|
||||||
|
sw x7, 7*4(x1)
|
||||||
|
sw x8, 8*4(x1)
|
||||||
|
sw x9, 9*4(x1)
|
||||||
|
sw x10, 10*4(x1)
|
||||||
|
sw x11, 11*4(x1)
|
||||||
|
sw x12, 12*4(x1)
|
||||||
|
sw x13, 13*4(x1)
|
||||||
|
sw x14, 14*4(x1)
|
||||||
|
sw x15, 15*4(x1)
|
||||||
|
sw x16, 16*4(x1)
|
||||||
|
sw x17, 17*4(x1)
|
||||||
|
sw x18, 18*4(x1)
|
||||||
|
sw x19, 19*4(x1)
|
||||||
|
sw x20, 20*4(x1)
|
||||||
|
sw x21, 21*4(x1)
|
||||||
|
sw x22, 22*4(x1)
|
||||||
|
sw x23, 23*4(x1)
|
||||||
|
sw x24, 24*4(x1)
|
||||||
|
sw x25, 25*4(x1)
|
||||||
|
sw x26, 26*4(x1)
|
||||||
|
sw x27, 27*4(x1)
|
||||||
|
sw x28, 28*4(x1)
|
||||||
|
sw x29, 29*4(x1)
|
||||||
|
sw x30, 30*4(x1)
|
||||||
|
sw x31, 31*4(x1)
|
||||||
|
|
||||||
|
/* update _irq_pending to the currently pending interrupts */
|
||||||
|
picorv32_getq_insn(t0, q1)
|
||||||
|
la t1, (_irq_pending)
|
||||||
|
sw t0, 0(t1)
|
||||||
|
|
||||||
|
/* prepare C handler stack */
|
||||||
|
lui sp, %hi(_irq_stack)
|
||||||
|
addi sp, sp, %lo(_irq_stack)
|
||||||
|
|
||||||
|
/* call C handler */
|
||||||
|
jal ra, isr
|
||||||
|
|
||||||
|
/* use x1 to index into irq_regs */
|
||||||
|
lui x1, %hi(irq_regs)
|
||||||
|
addi x1, x1, %lo(irq_regs)
|
||||||
|
|
||||||
|
/* restore q0 - q2 */
|
||||||
|
lw x2, 0*4(x1)
|
||||||
|
picorv32_setq_insn(q0, x2)
|
||||||
|
lw x2, 1*4(x1)
|
||||||
|
picorv32_setq_insn(q1, x2)
|
||||||
|
lw x2, 2*4(x1)
|
||||||
|
picorv32_setq_insn(q2, x2)
|
||||||
|
|
||||||
|
/* restore x3 - x31 */
|
||||||
|
lw x3, 3*4(x1)
|
||||||
|
lw x4, 4*4(x1)
|
||||||
|
lw x5, 5*4(x1)
|
||||||
|
lw x6, 6*4(x1)
|
||||||
|
lw x7, 7*4(x1)
|
||||||
|
lw x8, 8*4(x1)
|
||||||
|
lw x9, 9*4(x1)
|
||||||
|
lw x10, 10*4(x1)
|
||||||
|
lw x11, 11*4(x1)
|
||||||
|
lw x12, 12*4(x1)
|
||||||
|
lw x13, 13*4(x1)
|
||||||
|
lw x14, 14*4(x1)
|
||||||
|
lw x15, 15*4(x1)
|
||||||
|
lw x16, 16*4(x1)
|
||||||
|
lw x17, 17*4(x1)
|
||||||
|
lw x18, 18*4(x1)
|
||||||
|
lw x19, 19*4(x1)
|
||||||
|
lw x20, 20*4(x1)
|
||||||
|
lw x21, 21*4(x1)
|
||||||
|
lw x22, 22*4(x1)
|
||||||
|
lw x23, 23*4(x1)
|
||||||
|
lw x24, 24*4(x1)
|
||||||
|
lw x25, 25*4(x1)
|
||||||
|
lw x26, 26*4(x1)
|
||||||
|
lw x27, 27*4(x1)
|
||||||
|
lw x28, 28*4(x1)
|
||||||
|
lw x29, 29*4(x1)
|
||||||
|
lw x30, 30*4(x1)
|
||||||
|
lw x31, 31*4(x1)
|
||||||
|
|
||||||
|
/* restore x1 - x2 from q registers */
|
||||||
|
picorv32_getq_insn(x1, q1)
|
||||||
|
picorv32_getq_insn(x2, q2)
|
||||||
|
|
||||||
|
/* return from interrupt */
|
||||||
|
picorv32_retirq_insn()
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset handler, branched to from the vector.
|
||||||
|
*/
|
||||||
|
_crt0:
|
||||||
/* zero-initialize all registers */
|
/* zero-initialize all registers */
|
||||||
addi x1, zero, 0
|
addi x1, zero, 0
|
||||||
addi x2, zero, 0
|
addi x2, zero, 0
|
||||||
|
@ -33,5 +173,103 @@ _start:
|
||||||
addi x30, zero, 0
|
addi x30, zero, 0
|
||||||
addi x31, zero, 0
|
addi x31, zero, 0
|
||||||
|
|
||||||
|
/* mask all interrupts */
|
||||||
|
li t0, 0xffffffff
|
||||||
|
picorv32_maskirq_insn(zero, t0)
|
||||||
|
/* reflect that in _irq_mask */
|
||||||
|
la t1, _irq_mask
|
||||||
|
sw t0, 0(t1)
|
||||||
|
|
||||||
|
/* set main stack */
|
||||||
|
la sp, _fstack
|
||||||
|
|
||||||
/* jump to main */
|
/* jump to main */
|
||||||
jal ra, main
|
jal ra, main
|
||||||
|
|
||||||
|
1:
|
||||||
|
/* loop forever */
|
||||||
|
j 1b
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable interrupts by copying the software mask to the hardware mask
|
||||||
|
*/
|
||||||
|
.global _irq_enable
|
||||||
|
_irq_enable:
|
||||||
|
/* Set _irq_enabled to true */
|
||||||
|
la t0, _irq_enabled
|
||||||
|
addi t1, zero, 1
|
||||||
|
sw t1, 0(t0)
|
||||||
|
/* Set the HW IRQ mask to _irq_mask */
|
||||||
|
la t0, _irq_mask
|
||||||
|
lw t0, 0(t0)
|
||||||
|
picorv32_maskirq_insn(zero, t0)
|
||||||
|
ret
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable interrupts by masking all interrupts (the mask should already be
|
||||||
|
* up to date)
|
||||||
|
*/
|
||||||
|
.global _irq_disable
|
||||||
|
_irq_disable:
|
||||||
|
/* Mask all IRQs */
|
||||||
|
li t0, 0xffffffff
|
||||||
|
picorv32_maskirq_insn(zero, t0)
|
||||||
|
/* Set _irq_enabled to false */
|
||||||
|
la t0, _irq_enabled
|
||||||
|
sw zero, (t0)
|
||||||
|
ret
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set interrrupt mask.
|
||||||
|
* This updates the software mask (for readback and interrupt inable/disable)
|
||||||
|
* and the hardware mask.
|
||||||
|
* 1 means interrupt is masked (disabled).
|
||||||
|
*/
|
||||||
|
.global _irq_setmask
|
||||||
|
_irq_setmask:
|
||||||
|
/* Update _irq_mask */
|
||||||
|
la t0, _irq_mask
|
||||||
|
sw a0, (t0)
|
||||||
|
/* Are interrupts enabled? */
|
||||||
|
la t0, _irq_enabled
|
||||||
|
lw t0, 0(t0)
|
||||||
|
beq t0, zero, 1f
|
||||||
|
/* If so, update the HW IRQ mask */
|
||||||
|
picorv32_maskirq_insn(zero, a0)
|
||||||
|
1:
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
.section .bss
|
||||||
|
irq_regs:
|
||||||
|
/* saved interrupt registers, x0 - x31 */
|
||||||
|
.fill 32,4
|
||||||
|
|
||||||
|
/* interrupt stack */
|
||||||
|
.fill 256,4
|
||||||
|
_irq_stack:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bitfield of pending interrupts, updated on ISR entry.
|
||||||
|
*/
|
||||||
|
.global _irq_pending
|
||||||
|
_irq_pending:
|
||||||
|
.word 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Software copy of enabled interrupts. Do not write directly, use
|
||||||
|
* _irq_set_mask instead.
|
||||||
|
*/
|
||||||
|
.global _irq_mask
|
||||||
|
_irq_mask:
|
||||||
|
.word 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Software state of global interrupts being enabled or disabled. Do not write
|
||||||
|
* directly, use _irq_disable / _irq_enable instead.
|
||||||
|
*/
|
||||||
|
.global _irq_enabled
|
||||||
|
_irq_enabled:
|
||||||
|
.word 0
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue