5、RISC-V架构下的PLIC控制器及外部中断处理

        这一章的中断处理,讲的是处理外部中断。在中断处理中涉及到外设PLIC外部中断控制器。以及用于开启全局中断的CSR状态寄存器mstatus和mie。

一、mstatus和mie状态寄存器

        MCU的全局中断开关由mstatus中的第3位MIE控制,为1使能全局中断。

        在全局中断开启的情况下,可以通过mie寄存器更加细粒度的控制中断的使能。 中断共分类三种类型,分别是:外部中断、定时器中断、软件中断。分别用E、T、S来表示。mie寄存器如下图所示,想要开启外部中断,需要使第11位MEIE的值为1。

在代码中体现如下:

    csrr t0, mstatus
    ori t0, t0, 0x8
    csrw mstatus, t0

    csrr t0, mie
    li t1, 0x800
    or t0, t0, t1
    csrw mie, t0

 二、PLIC平台级中断控制器(外部中断控制器)

        PLIC控制中断的大体流程是:

        1、PLIC的Gateway根据当下有没有正在处理的中断,决定要不要处理下一个优先级最高的中断请求。

         2、响应某个中断请求后,判断中断优先级若为0,则不处理。

        3、若判断该类型中断请求未使能,不处理。

        4、若中断优先级小于设置阈值,不处理。

        5、设置pending位,示意MCU中断的到来。

        更具体的流程可查看:PLIC基础知识

        PLIC外设需要了解四个寄存器分别是中断优先级控制寄存器(地址:0x0c000000)、中断使能寄存器(地址:0x0c002000)、优先级阈值寄存器(地址:0x0c200000)、中断claim_complete寄存器(地址:0x0c200004)。

        我们需要通过基地址(通常为0x0c000000)以及偏移地址设置这些寄存器的值。通常优先级设置为除0外的任何值,中断使能需打开,优先级阈值设置为0。设置代码如下:

void PLIC_SetPriority(uint32_t source, uint32_t priority)
{
    volatile uint32_t *priority_reg = (uint32_t *)((PLIC_BASE) + PLIC_PRIORITY_OFFSET + \
                    + (source << PLIC_PRIORITY_SHIFT_PER_SOURCE));

    *priority_reg = priority;
}

void PLIC_EnableInterrupt(uint32_t source)
{
    volatile uint32_t *enable_reg = (uint32_t *)((PLIC_BASE) + PLIC_ENABLE_OFFSET + \
                     + (source >> 5) * 4);

    uint32_t current = *enable_reg;
    current = current | (1<<(source&0x1F));
    *enable_reg = current;
}

void PLIC_DisableInterrupt(uint32_t source)
{
    volatile uint32_t *enable_reg = (uint32_t *)((PLIC_BASE) + PLIC_ENABLE_OFFSET + (source >> 5) * 4);

    uint32_t current = *enable_reg;
    current = current & (~(1<<(source&0x1F)));
    *enable_reg = current;
}

void inline PLIC_SetThreshold(uint32_t thresh)
{
    volatile uint32_t *thresh_reg = (uint32_t *)((PLIC_BASE) + PLIC_THRESHOLD_OFFSET);

    *thresh_reg = thresh;
}

        PLIC通知MCU中断到来后,会在claim_complete寄存器指示中断类型。用户需要读取这个寄存器的值表示正在处理中断。处理完成后,需要把对应类型值(中断请求号)写入该寄存器,表示完成中断处理。以便PLIC可以处理下一个中断。对应写入读取代码如下:
 

uint32_t PLIC_ClaimInterrupt(void)
{
    volatile uint32_t *claim_reg = (uint32_t *)((PLIC_BASE) + PLIC_CLAIM_OFFSET);

    return (*claim_reg);
}

void PLIC_CompleteInterrupt(uint32_t source)
{
    volatile uint32_t *complete_reg = (uint32_t *)((PLIC_BASE) + PLIC_CLAIM_OFFSET);

    *complete_reg = source;
}

      PLIC寄存器的详细信息请查看:RISC-V SiFive U54内核——PLIC平台级中断控制器

三、中断处理 

        在中断到来时,程序会跳转到共用的处理函数入口,进行trap处理。我们需要根据claim_complete寄存器中指示的中断类型分别做出不同的响应。最后记得把中断号写回寄存器。

int read_external_irq()
{
	return PLIC_ClaimInterrupt();
}
void external_interrupt_handler()
{
	int irq=read_external_irq();
	if(irq == UART1_IRQ){
		printf("UART1 INTERUPPT\n");
		uart_isr();
	}
	else if(irq==GPIO){
		printf("GPIO INTERUPPT\n");
	}
	else{
		printf("unexpected interrupt irq = %d\n",irq);
	}
	PLIC_CompleteInterrupt(irq);
}

reg_t trap_handler(reg_t epc,reg_t cause){
    reg_t return_pc = epc;
    reg_t cause_code = cause & MCAUSE_MASK_ECODE;
	Uart_puts("traps!\n");
    if(cause & MCAUSE_MASK_INTERRUPT){
        switch (cause_code)
        {
        case 3:
            Uart_puts("software interruption!\n");
            break;
        
        case 7:
			Uart_puts("timer interruption!\n");
			break;
		case 11:
			Uart_puts("external interruption!\n");
			external_interrupt_handler();
			break;
		default:
			printf("Unknown async exception! Code = %ld\n", cause_code);
			break;
        }
    }else {
		/* Synchronous trap - exception */
		printf("Sync exceptions! Code = %ld\n", cause_code);
		panic("OOPS! What can I do!");
		return_pc += 4;
	}
    return return_pc;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值