关于ISA架构,之前写过一些总结。这里单独将其中一个技术点拿出来,对比分析不同架构下实现的差异。这个技术点就是算术指令中的溢出检测。
ARM体系结构中,通过CPSR的状态寄存器反映当前指令的溢出状态。
而MIPS,则是通过指令触发中断的方式产生溢出信号,通知处理器,比如,MIPS有如下计算指令:
加法操作:add rd, rs, rt
指令作用为:rd<-rs+rt.将地址为rs的通用寄存器的值与地址为rt的通用寄存器的值进行加法运算,结果保存到地址为rd的通用寄存器中。但是这里有一种特殊情况是,如果加法运算溢出,那么会产生溢出异常,同时不保存结果。
加法操作:addu rd, rs, rt
指令作用为rd<-rs+rt.将地址为RS的通用寄存器的值与地址为rt的通用寄存器的值进行加法运算,结果保存到地址为rd的寄存器中,与上面ADD指令的不同之处在于ADDU指令不进行溢出检查,总是将结果保存到目的寄存器。
减法操作:sub rd, rs, rt
指令作为用为,rd<-rs-rt,将地址为RS的通用寄存器的值与地址为rt的通用寄存器的值进行减法运算,结果保存到地址为RD的通用寄存器中,但是有一种特殊情况,如果减法操作溢出,那么将产生溢出一场,同时不保存结果。
减法操作:subu rd, rs, rt
指令作为用为,rd<-rs-rt,将地址为RS的通用寄存器的值与地址为rt的通用寄存器的值进行减法运算,结果保存到地址为RD的通用寄存器中,与SUB指令不同之处在于,subu指令不进行溢出检查,总是将结果保存到目的寄存器。
比较运算指令:slt rd, rs,rt
指令作用为: rd<-(rs<rt ? 1 : 0),将地址为RS的通用寄存器的值与地址为RT的通用寄存器的值按照有符号数进行比较,如果前者小于后者,那么将RD代表的寄存器设置为1,反之为0。
比较运算指令:sltu rd, rs,rt
指令作用为: rd<-(rs<rt ? 1 : 0),将地址为RS的通用寄存器的值与地址为RT的通用寄存器的值按照无符号数进行比较,如果前者小于后者,那么将RD代表的寄存器设置为1,反之为0。
所以,可以很明显看到RISCV&MIPS和ARM的不同之处。
现在我们通过波形来分析溢出的逻辑是如何处理的
仿真指令:
其中,上途中被颜色重点标记的指令OP分别为addu,ori,add,sub,subu,对应流水线执行阶段的aluop_i操作码为0x21,0x25,0x20,0x22, 0x23.
其中的ov_sum信号的变化规律如下:
ov_sum信号的控制逻辑,在数字电路中,减法也是通过加法器实现的,所以无论ISA中的指令是ADD还是SUM,最终在ALU电路层面都是加法器实现的计算。
当add两个操作数分别为0x80000001和0x80000010时,得到的数字溢出,可以看到OV_SUM信号被拉高。
ov_sum产生逻辑:
RISCV的实现:
软件检查溢出 大部分(但不是所有)程序都忽略整数算术溢出,因此 RISC-V 依赖于软件溢出检查。检查无符号加法的溢出只需要在指令后添加一个额外的分支指令:
addu t0,t1,t2;
bltu t0, t1,overflow。
对于带符号的加法,如果已知一个操作数的符号,则溢出检查只需要在加法后添加一条分支 指令:
addi t0,t1,+ imm;
blt t0,t1,overflow
这覆盖了常见的加立即数的情况。对于一般的带符号加法,我们需要在加法指令后添加三个 附加指令,当且仅当一个操作数为负数时,结果才能小于另一个操作数,否则就是溢出。
add t0, t1, t2
slti t3, t2, 0
slt t4,t0,t1
bne t3, t4, overflow