ch11-外部设备中断

·  阅读 279

1.RISC-V 中断(Interrupt)的分类

image-20210815233725116

  • 本地中断 Local
    1. 软中断 software-interrupt
    2. 定时器中断 timer-imterrupt
  • 全局中断 Global
    1. 外设中断 externle interrupt

2.RISC-V 中断编程中涉及的寄存器

image-20210815234055386

上节课,我们知道了:

  • mtvec
  • mepc
  • mcause
  • mtval
  • mstatus
  • mscratch

只有miemip没有讲

  • mie寄存器:之前mstatus的mie是全局中断的使能位,如果全局中断关了,那么所有的关了。如果全局中断是开着的,由mie寄存器进行更精细的中断管理.

    image-20210815234312740

  • mip寄存器:记录当前这种中断是否发生了,1表示发生了

    image-20210815234405541

3.RISC-V 中断处理流程

image-20210815234435743

image-20210815234517774

4.PLIC 介绍

image-20210815234557535

  • 外部中断,也就是外设的中断——如USB(鼠标、键盘),磁盘管理 等
  • 如果所有的外设都用芯片的一个单独的引脚去处理,会非常麻烦,所以引入了PLIC

image-20210815234805253

  • PLIC:平台级别的中断控制器
  • 左边有很多很多外设接到PLIC,右边针对一个hart,只有一个引脚,所以功能就是,只要有一个中断,那么就触发CPU的中断,而且如果有多个中断,当前时刻只会让一个中断进来,对左边的中断进行协调.

image-20210815235033957

中断源:Interrupt Source (串口设备UART也是一个中断源,这里定义的是10)

image-20210815235134775

  • PLIC本身也是一个外设,我们也是用寄存器去操纵这个外设

优先级 Priority:

image-20210815235315847

记录某路中断源是否发生:Pending

image-20210815235416687

记录某路中断源是否开了中断:Enable

image-20210815235613803

设置中断发生的阈值,如果该中断源的优先级<=阈值,就会屏蔽:Threshold

image-20210815235715695

获取当前处理的中断/消除这个中断 : Cliam/Complete:

image-20210815235823408

image-20210815235908642

5.采用中断方式从 UART 实现输入

如何在控制台上敲击键盘,然后屏幕上显示出来呢?

当敲击键盘时,触发外部中断(externel trap),然后去执行那个外部中断的处理函数,在处理函数中会执行uart_put去将这个字符写入到串口中,串口我们前面知道是用轮询的方式去检查是否有字符待处理,所以这样就可以去将字符展示在控制台上.

总的中断处理函数:

trap_vector:
	# save context(registers).
	csrrw	t6, mscratch, t6	# swap t6 and mscratch
	reg_save t6
	csrw	mscratch, t6

	# call the C trap handler in trap.c
	csrr	a0, mepc
	csrr	a1, mcause
	call	trap_handler

	# trap_handler will return the return address via a0.
	csrw	mepc, a0

	# restore context(registers).
	csrr	t6, mscratch
	reg_restore t6

	# return to whatever we were doing before trap.
	mret
复制代码

在这里可以看到,中断发生时,去调用trap_handler函数,如下:

reg_t trap_handler(reg_t epc, reg_t cause)
{
	reg_t return_pc = epc;
	reg_t cause_code = cause & 0xfff;
	
	if (cause & 0x80000000) {
		/* Asynchronous trap - 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:
			uart_puts("unknown async exception!\n");
			break;
		}
	} else {
		/* Synchronous trap - exception */
		printf("Sync exceptions!, code = %d\n", cause_code);
		panic("OOPS! What can I do!");
		//return_pc += 4;
	}

	return return_pc;
}
复制代码

11是外部中断,所以当我们敲击键盘时,执行case11,所以执行external_interrupt_handler,如下:

void external_interrupt_handler()
{
	int irq = plic_claim();	// 获取一个中断源的编号

	if (irq == UART0_IRQ){	// 如果是uart产生的中断,那么就去写到uart中
      		uart_isr();
	} else if (irq) {
		printf("unexpected interrupt irq = %d\n", irq);
	}
	
	if (irq) {	// 如果是一个中断源,那么就通知这个中断结束了,解决了
		plic_complete(irq);
	}
}
复制代码

最后,我们来看看uart_isr是如何将字符写到uart的.

/*
 * handle a uart interrupt, raised because input has arrived, called from trap.c.
 */
void uart_isr(void)
{
	while (1) {
		int c = uart_getc();	// 从uart中得到从键盘敲入的字符
		if (c == -1) {
			break;
		} else {
			uart_putc((char)c);	// 输出这个字符
			uart_putc('\n');
		}
	}
}
复制代码

所以,在这里我们将敲入的字符输出去了.

分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改