diff --git a/cpu/riscv_common/include/clic.h b/cpu/riscv_common/include/clic.h new file mode 100644 index 0000000000..582b5b40ff --- /dev/null +++ b/cpu/riscv_common/include/clic.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2020 Koen Zandberg + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ +/** + * @ingroup cpu_riscv_common + * @{ + * + * @file + * @brief RISCV CLIC interrupt controller definitions + * + * RISCV implementations using this peripheral must define the `CLIC_BASE_ADDR`, + * in order to use the PLIC as interrupt controller. Also required is + * CLIC_NUM_INTERRUPTS. + * + * @author Koen Zandberg + */ +#ifndef CLIC_H +#define CLIC_H + +#include "cpu_conf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief CLIC callback declaration + * + * @param irq Interrupt number + */ +typedef void (*clic_isr_cb_t)(unsigned irq); + +/** + * @brief RISC-V CLIC per interrupt configuration registers + */ +typedef struct __attribute((packed)) { + volatile uint8_t ip; /**< Interrupt pending */ + volatile uint8_t ie; /**< Interrupt enable */ + volatile uint8_t attr; /**< Interrupt attributes */ + volatile uint8_t ctl; /**< Interrupt control */ +} clic_clicint_t; + +/** + * @brief Initialize the CLIC interrupt controller + */ +void clic_init(void); + +/** + * @brief Enable a single interrupt + * + * @param irq Interrupt number to enable + * @param priority Priority level to configure + */ +void clic_enable_interrupt(unsigned irq, unsigned priority); + +/** + * @brief Disable a single interrupt + * + * @param irq Interrupt number to disable + */ +void clic_disable_interrupt(unsigned irq); + +/** + * @brief Set the priority of an interrupt + * + * @param irq Interrupt number to configure + * @param priority Priority level to configure + */ +void clic_set_priority(unsigned irq, unsigned priority); + +/** + * @brief Set the handler for an interrupt + * + * @param irq Interrupt number to configure + * @param cb Callback handler to configure + */ +void clic_set_handler(unsigned irq, clic_isr_cb_t cb); + +/** + * @brief CLIC interrupt handler + * + * @internal + * + * @param irq Interrupt number to call the handler for + */ +void clic_isr_handler(uint32_t irq); + +#ifdef __cplusplus +} +#endif + +#endif /* CLIC_H */ +/** @} */ diff --git a/cpu/riscv_common/include/irq_arch.h b/cpu/riscv_common/include/irq_arch.h index d6876de964..980409ed69 100644 --- a/cpu/riscv_common/include/irq_arch.h +++ b/cpu/riscv_common/include/irq_arch.h @@ -32,6 +32,11 @@ extern "C" { #endif +/** + * @brief Bit mask for the MCAUSE register + */ +#define CPU_CSR_MCAUSE_CAUSE_MSK (0x0fffu) + extern volatile int riscv_in_isr; /** diff --git a/cpu/riscv_common/irq_arch.c b/cpu/riscv_common/irq_arch.c index 5b3507a84f..22f6d67749 100644 --- a/cpu/riscv_common/irq_arch.c +++ b/cpu/riscv_common/irq_arch.c @@ -29,6 +29,7 @@ #include "panic.h" #include "sched.h" #include "plic.h" +#include "clic.h" #include "vendor/riscv_csr.h" @@ -50,7 +51,13 @@ void timer_isr(void); void riscv_irq_init(void) { /* Setup trap handler function */ - write_csr(mtvec, &trap_entry); + if (IS_ACTIVE(MODULE_PERIPH_CLIC)) { + /* Signal CLIC usage to the core */ + write_csr(mtvec, (uintptr_t)&trap_entry | 0x03); + } + else { + write_csr(mtvec, (uintptr_t)&trap_entry); + } /* Clear all interrupt enables */ write_csr(mie, 0); @@ -59,12 +66,17 @@ void riscv_irq_init(void) if (IS_ACTIVE(MODULE_PERIPH_PLIC)) { plic_init(); } + if (IS_ACTIVE(MODULE_PERIPH_CLIC)) { + clic_init(); + } /* Enable external interrupts */ set_csr(mie, MIP_MEIP); /* Set default state of mstatus */ set_csr(mstatus, MSTATUS_DEFAULT); + + irq_enable(); } /** @@ -76,11 +88,13 @@ void handle_trap(uint32_t mcause) * calling thread_yield(). */ riscv_in_isr = 1; + uint32_t trap = mcause & CPU_CSR_MCAUSE_CAUSE_MSK; /* Check for INT or TRAP */ if ((mcause & MCAUSE_INT) == MCAUSE_INT) { /* Cause is an interrupt - determine type */ switch (mcause & MCAUSE_CAUSE) { -#ifdef MODULE_PERIPH_TIMER + +#ifdef MODULE_PERIPH_CORETIMER case IRQ_M_TIMER: /* Handle timer interrupt */ timer_isr(); @@ -94,13 +108,18 @@ void handle_trap(uint32_t mcause) break; default: - /* Unknown interrupt */ - core_panic(PANIC_GENERAL_ERROR, "Unhandled interrupt"); + if (IS_ACTIVE(MODULE_PERIPH_CLIC)) { + clic_isr_handler(trap); + } + else { + /* Unknown interrupt */ + core_panic(PANIC_GENERAL_ERROR, "Unhandled interrupt"); + } break; } } else { - switch (mcause) { + switch (trap) { case CAUSE_USER_ECALL: /* ECALL from user mode */ case CAUSE_MACHINE_ECALL: /* ECALL from machine mode */ { @@ -115,7 +134,7 @@ void handle_trap(uint32_t mcause) default: #ifdef DEVELHELP printf("Unhandled trap:\n"); - printf(" mcause: 0x%" PRIx32 "\n", mcause); + printf(" mcause: 0x%" PRIx32 "\n", trap); printf(" mepc: 0x%lx\n", read_csr(mepc)); printf(" mtval: 0x%lx\n", read_csr(mtval)); #endif @@ -128,8 +147,9 @@ void handle_trap(uint32_t mcause) } /* Marking this as interrupt to ensure an mret at the end, provided by the - * compiler. Aligned to 4-byte boundary as per RISC-V spec */ -static void __attribute((aligned(4))) __attribute__((interrupt)) trap_entry(void) + * compiler. Aligned to 64-byte boundary as per RISC-V spec and required by some + * of the supported platforms (gd32)*/ +static void __attribute((aligned(64))) __attribute__((interrupt)) trap_entry(void) { __asm__ volatile ( "addi sp, sp, -"XTSTR (CONTEXT_FRAME_SIZE)" \n" diff --git a/cpu/riscv_common/periph/clic.c b/cpu/riscv_common/periph/clic.c new file mode 100644 index 0000000000..dfbcc5154b --- /dev/null +++ b/cpu/riscv_common/periph/clic.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 Koen Zandberg + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ +/** + * @ingroup cpu_riscv_common + * @{ + * + * @file + * @brief RISCV CLIC interrupt controller implementation + * + * @author Koen Zandberg + */ + +#include +#include +#include "clic.h" + +/** + * @brief CLIC registry offset helper + */ +#define CLIC_REGP(offset) ((volatile uint32_t *)((CLIC_BASE_ADDR) + (offset))) + +/** + * @name CLIC configuration registers + * @{ + */ +#define CLIC_CFG *((volatile uint8_t *)CLIC_REGP(0x0)) +#define CLIC_INFO *((volatile uint32_t *)CLIC_REGP(0x4) +#define CLIC_MTH *((volatile uint8_t *)CLIC_REGP(0xb) +#define CLIC_INT_ADDR CLIC_REGP(0x1000) +#define CLIC_INT ((volatile clic_clicint_t *)CLIC_INT_ADDR) +/** @} */ + +/* PLIC external ISR function list */ +static clic_isr_cb_t _ext_isrs[CLIC_NUM_INTERRUPTS]; + +void clic_init(void) +{} + +void clic_enable_interrupt(unsigned irq, unsigned priority) +{ + CLIC_INT[irq].ie = 1; + CLIC_INT[irq].attr = 0; + clic_set_priority(irq, priority); +} + +void clic_disable_interrupt(unsigned irq) +{ + CLIC_INT[irq].ie = 0; +} + +void clic_set_priority(unsigned irq, unsigned priority) +{ + CLIC_INT[irq].ctl = priority; +} + +void clic_set_handler(unsigned irq, clic_isr_cb_t cb) +{ + assert(irq < CLIC_NUM_INTERRUPTS); + _ext_isrs[irq] = cb; +} + +void clic_isr_handler(uint32_t irq) +{ + _ext_isrs[irq](irq); +} diff --git a/kconfigs/Kconfig.features b/kconfigs/Kconfig.features index f3d46df872..adc148224f 100644 --- a/kconfigs/Kconfig.features +++ b/kconfigs/Kconfig.features @@ -200,6 +200,11 @@ config HAS_PERIPH_MCG help Indicates that an MCG peripheral is present. +config HAS_PERIPH_CLIC + bool + help + Indicates that a RISC-V Core-local Interrupt Controller (CLIC) peripheral is present. + config HAS_PERIPH_PLIC bool help