CO | P7_流水线CPU中断异常响应
博客及仓库
苯人P7学的非常的水,对很多东西理解的都不到位,害怕误导大家,所以索性就放一些别的同学的博客,供大家参考
每年的教程和要求都会有不同程度的改动,大家还是要以自己的教程要求为准
P7 课下 & 课上总结 - 北航计算机组成原理 | Test Blog = FlyingLandlord’s Blog
[BUAA-CO-Lab] P7 MIPS 微体系 | ROIFE BLOG
BUAA-CO/p7 at master · roife/BUAA-CO (github.com)
BUAA-CO-2021/P7 at master · flyinglandlord/BUAA-CO-2021 (github.com)
下面几个是学长的设计文档,可能在测试上能提供一些帮助
【BUAA-CO】p7流水线CPU-Ultra | volca’s blog (volcaxiao.top)
P7 设计文档 | TARDIS (steve-strange.github.io)
讨论区一些很好的贴子
部分P7课下易错点
王暄雨 2218**** (学生)
以下只是我个人总结的一些易错点,如有错误欢迎大家批评指正。
1.当 *CP0* 中的 *Req* 信号处于高位时:
- 清空 D, E, M, W 级流水线寄存器。
- 将 PC 的值强制修改到异常处理程序的入口:
32'h00004180。 - 保证异常发生时不会对 DM 等外部模块产生写信号(即
m_data_byteen == 4'h0)。 - 正处于 E 级的指令不应修改乘除模块。
2.优先级
- 中断的优先级要高于异常,在写CP0时要注意
- reset > Req > eret > stall,在写pc时要注意
3.特殊处理PC和Delayslot:
当处于阻塞时外部产生了中断信号,我们记录的 EPC 将会是 0。这会导致错误的产生。
所以要在阻塞时让 pc 和 BD 这两个信号依旧正常流水。
4.延迟槽异常的特殊处理
对于异常情况只要考虑异常指令是不是延迟槽指令,如果是延迟槽指令,那么存的是异常指令的 PC - 4,如果不是,那么就存异常指令的 PC。
P7第二次上机题目分享
蒋孝淳 2237**** (学生)
分享一下本学期第二次P7上机的题目及笔者的实现思路,供大家交流与参考~
本次上机共5题,完成4题即可通过。其中前4题是对课下的强测,第5题为指令的修改。
前四题
分别针对所实现cpu的功能、异常、中断、冒险进行强测,如果课下的实现没有问题,应该能够顺利通过。
第五题
题意转译
条件判断访问外设im和dm的地址是否在合法区间中,其中im(即取值pc)合法区间为,dm(即读写合法区间)为。 题面很长,主要有两层意思。
- 在
cp0模块中新增7号寄存器和21号寄存器,应使它们可以通过mfc0和mtc0指令正常访问。
其中7号寄存器在片选信号sel(即这两条指令的后三位,见英文指令集)为0时,保存数值为imLoBoundary,sel为1时,保存数值为dmLoBoundary。
21号寄存器在片选信号sel为0时,保存数值为imHiBoundary,sel为1时,保存数值为dmHiBoundary。

- 实现
cp0中SR寄存器的16、17位。 当且仅当SR[16]==1时,对访问im的地址合法性进行检查。若不合法,应给出AdEL异常。
当且仅当SR[17]==1时,对访问dm的地址合法性进行检查。若为load型指令且不合法,给出AdEL异常;若为Store型指令且不合法,给出AdES异常。
应当保证其他指令不会出现课下没有实现过的异常。
需要注意的地方: 以一个例子说明,lw指令计算出来的地址为0x0000_0000,那么你需要保证0x0000_0003也要在合法区间中;而且只需要考虑dm,无需考虑时钟或中断发生器的访问地址。
做法分享
针对1,只需要在cp0中增加相关寄存器,并修改mfc0和mtc0的相关实现即可。
针对2,只需要将cp0中的check信号和边界信息给出,并增加相关异常的判断即可。
此外,cp0指令可能带来修改check信号/修改边界信息带来的“后续指令对im访问不合法,但缺少阻塞”问题,将新增的检查与cp0放在流水线同一级(笔者的设计中,位于M级),此时能够保证指令到达该处时,前面的指令对cp0中寄存器的更新已经完成(如有),保证运行的正确性。
以上就是P7第二次上机的主要题目分析,欢迎有兴趣的同学继续研究讨论~
预祝各位同学上机顺利,取得理想的成绩!
p7-课下总结
童欣 2237**** (学生)
以下是我在写p7课下时看教程与博客产生的一些问题,记录一下它们的回答~
eret不支持延迟槽,若正常操作会带来什么影响?应如何操作以避免这种影响?
如图所示,
设计1:当eret流到D级时检测eret,并把F级NPC改成EPC.
NPC = (D_eret) ? EPC : else; |
导致的问题:直接类比beq的跳转,eret的下一条指令A不可避免地会从IM中被取出来,而它本来不应该流进来的.故这样的设计会导致PC一个周期的错误.但是,之前beq如此设计是因为只有流到D级利用D级CMP模块比较两个数,才能得到跳转结果(即NPC);而eret的下一条指令的PC一定是EPC.
所以改成设计2:当eret流到D级时检测eret,若为eret则F级PC直接输出为EPC,否则仍为原来的值.NPC从而为PC + 4
F_PC = (D_eret) ? EPC : F_PC_tmp; |
直观的图像表示:(来自答疑时一位同学画的图)

