MIPS的VEIC(转)

来源:互联网 发布:人工神经网络 知乎 编辑:程序博客网 时间:2024/04/30 14:57

转自:http://hi.baidu.com/juventus/blog/item/5d540923e75708429258076b.html

从MIPS Release2开始,MIPS引入了VINT和VEIC,同时加入了shadow寄存器。

对于MIPS来说,一般外部中断只有一个入口(0x180),而且是很多异常共享这一个入口,所以需要额外的代码来判断异常的类型。确定是外部中断后,还需要根据IP位来dispatch中断。这就造成MIPS的interrupt latency比较长。所以在Release 2中引入了上面提到的VINT和VEIC。

VINT也叫Vectored Interrupt,就是把中断的异常入口从0x180移到了0x200,并且专门用于外部中断。从0x200开始可以有8个不同的外部中断入口,分别对应IP0到IP7。而且,可以在IntCtl[VS]指定每一个外部中断向量的长度。

VEIC需要特殊的外部中断控制器配合(目前好像只有1004K有这个支持),类似于VINT,只不过外部中断数量可以增加到63个(1-63),也就是最多有63个外部中断入口(IP2到IP7组合)。其他的与VINT一样。

这样就可以放更多的代码到interrupt vector中,而且可以省去很多dispatch的代码,大大减少了interrupt latency。

目前在Linux kernel中,这块代码是由cpu_has_vint和cpu_has_veic控制的。

#if defined(CONFIG_CPU_MIPSR2_IRQ_VI) && !defined(cpu_has_vint)
# define cpu_has_vint           (cpu_data[0].options & MIPS_CPU_VINT)
#elif !defined(cpu_has_vint)
# define cpu_has_vint                   0
#endif

#if defined(CONFIG_CPU_MIPSR2_IRQ_EI) && !defined(cpu_has_veic)
# define cpu_has_veic           (cpu_data[0].options & MIPS_CPU_VEIC)
#elif !defined(cpu_has_veic)
# define cpu_has_veic                   0
#endif

对于一个处理器是否支持VEIC可以从config3寄存器的VEIC位决定,该位是只读位,如果为1表示该处理器支持VEIC,则异常向量初始化需要考虑VEIC。

具体的异常向量初始化代码参见arch/mips/kernel/traps.c。大概执行流程如下:
1、计算所有外部中断向量的size(个数xIntCtl[VS])
2、通过size计算ebase,并将结果写入EBASE寄存器
3、循环调用set_vi_handler初始化外部中断向量(根据VEIC或者VINT支持的外部中断个数决定循环次数)。handler地址如果是NULL,则会指向do_default_vi。细节还由是否支持shadow寄存器决定。do_default_vi就是产生一个kernel panic。

set_vi_handler就是调用set_vi_srs_handler,其中srs参数为0,表示目前没有支持shadow寄存器。set_vi_srs_handler流程如下:
1、获得中断handler的地址,如果是NULL则使用do_default_vi
2、计算对应中断向量的基址(ebase+x200+nxIntCtl[VS])
3、计算中断vector的长度(except_vec_vi_end - vec_start)。vec_start等于except_vec_vi
4、copy从vec_start开始,handler长度的代码到该中断向量的基址。目前copy的是默认代码
5、计算handler中lui和ori指令的地址,修改这两条指令的内容,使得它们load真正的中断handler的地址到v0寄存器中
6、flush icache

注意,这里面有一个技巧,就是修改默认的lui和ori指令。默认的行为是load地址0到v0中,修改后就是load真正的中断handler到v0中。

最终每一个中断vector都是这个样子:
保存部分上下文(包括切换堆栈)
load真正的中断handler到v0中
跳转到except_vec_vi_handler
在except_vec_vi_handler中,保存剩下的上下文,load ret_from_irq到ra中
jr v0跳转到真正的中断handler处理中断