P7 设计文档

5 级流水线 CPU 设计文档 + 中断支持 ## 支持指令

R, add, sub, And, Or, Xor, slt, sltu

addi, andi, xori, ori, lui

lb, lh, lw, sb, sh, sw, lbu, lhu

mult, multu, div, divu, mfhi, mflo, mthi, mtlo

beq, bne, j, jal, jr, bltzal

nop, eret, mtc0, mfc0, syscall

流程模块设计

image-20230311001413097

CP0

  • 处理来自 CPU 的内部异常以及来自中断发生器与 timer 的外部中断,产生异常控制信号给 CPU
  • 放置在 M 级,接收 CPU 在 M 级的 mtc0,mfc0,eret 指令
  • 其中包含三个寄存器 SR、Cause、EPC,SR 为中断异常使能控制,Cause 为异常中断情况,EPC 为异常处理结束后需要返回的 PC
  • 具体 SR、Cause 的特定位如下宏定义所示,后续控制逻辑由其产生
1
2
3
4
5
6
`define IM SR[15:10]        //Interrupt Mask 由 mtc0 修改,屏蔽中断
`define EXL SR[1] //Exception Level 表明进入中断异常,禁止所有中断和异常
`define IE SR[0] //Interrupt Enable 全局中断使能(不影响异常)
`define BD Cause[31] //Branch Delay EPC 是否指向前一条(延迟槽)指令
`define IP Cause[15:10] //Interrupt Pending 表明 6 个外部中断有无,由计时器和外部中断修改
`define ExcCode Cause[6:2] //ExcCode 异常编码,记录当前发生的是什么异常。
  • 通过 EXL 和 ExcCodeIn 判断有无异常产生,通过 EXL 和 IE,以及每一位有没有既有中断使能,又有中断信号判断有无中断产生。

  • 注意产生异常或中断时指令在延迟槽,返回 PC 应为到上一条跳转指令的 PC,需要从 D 级一直流水 BDIn 信号,tmp_EPC = Req?(BDIn?VPC-4:VPC):EPC

  • 其中 VPC 为 M 级 PC,即在外界观察到的宏观 PC

  • 写寄存器只可写 SR 以及 EPC,判断写入地址是否是 12 或 14 并且有写使能

  • 读寄存器可以直接读,根据地址 12 13 14 读三个寄存器

  • 端口

Port name Direction Type Description
clk input
reset input
en input 写使能信号 (mtc0)
CP0Add input [4:0] 读写寄存器的编号
CP0In input [31:0] CP0 写入数据
CP0Out output [31:0] CP0 读出数据
VPC input [31:0] 受害 PC
BDIn input 是否是延迟槽指令
EPCOUt output [31:0] EPC 的值
EXLClr input 用来复位 EXL(M 级指令是 eret,即退出异常)
ExcCodeIn input [4:0] 记录异常类型
HWInt input [5:0] 输入 6 个设备中断信号
Req output 进入处理程序请求(有异常或中断)

系统桥

  • 对 CPU 向外设写入的数据进行分流,对外设向 CPU 写入的数据进行选择。

image-20230311001425849

  • CPU 从外设读:根据地址用 MUX 筛选;CPU 向外设写:写地址、数据直接全部发送,写使能用 byteen 以及写地址决定决定
  • 修改层级结构,在最高层通过系统桥将 CPU 与外置 DM,TC0,TC1, 以及中断生成器相连
  • 注意传给 DM 的按位读写使能要再读写其他外设时置为 0
  • 端口:
Port name Direction Type Description
CPUAddr input [31:0] CPU 读写地址
CPUWD input [31:0] CPU 往外设写数据
CPUbyteen input [3:0] 按位读写使能
TC0Write output 写 TC0
TC1Write output 写 TC1
DEV_Addr output [31:0] 往外设写入的地址
DEV_WD output [31:0] 往外设写入的数据
temp_m_data_byteen output [3:0] 传给 DM 的按位读写使能
DMRD input [31:0] 三个外设写入 CPU 的数据
TC0RD input [31:0]
TC1RD input [31:0]
CPURD output [31:0] 最后决定写入 CPU 的数据

