计算机与信息学院《系统硬件综合设计》课程设计报告学生姓名:李学号: 1234567890 专业班级:计算机2017 年 07 月 01日一、实验原理及设计本次试验我主要根据上图进行理解和编程,起先参考了5个基础实验,期间又翻阅了自己动手写cpu,并且在网上查了很多资料,下面我将对该图做出我的理解和设计:1.pcf部分always @(posedge Clk)beginPCPlus4F_Reg = PCPlus4F;if (BranchM&ZeroM) PCF = PCBranchM;else PCF = PCPlus4F;InstructionF_Reg = InstructionF;if (InstructionF[31:26] == 6'b000010)beginPCF = {6'h0,InstructionF[25:0]};PCF = PCF << 2;endEndassign PCPlus4F = PCF + 4;assign ImemRdAddrF = PCF;每个时钟上升沿到来,根据上一个时钟的PCSrcM判断是否为分支指令,若是,则选择PCBranchM作为这个时钟的指令地址,否则选PCF+4作为这个指令的指令地址,另外对于J类指令,我设计了一个特定的OpCode==“000010”,即为跳转指令,因为每个指令以字节格式存储,占用,4个字节,故将后26位立即数进行位扩展后将其左移两位,效果等同于乘4,再将其赋值给PCF,这样下一跳的指令地址即为所要跳转的地址。
对于这个部分,我起先是准备将其设计成一个模块的,之后由于模块接口连接时出现了无法解决的错误:输出PCF要作为Instruction Memory的输入,又要作为自身模块下一跳的输入,导致三者关联一起变化,程序报错,后来我又想到将PCF的输出改成两个,PCFout 及PCFnext,PCFout作为Instruction Memory的输入,PCFnext作为自身模块下一跳的输入,但是程序仍无法正常运行,最后我想到了在top模块中对PCF进行处理并得以实现。
2. Instruction Memory模块initialbegin$readmemh("instruction", InstMem, 8'h0);endalways @(ImemRdAddr)beginInstruction <= InstMem[ImemRdAddr>>2];end这个模块很简单,主要是通过instructin文档来存储指令,以PCF作为地址取出指令输出至Control,SignExtend,Register三个模块。
3.Ctr模块always @(OpCode)begincase(OpCode)// R-I// addiu6'b001001:beginRegDstD = 1;ALUSrcD = 1;MemtoRegD = 0;RegWriteD = 1;MemWriteD = 0;BranchD = 0;ALUOp = 2'b10;end//addi......endcaseendalways @(ALUOp or Funct)begincasex({OpCode ,ALUOp, Funct})14'b10001100xxxxxx: ALUControlD = 5'b00010; // LW : add14'b00010001xxxxxx: ALUControlD = 5'b00110; // SW : substract beq......endcaseendendmoduleControl模块主要对来自Instruction Memory模块的指令进行分解,得到OpCode(指令高六位),Funct(指令低六位),在通过分析OpCode得到RegDstD,ALUSrcD ,MemtoRegD,RegWriteD,BranchD ,ALUOp这六个信号量,用于后面的运算,再使用OpCode ,ALUOp, Funct三者的组合对指令的运算方法进行分析,得到相应的ALUControlD输出至ALU模块。
4.Register模块initialbegin$readmemh("register", regFile, 32'h0);end// write on falling clock edgealways @(posedge Clk)beginif(RegWrite == 1'b1)regFile[RegWrAddr] <= RegWrData;endassign RegARdData = (RegARdAddr >= 0)? regFile[RegARdAddr]:0;assign RegBRdData = (RegBRdAddr >= 0)? regFile[RegBRdAddr]:0;通过信号量RegWrite来判断读写操作,RegWrite=1即为写操作,0为读操作,读写皆操作于建立的register文档中,另在top模块中有assign RegARdAddr = InstructionD[25:21];assign RegBRdAddr = InstructionD[20:16];对寄存器地址赋值,register读出的两个数可供ALU选择使用。
5.ALU模块assign Zero = (ALURes == 0)? 1:0;//ALURes 0 跳转always @(SrcA or SrcB or ALUCtr)beginOverFlow = 0;TmpForSrcB = 0;HI = 0;LO = 0;A = 0;B = 0;case(ALUCtr)5'b10011:beginTmpForSrcB = SrcB;TmpForSrcB[31] = (TmpForSrcB[31]+1)%2;ALURes = SrcA + TmpForSrcB;if ((SrcA[31] != TmpForSrcB[31]) || (SrcA[31] == TmpForSrcB[31] && ALURes[31] == SrcA[31]))beginOverFlow = 1'b0;endelseif (SrcA[31] == TmpForSrcB[31] && ALURes[31] != SrcA[31])beginOverFlow = 1'b1;end......default: ALURes = 32'h0;endcaseendALU模块进行的是运算操作,本模块通过来自Control模块的ALUCtr判断所要执行的运算,在通过Register模块读出的值或者从Instruction中得到的立即数进行运算,结果ALURes根据信号量MemToReg来判断是否写入DataMemory,这一块写在top中,另外ALU还对Zero信号量进行了赋值,Zero信号量用于对分支指令的判断。
6.Data Memory模块initialbegin$readmemh("Data", DataMem, 10'h0);endalways @(posedge Clk)beginif(DmemWrite == 1'b1)DataMem[DmemAddr>>2] <= DmemWrData;endassign DmemRdData = (DmemWrite == 1'b0)? DataMem[DmemAddr>>2]:0;endmodule本模块通过从Ctr模块得来的信号量DmemWrite选择进行读写操作,读写皆操作于所建立的Data文档,另外DmemAddr左移两位跟上述PCF左移两位异曲同工。
7.top模块这个模块相比前面的6个模块要复杂得多,也是我在实验时出现问题最多,所花时间最长的模块。
Top模块主要用于各个模块之间的数据连接,以及一些模块外的操作。
PCF 的设计我是放在这个模块的,另外像二选一数据选择器我也是放在这里的,本来是写了一个小模块来做这个工作,但是本次试验用到太多次二选一数据选择器了,为了防止数据传输紊乱,我决定在top中解决这个小操作。
由于本模块代码太长,这里就不一一阐述,仅以Ctr的例化和接口连接为例简要说明:Ctr Ctr(.OpCode(OpCode),.Funct(Funct),.RegWriteD(RegWriteForCtrD),.MemtoRegD(MemtoRegD),.MemWriteD(MemWriteD),.BranchD(BranchD),.ALUControlD(ALUControlD),.ALUSrcD(ALUSrcD),.RegDstD(RegDstD));assign OpCode = InstructionD[31 : 26];assign Funct = InstructionD[5 : 0];assign RegWrDataD = (MemtoRegW)? ReadDataW : ALUOutW;always @(posedge Clk)beginMemtoRegD_Reg = MemtoRegD;MemWriteD_Reg = MemWriteD;BranchD_Reg = BranchD;ALUControlD_Reg = ALUControlD;ALUSrcD_Reg = ALUSrcD;RegDstD_Reg = RegDstD;end输入来源OpCode来自于取指阶段Instruction的高6位,Funct来自于取指阶段Instruction的低6位,RegWriteD通过信号量MemToRegW来选择ReadDataW 或ALUOutW,输出信号量MemtoRegD,MemWriteD,BranchD,ALUControlD,ALUSrcD,RegDstD作为Reg模块的输入。
二、指令设计本次试验实现了3种34条指令,实验时原以为指令格式为固定的,查阅很多资料都没得到想要的OpCode与指令操作一一对应的关系,问了指导实验的学长才知道,OpCode 是自己设计的,后又参考《自己动手写cpu》的指令设计技巧,才总结设计出指令。
3种指令:R类型:具体操作由OpCode,Funct来控制,rs,rt为源寄存器,rd为目的寄存器,sa为移位位数。
I类型:具体操作由OpCode控制,低16位是立即数,经过位扩展作为另一个源操作数参与用算。