P7流水线CPU设计文档
设计整体概述
- 预计实现指令集:
add、sub、and、or、nor、xor、slt、sltu、
ori、Lui、addi、andi、
lw、lh、lhu、lb、lbu、sw、sh、sb、
mult、multu、div、divu、mfhi、mflo、mthi、mtlo、
beq、bne、nop、j、jr、jal、jalr、
mfc0、mtc0、eret、syscall
实际MIPS微系统支持指令与课下要求实现一致。
- R型指令:add、sub、and、or、nor、xor、slt、sltu、
opcode |
rs |
rt |
rd |
shamt |
funct |
6 |
5 |
5 |
5 |
5 |
6 |
-
I型指令:lui、ori、andi、addi、lw、lh、lhu、lb、lbu、sw、sh、sb、beq、bne、
| opcode | rs | rt | offset |
| :----: | :—: | :—: | :----: |
| 6 | 5 | 5 | 16 |
-
J型指令:j、jal
| opcode | instr_index |
| :----: | :---------: |
| 6 | 26 |
-
乘除器操作指令:mult、multu、div、divu、mfhi、mflo、mthi、mtlo
-
CP0指令:mfc0、mtc0
其中,nop指令为空指令,不执行任何操作,add、sub不考虑溢出,故实际执行addu和subu
-
实现功能:实现MIPS微系统,内含五级流水线CPU、系统桥以及计时器,能够识别外部中断和内部异常并执行异常处理程序。CPU通过F、D、E、M、W五个阶段实现指令的执行,其中F阶段为取指,D阶段为译码,E阶段为执行,M阶段为访存,W阶段为写回,各阶段均通过流水线寄存器实现流水线操作,通过转发和阻塞机制解决冒险问题。系统桥根据地址范围将数据分发到内存的不同部分,计时器通过状态机实现计时功能。(课程组已给出)
-
顶层设计图:
整体架构符合课程组要求
-
CPU封装为单周期,CP0置于M级,IM与DM实现外置,通过系统桥连接到外部输入输出端口。
-
命名格式:模块命名采用所在级_模块名
方式,模块内部信号名采取来源_功能
方式命名,顶层信号采用所在级_模块名_信号名
方式命名。
数据通路模块设计
为实现MIPS微系统功能,需要将CPU封装,满足中断需求,设计较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
| module mips( input wire clk, input wire reset, input wire interrupt, output wire [31:0] macroscopic_pc,
output wire [31:0] i_inst_addr, input wire [31:0] i_inst_rdata,
output wire [31:0] m_data_addr, input wire [31:0] m_data_rdata, output wire [31:0] m_data_wdata, output wire [3 :0] m_data_byteen,
output wire [31:0] m_int_addr, output wire [3 :0] m_int_byteen,
output wire [31:0] m_inst_addr,
output wire w_grf_we, output wire [4 :0] w_grf_addr, output wire [31:0] w_grf_wdata,
output wire [31:0] w_inst_addr );
endmodule;
|
CPU设计
加入了异常中断处理模块CP0,同时添加了宏观PC端口,用于对封装为单周期的CPU进行判断。
F级:取指Fetch
F_IFU 指令单元
- 定义:模块内部包含
PC
程序计数器、IM
指令存储器以在外部实现,因此只负责根据NPC
传入的PC值,依照控制信号选择PC值。
- 端口定义
信号名 |
方向 |
位宽 |
说明 |
Next_PC |
I |
32 |
NPC 模块输出的下一条指令地址 |
EPC |
I |
32 |
异常返回地址 |
D_isEret |
I |
1 |
异常返回指令信号 |
D_NPCSel |
I |
3 |
下一条指令PC选择信号 |
clk |
I |
1 |
时钟信号 |
reset |
I |
1 |
复位信号 |
Req |
I |
1 |
异常中断信号 |
stop |
I |
1 |
停机信号 |
F_PC |
O |
32 |
当前指令地址 |
F_PC8 |
O |
32 |
当前指令地址+8 |
F_BDIn |
O |
1 |
分支延迟槽信号 |
IFU_ExcCode |
O |
5 |
指令异常码 |
- 实现功能
序号 |
功能 |
描述 |
1 |
同步复位 |
当reset 信号为1且处于时钟上升沿时,PC 置0x00003000 |
2 |
停机 |
当stop 信号为1时,PC 和指令保持不变 |
3 |
取指 |
当stop 信号为0时,在clk上升沿得到的下一条指令地址PC ,传入顶层模块到i_inst_addr 读取指令 |
4 |
异常 |
当Req 信号为1时,将D_NPC生成的异常处理指令地址0x00004180赋值给PC ,同时将异常码传入顶层模块 |
5 |
异常返回 |
当D_isEret 信号为1时,将EPC 赋值给PC |
6 |
输出异常 |
当PC未字对齐或PC越界时,将异常码传入顶层模块 |
PC
初始化为0x00003000
外接IM具体实现
1 2
| assign i_inst_addr = F_PC; assign F_Instr = i_inst_rdata;
|
D级:译码decode
D_REG D级流水线寄存器
- 定义:用于在时钟上升沿将F级的指令和PC值传递到D级,并根据复位信号重置寄存器值,根据Req信号清空寄存器值,到并在停机信号为1时保持不变
- 端口定义
信号名 |
方向 |
位宽 |
说明 |
clk |
I |
1 |
时钟信号 |
reset |
I |
1 |
复位信号 |
stall |
I |
1 |
停机信号 |
Req |
I |
1 |
异常请求信号 |
F_Instr |
I |
32 |
F级的32位指令 |
F_PC |
I |
32 |
F级的PC值 |
F_PC8 |
I |
32 |
F级的PC+8值 |
F_BDIn |
I |
1 |
F级的分支延迟槽信号 |
F_ExcCode |
I |
5 |
F级的异常码 |
D_Instr |
O |
32 |
D级的32位指令 |
D_PC |
O |
32 |
D级的PC值 |
D_PC8 |
O |
32 |
D级的PC+8值 |
D_BDIn |
O |
1 |
D级的分支延迟槽信号 |
D_ExcCode |
O |
5 |
传入D级的异常码 |
- 实现功能
序号 |
功能 |
描述 |
1 |
同步复位 |
当reset 信号为1且处于时钟上升沿时,D_Instr 置0,D_PC 置PC_Reset ,D_PC8 置PC_Reset + 4 ,D_BDIn 置0,D_ExcCode 置ExcCode_Default |
2 |
异常请求 |
当Req 信号为1时,D_Instr 置0,D_PC 置Exc_Addr ,D_PC8 置Exc_Addr + 4 ,D_BDIn 置0,D_ExcCode 置ExcCode_Default |
3 |
停机 |
当stall 信号为1时,保持D_Instr 、D_PC 、D_PC8 、D_BDIn 和D_ExcCode 的值不变 |
4 |
正常传递 |
当stall 信号为0时,在时钟上升沿将F_Instr 、F_PC 、F_PC8 、F_BDIn 和F_ExcCode 的值传递到D_Instr 、D_PC 、D_PC8 、D_BDIn 和D_ExcCode |
D_NPC 指令计算单元
- 定义:根据当前PC值、指令类型和条件分支结果,计算下一条指令的PC值。
- 端口定义
信号名 |
方向 |
位宽 |
说明 |
F_PC |
I |
32 |
当前指令的PC值 |
Instr_index |
I |
26 |
跳转指令的目标地址 |
offset |
I |
16 |
分支指令的偏移量 |
rs |
I |
32 |
寄存器rs的值 |
NPCSel |
I |
3 |
下一条指令PC选择信号 |
CMPOut |
I |
1 |
比较结果信号 |
CMPLabJump |
I |
32 |
LAB跳转地址 |
EPC |
I |
32 |
异常返回地址 |
D_isEret |
I |
1 |
异常返回信号 |
Req |
I |
1 |
异常中断请求信号 |
Next_PC |
O |
32 |
计算得到的下一条指令的PC值 |
- 实现功能
序号 |
功能 |
描述 |
1 |
计算分支地址 |
根据当前PC值和偏移量计算分支指令的目标地址 |
2 |
计算跳转地址 |
根据当前PC值和指令索引计算跳转指令的目标地址 |
3 |
选择下一条指令PC |
根据NPCSel信号选择下一条指令的PC值,支持顺序执行、分支、跳转和寄存器跳转 |
4 |
异常处理 |
根据Req信号和D_isEret信号选择异常处理地址或异常返回地址 |
选择信号 |
位宽 |
数值 |
描述 |
BranchType |
3 |
3’b011 |
B型跳转,与CMPOut共同决定跳转 |
JrType |
3 |
3’b010 |
JR型跳转 |
JumpType |
3 |
3’b001 |
J型跳转 |
LABType |
3 |
3’b100 |
LAB跳转 |
D_EXT 指令扩展
- 定义:根据控制信号,对输入的16位立即数进行零扩展或符号扩展。
- 端口定义
信号名 |
方向 |
位宽 |
说明 |
offset |
I |
16 |
16位立即数 |
ExtOp |
I |
1 |
扩展选择信号,0为零扩展,1为符号扩展 |
Extend |
O |
32 |
扩展后的32位立即数 |
- 实现功能
序号 |
功能 |
描述 |
1 |
符号扩展 |
当ExtOp 信号为1时,Extend 输出符号扩展后的32位立即数 |
2 |
零扩展 |
当ExtOp 信号为0时,Extend 输出零扩展后的32位立即数 |
D_CMP 比较器
- 定义:根据控制信号,对输入的两个数进行比较,输出比较结果。
- 端口定义
信号名 |
方向 |
位宽 |
说明 |
RD1 |
I |
32 |
读寄存器1数据 |
RD2 |
I |
32 |
读寄存器2数据 |
CMPOp |
I |
3 |
比较操作选择信号 |
CMPOut |
O |
1 |
比较结果输出 |
- 实现功能
序号 |
功能 |
描述 |
1 |
相等比较 |
当CMPOp 信号为CMP_BEQ 时,若RD1 等于RD2 ,CMPOut 输出1,否则输出0 |
2 |
不等比较 |
当CMPOp 信号为CMP_BNE 时,若RD1 不等于RD2 ,CMPOut 输出1,否则输出0 |
3 |
小于等于零 |
当CMPOp 信号为CMP_BLEZ 时,若RD1 小于等于0,CMPOut 输出1,否则输出0 |
4 |
大于零 |
当CMPOp 信号为CMP_BGTZ 时,若RD1 大于0,CMPOut 输出1,否则输出0 |
GRF 寄存器堆
- 定义:根据读写信号,对寄存器堆进行读写操作。
- 端口定义
信号名 |
方向 |
位宽 |
说明 |
A1 |
I |
5 |
读寄存器1地址 |
A2 |
I |
5 |
读寄存器2地址 |
RegAddr |
I |
5 |
写寄存器地址 |
RegData |
I |
32 |
写数据 |
clk |
I |
1 |
时钟信号 |
reset |
I |
1 |
复位信号 |
RegWrite |
I |
1 |
写使能信号 |
PC |
I |
32 |
当前PC地址,用于输出 |
RD1 |
O |
32 |
读寄存器1数据 |
RD2 |
O |
32 |
读寄存器2数据 |
输出寄存器堆中寄存器写入输出到官方mips_tb.v
,具体实现如下
1 2 3 4
| assign w_grf_we = W_RegWrite; assign w_grf_addr = W_RegAddr; assign w_grf_wdata = W_RegData; assign w_inst_addr = W_PC;
|
- 实现功能
序号 |
功能 |
描述 |
1 |
复位 |
当reset为1时,将所有寄存器置为0 |
2 |
读 |
当RegWrite为0时,将A1、A2对应的寄存器数据输出到RD1、RD2 |
3 |
写 |
当RegWrite为1时,当RegAddr不为0时,在时钟上升沿将RegAddr对应的寄存器写入RegData |
A1、A2分别读取Rs、Rt寄存器,RegAddress根据RegDst选择Rt或R),RegData根据MemRead选择ALU的运算结果或I型指令的立即数
- 内部转发
当RegWrite
为1时,若D级读取的寄存器和W级写入的寄存器相同且不为0,则将RegAddr
对应的寄存器的值输出到RD1
或RD2
。
1 2 3 4
| assign RD1 = (A1 == RegAddr && RegWrite && RegAddr != 0) ? RegData : GRF_reg[A1]; assign RD2 = (A2 == RegAddr && RegWrite && RegAddr != 0) ? RegData : GRF_reg[A2];
|
E级:执行Execute
E_REG E级流水线寄存器
- 定义:存储E级流水线寄存器的数据。
- 端口定义
信号名 |
方向 |
位宽 |
说明 |
clk |
I |
1 |
时钟信号 |
reset |
I |
1 |
复位信号 |
clr |
I |
1 |
清除信号 |
Req |
I |
1 |
异常请求信号 |
D_Instr |
I |
32 |
D级指令 |
D_PC |
I |
32 |
D级PC值 |
D_PC8 |
I |
32 |
D级PC+8值 |
D_CMPOut |
I |
1 |
D级比较结果 |
D_RD1 |
I |
32 |
D级寄存器数据1 |
D_RD2 |
I |
32 |
D级寄存器数据2 |
D_Tnew |
I |
2 |
D级新指令周期 |
D_Extend |
I |
32 |
D级扩展数据 |
D_RegAddr |
I |
5 |
D级寄存器地址 |
D_ExcCode_Fixed |
I |
5 |
D级更新异常码 |
D_BDIn |
I |
1 |
D级分支延迟槽信号 |
E_Instr |
O |
32 |
E级指令 |
E_PC |
O |
32 |
E级PC值 |
E_PC8 |
O |
32 |
E级PC+8值 |
E_A1 |
O |
5 |
E级寄存器地址1 |
E_A2 |
O |
5 |
E级寄存器地址2 |
E_CMPOut |
O |
1 |
E级比较结果 |
E_RD1 |
O |
32 |
E级寄存器数据1 |
E_RD2 |
O |
32 |
E级寄存器数据2 |
E_Tnew |
O |
2 |
E级新指令周期 |
E_Extend |
O |
32 |
E级扩展数据 |
E_RegAddr |
O |
5 |
E级寄存器地址 |
E_ExcCode |
O |
5 |
E级异常码 |
E_BDIn |
O |
1 |
E级分支延迟槽信号 |
- 实现功能
序号 |
功能 |
描述 |
1 |
同步复位 |
当reset 信号为1或clr 信号为1或Req 信号为1时,所有输出信号清空,对于暂停操作而言相当于插入nop指令,但clr 需保持D_PC的值不变,Req 需跳转到异常处理程序 |
2 |
正常传递 |
在时钟上升沿,将D级输入信号传递到E级输出信号 |
ALU 算术逻辑单元
- 定义:根据控制信号,对输入的两个数进行算术或逻辑运算,并处理溢出异常。
- 端口定义
信号名 |
方向 |
位宽 |
说明 |
A |
I |
32 |
输入操作数A |
B |
I |
32 |
输入操作数B |
ALU_Op |
I |
4 |
ALU操作码 |
E_isSt |
I |
1 |
存储操作标志 |
E_isLd |
I |
1 |
加载操作标志 |
ALU_OvExc |
I |
1 |
溢出异常标志 |
ALU_Res |
O |
32 |
运算结果 |
ALU_ExcCode |
O |
5 |
异常码 |
- 实现功能
根据ALU_Op的值,对输入的A和B进行相应的算术或逻辑运算,并将结果输出到ALU_Res,同时处理溢出异常。
序号 |
功能 |
描述 |
1 |
ADD |
A + B |
2 |
SUB |
A - B |
3 |
AND |
A & B |
4 |
OR |
A | B |
5 |
XOR |
A ^ B |
6 |
LUI |
加载到高位 |
7 |
NOR |
~(A | B) |
8 |
SLT |
$signed (A) < $signed (B) ? 1 : 0 |
9 |
SLTU |
A < B ? 1 : 0 |
10 |
LAB |
实验指令逻辑 |
11 |
输出异常码 |
根据输入的可能异常类型,结合溢出判断,输出异常码 |
1 2 3 4
| assign ALU_ExcCode = (Overflow && E_isLd) ? `ExcCode_AdEL : (Overflow && E_isSt) ? `ExcCode_AdES : (Overflow && ALU_OvExc) ? `ExcCode_Ov : 5'b0;
|
MDU 乘除单元
- 定义:根据控制信号,对输入的两个数进行乘法或除法运算。
- 端口定义
信号名 |
方向 |
位宽 |
说明 |
A |
I |
32 |
输入操作数A |
B |
I |
32 |
输入操作数B |
MDUOp |
I |
4 |
MDU操作码 |
clk |
I |
1 |
时钟信号 |
reset |
I |
1 |
复位信号 |
Req |
I |
1 |
异常中断信号 |
start |
I |
1 |
开始状态信号 |
MDURes |
O |
32 |
运算结果 |
Hi |
O |
32 |
高位结果 |
Lo |
O |
32 |
低位结果 |
busy |
O |
1 |
忙状态信号 |
- 实现功能
当非异常中断时,根据MDUOp的值,对输入的A和B进行相应的乘法或除法运算,并将结果输出到Hi和Lo寄存器,同时输出忙信号表示运算状态。
序号 |
功能 |
描述 |
1 |
MULT |
有符号乘法,结果存储在Hi和Lo中 |
2 |
MULTU |
无符号乘法,结果存储在Hi和Lo中 |
3 |
DIV |
有符号除法,商存储在Lo中,余数存储在Hi中 |
4 |
DIVU |
无符号除法,商存储在Lo中,余数存储在Hi中 |
5 |
MTHI |
将A的值存储到Hi中 |
6 |
MTLO |
将A的值存储到Lo中 |
7 |
MFHI |
将Hi的值输出到MDURes |
8 |
MFLO |
将Lo的值输出到MDURes |
设置临时变量hitemp和loemp,分别存储Hi和Lo的值,根据计数逻辑在完成乘除运算后,将hitemp和loemp的值赋值给Hi和Lo。
M级:存储Memory
M_REG M级流水线寄存器
- 定义:存储M级流水线寄存器的数据,并在时钟上升沿将E级输入信号传递到M级输出信号。
- 端口定义
信号名 |
方向 |
位宽 |
说明 |
clk |
I |
1 |
时钟信号 |
reset |
I |
1 |
复位信号 |
Req |
I |
1 |
异常请求信号 |
E_Instr |
I |
32 |
E级指令 |
E_PC |
I |
32 |
E级PC值 |
E_PC8 |
I |
32 |
E级PC+8值 |
E_RegAddr |
I |
5 |
E级寄存器写地址 |
E_CMPOut |
I |
1 |
E级比较结果 |
E_RD1 |
I |
32 |
E级寄存器数据1 |
E_RD2 |
I |
32 |
E级寄存器数据2 |
E_ALU_Res |
I |
32 |
E级ALU运算结果 |
E_MDURes |
I |
32 |
E级MDU运算结果 |
E_Tnew |
I |
2 |
E级新指令周期 |
E_ALU_Fwd_MUXOut |
I |
32 |
E级ALU转发MUX输出 |
E_BDIn |
I |
1 |
E级分支延迟槽信号 |
E_ExcCode_Fixed |
I |
5 |
E级更新异常码 |
M_Instr |
O |
32 |
M级指令 |
M_PC |
O |
32 |
M级PC值 |
M_PC8 |
O |
32 |
M级PC+8值 |
M_RegAddr |
O |
5 |
M级寄存器写地址 |
M_CMPOut |
O |
1 |
M级比较结果 |
M_RD1 |
O |
32 |
M级寄存器数据1 |
M_RD2 |
O |
32 |
M级寄存器数据2 |
M_ALU_Res |
O |
32 |
M级ALU运算结果 |
M_MDURes |
O |
32 |
M级MDU运算结果 |
M_Tnew |
O |
2 |
M级新指令周期 |
M_ALU_Fwd_MUXOut |
O |
32 |
M级ALU转发MUX输出 |
M_BDIn |
O |
1 |
M级分支延迟槽信号 |
M_ExcCode |
O |
5 |
M级异常码 |
- 实现功能
序号 |
功能 |
描述 |
1 |
同步复位 |
当reset 或Req 信号为1时,所有输出信号清空,对于暂停操作而言相当于插入nop指令,注意对Req需跳转到异常处理程序,且延迟槽BDIn需要保持 |
2 |
正常传递 |
在时钟上升沿,将E级输入信号传递到M级输出信号 |
M_DM 数据存储器
由于内存外置,故原DM模块实现转移到BE和DE中
M_BE 缓存写回模块
- 定义:根据读写信号和地址,对数据进行字节、半字或字的写操作,并处理地址对齐和异常情况。
信号名 |
方向 |
位宽 |
说明 |
LSOp |
I |
3 |
读写操作选择信号 |
BEAddr |
I |
32 |
缓存写回地址 |
BE_WD_In |
I |
32 |
缓存写回输入数据 |
MemWrite |
I |
1 |
内存写使能信号 |
BE_isSt |
I |
1 |
存储操作标志 |
byteen |
O |
4 |
字节使能信号 |
BE_WD_Out |
O |
32 |
缓存写回输出数据 |
BE_ExcCode |
O |
5 |
缓存写回异常码 |
- 实现功能
序号 |
功能 |
描述 |
1 |
字写操作 |
当LSOp 为Ls_Word 时,byteen 为4'b1111 ,BE_WD_Out 为BE_WD_In |
2 |
半字写操作 |
当LSOp 为Ls_Half 时,根据BEAddr 的值选择高16位或低16位进行写操作 |
3 |
字节写操作 |
当LSOp 为Ls_Byte 时,根据BEAddr 的值选择相应的字节进行写操作 |
4 |
默认操作 |
当MemWrite 为0或LSOp 为其他值时,byteen 为4'b0000 ,BE_WD_Out 为32'h00000000 |
5 |
异常处理 |
根据AlignExc, AddrExc, TimerExc, CountExc情况,设置BE_ExcCode 的值 |
M_DE 缓存读模块
-
定义:根据读信号和地址,对数据进行字节、半字或字的读操作,并处理地址对齐和异常情况。
-
端口定义
信号名 |
方向 |
位宽 |
说明 |
LSOp |
I |
3 |
读操作选择信号 |
DEAddr |
I |
32 |
缓存读出地址 |
DE_m_data_rdata |
I |
32 |
缓存读出输入数据 |
DE_isLd |
I |
1 |
读操作标志 |
DE_RD |
O |
32 |
缓存读出输出数据 |
DE_ExcCode |
O |
5 |
缓存读出异常码 |
- 实现功能
序号 |
操作 |
功能 |
1 |
字读操作 |
当LSOp 为Ls_Word 时,DE_RD 为DE_m_data_rdata |
2 |
半字读操作 |
当LSOp 为Ls_Half 时,根据DEAddr 的值选择高16位或低16位进行读操作 |
3 |
字节读操作 |
当LSOp 为Ls_Byte 时,根据DEAddr 的值选择相应的字节进行读操作 |
4 |
半字无符号读操作 |
当LSOp 为Ls_Half_Unsigned 时,根据DEAddr 的值选择高16位或低16位进行无符号读操作 |
5 |
字节无符号读操作 |
当LSOp 为Ls_Byte_Unsigned 时,根据DEAddr 的值选择相应的字节进行无符号读操作 |
6 |
默认操作 |
当LSOp 为其他值时,DE_RD 为32'h00000000 |
7 |
地址对齐异常检测 |
当LSOp 为Ls_Word 且未对齐时,或LSOp 为Ls_Half 且未对齐时,AlignExc 为1 |
8 |
地址范围异常检测 |
当DEAddr 不在合法范围内时,AddrExc 为1 |
9 |
定时器异常检测 |
当LSOp 为Ls_Half 或Ls_Byte 且DEAddr 在定时器地址范围内时,TimerExc 为1 |
10 |
异常码输出 |
当DE_isLd 为1且存在异常时,DE_ExcCode 为ExcCode_AdEL ,否则为5'b0 |
CP0 模块
-
定义:CP0 模块用于处理异常和中断信号,管理状态寄存器(SR)、异常原因寄存器(CAUSE)和异常程序计数器(EPC)。
-
端口定义
信号名 |
方向 |
位宽 |
说明 |
clk |
I |
1 |
时钟信号 |
reset |
I |
1 |
复位信号 |
enable |
I |
1 |
使能信号 |
CP0Addr |
I |
5 |
CP0 寄存器地址 |
CP0_WD_In |
I |
32 |
CP0 写入数据 |
VPC |
I |
32 |
虚拟程序计数器 |
BDIn |
I |
1 |
分支延迟槽信号 |
ExcCodeIn |
I |
5 |
输入异常码 |
HWInt |
I |
6 |
硬件中断信号 |
EXLClr |
I |
1 |
EXL 清除信号 |
CP0Out |
O |
32 |
CP0 输出数据 |
Req |
O |
1 |
异常请求信号 |
EPCOut |
O |
32 |
异常程序计数器输出 |
- 实现功能
序号 |
操作 |
功能 |
1 |
复位操作 |
当reset 信号为1时,SR 、CAUSE 、EPC 和CP0Out 清零 |
2 |
硬件中断处理 |
当Req 信号为1时,EXL 置1,ExcCode 根据HWInt 和IM 设置,EPC 根据BDIn 设置,BD 置BDIn |
3 |
EXL 清除操作 |
当EXLClr 信号为1时,EXL 置0 |
4 |
CP0 寄存器写操作 |
当enable 信号为1时,根据CP0Addr 写入SR 、CAUSE 或EPC |
5 |
CP0 寄存器读操作 |
根据CP0Addr 读取SR 、CAUSE 或EPC 到CP0Out |
6 |
异常请求检测 |
当EXL 为0且ExcCodeIn 不为0或HWInt 和IM 不为0且IE 不为0时,Req 置1 |
7 |
EPC 输出 |
将EPC 输出到EPCOut |
W级:写回WriteBack
W_REG W级流水线寄存器
- 定义:存储W级流水线寄存器的数据。
- 端口定义
信号名 |
方向 |
位宽 |
说明 |
clk |
I |
1 |
时钟信号 |
reset |
I |
1 |
复位信号 |
Req |
I |
1 |
异常请求信号 |
M_Instr |
I |
32 |
M级指令 |
M_PC |
I |
32 |
M级PC值 |
M_PC8 |
I |
32 |
M级PC+8值 |
M_A1 |
I |
5 |
M级寄存器地址1 |
M_A2 |
I |
5 |
M级寄存器地址2 |
M_RegAddr |
I |
5 |
M级寄存器写地址 |
M_RD1 |
I |
32 |
M级寄存器数据1 |
M_RD2 |
I |
32 |
M级寄存器数据2 |
M_ALU_Res |
I |
32 |
M级ALU运算结果 |
M_MDURes |
I |
32 |
M级ALU运算结果 |
M_DM_RD |
I |
32 |
M级数据存储器读数据 |
RegWrite_M |
I |
1 |
M级寄存器写使能信号 |
RegDataSel_M |
I |
3 |
M级寄存器数据选择信号 |
M_Tnew |
I |
2 |
M级新指令周期 |
W_Instr |
O |
32 |
W级指令 |
W_PC |
O |
32 |
W级PC值 |
W_PC8 |
O |
32 |
W级PC+8值 |
W_A1 |
O |
5 |
W级寄存器地址1 |
W_A2 |
O |
5 |
W级寄存器地址2 |
W_RegAddr |
O |
5 |
W级寄存器写地址 |
W_RD1 |
O |
32 |
W级寄存器数据1 |
W_RD2 |
O |
32 |
W级寄存器数据2 |
W_ALU_Res |
O |
32 |
W级ALU运算结果 |
W_DM_RD |
O |
32 |
W级数据存储器读数据 |
RegWrite_W |
O |
1 |
W级寄存器写使能信号 |
RegDataSel_W |
O |
3 |
W级寄存器数据选择信号 |
W_Tnew |
O |
2 |
W级新指令周期 |
- 实现功能
序号 |
功能 |
描述 |
1 |
同步复位 |
当reset 或者Req 信号为1时,所有输出信号清空,对于暂停操作而言相当于插入nop指令 |
2 |
正常传递 |
在时钟上升沿,将M级输入信号传递到W级输出信号 |
MCU 控制单元
-
定义
MCU 控制单元用于生成控制信号,控制其他模块的行为。
-
端口定义
信号名 |
方向 |
位宽 |
说明 |
Instr |
I |
32 |
指令 |
CMPOut |
I |
1 |
比较结果信号 |
RegDstSel |
O |
3 |
寄存器目标选择信号 |
ALUBSrcSel |
O |
1 |
ALU_B源选择信号 |
RegDataSel |
O |
3 |
寄存器数据选择信号 |
RegWrite |
O |
1 |
寄存器写使能信号 |
MemWrite |
O |
1 |
内存写使能信号 |
NPCSel |
O |
3 |
下一条指令PC选择信号 |
ExtOp_D |
O |
1 |
扩展操作选择信号 |
CMPOp |
O |
3 |
比较操作选择信号 |
ALU_Op |
O |
4 |
ALU操作码 |
MDUOp |
O |
4 |
MDU操作码 |
MDU_Start |
O |
1 |
MDU启动信号 |
LSOp |
O |
3 |
存储操作码 |
Tuse_rs |
O |
2 |
rs使用需要周期数 |
Tuse_rt |
O |
2 |
rt使用需要周期数 |
Tnew_D |
O |
2 |
产生数据所用周期数 |
CU_ExcCode |
O |
5 |
控制单元异常码 |
isEret |
O |
1 |
异常返回信号 |
BE_isSt |
O |
1 |
存储操作标志 |
DE_isLd |
O |
1 |
加载操作标志 |
ALU_AdExc |
O |
1 |
地址异常信号 |
ALU_OvExc |
O |
1 |
溢出异常信号 |
Cp0Enable |
O |
1 |
CP0使能信号 |
isLAB |
O |
1 |
LAB信号 |
- 实现功能
序号 |
功能 |
描述 |
1 |
指令译码 |
根据操作码和功能码生成对应的控制信号 |
2 |
控制信号生成 |
生成寄存器目标选择信号、ALU B源选择信号、寄存器数据选择信号等 |
3 |
冒险时间生成 |
生成rs和rt的使用时间以及新指令周期 |
-
信号生成说明
将所有指令译码后根据类型分为cal_r
、cal_i
、ld
、st
、md
、mt
、mf
、b_type
、j_type
,对于共性控制信号直接按照类型生成
-
多路选择器说明
- RegDstSel:寄存器目标选择信号,用于选择写入寄存器的目标寄存器。
类型 |
方向 |
位宽 |
说明 |
RaType |
I |
32 |
写回ra寄存器 |
RdType |
I |
32 |
写回rd寄存器 |
RtType |
I |
32 |
写回rt寄存器 |
- ALUBSel:ALU B源选择信号,用于选择ALU B输入端的源。
类型 |
方向 |
位宽 |
说明 |
E_ALU_Fwd_MUXOut |
I |
32 |
寄存器值 |
EXT |
I |
32 |
扩展后的立即数 |
- RegDataSel:寄存器数据选择信号,用于选择写入寄存器的数据源。
类型 |
方向 |
位宽 |
说明 |
FromMDU |
I |
32 |
来自MDU |
FromPC |
I |
32 |
来自PC8 |
FromALU |
I |
32 |
来自ALU |
FromDM |
I |
32 |
来自DM |
HCU 冒险控制单元
- 定义:冒险控制单元用于检测流水线中的数据冒险,并生成相应的转发和停顿信号。
- 端口定义
信号名 |
方向 |
位宽 |
说明 |
Tuse_rs |
I |
2 |
rs使用需要周期数 |
Tuse_rt |
I |
2 |
rt使用需要周期数 |
Tnew_E |
I |
2 |
E级产生数据需要周期 |
Tnew_M |
I |
2 |
M级产生数据需要周期 |
Tnew_W |
I |
2 |
W级产生数据需要周期 |
D_A1 |
I |
5 |
D级寄存器地址1 |
D_A2 |
I |
5 |
D级寄存器地址2 |
D_MDUOp |
I |
4 |
D级MDU操作控制信号 |
D_isEret |
I |
1 |
D级异常返回信号 |
E_A1 |
I |
5 |
E级寄存器地址1 |
E_A2 |
I |
5 |
E级寄存器地址2 |
E_A3 |
I |
5 |
E级寄存器写地址 |
E_rd |
I |
5 |
E级CP0寄存器地址 |
E_Busy |
I |
1 |
E级MDU忙状态 |
E_Start |
I |
1 |
E级MDU开始状态 |
E_Cp0Enable |
I |
1 |
E级CP0使能信号 |
M_A2 |
I |
5 |
M级寄存器地址2 |
M_A3 |
I |
5 |
M级寄存器写地址 |
M_rd |
I |
5 |
M级CP0寄存器地址 |
M_Cp0Enable |
I |
1 |
M级CP0使能信号 |
W_A3 |
I |
5 |
W级寄存器写地址 |
RegWrite_E |
I |
1 |
E级寄存器写使能信号 |
RegWrite_M |
I |
1 |
M级寄存器写使能信号 |
RegWrite_W |
I |
1 |
W级寄存器写使能信号 |
E_isLAB |
I |
1 |
E级LAB信号 |
M_isLAB |
I |
1 |
M级LAB信号 |
CMP_RD1_Fwd_D |
O |
3 |
比较器RD1转发信号 |
CMP_RD2_Fwd_D |
O |
3 |
比较器RD2转发信号 |
ALU_A_Fwd_E |
O |
3 |
ALU A转发信号 |
ALU_B_Fwd_E |
O |
3 |
ALU B转发信号 |
DM_WD_Fwd_M |
O |
3 |
数据存储器写数据转发信号 |
stall |
O |
1 |
停顿信号 |
- 实现功能
序号 |
功能 |
描述 |
1 |
停顿检测 |
根据寄存器地址和使用周期数,检测是否需要停顿 |
2 |
转发信号生成 |
生成比较器、ALU和数据存储器的转发信号 |
3 |
停顿信号生成 |
生成流水线停顿信号 |
暂停Stall
使用A-T法判断,当address相同时,若D级的Tuse_rs或Tuse_rt比E、M、W级的Tnew小,则说明在D级需要使用寄存器中数据时,E、M、W级相应的寄存器数据还未准备好,需要暂停流水线。
1 2 3 4 5 6 7 8 9
| E_Stall_Rs = (E_A3 == D_A1) && (D_A1 != 5'b0) && (Tnew_E > Tuse_rs) && RegWrite_E; E_Stall_Rt = (E_A3 == D_A2) && (D_A2 != 5'b0) && (Tnew_E > Tuse_rt) && RegWrite_E; M_Stall_Rs = (M_A3 == D_A1) && (D_A1 != 5'b0) && (Tnew_M > Tuse_rs) && RegWrite_M; M_Stall_Rt = (M_A3 == D_A2) && (D_A2 != 5'b0) && (Tnew_M > Tuse_rt) && RegWrite_M; E_stall_md = D_MDUOp != `MDU_NONE && (E_Busy || E_Start); Stall_Eret = D_isEret && (E_Cp0Enable && E_rd == `CP0_EPC || M_Cp0Enable && M_rd == `CP0_EPC);
stall = E_Stall_Rs || E_Stall_Rt || M_Stall_Rs || M_Stall_Rt || E_stall_md || Stall_Eret;
|
转发Foward
执行暴力转发,使用A-T法判断,当address相同且D级的Tuse_rs或Tuse_rt大于E、M级相应的Tnew时,说明D级需要使用的数据在E、M级中,需要转发。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| assign CMP_RD1_Fwd_D = (M_A3 == D_A1 && RegWrite_M && D_A1) ? `FWD_MEM : (W_A3 == D_A1 && RegWrite_W && D_A1) ? `FWD_WB : `FWD_GRF;
assign CMP_RD2_Fwd_D = (M_A3 == D_A2 && RegWrite_M && D_A2) ? `FWD_MEM : (W_A3 == D_A2 && RegWrite_W && D_A2) ? `FWD_WB : `FWD_GRF;
assign ALU_A_Fwd_E = (M_A3 == E_A1 && RegWrite_M && E_A1) ? `FWD_MEM : (W_A3 == E_A1 && RegWrite_W && E_A1) ? `FWD_WB : `FWD_GRF;
assign ALU_B_Fwd_E = (M_A3 == E_A2 && RegWrite_M && E_A2) ? `FWD_MEM : (W_A3 == E_A2 && RegWrite_W && E_A2) ? `FWD_WB : `FWD_GRF;
assign DM_WD_Fwd_M = (W_A3 == M_A2 && RegWrite_W && M_A2) ? `FWD_WB : `FWD_GRF;
|
Bridge 系统桥
-
定义:Bridge 模块用于在 CPU 和外设之间进行数据传输和中断管理。它根据地址范围选择目标外设,并处理读写操作和中断信号。
-
端口定义
信号名 |
方向 |
位宽 |
说明 |
interrupt |
I |
1 |
外部中断信号 |
TC0_IRQ |
I |
1 |
定时器0中断请求信号 |
TC0_Dout |
I |
32 |
定时器0数据输出 |
TC1_IRQ |
I |
1 |
定时器1中断请求信号 |
TC1_Dout |
I |
32 |
定时器1数据输出 |
m_data_rdata |
I |
32 |
内存数据读取 |
CPU_m_data_addr |
I |
32 |
CPU 数据地址 |
CPU_m_data_wdata |
I |
32 |
CPU 数据写入 |
CPU_m_data_byteen |
I |
4 |
CPU 字节使能信号 |
CPU_m_inst_addr |
I |
32 |
CPU 指令地址 |
HWInt |
O |
6 |
硬件中断信号 |
TC0_Addr |
O |
32 |
定时器0地址 |
TC0_WE |
O |
1 |
定时器0写使能 |
TC0_Din |
O |
32 |
定时器0数据输入 |
TC1_Addr |
O |
32 |
定时器1地址 |
TC1_WE |
O |
1 |
定时器1写使能 |
TC1_Din |
O |
32 |
定时器1数据输入 |
CPU_m_data_rdata |
O |
32 |
CPU 数据读取 |
m_data_addr |
O |
32 |
内存数据地址 |
m_data_wdata |
O |
32 |
内存数据写入 |
m_data_byteen |
O |
4 |
内存字节使能信号 |
m_inst_addr |
O |
32 |
内存指令地址 |
- 功能实现
序号 |
功能 |
描述 |
1 |
地址选择 |
根据 CPU 数据地址选择目标外设(定时器0、定时器1或内存) |
2 |
数据传输 |
根据选择的目标外设进行数据读写操作 |
3 |
中断管理 |
处理外部中断信号和定时器中断请求信号,并生成硬件中断信号 |
4 |
写使能控制 |
根据字节使能信号和目标外设生成定时器写使能信号 |
TC 计时器模块
课程组官方提供
- 空闲(IDLE):如果ctrl寄存器的第0位为1,则进入加载状态。
- 加载(LOAD):将预置值加载到计数器中,然后进入计数状态。
- 计数(CNT):如果ctrl寄存器的第0位为1,则计数器减1,直到计数器为0,然后进入中断状态。
- 中断(INT):设置中断信号_IRQ为1,然后根据ctrl寄存器的第2、3位决定是否重置计数器或清除中断信号,最后回到空闲状态。
当前状态 |
条件 |
下一个状态 |
动作 |
IDLE |
ctrl[0] 为1 |
LOAD |
_IRQ 置0 |
LOAD |
- |
CNT |
count 置preset |
CNT |
ctrl[0] 为1且count >1 |
CNT |
count 减1 |
CNT |
ctrl[0] 为1且count <=1 |
INT |
count 置0,_IRQ 置1 |
CNT |
ctrl[0] 为0 |
IDLE |
- |
INT |
ctrl[2:1] 为2’b00 |
IDLE |
ctrl[0] 置0 |
INT |
其他 |
IDLE |
_IRQ 置0 |
测试方案
沿用P6的随机测试程序,用于检测指令执行与转发阻塞的正确性,对于异常处理,则手动构造所有可能的异常情况,依次并验证其正确性。对于中断处理,使用官方tb,检测在对应的target_PC是否执行中断。
P7思考题
- 请查阅相关资料,说明鼠标和键盘的输入信号是如何被 CPU 知晓的?
键盘和鼠标本质上都是输入设备,通过设备自身程序释放中断信号,指引处理器处理相应的输入信息。
- 请思考为什么我们的 CPU 处理中断异常必须是已经指定好的地址?如果你的 CPU 支持用户自定义入口地址,即处理中断异常的程序由用户提供,其还能提供我们所希望的功能吗?如果可以,请说明这样可能会出现什么问题?否则举例说明。(假设用户提供的中断处理程序合法)
是可以的,我们可以通过改变出现异常中断时的跳转地址,但是这样可能会出现用户自定义程序与系统程序冲突的问题,对于不同的用户需要重新修改处理器,这样会使得处理器变得复杂,并且无法通用。
- 为何与外设通信需要 Bridge?
由于外设的种类是多样的,而CPU在处理指令时只需要处理数据,因此需要通过Bridge来进行数据的中转,从而在增加外设时无需修改CPU的设计,使得CPU可以通用。
- 请阅读官方提供的定时器源代码,阐述两种中断模式的异同,并分别针对每一种模式绘制状态移图。
模式0:
[*] --> IDLE
IDLE --> IDLE:reset
IDLE --> LOAD:ctrl[0] = 1
LOAD --> CNT
CNT --> CNT:ctrl[0] && count > 1
CNT --> INT:ctrl[0] && count <= 1
INT --> IDLE:ctrl[0] = 0 && Int = 1
CNT --> IDLE:!ctrl[0]
模式1:
[*] --> IDLE
IDLE --> IDLE:reset
IDLE --> LOAD:ctrl[0] = 1
LOAD --> CNT
CNT --> CNT:ctrl[0] && count > 1
CNT --> INT:ctrl[0] && count <= 1
INT --> IDLE:Int = 0
CNT --> IDLE:ctrl[0] == 0
- 倘若中断信号流入的时候,在检测宏观 PC 的一级如果是一条空泡(你的 CPU 该级所有信息均为空)指令,此时会发生什么问题?在此例基础上请思考:在 P7 中,清空流水线产生的空泡指令应该保留原指令的哪些信息?
如果此时PC为空,那么在流水线中会一直空泡,导致宏观PC也将表现为0,因此需要保留原指令的PC信息。
- 为什么 jalr 指令为什么不能写成 jalr $31, $31?
因为jalr指令的跳转地址是PC+4+立即数,而$31是PC,因此如果将jalr指令写成jalr $31, $31,那么如果jalr的延迟槽内出现异常中断,那么中断异常结束后,会再次执行 jalr 指令,由于无法撤销已经执行的jalr指令,导致寄存器值已经修改,这就会跳转到不正确的 PC 地址。