TC

  • 包含三个 32 位寄存器,ctrl, preset, count
  • ctrl[3]表示中断屏蔽(1 允许中断),[2:1]为模式选择,[0]为计数器使能
  • 四个状态的状态机,在 INT 状态下,如果中断没有屏蔽,则向外发送中断信号
    • idle 状态下,如果计数器使能为 1 则转至 load 状态
    • load 状态下,加载初始值之后转至 cnt 状态
    • cnt 状态下,如计数器使能为 1 则开始倒计数,cnt==0 之后产生一周期终端信号,状态变为 interrupt;如果计数器使能为 0,则回到 idle
    • interrupt 状态下,如果在模式 0,计数到 0 时计数器使能变 0;如果在模式 1,计数为 0 时中断变 0。之后回到 idle,等待计数器使能变 1 往复
  • 端口
Port name Direction Type Description
clk input
reset input
Addr input [31:2]
WE input
Din input [31:0]
Dout output [31:0]
IRQ output

内部异常设计

  • 所有异常级别低于中断级别,并均低于 Reset

  • 每一级的异常流水需要遵循距离 M 级远的优先级更高,即在上一级有异常时,按上一级往后传;没有异常时,再判断当前阶段有无异常。

  • F 级有 F_Exc_AdEL,取指令异常,即取地址低位没有对齐或者超出地址存储区域。注意在有 eret 信号时,直接跳转到中断处理程序,不产生异常。

  • D 级 D_Exc_RI,即未知指令与 D_Exc_syscall,即 syscall 指令,从 CU 增加两个控制信号即可。

  • E 级有 E_Exc_AriOv,即计算指令溢出。同时还可产生 E_Exc_DMOv,即地址指令溢出,但需要注意该异常需要在 M 级才真正出现,E 级只是提前计算,访存指令还未执行,需要跳过 E 级异常流水,直接传给 M 级再加入异常流水判断。

  • M 级有 M_Exc_AdES 与 M_Exc_AdEL,即写入地址错误与读出地址错误。注意除了地址不对齐、超出范围之外,还有 M 级的地址运算溢出,以及不可用 lb,lh 读写 timer 中三个寄存器和不可写 timer 中 count 寄存器的要求

  • 最后传至 CP0 的即 M 级的 ExcCode

CU 模块设计

  • 相较 P4,省去 RegWrite 信号,直接译出当前指令需要写入的地址,如不需写入,默认写至 0,在写入 GRF 时直接略去
  • 直接译出当前指令 rs, rt, rd, shamt, imm16, imm26 以及所有控制信号供每个阶段选取使用,还需译出 Tuse_rs/rt 以及 E_Tnew 与 M_Tnew,各级输出对应信号至 Conflict 模块
  • 将指令分类,分为:cal_r,cal_i,md,mt,mf,load,save,branch,branch_ucl,branch_cl,shift,jreg,jadd,jlink(ori 被归为 cal_i)
  • 增加四个指令,增加 GRF 写入数据来源、写入地址的选择
  • 增加 ALUDM、ALUAri 输出端口,表示当前使用 ALU 计算的指令是地址访存指令还是计算指令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   assign cal_r=(add||sub||And||Or||Xor||slt||sltu);
assign cal_i=(addi||andi||xori||ori||lui);

assign md =(mult||multu||div||divu);
assign mf =(mfhi||mflo);
assign mt =(mthi||mtlo);

assign load=(lw||lh||lhu||lb||lbu);
assign save=(sw||sh||sb);

assign branch=(beq||bne||branch_ucl||branch_cl);
assign branch_ucl=bltzal;
assign branch_cl=0;

assign jreg = jr;
assign jadd = (j||jal);
assign jlink = jal;

