P7-MIPS微体系¶
这些博客有助于理解:
- P7 课下 & 课上总结 - 北航计算机组成原理 | Test Blog = FlyingLandlord's Blog
- [BUAA-CO-Lab] P7 MIPS 微体系
- 计算机组成-支持中断的CPU Thysrael还讲了测试方案
- CO-P7 支持异常的流水线 CPU
课下¶
Day 1¶
aaa不知道要干什么…
Day 2¶
要做的事:
- 更改流水线各级使之可以产生异常
- 添加 CP0 处理异常
- 添加 Bridge 与外设交互
异常 Exception & 中断 Interrupt 发生后的行为:
- 跳到固定位置
- 要把当前指令地址写到 EPC (延迟槽写当前地址-4)
检测异常:
- 各级给出本级的异常信号
- 流水到 M 级的 CP0
- CP0 决定是否处理这个异常,是的话就跳转
新增指令:
eret- 跳回 EPC 中的地址
- 其后的指令不能执行
- 在 D 级判断,置位 pcF 为 EPC,npc 为 EPC + 4 (当D级是
eret时,马上就会把F级读出的指令改变为EPC位置的指令,下一个周期用npc直接读出下一条)
清除异常后的指令: 指令流经流水线时,可能留下影响的操作只有修改 PC ,写入流水线寄存器,写入乘除模块,写入 DM 和写入 GRF
- 由于后续需要跳转到异常处理程序,
PC无需处理 - 流水线寄存器储存的是指令中间信息,直接清空
最后异常处理程序将会使用命令 eret 跳转到 EPC 对应地址,即从异常处理程序返回
由于阻塞的存在,受害指令可能有两种情况:
- 一条平平无奇的普通指令
- 由于阻塞产生的空泡指令
Day 3¶
发生异常的指令在一条转移指令的延迟槽内,EPC 要指向转移指令
Week 2 Tue¶
评测机初尝试🥳
Week 2 Wed¶
我的评测机为我检测出了我的一个bug,先前的 HILO 逻辑:
最后的 else 必然是 cnt==1 的情况,此时进行 cnt 的自减,HI / LO 的写入是合理的。然而 P7 当 Req 有效时,我们说不能开始 md / mt 指令:
当 cnt==0 且M级 Req==1 时,本来应该不进行任何操作 (既不计算临时值,也不修改 cnt),但是上述代码会进入 else 并执行自减 cnt ,这样 cnt 将变为 5'b11111 ,以后的乘除指令将无法正常启动!甚至非乘除指令也会触发!
修改为:
搭好了评测机,好耶🥳🥰
Week 2 Thu¶
走查代码,一些疑惑:
PC: EXCAdEL why except EretD
中断:
- 全局中断使能位 SR(IE) 必须置 1,否则不会服务任何中断
- SR(EXL) 如果为 1 则禁止中断 (因为正在处理异常/中断)
- SR(IM) 为 1 时该中断才能发生
当M级的CP0决定响应异常/中断时,此时Req为1,下一个上跳沿:
- PC:
- D / E / M / W 级均为 0x4180 (清除4条指令)
- F 级为 0x4180 (进入处理程序)
- Instr:
- D / E / M / W 级均为 0
- F 级为从 0x4180 取出的
当D级正为 eret :
- F 级 PC 应该立刻变成 EPC (组合逻辑才能反应)
- D 级的 npc 设为 EPC+4
检测出错即可清除这条指令
我感觉我梦到过讨论区说 assign d_instr = d_err_ri ? 32'b0 : d_tmp_instr; 清零与否
CPU地址空间被划分为若干区域,每个区域对应一个存储器 / 设备。CPU 不能为每一个设备提供一套地址。增加系统桥。CPU侧一组接口,设备侧N组接口。由Bridge完成地址、数据转换,控制信号产生
中断发生器: 会随机的产生外部中断信号,产生的中断信号在 CPU 响应前会持续置高
中断发生器都是在测试的 tb 上实现的:
- 通过外部端口接受外部中断信号 (计时器中实现)
- 通过访问地址
0x7F20的store类指令,改变对应的微系统输出信号 (m_int_addr,m_int_byteen)
m_int_addr[31:0] : 中断发生器待写入地址。当该信号命中中断发生器响应地址,且字节使能信号有效时,视为响应外部中断
m_int_byteen : 中断发生器字节使能信号,当该信号任意一位置位时视为有效
问题¶
eret_m 的时候发生了中断?先处理谁?
Fly 和 zlr 在 CP0 写了 testResponse ,有什么作用?
Bug 记录¶
重大Bug: instr¶
有溢出的 add 指令即将进入 E 级时卡住,信号不断变化。
原因: add 即将进入 E 级时, instr_e 是 temp_instr_e ,正常转发,之后检测出溢出,此时 instr_e 置0,传入 FU,不再进行转发, rt 只读到了0,瞬间算出 exc_code_e 没有异常,由会把 instr_e 变为 temp_instr_e ,陷入循环
在 F / D / E / M 级我都用到了 temp_instr ,再由此进行组合逻辑运算,但是 SU 和 FU 用的是 instr ,应该把 temp_instr 传入 SU / FU 判断。
debug: 拖了很多信号进波形图,发现 new_rd2_e 反复跳动,由此发现症结所在
DE 的 AdEL 判断错误¶
load 类指令是可以读 Count 寄存器的值的,直接复制 BE 模块就出错了
指令是否处在延迟槽¶
eret 之后的指令的BD会被置1,如果有两个连续异常的指令,处理完第一个指令的异常后,由 eret 返回,第二个指令被当成延迟槽,会一直循环执行……修改为:
测出错误的代码:
很意外地发现了错误,一念之间,很幸运地编两个连续溢出样例!
何时不是延迟槽?普通的 pc4, eret 之后,其余都是了。
课上测试¶
四道强测+一条新指令
withdraw
题意:在CP0中新增4个寄存器: LastAddr , LastData , IMBoundaryHi , IMBoundaryLo 存放 sw 指令修改的那片内存之前的信息。 withdraw 指令到来时,就根据 Last 中的信息,“撤销” sw 的影响,恢复内存。
细节:
- reset 为1时,四个寄存器置0
IMBoundaryHi,IMBoundaryLo支持mfc0mtc0LastAddr,LastData支持mfc0, 会忽略mtc0指令对其的写入- 自reset以来未出现
sw就进行withdraw,忽略该指令 - 如果
LastAddr不在 Boundary 范围内,发生AdES异常
在我的架构内,F / D / E 级就会完成所有指令的异常判定,所以能流水到 M 级的 sw 就一定是正确的。在 M 级只需要由 CP0 判断是否有新增的指令的异常
当 sw 指令到达 M 级时,BE 模块会写内存,同时,我们要通知 CP0,让它保存 sw 地址的原信息,存入 Last 寄存器。原地址和 sw 要写的地址一样,原数据需要从内存中读出来——需要更改 DE。DE 遇到 sw 指令时,要从内存读出字,并交给 CP0。CP0还要增设 start 寄存器,置1表示出现了 sw 指令。
此时不会发生任何异常。
当 withdraw 指令到达 M 级时,CP0要判断是否重写内存,给出 OK 信号,判断是否出现异常 AdES :
- 先前没有
sw, 不重写,无异常 LastAddr超范围,发生异常,此时 Req 会变为 1,BE 不会重写- 除此之外,重写
BE 接收 OK 信号,如果是 withdraw 指令并且 OK,则写内存,否则不写。
BE 写的数据要在 LastData 和 GPR[rt] 中选。
And after that…AC…Congratulations!
最后一次的助教提答,问了宏观PC,测试思路,eret的行为,得到了巧克力🍫,Awesome CO! (◡ᴗ◡✿)