上回说到了我们已经把系统的心跳动起来了,但是这里面还有一个问题,我们都知道timer中断,中断的trap怎么来的呢。
这回就来解决这个事情。
作者:wangyijieonline
链接:https://blog.csdn.net/wangyijieonline/article/details/109726839
来源:CSDN
著作权归作者所有。商业转载请联系作者获得授权,非商业转载必须注明出处。
回顾一下官方Guide:Using FreeRTOS on RISC-V Microcontrollers
要为 RISC-V 内核构建 FreeRTOS,您需要:
1、在项目中包括核心 FreeRTOS 源文件和 FreeRTOS RISC-V 端口层源文件。
2、确保汇编器的包含路径包括描述任何芯片特定实现详细信息的标头文件的路径。
3、定义 FreeRTOSConfig.h 中的常量或链接器变量以指定要用作中断堆栈的内存。
4、在 freeRTOScong.h configMTIME_BASE_ADDRESS定义configMTIMECMP_BASE_ADDRESS和定义。
5、对于汇编#define portasmHANDLE_INTERRUPT,请使用芯片或工具供应商为处理外部中断而提供的功能的名称。
6、安装 FreeRTOS 陷阱处理程序。
再来看一下,FreeRTOS/Source/portable/GCC/RISC-V
目录,这里面有几个需要我们定义的portasmHAS_MTIME
和portasmHANDLE_INTERRUPT
在上一篇博文中我们其实已经定义好了portasmHAS_MTIME
,表明我们的立场是用mtime
作为整个系统的时基,现在只需要定义一个trap,能正常的让我们的中断函数掉进实现注册好的中断里,要注意,这个地方很重要,因为mtime
也需要这个中断来服务。
细心的小伙伴应该恍然大悟了,这个我们也已经实现了,就是之前我们裸机的时候用的中断注册和处理函数,具体实现还真的是没办法细说,SOC这玩意就是这样,每个厂家都有自己的想法,都想做自己特有的东西,但是好在我们已经准备好了裸机再来做操作系统,什么?你告诉我你并没有准备好裸机的中断注册和处理函数,那我真的无话可说了。
这里给出一种实现作为参考:
#include
#include "platform.h"
typedef void (*isr_func)(void);
void default_irq_handler(void)
{
printf("Default interrupt handler\n");
}
void wdt_irq_handler(void) __attribute__((weak, alias("default_irq_handler")));
void rtc_period_irq_handler(void) __attribute__((weak, alias("default_irq_handler")));
void rtc_alarm_irq_handler(void) __attribute__((weak, alias("default_irq_handler")));
void pit_irq_handler(void) __attribute__((weak, alias("default_irq_handler")));
void spi1_irq_handler(void) __attribute__((weak, alias("default_irq_handler")));
void spi2_irq_handler(void) __attribute__((weak, alias("default_irq_handler")));
void i2c_irq_handler(void) __attribute__((weak, alias("default_irq_handler")));
void gpio_irq_handler(void) __attribute__((weak, alias("default_irq_handler")));
void uart1_irq_handler(void) __attribute__((weak, alias("default_irq_handler")));
void uart2_irq_handler(void) __attribute__((weak, alias("default_irq_handler")));
void dma_irq_handler(void) __attribute__((weak, alias("default_irq_handler")));
void bmc_irq_handler(void) __attribute__((weak, alias("default_irq_handler")));
void swint_irq_handler(void) __attribute__((weak, alias("default_irq_handler")));
void sdc_irq_handler(void) __attribute__((weak, alias("default_irq_handler")));
void mac_irq_handler(void) __attribute__((weak, alias("default_irq_handler")));
void standby_irq_handler(void) __attribute__((weak, alias("default_irq_handler")));
void wakeup_irq_handler(void) __attribute__((weak, alias("default_irq_handler")));
const isr_func irq_handler[] = {
wdt_irq_handler,
rtc_period_irq_handler,
rtc_alarm_irq_handler,
pit_irq_handler,
spi1_irq_handler,
spi2_irq_handler,
i2c_irq_handler,
gpio_irq_handler,
uart1_irq_handler,
uart2_irq_handler,
dma_irq_handler,
bmc_irq_handler,
swint_irq_handler,
default_irq_handler,
default_irq_handler,
default_irq_handler,
default_irq_handler,
default_irq_handler,
sdc_irq_handler,
mac_irq_handler,
default_irq_handler,
default_irq_handler,
default_irq_handler,
default_irq_handler,
default_irq_handler,
default_irq_handler,
standby_irq_handler,
wakeup_irq_handler,
default_irq_handler,
default_irq_handler,
default_irq_handler,
default_irq_handler
};
/* At the time of writing, interrupt nesting is not supported, so do not use
the default mext_interrupt() implementation as that enables interrupts. A
version that does not enable interrupts is provided below. THIS INTERRUPT
HANDLER IS SPECIFIC TO FREERTOS WHICH USES PLIC! */
void mext_interrupt(void)
{
unsigned int irq_source = __nds__plic_claim_interrupt();
/* Do interrupt handler */
irq_handler[irq_source]();
__nds__plic_complete_interrupt(irq_source);
}
void trap_handler(unsigned long mcause, SAVED_CONTEXT *context)
{
#ifdef __riscv_32e
/* Syscall num is passed in t0 (x5) for RV32E. */
long which = context->x5;
#else
long which = context->x17;
#endif
/* Do your trap handling */
if ((mcause & MCAUSE_INT) && ((mcause & MCAUSE_CAUSE) == IRQ_M_EXT)) {
/* Machine-level interrupt from PLIC */
mext_interrupt();
} else if ((mcause & MCAUSE_INT) && ((mcause & MCAUSE_CAUSE) == IRQ_M_TIMER)) {
/* Machine timer interrupt */
mtime_handler();
} else if ((mcause & MCAUSE_INT) && ((mcause & MCAUSE_CAUSE) == IRQ_M_SOFT)) {
/* Machine SWI interrupt */
mswi_handler();
/* Machine SWI is connected to PLIC_SW source 1 */
__nds__plic_sw_complete_interrupt(1);
} else if (!(mcause & MCAUSE_INT) && ((mcause & MCAUSE_CAUSE) == TRAP_M_ECALL)) {
/* Machine Syscal call */
syscall_handler(which, context->x10, context->x11, context->x12, context->x13);
context->mepc += 4;
}else {
/* Unhandled Trap */
context->mepc = except_handler(mcause, context->mepc, context->caller_regs);
}
}
其中trap_handler
的入口是在start.S里面。
其实在写这一篇的时候我比较纠结,每个厂家的实现细节都不一样,很难来说中断注册或者中断处理函数怎么来描述,这个还是要靠广大读者来自己啃下这块硬骨头,我当时也是在这个地方卡了一两天,但是总的来说,只要这个搞通了,基本就没啥大问题了。