OR1200处理器的可编程中断控制器PIC分析

2019-04-15 14:44发布

以下内容摘自《步步惊芯——软核处理器内部设计分析》一书

16.3 可编程中断控制器PIC分析

16.3.1 PIC介绍

      可编程中断控制器Programmable Interrupt ControllerPIC)用来响应各种中断事件,如:键盘事件、串口数据到达等,PIC收集所有的中断,并通知CPU中断到达,后者转入到中断处理例程进行处理。OR1200最多支持32个中断。其功能实现主要依靠两个特殊寄存器:中断屏蔽寄存器PICMR、中断状态寄存器PICSR。通过PICMR可以设置是否屏蔽某些中断,通过PICSR可以知道中断源的信息。PICMRPICSR是第9组特殊寄存器,如表16.7所示。       中断屏蔽寄存器PICMR的格式如表16.8所示,该寄存器可读、可写。标志IUM中的值表示对应中断是否被屏蔽,IUM0x0,表示所有中断都被屏蔽,IMU0xFFFFFFFF,表示所有中断都没有被屏蔽。在OR1200中,IUM的位数可以配置,配置范围是2-31,这是因为IUM的最低两位始终保持为1,即中断源01设置为不可屏蔽,可以将这两个中断源对应为电源关闭、系统复位。       中断状态寄存器PICSR的格式如表16.9所示,该寄存器可读、可写。标志IS中的值表示对应中断是否发生并等待处理,IS0x0表示没有中断发生,第i位为1表示第i个中断源发生了中断。判断中断是否发生的方法有电平触发、边沿触发,OR1200支持的是电平触发,此时,IS的第i位实际对应的就是第i个中断源的当前电平值。       OR1200中断处理过程如下:       (1)中断源声明中断发生,PICSR[i]1,如果PICMR[i]1,那么通知处理器中断到达       (2)处理器转入中断处理例程进行中断处理       (3)中断已处理,中断处理例程通知中断源       (4)中断源取消中断声明       (5)中断处理例程设置PICSR[i]0,退出中断处理例程       注意一点,单独设置PICSR[i]并不会清除中断,必须通知中断源,由后者取消中断声明。

16.3.2 PIC的对外连接关系及相关宏定义

      OR1200处理器中可编程中断控制器PIC的对外连接关系如图16.3所示,通过箭头方向表示该信号是输入还是输出。以spr_xxx开始的接口都是与特殊寄存器读写有关的信号,含义也很明了,此外,pic_int是一个宽度可配置的接口,最大为32,对应32个外部中断源输入。当中断发生时通过intr接口通知CPU模块,其值为1表示中断发生,连接到CPU模块的接口sig_int,同时还要通过pic_wakeup接口通知电源管理模块PM,如果中断发生时,处理器处于SleepDoze模式,PM模块在接收到pic_wakeup1后,会恢复正常模式,参考16.1节中对PM模块的分析。       OR1200中与可编程中断控制器PIC有关的宏定义如下: or1200_defines.v `define OR1200_PIC_IMPLEMENTED //是否实施PIC模块,PIC模块是可选模块 `define OR1200_PIC_INTS 20 //定义外部中断源的数目,最多是32个,默认是20个 `define OR1200_PIC_OFS_PICMR 2'd0 //PICMR、PICSR寄存器在第9组特殊寄存器中的索引 `define OR1200_PIC_OFS_PICSR 2'd2 `define OR1200_PICOFS_BITS 1:0 `define OR1200_PIC_PICMR //需要定义这个宏,才可以使用PICMR寄存器 `define OR1200_PIC_PICSR //需要定义这个宏,才可以使用PICSR寄存器 `define OR1200_PIC_READREGS //有了这个宏定义,才可以读PIC中的特殊寄存器PICMR、PICSR `define OR1200_PIC_UNUSED_ZERO
 