assign shift=sll;
  • 控制信号新增:MDU, MDUStart, MDUSelect, MFSelect, ByteSelect, DESelect
  • 控制信号调整:GRF_WA, GRF_WDSrc, ALUSelect, EXTSelect(cal_i 各个指令行为不同,注意对照指令集),BranchSelect
Port name Direction Type Description
Ins input [31:0] 当级指令
branchTrue input 分支控制信号
控制信号
GRF_WA output [4:0] 写入的地址
GRF_WDSrc output [2:0] 写入数据选择
EXTSelect output EXT 位拓展类型选择
ALUSrc output ALU_B 的数据源选择
ALUSelect output [3:0] ALU 运算类型选择
MDU output 乘除运算 + 读写 HI LO 信号(需要阻塞)
MDUStart output 乘除运算开始信号
MDUSelect output [2:0] 乘除运算 + 写 HI LO 功能选择
MFSelect output [1:0] 读 HI LO 功能选择
MemWrite output 内存写入控制
BranchSelect output [3:0] branch 判断类型选择
NPCSelect output [2:0] NPC 类型选择
ByteSelect output [1:0] 访存数据类型选择
DESelect output [2:0] 读取内存后结果拓展类型
指令译码
opcode output [5:0]
funct output [5:0]
rs output [4:0]
rt output [4:0]
rd output [4:0]
shamt output [4:0]
imm16 output [15:0]
imm26 output [25:0]
T 计算
Tuse_rs output [1:0]
Tuse_rt output [1:0]
E_Tnew output [1:0]
M_Tnew output [1:0]

T 计算表格

  • 注意新增的乘除指令的 AT
Ins Tuse_rs Tuse_rt E_Tnew M_Tnew
cal_r 1 1 1
cal_i 1 1
md 1 1
mt 1
mf 1
load 2 2 1
save 1 2
branch 0 0
jreg 0

Conflit 模块设计:AT 控制阻塞,直接转发

阻塞

  • D 级判断将要使用的寄存器数据是否能得到转发更新,即后续写入相同寄存器的 Tnew 是否有大于 Tuse 的,如果有则需要阻塞,以在后续能得到转发更新。特判 0 号寄存器不需要阻塞,能够直接获得数据 0
  • 需要得到 D 级指令 rs, rt 的 Tuse,以及后续 E, M 级指令的 Tnew,在各级 CU 中计算,发送至冲突单元(W 级 Tnew 全是 0 不需要考虑,都可以内部转发解决)
  • 阻塞时需要暂停更新 PC 以及 F 级读出的指令,并且清空 D 级当前指令的译码输出,以替换为 nop 空泡
  • P6 新增乘除 Stall,在乘除运算即将开始或正在进行时如遇到乘除指令需要 Stall
  • P7 新增 eret 的 Stall,eret 与 mtc0 的写后读冲突,需要单独判断阻塞,判断方法为当 D 级为 eret 即将读 CP0 的 EPC 时,EM 级如果有 mtc0 即将写入 CP0 的 EPC,即 rd 为 14 时,阻塞。

image-20230311001016596

转发

  • 阻塞后,所有指令在需要读寄存器数据的时候都能够获得后续计算完毕的数据,每级转发出已算出的数据,发送给之前各级即可。

  • 需要读寄存器:D 级 GRF,Branch 计算需要 rs, rt 数据;E 级 ALU 需要 rs,rt 数据;M 级 DM 写入数据口需要 rt 数据

  • 需要写寄存器:E 级可转发出 D 级算的 PC+8;M 级可转发出 D 级算的 PC+8 和 E 级算的的 ALU_Y;W 级可转发出 D 级算的 PC+8,E 级算的的 ALU_Y 和 M 级读出的 DM 数据。根据当前指令 CU 译码得到的 GRF_WDSrc 进行选择。此外还有 W 级寄存器写入,可直接内部转发至 D 级读出

