一、单周期CPU介绍
单周期CPU顾名思义就是一个指令周期内只执行一条指令的CPU。
比如下面的指令
在单周期CPU中执行的过程,体现为:
在每一个周期(时钟上升沿),就执行完一条指令。
二、CPU设计
CPU是由一个指令存储器(IM,instruction Memory)和数据存储器(DM,DataMemory)还有CPU组成。其架构如下:
模块 | 功能描述 |
CPU | |
信号 | 描述 |
clock(clock) | 时钟信号 |
reset(rst) | 复位信号 |
instruction(Instruction) | 执行的指令 |
ReadMemData(ReadMemData) | 从内存中读出的数据 |
InstAddress(PC) | 指令地址 |
AluResult(MemDataAddr) | 从内存中读数据的地址 |
MemWrite(MemWrite) | 是否写内存 |
WriteMemData(WriteMemData) | 写入内存的数据 |
WriteMemDataLength(WriteMemDataLength) | 表示写入内存的数据的位置,长度 |
模块 | 功能描述 |
InstructionMemory | 指令存储器 |
信号 | 描述 |
PC[8:2] | 指令地址 |
Instruction | 指令 |
模块 | 功能描述 |
DataMemory | 数据存储器 |
信号 | 描述 |
clock | 时钟信号 |
MemDataAddr[8:2] | 写入/读出内存数据的地址 |
MemWrite | 是否写入内存 |
WriteMemData | 写入内存的数据 |
WriteMemDataLength | 表示写入内存的数据的位置,长度 |
ReadMemData | 从内存中读出的数据 |
首先,CPU向指令存储器传入指令地址,也就是PC中存储的值,指令存储器就将对应的指令地址传给CPU。CPU开始执行指令 ,有的指令涉及到和数据存储器,比如LW,LB,LBU,LH,LHU指令需要读取数据存储器中的数据,SW,SH,SB指令需要将运算结果写入数据存储器中。就涉及到了CPU将数据和数据要存放的地址传给DM,也涉及到CPU将要读取的地址传给DM,DM将对应数据传回CPU。这就是CPU运作的大致流程,在单周期或者流水线CPU中基本没有什么变化。
而指令如何执行,CPU内部如何实现就是单周期和流水线CPU的差别所在。
CPU内部架构如图所示(看不清没办法了),大致描述一下过程,PC将指令地址传给IM,IM将指令送到控制器,控制器根据指令解析出指令的信号 (指令是否需要写入内存,指令是否需要写入寄存器,指令是否要进行符号扩展,指令的运算符是什么,下一个指令地址是什么等等) 之后,将信号传到各自的模块运算。
模块 | 功能描述 |
PC | 负责PC更新,仅在 clk 或者 rst 为 posedge 时更新 PC。 |
信号 | 描述 |
NextPC(NextInstAddress) | 从NextPC模块送来的下一条指令地址 |
PC(InstAddress) | 指令地址 |
clock(clock) | 时钟信号 |
reset(reset) | 复位信号 |
模块 | 功能描述 |
NextPC | 计算下一条指令地址 |
信号 | 描述 |
NextPC(NextInstAddress) | 计算出的PC值 |
PC(InstAddress) | 当前正在处理的指令的PC值 |
NextPCSignal(NextPCSignal) | 控制信号,选择哪一个作为下一个PC值 |
JumpAddr(JumpAddr) | J/JAL指令中的25位立即数作为跳转地址 |
JumpReg(RegSourceData) | JR/JARL指令跳转地址 |
模块 | 功能描述 |
CU | 控制器模块,产生控制信号 |
信号 | 描述 |
Opcode | 指令中的opcode字段 |
Func | 指令中的function字段 |
RegTarget | 指令中的rt字段,(用于区分bltz,bltzal...) |
AluZeroSignal | 用于判断分支指令是否跳转 |
RegWrite | 是否写寄存器 |
MemWrite | 是否写内存 |
SignExt | 是否扩展立即数 |
RegimmSignal | 表示是否是(BLTZ,BLTZAL...) |
AluOpcode | ALU的操作符 |
NextPCSignal | 下一条指令地址选择信号 |
AluSrcASignal | 操作数A选择信号 |
AluSrcBSignal | 操作数B选择信号 |
WriteRegDataSignal | 写入寄存器数据的选择信号 |
WriteRegSignal | 写入寄存器地址的选择信号 |
MemRead | 是否读内存 |
模块 | 功能描述 |
Register | 通用寄存器组, |
信号 | 描述 |
clock(clock) | 时钟信号 |
reset(reset) | 复位信号 |
RegWrite(RegWrite) | 是否写寄存器 |
ReadRegAddr1(RegSource) | 读rs的地址 |
ReadRegAddr2(RegTarget) | 读rt的地址 |
WriteRegAddr(WriteRegAddr) | 写寄存器的地址 |
WriteRegData(WriteRegData) | 写寄存器的数据 |
ReadRegData1(RegSourceData) | 读出rs的数据 |
ReadRegData2(WriteMemData) | 读出rt的数据 |
模块 | 功能描述 |
SRC_A | 选择ALU的操作数A |
信号 | 描述 |
DataIn0(RegSourceData) | 读出rs的数据 |
DataIn1(Shamt) | 移位指令的位移量 |
Signal(AluSrcASignal) | ALU的操作数A选择信号 |
DataOut(AluOperandA) | 操作数A |
模块 | 功能描述 |
SRC_B | 选择ALU的操作数B |
信号 | 描述 |
DataIn0(WriteMemData) | 读出rt的数据 |
DataIn1(Imm32) | 扩展后的32位立即数 |
Signal(AluSrcBSignal) | ALU的操作数B选择信号 |
DataOut(AluOperandB) | 操作数B |
模块 | 功能描述 |
ALU | 运算器模块 |
信号 | 描述 |
AluOperandA(AluOperandA) | 操作数A |
AluOperandB(AluOperandB) | 操作数B |
AluOpcode(AluOpcode) | ALU的操作符 |
MemRead(MemRead) | 是否读内存 |
MemWrite(MemWrite) | 是否写内存 |
AluResult(AluResult) | 运算结果 |
AluZeroSignal(AluZeroSignal) | 判断运算结果与0的关系 |
AluOverflowSignal(AluOverflowSignal) | 判断运算结果是否溢出 |
WriteMemDataLength(WriteMemDataLength) | 表示写入内存的数据的位置,长度 |
ReadMemExtSignal(ReadMemExtSignal) | 表示从内存读出的数据如何拓展 |
RegimmSignal(RegimmSignal) | 表示操作数B是否是0,针对(BLTZ等指令) |
模块 | 功能描述 |
WR_REG_ADDR | 选择写寄存器的地址 |
信号 | 描述 |
DataIn0(RegDst) | 写入rd寄存器 |
DataIn1(RegTarget) | 写入rt寄存器 |
.DataIn2(5'b11111) | 31号寄存器 |
Signal(WriteRegSignal) | 选择信号 |
DataOut(WriteRegAddr) | 写寄存器的地址 |
模块 | 功能描述 |
WR_REG_DATA | 选择写寄存器的数据 |
信号 | 描述 |
DataIn0(AluResult) | 运算器运算结果 |
DataIn1(MemDataExt) | 从内存读出并扩展后的数据 |
DataIn2(InstAddress + 4) | 当前指令的下一条指令地址,(JALR,JAL,BLTZAL...) |
Signal(WriteRegDataSignal) | 选择信号 |
DataOut(WriteRegData) | 写寄存器的数据 |
模块 | 功能描述 |
SignExtension | 扩展立即数 |
信号 | 描述 |
Imm16 | 指令中的16位立即数 |
SignExt | 扩展信号,表示零扩展还是符号扩展 |
Imm32 | 扩展后的32位立即数 |
模块 | 功能描述 |
MemDataExtension | 扩展从内存中读出的数据 |
信号 | 描述 |
ReadMemData | 读出的数据 |
ReadMemExtSignal | 扩展信号 |
MemDataExt | 扩展后的数据 |
这里带大家模拟执行一条指令,便于大家理解。
就比如第一条指令
首先,PC将00400000传给IM,IM读取到指令,2402ff9d(就是addiu $2,$0,-99的二进制码,表示将0号寄存器中的值加上-99,存储到2号寄存器中)传给CPU的控制器, 控制器解析指令,得到这条指令需要写入寄存器,需要对立即数进行负号扩展,运算器运算符是00001(定义为加法),下一个指令地址(NextPC)是顺序执行,在当前指令地址上加4。第一个运算数是寄存器R0(AluSrc1Signal是0表示读寄存器),第二个运算数是立即数(AluSrc2Signal是1),写入寄存器的数据不需要扩展,不需要读内存。这些信号传到各自模块中后,ALUSRC1会根据指令中的对应字段确认是几号寄存器,去寄存器中读取值,ALUSRC2会从立即数扩展器中得到扩展后的立即数,ALU得到ALUSRC1和ALUSRC2的两个运算符,得到AluCode是加法,计算之后,将结果传给寄存器,寄存器写入对应寄存器。这条指令就执行完成了。
详细资源:
chris-william0829/bit-mini-mips-single-cycle-cpu (github.com)