16.3.3 PIC代码分析

      PIC的主要代码就是配置PICMRPICSR寄存器,以及依据PICMRPICSR的值判断中断是否发生,分析如下(为了方便理解,笔者改变了代码顺序): or1200_pic.v module or1200_pic( clk, rst, spr_cs, spr_write, spr_addr, spr_dat_i, spr_dat_o, pic_wakeup, intr, pic_int ); …… output pic_wakeup; //输出到PM模块 output intr; //输出到CPU模块 input [`OR1200_PIC_INTS-1:0] pic_int; //输入的中断信号,其宽度可通过OR1200_PIC_INTS配置 `ifdef OR1200_PIC_IMPLEMENTED `ifdef OR1200_PIC_PICMR reg [`OR1200_PIC_INTS-1:2] picmr; //PICMR寄存器的宽度也和OR1200_PIC_INTS相匹配,但少了两位, //这是由于中断源0、1不可屏蔽,所以PICMR的宽度比中断输入 //pic_int的宽度少了两位 `else wire [`OR1200_PIC_INTS-1:2] picmr; `endif `ifdef OR1200_PIC_PICSR reg [`OR1200_PIC_INTS-1:0] picsr; //PICSR寄存器的宽度也和OR1200_PIC_INTS相匹配 `else wire [`OR1200_PIC_INTS-1:0] picsr; `endif …… //如果spr_cs为1,那么依据spr_addr的最低两位判断指令l.mfspr/l.mtspr的访问目标是PICMR还是PICSR //picmr_sel为1,表示访问目标是PICMR,picsr_sel为1,表示访问目标是PICSR assign picmr_sel = (spr_cs && (spr_addr[`OR1200_PICOFS_BITS] == `OR1200_PIC_OFS_PICMR)) ? 1'b1 : 1'b0; assign picsr_sel = (spr_cs && (spr_addr[`OR1200_PICOFS_BITS] == `OR1200_PIC_OFS_PICSR)) ? 1'b1 : 1'b0; //将pic_int与{picmr,2’b11}相与,得出未屏蔽的中断信息保存在um_ints,注意中断源0、1不可屏蔽, //与PICMR的值无关,此处直接就是2’b11 assign um_ints = pic_int & {picmr, 2'b11}; //um_ints不为0,表示有中断发生,设置intr为1,intr输出到CPU模块的sig_int接口 assign intr = |um_ints; //中断发生后还要通过pic_wakeup接口通知PM模块 assign pic_wakeup = intr; `ifdef OR1200_PIC_PICMR always @(posedge clk or `OR1200_RST_EVENT rst) if (rst == `OR1200_RST_VALUE) picmr <= {1'b1, {`OR1200_PIC_INTS-3{1'b0}}}; else if (picmr_sel && spr_write) begin picmr <= spr_dat_i[`OR1200_PIC_INTS-1:2]; //写PICMR,注意最低两位不受影响 end `else assign picmr = (`OR1200_PIC_INTS)'b1; `endif //给PICSR寄存器赋值,PICSR的值取决于指令l.mtspr写入的数据,以及当前发生的外部中断情况, //从这里也可以发现:当中断发生时,单独使用l.mtspr清除PICSR寄存器并不会使得PICSR的值改 //变,因为外部设备没有取消中断声明,um_ints的值没变,所以必须要外部设备取消中断声明 `ifdef OR1200_PIC_PICSR always @(posedge clk or `OR1200_RST_EVENT rst) if (rst == `OR1200_RST_VALUE) picsr <= {`OR1200_PIC_INTS{1'b0}}; else if (picsr_sel && spr_write) begin picsr <= spr_dat_i[`OR1200_PIC_INTS-1:0] | um_ints; end else picsr <= picsr | um_ints; `else assign picsr = pic_int; `endif always @(spr_addr or picmr or picsr) case (spr_addr[`OR1200_PICOFS_BITS]) // synopsys parallel_case `OR1200_PIC_OFS_PICMR: begin spr_dat_o[`OR1200_PIC_INTS-1:0] = {picmr, 2'b11}; //读PICMR,最低两位始终为1 spr_dat_o[31:`OR1200_PIC_INTS] = {32-`OR1200_PIC_INTS{1'b0}}; end default: begin spr_dat_o[`OR1200_PIC_INTS-1:0] = picsr; //读PICSR spr_dat_o[31:`OR1200_PIC_INTS] = {32-`OR1200_PIC_INTS{1'b0}}; end endcase `else //没有配置PIC模块的情况如下 assign intr = pic_int[1] | pic_int[0]; //没有配置PIC模块时,中断0、1还可以使用 assign pic_wakeup= intr; assign spr_dat_o[`OR1200_PIC_INTS-1:0] = `OR1200_PIC_INTS'b0; assign spr_dat_o[31:`OR1200_PIC_INTS] = 32-`OR1200_PIC_INTS'b0; `endif endmodule
      当中断发生后,PIC模块置高intr,参考图16.3可知,intr连接到CPU模块的sig_int接口,CPU内部的EXCEPTION模块接收该信号,并进行处理,与计时器中断相似,并不会立即响应该中断,而是需要满足一定条件才会响应,如下: or1200_exception.v assign int_pending = sig_int & (sr[`OR1200_SR_IEE] | (sr_we & to_sr[`OR1200_SR_IEE])) & id_pc_val & delayed_iee[2] & ~ex_freeze & ~ex_branch_taken & ~ex_dslot & ~(sr_we & ~to_sr[`OR1200_SR_IEE]);       各个条件的说明参考计时器中断,当条件满足后,int_pending为1,处理器进入外部中断异常的处理,具体过程与计时器中断一样,此处不再赘述。