image-20230311001109169

image-20230311001116883

image-20230311001122673

image-20230311001129478

  • 在主模块中,获取各级需要读的寄存器编号(D_rs,D_rt,E_rs,E_rt,M_rt),寄存器原读数(D_rs_data,D_rt_data,E_rs_data,E_rt_data,M_rt_data),写入的寄存器编号(E_GRF_WA,M_GRF_WA,W_GRF_WA)和数据(E_GRF_WD,M_GRF_WD,W_GRF_WD)
  • 比较读的编号和写的编号是否有相等的,如有相等的则代表有数据已经更新需要转发,转发优先级为更新次序,最后一次更新优先转发,即优先转发距离需要数据的阶段近的数据,特判如果需要读 0 号寄存器的数据,直接转发 0
  • 转发的数据(D_rs_fw,D_rt_fw,E_rs_fw,E_rt_fw,M_rt_fw)发送至各级需要的部分运算,并传递给下一级

image-20230311001150971

五级模块设计

image-20230311001621204

  • 每个阶段之间以寄存器隔开,寄存器设计在每个模块输出处,使用 reg 类型

  • 每个阶段之间需要流水传递 Ins,PC,传给各级 CU 以译码出当前阶段的 rs,rt 以及需要写入的地址和写入数据的选择

  • 部分阶段前后间需要传递需要使用的 NPC, EXTout, ALU_Y, DM_RD

  • P7 新增:各级传出 ExcCode 并流水传递以及 DS(指令是否在延迟槽中);CU 需在 D 和 M 级多译出 rd,为对 CP0 的读写提供阻塞条件与地址

P6 更新乘除槽与储存器外置以及按字节访存

  • 删去 F_IFU 与 M_DM,添加 M_DE 与 E_MDU
  • 乘除槽有两个寄存器,其中数据需要在 EMW 级流水,以便进行转发,并且需要添加转发信号控制
  • 外置储存器需要修改数据通路,前寄存器发送写入数据,后寄存器接收读出数据

P7 宏观 PC

  • 在外界的视角,仅需知道当前周期的情况,外界通过给出中断与 CPU 沟通,中断处理器位于 M 级,所以 M 级表现在外,宏观 PC 为 M 级 PC

  • 为了保证做出单周期的表现,需要在出现异常中断时,所有流水寄存器统一做出跳转到 4180 中断处理程序的形态,并停止流水线中所有正在执行的指令的行为

1. Fetch

  • 包含 FDReg
  • Fetch
Port name Direction Type Description
clk input
reset input
F_Flush input 清空延迟槽信号
F_Stall input 阻塞更新 PC
NPC input [31:0] D 级 NPC 计算出的 NPC 传入
F_PC output reg [31:0] <=NPC,传出至外部指令储存器
F_Ins input [31:0] 需要从外部指令储存器读入 Ins
FD 寄存器
D_Stall input 阻塞更新 FD 间寄存器
D_Flush input 清除延迟槽信号
D_PC output reg [31:0] <=F_PC
D_Ins output reg [31:0] <=F_Ins
  • F 级与指令储存的数据交换

image-20230311001208043

2. Decode

  • 包括 D_CU, EXT, NPC (Branch), DEReg
Port name Direction Type Description
clk input
reset input
D_PC input [31:0] PC 流水
D_Ins input [31:0] 指令流水
Conflict/Forward
Tuse_rs output [1:0] AT 算阻塞
Tuse_rt output [1:0]
D_rs output [4:0] D 级指令读寄存器的编号
D_rt output [4:0]
D_rs_data output [31:0] D 级指令读寄存器原数据
D_rt_data output [31:0]
D_rs_fw input [31:0] D 级转发后寄存器数据
D_rt_fw input [31:0]
EXT
imm16 [15:0] EXT 输入
EXTSelect EXT 功能选择
D_EXT_out [31:0] EXT 输出
NPC
NPCSelect [2:0] 下一指令地址选择
D_branchTrue 是否分支信号,进入流水
F_PC input [31:0] 算 NPC 用
NPC output [31:0] 传给 F 级 IFU
DEReg
E_Flush input 阻塞清空 DE 寄存器
E_PC output reg [31:0] <=D_PC
E_Ins output reg [31:0] <=D_Ins
E_rs_data output reg [31:0] <=D_rs_fw
E_rt_data output reg [31:0] <=D_rt_fw
E_EXT_out output reg [31:0] <=D_EXT_out
E_branchTrue output reg [31:0] <=D_branchTrue

