以下内容摘自《步步惊芯——软核处理器内部设计分析》一书
EXCEPTION模块的作用
为了便于说明,在进行具体指令分析之前,给出在异常处理类指令执行过程中涉及到的主要模块连接关系,如图7.1所示,图中的大部分模块在前几章分析指令的时候都有所涉及。但在本章会使用不同的输入输出接口。该图仍然采用模块左边是输入接口,右边是输出接口的绘制方式,这样做的好处是一目了然知道哪些接口是输入,哪些接口是输出。
图7.1 异常处理过程涉及到的主要模块的局部连接关系
新增加的模块有LSU、EXCEPTION,其中EXCEPTION模块虽然在第6章有所涉及,但只是使用了其中一小部分功能,所以也将其算作新增加的模块。LSU模块主要用在加载/存储数据过程中。EXCEPTION模块从名字上也可以知道该模块主要用在异常处理,实际上EXCEPTION模块是异常处理的关键模块,因此本节将提前对其进行说明,以便读者朋友能够对其在异常处理类指令执行过程中的作用有一个大概了解。
从图7.1中观察EXCEPTION的输入接口,发现有很多接口名称是以“sig_”开始的,这些都是异常声明信号,当异常发生时,相应的信号置为1,比如:计时器中断异常发生时,EXCEPTION的输入sig_tick就为1。EXCEPTION有很多异常声明信号,来自不同的功能模块,从信号名称上就可以知道该信号代表的是哪种异常。LSU模块主要是加载/存储数据,所以其可能产生的异常就是:对齐异常、DTLB失靶、数据页失效、数据总线错误,相应的EXCEPTION输入接口分别是sig_align、sig_dtlbmiss、sig_dmmufault、sig_dbuserr。IF模块主要是取指,所以其可能产生的异常就是:ITLB失靶、指令页失效、指令总线错误,相应的EXCEPTION输入接口分别是sig_itlbmiss、sig_immufault、sig_ibuserr。
EXCEPTION模块收到异常声明信号后,会进入异常处理过程,输出一系列的控制信号,这一系列控制信号除了完成在7.2节中提到的设置EPCR、EEAR、保存SR、设置新的SR、跳转到异常处理例程等工作外,还有一项很重要的工作——清空流水线,比如:处理器在指令l.sys进入流水线的执行阶段时才会知道这是一个系统调用异常,但此时在流水线上已经有了其余的指令位于译码、取指阶段,所以处理器必须要清空流水线。以下摘要EXCEPTION模块中的主要代码,可以清晰的明白EXCEPTION的工作过程。or1200_except.v
//下式中ex_exceptflags等于{ sig_ibuserr, sig_itlbmiss, sig_immufault },
//du_dsr是与调试单元有关的一个变量,不考虑调试单元,认为du_dsr为0,所以
//except_trig的值只取决于EXCEPTION模块输入的异常声明信号的值,当except_trig
//不为0时,就表示有异常发生了
assign except_trig = {
ex_exceptflags[1] & ~du_dsr[`OR1200_DU_DSR_IME],
ex_exceptflags[0] & ~du_dsr[`OR1200_DU_DSR_IPFE],
ex_exceptflags[2] & ~du_dsr[`OR1200_DU_DSR_BUSEE],
sig_illegal & ~du_dsr[`OR1200_DU_DSR_IIE],
sig_align & ~du_dsr[`OR1200_DU_DSR_AE],
sig_dtlbmiss & ~du_dsr[`OR1200_DU_DSR_DME],
sig_trap & ~du_dsr[`OR1200_DU_DSR_TE],
sig_syscall & ~du_dsr[`OR1200_DU_DSR_SCE] & ~ex_freeze,
sig_dmmufault & ~du_dsr[`OR1200_DU_DSR_DPFE],
sig_dbuserr & ~du_dsr[`OR1200_DU_DSR_BUSEE],
range_pending & ~du_dsr[`OR1200_DU_DSR_RE],
fp_pending & ~du_dsr[`OR1200_DU_DSR_FPE],
int_pending & ~du_dsr[`OR1200_DU_DSR_IE],
tick_pending & ~du_dsr[`OR1200_DU_DSR_TTE]
};
//except_flushpipe变量用来表示是否要开始进入异常处理程序,没有异常的时候,
//except_flushpipe为0,当有异常发生时,except_flushpipe等于1
assign except_flushpipe = |except_trig & ~|state;
//下面是一个有限状态机
always @(posedge clk or `OR1200_RST_EVENT rst) begin
if (rst == `OR1200_RST_VALUE) begin
state <= `OR1200_EXCEPTFSM_IDLE;
except_type <= `OR1200_EXCEPT_NONE;
extend_flush <= 1'b0;
epcr <= 32'b0;
eear <= 32'b0;
esr <= {2'h1, {`OR1200_SR_WIDTH-3{1'b0}}, 1'b1};
extend_flush_last <= 1'b0;
end
else begin
case (state) // synopsys parallel_case
`OR1200_EXCEPTFSM_IDLE:
//except_flushpipe为1表示异常发生,需要进入异常处理程序
if (except_flushpipe) begin
state <= `OR1200_EXCEPTFSM_FLU1; //进入状态OR1200_EXCEPTFSM_FLU1
extend_flush <= 1'b1;
esr <= sr_we ? to_sr : sr; //保存寄存器SR的值到寄存器ESR
casez (except_trig) //下面的代码依据不同的异常类型,
//设置不同的EPCR、EEAR
//注意这里的顺序决定了各个异常的优先级
14'b1?_????_????_????: begin //ITLB失靶异常
except_type <= `OR1200_EXCEPT_ITLBMISS;
//其中ex_dslot用于判断当前处于执行阶段的指令是否是延迟槽中的指令,
//从而保存不同的值到EPCR
eear <= ex_dslot ? ex_pc : ex_pc;
epcr <= ex_dslot ? wb_pc : ex_pc;
end
14'b01_????_????_????: begin //指令页失效异常
except_type <= `OR1200_EXCEPT_IPF;
eear <= ex_dslot ? ex_pc : delayed1_ex_dslot ?
id_pc : delayed2_ex_dslot ? id_pc : id_pc;
epcr <= ex_dslot ? wb_pc : delayed1_ex_dslot ?
id_pc : delayed2_ex_dslot ? id_pc : id_pc;
end
14'b00_1???_????_????: begin // 总线(指令)错误异常
except_type <= `OR1200_EXCEPT_BUSERR;
eear <= ex_dslot ? wb_pc : ex_pc;
epcr <= ex_dslot ? wb_pc : ex_pc;
end
14'b00_01??_????_????: begin //无效指令
except_type <= `OR1200_EXCEPT_ILLEGAL;
eear <= ex_pc;
epcr <= ex_dslot ? wb_pc : ex_pc;
end
14'b00_001?_????_????: begin //对齐异常
except_type <= `OR1200_EXCEPT_ALIGN;
eear <= lsu_addr;
epcr <= ex_dslot ? wb_pc : ex_pc;
end
14'b00_0001_????_????: begin //DTLB失靶异常
except_type <= `OR1200_EXCEPT_DTLBMISS;
eear <= lsu_addr;
epcr <= ex_dslot ? wb_pc : delayed1_ex_dslot ? dl_pc : ex_pc;
end
14'b00_0000_1???_????: begin //自陷异常
except_type <= `OR1200_EXCEPT_TRAP;
epcr <= ex_dslot ? wb_pc : delayed1_ex_dslot ? id_pc : ex_pc;
end
14'b00_0000_01??_????: begin //系统调用异常
except_type <= `OR1200_EXCEPT_SYSCALL;
epcr <= ex_dslot ? wb_pc : delayed1_ex_dslot ?
id_pc : delayed2_ex_dslot ? id_pc : id_pc;
end
14'b00_0000_001?_????: begin //数据页失效异常
except_type <= `OR1200_EXCEPT_DPF;
eear <= lsu_addr;
epcr <= ex_dslot ? wb_pc : delayed1_ex_dslot ? dl_pc : ex_pc;
end
14'b00_0000_0001_????: begin // 总线(数据)错误异常
except_type <= `OR1200_EXCEPT_BUSERR;
eear <= lsu_addr;
epcr <= ex_dslot ? wb_pc : delayed1_ex_dslot ? dl_pc : ex_pc;
end
14'b00_0000_0000_1???: begin //溢出
except_type <= `OR1200_EXCEPT_RANGE;
epcr <= ex_dslot ? wb_pc : delayed1_ex_dslot ?
id_pc : delayed2_ex_dslot ? id_pc : id_pc;
end
14'b00_0000_0000_01??: begin //浮点单元异常
except_type <= `OR1200_EXCEPT_FLOAT;
epcr <= id_pc;
end
14'b00_0000_0000_001?: begin //外部中断
except_type <= `OR1200_EXCEPT_INT;
epcr <= id_pc;
end
14'b00_0000_0000_0001: begin //计时器中断
except_type <= `OR1200_EXCEPT_TICK;
epcr <= id_pc;
end
default:
except_type <= `OR1200_EXCEPT_NONE;
endcase
end
else if (pc_we) begin
state <= `OR1200_EXCEPTFSM_FLU1;
extend_flush <= 1'b1;
end
else begin
if (epcr_we)
epcr <= datain;
if (eear_we)
eear <= datain;
if (esr_we)
esr <= {datain[`OR1200_SR_WIDTH-1], 1'b1, datain[`OR1200_SR_WIDTH-3:0]};
end
`OR1200_EXCEPTFSM_FLU1:
//在满足一定条件下进入状态OR1200_EXCEPTFSM_FLU2
if (icpu_ack_i | icpu_err_i | genpc_freeze)
state <= `OR1200_EXCEPTFSM_FLU2;
`OR1200_EXCEPTFSM_FLU2:
//如果是自陷异常,则在此就结束异常处理过程
if (except_type == `OR1200_EXCEPT_TRAP) begin
state <= `OR1200_EXCEPTFSM_IDLE;
extend_flush <= 1'b0;
extend_flush_last <= 1'b0;
except_type <= `OR1200_EXCEPT_NONE;
end
else
//对于非自陷异常,还要进入状态OR1200_EXCEPTFSM_FLU3
state <= `OR1200_EXCEPTFSM_FLU3;
`OR1200_EXCEPTFSM_FLU3:
begin
//进入状态OR1200_EXCEPTFSM_FLU4
state <= `OR1200_EXCEPTFSM_FLU4;
end
`OR1200_EXCEPTFSM_FLU4: begin
//进入状态OR1200_EXCEPTFSM_FLU5
state <= `OR1200_EXCEPTFSM_FLU5;
extend_flush <= 1'b0;
extend_flush_last <= 1'b0; // damjan
end
`ifdef OR1200_CASE_DEFAULT
default: begin
`else
`OR1200_EXCEPTFSM_FLU5: begin
`endif
if (!if_stall && !id_freeze) begin
//在满足一定条件下,状态机回到IDLE状态,异常处理结束
state <= `OR1200_EXCEPTFSM_IDLE;
except_type <= `OR1200_EXCEPT_NONE;
extend_flush_last <= 1'b0;
end
end
endcase
end
end
EXCEPTION模块的代码还有很多,上面只是其中一部分,而且对这一部分笔者也没有做详细的解释,因为此处给出代码的目的是为了读者朋友对异常处理的步骤有一个全局性的认识,在后面分析具体指令的时候还会详细解释。从代码中可以明白变量except_trig收集各种异常信号,当其不为0时就表示有异常发生,然后会设置except_flushpipe,该值为1时就会进入异常处理过程,从这个变量的名称也可以知道异常处理过程中需要刷新流水线(flushpipe)。从EXCEPTION模块的代码中还可以发现异常处理过程使用了一个有限状态机,其状态转移如图7.2所示。
图7.2 异常处理过程状态转移图(米里型)
共有六个状态,一般情况下处于OR1200_EXCEPTFSM_IDLE状态(以下对所有的状态都只使用状态名的最后一个单词表示,例如OR1200_EXCEPTFSM_IDLE就简称为IDLE状态),说明如下:
- 在IDLE状态,当except_flushpipe为1时,表示异常发生,开始异常处理过程,会依据异常类型设置except_type的值,同时设置EPCR、ESR的值,进入FLU1状态,需要注意的是使用指令l.mtspr写NPC寄存器也会进入FLU1状态(此时pc_we为1),因为一旦写了NPC寄存器,那么同样需要刷新流水线
- 在FLU1状态下,当icpu_ack_i、icpu_err_i、genpc_freeze其中有一个为1时会进入FLU2状态
- 在FLU2状态下,如果异常类型是自陷异常,那么会直接回到IDLE状态,表示异常处理结束,反之进入FLU3状态
- 在FLU3状态下,会在下一个时钟周期直接进入FLU4
- 在FLU4状态下,设置extend_flush、extend_flush_last为0,进入下一状态FLU5
- 在FLU5状态下,当if_stall、id_freeze都为0时,会回到IDLE状态,异常处理过程结束