From 923e9d39a02698e5a3c5e2bac25c30839f8bb8f7 Mon Sep 17 00:00:00 2001 From: vhaudiquet Date: Fri, 3 Nov 2023 11:25:58 +0100 Subject: [PATCH] Better exception/interrupt handle --- src/cpu/exception.c | 8 ++++++-- src/cpu/interrupt.c | 48 +++++++++++++++++++++++++++------------------ src/cpu/rv32cpu.c | 9 +++++++-- 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/cpu/exception.c b/src/cpu/exception.c index f2db177..cf22c99 100644 --- a/src/cpu/exception.c +++ b/src/cpu/exception.c @@ -15,6 +15,8 @@ __attribute__((noreturn)) void exception_trigger(rv32_cpu_t* cpu, uint32_t scaus // Exceptions cannot be disabled + // TODO : Check medeleg to see if we should handle exception in S mode or M mode + // Unset SIE (interrupt enable) bit csr_write(cpu, CSR_SSTATUS, csr_read(cpu, CSR_SSTATUS) & (~STATUS_SIE)); @@ -37,8 +39,10 @@ __attribute__((noreturn)) void exception_trigger(rv32_cpu_t* cpu, uint32_t scaus csr_write(cpu, CSR_SSTATUS, csr_read(cpu, CSR_SSTATUS) & (~STATUS_SPIE)); // Set previous privilege mode in xSTATUS.xPP - // TODO : Allow user mode exceptions (by not setting this) - csr_write(cpu, CSR_SSTATUS, csr_read(cpu, CSR_SSTATUS) | STATUS_SPP); + if(cpu->privilege_mode == SUPERVISOR) + csr_write(cpu, CSR_SSTATUS, csr_read(cpu, CSR_SSTATUS) | STATUS_SPP); + else if(cpu->privilege_mode == USER) + csr_write(cpu, CSR_SSTATUS, csr_read(cpu, CSR_SSTATUS) & (~STATUS_SPP)); // Set privilege mode for exception handling, checking for delegation // TODO diff --git a/src/cpu/interrupt.c b/src/cpu/interrupt.c index ed34b0d..65871a5 100644 --- a/src/cpu/interrupt.c +++ b/src/cpu/interrupt.c @@ -4,28 +4,38 @@ #include #include -void interrupt_trigger(rv32_cpu_t* cpu, uint32_t scause) +uint32_t interrupt_mi_from_scause(uint32_t scause) { - // Make sure that interrupts are enabled - if(cpu->privilege_mode == MACHINE) - { - // In machine mode, we check mstatus.sie (bit 1) - if(!(cpu->csr[CSR_MSTATUS] & 0b10)) - return; - } - else if(cpu->privilege_mode == SUPERVISOR) + switch(scause) { - // In supervisor mode, we check sstatus.sie (bit 1) - if(!(cpu->csr[CSR_SSTATUS] & 0b10)) - return; + case SCAUSE_SUPERVISOR_TIMER_INTERRUPT: + return 0x20; + default: + fprintf(stderr, "interrupt_mie_bit_from_scause: wrong scause 0x%x\n", scause); + exit(EXIT_FAILURE); } - else + + return 0; +} + +void interrupt_trigger(rv32_cpu_t* cpu, uint32_t scause) +{ + // Make sure that interrupts are enabled globally + if(!(csr_read(cpu, CSR_MSTATUS) & STATUS_SIE)) + return; + + // Make sure that current interrupt is enabled + if(!(csr_read(cpu, CSR_MIE) & interrupt_mi_from_scause(scause))) { - // TODO - fprintf(stderr, "interrupt_trigger in non M/S-mode not implemented yet\n"); - exit(EXIT_FAILURE); + // Mark interrupt as pending + csr_write(cpu, CSR_MIP, csr_read(cpu, CSR_MIP) | interrupt_mi_from_scause(scause)); + + return; } + // TODO : CHECK mideleg to see wether we should handle interrupt is S mode or M mode + + // An interrupt can only be triggered from outside // of the cpu, so we are on a different thread // and we don't already own the CPU mutex @@ -39,14 +49,14 @@ void interrupt_trigger(rv32_cpu_t* cpu, uint32_t scause) cpu->csr[CSR_SCAUSE] = 0x80000000 | scause; // Set xSTATUS.xPIE (previous interrupt enable) bit - cpu->csr[CSR_SSTATUS] |= 0x80; + cpu->csr[CSR_MSTATUS] |= STATUS_SPIE; // Set xSTATUS.xPP (Previous Privilege) bit // TODO : Allow user mode interrupts (by not setting this) - cpu->csr[CSR_SSTATUS] |= 0x100; + cpu->csr[CSR_MSTATUS] |= 0x100; // Unset xSTATUS.xIE (interrupt enable) bit - cpu->csr[CSR_SSTATUS] &= ~0b10U; + cpu->csr[CSR_MSTATUS] &= (~STATUS_SIE); // Set xEPC : PC at interruption cpu->csr[CSR_SEPC] = cpu->pc; diff --git a/src/cpu/rv32cpu.c b/src/cpu/rv32cpu.c index a28c51a..30bc5f8 100644 --- a/src/cpu/rv32cpu.c +++ b/src/cpu/rv32cpu.c @@ -533,13 +533,18 @@ static void cpu_execute(rv32_cpu_t* cpu, instruction_t* instruction) csr_write(cpu, CSR_SSTATUS, csr_read(cpu, CSR_SSTATUS) | STATUS_SIE); // Restore privilege mode from Previous Privilege - // TODO + if(!(csr_read(cpu, CSR_SSTATUS) & STATUS_SPP)) + { + // Previous Privilege was 0, return to user mode + fprintf(stderr, "SRET to user mode : not implemented yet\n"); + exit(EXIT_FAILURE); + } // Set Previous Interrupt Enable to 1 csr_write(cpu, CSR_SSTATUS, csr_read(cpu, CSR_SSTATUS) | STATUS_SPIE); // Set Previous Privilege to 0 - // TODO + csr_write(cpu, CSR_SSTATUS, csr_read(cpu, CSR_SSTATUS) & (~STATUS_SPP)); // Saved PC before interrupt is in CSR SEPC, jump back cpu->pc = csr_read(cpu, CSR_SEPC) - 4;