3. Execute

  • 包括 E_CU, E_ALU, E_MDU, EMReg
  • 需在此处多向 Conflict 传递 MDU 指令以及乘除运行信息,并向流水中传递 HI, LO 以便 mf 指令 W 级读取
Port name Direction Type Description
clk input
reset input
E_PC input [31:0] PC 流水
E_Ins input [31:0] 指令流水
Conflict/Forward
E_branchTrue input 是否分支信号
E_Tnew output [1:0] AT 算阻塞
E_rs output [4:0] E 级指令读寄存器的编号
E_rt output [4:0]
E_rs_data output [31:0] E 级指令读寄存器原数据
E_rt_data output [31:0]
E_GRF_WA output [4:0] E 级指令写寄存器的编号
E_rs_fw input [31:0] E 级接收转发后寄存器数据
E_rt_fw input [31:0]
GRF_WDSrc [2:0] E 级指令写寄存器的数据选择
E_GRF_WD output [31:0] E 级指令写寄存器的数据
ALU
E_EXT_out input [31:0]
ALUSrc ALU_B 数据源选择
ALUSelect [3:0] ALU 功能选择
E_ALU_A [31:0] =E_rs_fw:ALU_A 口数据
E_ALU_B [31:0] =E_rt_fw/E_EXT_out:ALU_B 口数据
MDU
MDU output MDU 指令
MDUSelect [2:0] CU 给 MDU 的功能选择
MDUStart output MDU 运算开始
MDUBusy output MDU 运算进行(发给 Conflict 判断阻塞)
E_HI [31:0] 待转发的 E 级 MDU 的 HI 结果
E_LO [31:0] 待转发的 E 级 MDU 的 LO 结果
EMReg
M_PC output reg [31:0] <=E_PC
M_Ins output reg [31:0] <=E_Ins
M_ALU_Y output reg [31:0] <=E_ALU_Y
M_rt_data output reg [31:0] <=E_rt_fw
M_branchTrue output reg <=E_branchTrue
M_HI output reg [31:0] <=E_HI
M_LO output reg [31:0] <=E_LO
  • #####E_ALU
Port name Direction Type Description
op input [3:0]
A input [31:0]
B input [31:0]
Y output [31:0]
  • #####E_MDU

    • 当指令为 mthi, mtlo,将寄存器数据写入 HI, LO 时,始终上升沿直接给 HI, LO 赋为 A
    • 当为其余四条运算指令时,设置临时计数变量 cnt,初始为 0,接受到 Start 信号时,开始设置 Busy 为 1;根据 MDU 功能选择编码,分别直接计算出 HI, LO 对应结果赋值,因为其他乘除操作已被阻塞,不会提前读取或写入;设置 cnt 为 5 或 10,每周期 -1,cnt==1 代表运算结束,持续保持 Busy 为 5/10 周期后将 cnt, Busy 归零。
Port name Direction Type Description
clk input
reset input
Start input CU 传入开始乘除运算信号
MDUSelect input [2:0] CU 传入乘除功能选择
A input [31:0]
B input [31:0]
Busy output reg 正在运算信号
HI output reg [31:0]
LO output reg [31:0]

4. Memory

  • 包括 M_CU, M_DE
  • 因储存器外置,删除 DM,加入对字节存取数据的操作,包括通过控制四位 ByteEn 各位