syscall的exccode在哪一级判断?
无所谓.在我的设计中,我在D级判断.
不过其实在教程中异常处理流的实现->异常码这一节中,异常码exccode越小的异常,优先级越高.
在未出现异常时,exccode应该用哪个数字表示?
教程中,中断的exccode为0,但是中断是从外部输入的,与内部异常无关;而exccode是由内部异常被判断出来的.故把未出现异常的exccode设置为0即可. (不要设置一些奇怪的数,比如5'd31之类的()一是因为上机可能增加新的异常exccode,二是因为这样每一级判断exccode的时候把异常exccode当成"非零值",而非"非31值"处理,从而出错)
F级中NPC模块对rst/req/stall/eret的处理?
- 优先级rst>req>stall>D_eret
- 为什么req>D_eret? 其实这种情况不会发生.req的同时D_eret,意味着异常处理程序出现了异常,而这是得到保证不会出现的.
- 为什么stall>D_eret? 当D_eret和stall同时发生,即D级是eret,E/M级存在
mtc0指令产生stall信号,这意味着EPC还没有准备好,还需要再stall一会儿.那么必须要优先处理stall,再处理D_eret. - 以上讨论也可以理解为,D_eret只是流水级内部的一个小指令,而req/stall都更为宏观,故应处理更为宏观的信号.
PC\bd在流水时对rst/req/stall的异常处理?
(基于CP0设置在M级)
-
bd在F级判断后,向后流水,直至流到CP0模块用于判断EPC
-
rst清空:PC归为32'h0000_3000,其它信号都为0req清空:PC归为32'h0000_4180,其它信号都为0注意以下的信号优先级!!否则中测会出现interrupt count的问题!
-
FD级:优先级:rst>req>stall
rst:全部流水信号按照rst清空;req:部分流水信号按照req清空stall:所有流水信号保持不变.
-
DE级:优先级rst>req>stall
rst:全部流水信号按照rst清空;req:全部流水信号按照req清空stall:部分流水信号清空,PC,bd继续向下一级正常流水
-
EM级:优先级rst>req
rst:全部流水信号按照rst清空req:全部流水信号按照req清空
-
MW级:优先级rst>req
rst:全部流水信号按照rst清空req:全部流水信号按照req清空
怎么保证异常指令不会产生结果?
这里的结果指向各种寄存器里存进去了值,包括GRF,DM,hi,lo 一种比较粗暴的实现方法:假如这条指令有异常,那么就把它视作nop向后流水,具体来说是这样:
E_instr = (E_exccode != 0) ? 0 : E_instr_tmp;//以E级要从EM流水线寄存器向M级流水的E_instr为例子 |
通过这种方法,大多数异常指令都将不能发挥作用.但是当CP0放在M级时,有可能产生一个小bug,见下一个问题
CP0设在M级时,怎么保证DM不会存进异常指令让它存的值?
有两种方法.
-
法一:将存数/取数异常(
adel/ades)置于E级判断.即在ALU运算出ALU_result之后,判断其是否是这两种异常,得到E_exccode. 由上一个问题的例子可知,因为E_exccode不为0,我们将从E级准备向M级流水的E_instr清零了,从而M级得不到存数/取数的相关 信息, 不会对DM产生影响. -
法二:将存数/取数异常(
adel/ades)置于M级初判断,得到M_exccode接入CP0.若CP0显示产生异常(即req为1),那么将DM的写使能byteen置0,从而无法写入(这里易错,上一个问题提到的"视为nop"处理方法在这种架构中会因为太晚判断(即到了CP0所在级才判断)exccode而失效,必须要改DM写使能)具体来说:
assign m_data_byteen = (req) ? 4'b0 :
m_data_byteen_tmp;
乘除槽MDU怎么对中断信号进行反应?
根据教程P7提交要求->中断异常约束中下图:

我们只需要在mult/div/multu/divu等乘除法相关指令的启动信号判断中加入!req,在mthi/mtlo等写hi/lo相关指令的写使能信号中加入!req即可.
p7课上题目记录
张奕彤 2237****(学生)
前四题
- 前四题均是对课下正确性的检验,没有任何需要新增的指令。
第五题
- 第五题需要新增两条指令(ll与sc),感觉难度并不低。
- 首先需要做的是在CP0内增加一个一位的寄存器LLbit。
- LL类似LW,SC类似SW,可以访问或存储计时器的外设和DM,异常类型分别与LW,SW相同。
LL指令(Load Linked Word)
- 编码:

- 格式:
ll rt, offset(base) - 描述:
- GPR[rt] ← memory[GPR[base] + offset];
- LLbit置为高电平。
- 操作:
vAddr ← sign_extend(offset) + GPR[base] |
- 其他:当reset为高电平或者eret指令执行时,LLbit需要置为低电平。
SC指令(Store Conditional Word)
- 编码:

- 格式:
sc rt, offset(base) - 描述:
- 如果LLbit为高电平,则将GPR[rt]存入memory[GPR[base] + offset];
- 无论LLbit是否为高电平,均需要将LLbit无符号扩展至32位存入GPR[rt]。
- 操作:
vAddr ← sign_extend(offset) + GPR[base] |
- 其他:记得不太清楚了,大概是为了保证数据合法性的约束,不过我当时做的时候并没有多么考虑这条指令的其他部分。