Port name Direction Type Description
clk input
reset input
M_PC input [31:0]
M_Ins input [31:0]
Conflict/Forward
M_branchTrue input
M_Tnew output [1:0] AT 算阻塞
M_GRF_WA output [4:0] M 级指令写寄存器编号
M_GRF_WD output [31:0] M 级指令写寄存器数据
M_rt output [4:0] M 级指令读寄存器编号
GRF_WDSrc [2:0] M 级指令写寄存器数据选择
MFSelect [1:0] 读 HI LO 功能选择
M_HI input [31:0] 待转发的 E 级 MDU 的 HI 结果
M_LO input [31:0] 待转发的 E 级 MDU 的 LO 结果
M_ALU_Y input [31:0] 待转发的 E 级 ALU 计算结果
M_BE(ByteEnable)
lowAddr [1:0] =M_ALU_Y[1:0],DM 写入地址地两位
M_rt_fw input [31:0] M 级接收转发后将写入 DM 的数据
ByteSelect [1:0] CU 访存数据类型选择
MemWrite DM 写使能
ByteEn output reg [3:0] 控制每一位是否读写的信号输出
M_DM_WD output reg [31:0]
M_DE(DataExtend)
DESelect [2:0] 字节数据拓展类型
M_DM_RDin input [31:0]
M_DM_RDout [31:0]
MWReg
W_PC output reg [31:0] <=M_PC
W_Ins output reg [31:0] <=M_Ins
W_ALU_Y output reg [31:0] <=M_ALU_Y
W_DM_RD output reg [31:0] <=M_DM_RDout
W_branchTrue output reg <=M_branchTrue
W_HI output reg [31:0] <=M_HI
W_LO output reg [31:0] <=M_LO
  • M_BE(计算字节访存使能,调整四字节写入数据)
    • 合并在 Memory 中,在写入的条件下,根据写入数据类型和写入地址低两位产生四个字节的每一位控制信号,即四位 ByteEn

    • 后续再根据 ByteEn 调整将写入内存的数据,需将待写入的字节移动到对应为 En1 的位置

  • M_DE(调整内存读出数据,截取需要的字节后拓展)
    • 注意 DESelect 种类编码,注意需要将读出字节移动至低位,高位进行拓展补齐

      Port name Direction Type Description
      lowAddr input [1:0]
      DESelect input [2:0]
      in input [31:0]
      out output [31:0]
  • M 级与内存数据交换
  • image-20230311001314893

5. Writeback

  • 包括 W_CU
Port name Direction Type Description
clk input
reset input
W_Ins input [31:0]
W_PC input [31:0]
Conflict/Forward
W_branchTrue input
W_GRF_WA output [4:0] W 级指令写寄存器编号
GRF_WDSrc [2:0] W 级指令写寄存器数据选择
MFSelect [1:0] 读 HI LO 功能选择
W_ALU_Y input [31:0] 待转发的 E 级 ALU 计算结果
W_DM_RD input [31:0] 待转发的 M 级 DM 读出数据
W_HI input [31:0] 待转发的 E 级 MDU 的 HI 结果
W_LO input [31:0] 待转发的 E 级 MDU 的 LO 结果
W_GRF_WD output [31:0] W 级指令写寄存器数据

测试

  • 非中断异常测试同 P6
  • 中断测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
.text 
lui $1,0xffff
ori $1,$1,0xfc01
mtc0 $1,$12
lui $2,0xffff
#int-grf
ori $2,$2,0x1234
#int-store
sw $2,0($0)
ori $3,$0,0xfc01
#int-mfc0
mtc0 $3,$12
#int-load
lw $4,0($0)
lw $5,0($0)
#int-stall
add $6,$5,$4
lui $7,0x7fff
lui $8,0x7fff
#int-beq
beq $7,$8,label1
#int&exc-BD
add $9,$7,$8
#int-D beq
addi $10,$0,0x0001
addi $11,$0,0x0002
beq $7,$8,label1
nop

label1:
mult $7,$8
syscall
div $7,$8
syscall
mthi $7
syscall
mtlo $8
syscall
mfhi $10
mflo $11
mult $7,$8
beq $7,$8,label2
#int-many nop
mflo $12
addi $12,$0,0x0001
addi $12,$0,0x0002

label2:
addi $13,$0,0x0001

end:
beq $0,$0,end
nop


.ktext 0x4180
main_handler:
mfc0 $26,$13
mfc0 $27,$14
ori $27,$0,0x007c
and $26,$27,$26
beq $0,$26,interrupt
nop
mfc0 $26,$14
add $26,$26,4
mtc0 $26,$14
beq $0,$0,return
nop

interrupt:
ori $27,$0,0x2137
sw $27,0x7f20($0)
beq $0,$0,return
nop

return:
eret

image-20230311001726074

  • 异常测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
.text
mtc0 $0, $12
ori $at, $0, 0xfffc

#====OV=====
lui $t0, 0x7fff
lui $t1, 0xffff
add $t2, $t0, $t1
sub $t2, $t0, $t1
sub $t2, $t1, $t0
lui $t1, 0x7fff
add $t2, $t0, $t1
ori $t1, $t1, 0xffff
addi $t2, $t1, 0xfffffff0
addi $t1, $t1, 0x0010

#=====SYSCALL=====
syscall

#=====ADEL=====
lui $t1, 0x7fff
jal label1
add $ra, $ra, $t1
label1:
jr $ra
nop
jal label2
addi $ra, $ra, 1
label2:
jr $ra
nop
ori $t0, $0, 0x7f00
ori $t2, $0, 0x7f20
sw $t0, 0($0)
lw $t0, 0($0)
lw $t0, 1($0)
lw $t0, 2($0)
lh $t0, 3($0)
lh $t0, 0($t0)
lh $t0, 2($t0)
lb $t0, 0($t0)
lb $t0, 3($t0)
loop_timer1:
lw $t1, 0($t0)
addi $t0, $t0, 4
bne $t0, $t2, loop_timer1
nop
ori $t0, $0, 0x3000
lw $t0, 0($t0)
lui $t0, 0x7fff
ori $t0, $t0, 0xffff
lw $t0, 1($t0)
lw $t0, -4($0)

#=====ADES=====
sw $0, 1($0)
sw $0, 2($0)
sh $0, 3($0)
sw $0, 4($0)
sh $0, 6($0)
sb $0, 7($0)
ori $t0, $0, 0x7f00
sh $0, 0($t0)
sh $0, 2($t0)
sb $0, 0($t0)
sb $0, 3($t0)
ori $t1, $0, 0x7f30
loop_timer2:
sw $0, 0($t0)
addi $t0, $t0, 4
bne $t0, $t1, loop_timer2
nop
ori $t0, $0, 0x3000
sw $0, 0($t0)
lui $t0, 0x7fff
ori $t0, $t0, 0xffff
sw $0, 1($t0)
sw $0, -1($0)

#=====ALTOGETHER=====
lui $t0, 0x7fff
ori $t1, $t0, 0xffff
sw $0, 0($t0)
addi $t1, $t1, 1
syscall

sw $0, 0($t0)
addi $t1, $t1, 1
nop

sw $0, 0($t0)
addi $t1, $t1, 0
syscall

sw $0, 0($0)
addi $t1, $t1, 1
syscall

lui $t0, 0x8000
addi $t1, $t1, 1
beq $t0, $t1, end
nop

end:
beq $0, $0, end
nop

.ktext 0x4180
_main_handler:
mfc0 $k0, $13
mfc0 $k0, $14
and $k0, $k0, $at
addi $k0, $k0, 4
mtc0 $k0, $14
eret

image-20230311001804942

掉的坑

  • 逻辑判断式中信号不可有高阻态否则出 x,删除赋值逻辑时需给 0,否则删除所有位置的这个信号
  • 地址异常中,合法地址包括 DM,TC0,TC1 以及 中断发生器 响应地址

image-20230311001827809

  • 注意出现异常或中断时,除了需要将所有级寄存器修改至即将跳转至 handler 的样貌之外,还需要将 M 级读写字节使能设为 0000,防止后续指令的继续进行以及 当前指令在 M 级对内存的写入
  • 注意 eret 与 mtc0 的写后读冲突,需要单独判断阻塞,判断方法为当 D 级为 eret 即将读 CP0 的 EPC 时,EM 级如果有 mtc0 即将写入 CP0 的 EPC,即 rd 为 14 时,阻塞。
  • 宏观 PC,与提供给 CP0 的 VPC 均为 M 级 PC
  • timer 中不可写 count 寄存器,并且只能用 lw,sw 进行读写

思考题

  1. 当键盘鼠标按键时,会发出一个中断信号,经过中断控制器处理传到 CPU,然后 CPU 根据不同的中断号执行不同的中断响应程序,然后进行相应的 IO 操作,如把按下的按键编码读到寄存器,执行相应功能。

  2. 为了不与正常的指令范围冲突,需要在特定地址提前放置中断处理程序并且其他指令与数据不能包含这段地址范围。用户不可自定义入口地址,因为自定义的地址上的中断处理程序可能会被其他数据覆盖。并且如果由用户提供中断异常处理程序的话,跳转的地址也是计算出来的,但是如果在计算跳转地址的时候出现了错误,异常处理就无法正常进行。

  3. CPU 外设数量可能会更多,并且是变化的,不可硬性直接相连。需要添加桥,根据读写地址或者根据外设特定信号,动态选择读写外设。

  4. idle,load,cnt 状态行为相同,只有 interrupt 状态控制功能不同

    1. idle 状态下,如果计数器使能为 1 则转至 load 状态

    2. load 状态下,加载初始值之后转至 cnt 状态

    3. cnt 状态下,如计数器使能为 1 则开始倒计数,cnt==0 之后产生一周期终端信号,状态变为 interrupt;如果计数器使能为 0,则回到 idle

    4. interrupt 状态下

      1. 如果在模式 0,计数到 0 时计数器使能变 0,持续产生中断,变为 idle 状态,直到 en 为 1,中断才清零,重新倒计时
      2. 如果在模式 1,计数为 0 时中断直接变 0,仅持续一个周期,但 en 仍为 1,变为 idle 状态后可以自动循环继续倒计时,产生周期中断脉冲
  5. 倘若中断信号流入的时候,在检测宏观 PC 的一级 CPU 该级所有信息均为空,则无法获得当前的 PC 以及当前指令是否在延迟槽中,无法获得执行完中断程序后的正确返回地址。所以清空流水线时需要保留原指令的地址以及是否处于延迟槽的信号。

  6. Register specifiers rs and rd must not be equal, because such an instruction does not have the same effect when reexecuted. The result of executing such an instruction is UNPREDICTABLE. This restriction permits an exception handler to resume execution by re-executing the branch when an exception occurs in the branch delay slot.

  7. 指令集要求。寄存器说明符 rs 和 rd 不得相等,因为此类指令在重新执行时不具有相同的效果。执行此类指令的结果是不可预测的。此限制允许异常处理程序在分支延迟槽中发生异常时通过重新执行分支来恢复执行。

结算页面

计组实验结束了,当你经历过面向对,象、操作系统、编译技术等课程的洗礼,或许你又会觉得,当年的计组是那么和蔼可亲。但请相信,没有北航人跨不过的坎”我们总要背起行囊,扬起风帆,向尽头之海进发,一往无前。

感谢你一路以来的不离不弃,坚守相伴,这一切的洗礼才刚刚开始,长路漫漫祝你前程似锦。

​ 计组课程团队 ​ @新北 5 号 ​ 2022.12.21