实验报告课程名称:计算机系统结构实验学院:计算机科学与工程学院专业:计算机科学与技术指导教师:好老师学生姓名:爱学习的小学生2014666666666实验成绩:日期:2017年5月19日电子科技大学计算机学院实验中心1/13电子科技大学实验报告一、实验项目名称:解决数据冒险二、实验室名称:主楼A2-412实验时间:2017年5月19日三、实验目的在给出的流水线代码基础上,增加内部前推数据通路、暂停流水线数据通路和关闭写使能信号的数据通路,解决普通的数据冒险和load数据冒险,通过完成本次实验,更好地理解和掌握解决数据冒险的原理,学以致用,增强编写程序的能力。四、实验原理(一)数据冒险的定义由于流水线上指令重叠执行,改变了原来串行执行的读/写操作数顺序,使得后面依赖前面指令结果的指令得不到准备好的数据,这样的现象叫做数据冒险(数据相关)。回顾数据冒险的程序例子I1:addr1,r2,r3I2:subr4,r1,r5I3:andr6,r7,r1I4:orr8,r1,r9I5:addir10,r1,100I1下面有3条指令不能从寄存器r1读出正确的数据。2/13(二)数据冒险的解决方案1、暂停流水线如上图所示,暂停流水线到最初的指令执行完毕,可以解决数据冒险,但是会涉及到两个问题,即“如何检测出数据冒险”和“如何暂停流水线”。如何检测数据冒险?a.比较器;I1指令写目的寄存器rd,I2和I3的源操作数是寄存器rs1或rs2中的数据,I2、I3的rs1或rs2与I1的目的寄存器号rd相等时才有可能发生数据冒险。b.操作码参与检测;由于指令格式中源寄存器号rs2与立即数部分重叠,而立即数是不会出现冒险的,因此,指令操作码必须要参与检测(区分是寄存器操作数还是立即数)。c.WREG信号也应参与检测(实际上,WREG也是从操作码中得出的);3/13如何暂停流水线?暂停条件判断电路STALL的输入包括OPCODE,ID_rsl,ID_rs2,EXE_rd,EXE_WREG,MEM_rd和MEM_WREG。输出为DEPEN。封锁本条指令所产生的影响的方法是把DEPEN分别和译码器的输出Decoder_WZ,Decoder_WMEM和Decoder_WREG相与,再送到ID级和EXE级之间的流水线寄存器的输入端。因为只有这些写信号才改变处理机或存储器的状态,因而我们不必去封锁其它诸如ALUOP或多路器的选择信号。封锁其后续指令的方法是禁止向IR及PC写入新的数据,即把DEPEN接到IR和PC的写使能端WIR和WPC。当这两个信号为0时,禁止向IR和PC写人数据。2、内部前推技术数据相关本质:一条指令执行时要用到上面指令的计算结果,但这个结果尚未被写入寄存器堆。而实质上,此时结果已经由ALU计算出来了,在流水线寄存器R和C中。由此,我们可以在ALU的两个数据输入端各加一个多路器,使R和C中的数据能被直接送到ALU的输入端,这就是所谓的内部前推。以下是检测数据相关的完整信号:EXE_A_DEPEN=(ID_rs1==EXE_rd)(EXE_WREG==1)(ID_rs1IsReg)EXE_B_DEPEN=(ID_rs2==EXE_rd)(EXE_WREG==1)(ID_rs2IsReg)+(ID_rd==EXE_rd)(EXE_WREG==1)(store)MEM_A_DEPEN=(ID_rs1==MEM_rd)(MEM_WREG==1)(ID_rs1IsReg)MEM_B_DEPEN=(ID_rs2==MEM_rd)(MEM_WREG==1)(ID_rs2IsReg)+(ID_rd==MEM_rd)(MEM_WREG==1)(store)ID_rs1IsReg=and+andi+or+ori+add+addi+sub+subi+load+store4/13ID_rs2IsReg=and+or+add+sub(EXE_WREG==1)表示EXE级的指令确实要更新寄存器,没被取消(ID_rs1IsReg)条件是为了确认是寄存器,而不是立即数EXE_rd表示EXE级的流水线目标寄存器rdDEPEN=A_DEPEN+B_DEPENA_DEPEN=EXE_A_DEPEN+MEM_A_DEPENB_DEPEN=EXE_B_DEPEN+MEM_B_DEPEN(三)load指令的处理——暂停与内部前推相结合ALU指令在EXE级结束后,结果就出现在流水线寄存器R中,后续指令可以通过内部前推电路来直接使用它。但有一种情况是例外:loadr1,200(r2);5/13addr3,r1,r4;load指令在EXE级结束后,还在忙着访问存储器。在MEM级结束后,结果才出现在流水线寄存器D中,见下图。这时,即使使用内部前推技术也无法消除load指令与它的下一条相关指令之间的第一个“气泡”。我们的是由硬件负责检测与load指令的相关性。为了保证操作结果的正确性,我们采用暂停流水线一个周期的方法。第二个“气泡”用内部前推技术加以消除。我们给出下面的用于实现load流水线暂停的控制信号的表达式。这个信号是在ID级产生,并且使用与暂停ALU流水线类似的方法:LOADDEPEN=EXE_A_DEPEN+EXE_B_DEPENEXE_A_DEPEN=(ID_rs1==EXE_rd)(EXE_SLD==1)(ID_rs1IsReg)EXE_B_DEPEN=(ID_rs2==EXE_rd)(EXE_SLD==1)(ID_rs2IsReg)+(ID_rd==EXE_rd)(EXE_SLD==1)(store)ID_rs1IsReg=and+andi+or+ori+add+addi+sub+subi+load+storeID_rs2IsReg=and+or+add+sub其中,EXE_SLD==1表示WB级的多路选择器选择中间寄存器D的值EXE_SLD,表示EXE级是load指令。或者也可以把译出的load指令打入流水线寄存器,使用它而不是EXE_SLD。6/13五、实验内容在给出的流水线代码基础上,增加内部前推数据通路、暂停流水线数据通路和关闭写使能信号的数据通路以解决普通的数据冒险和load数据冒险。六、实验器材(设备、元器件)ISEDesignSuite14.7集成开发环境,编程语言:VerilogHDL硬件描述语言七、实验步骤(一)创建工程DataHazardAndForwarding(二)将原工程文件导入(三)在pipelinedcpu中添加下列变量:wire[1:0]idadepend,idbdepend;//id级a、bdependwire[1:0]exeadepend,exebdepend;//exe级a、bdependwirewpc;//写pc为了解决数据冒险,需要将depend的值保存下来,所以需要两个a、bdepend值wpc用于暂停流水线,从而实现load冒险时暂停指令留出以及写寄存器和写mem(四)为了实现数据通路,需要在下列模块的参数列表上增加一些参数7/13增加后的代码如下pipepcprog_cnt(npc,clock,resetn,pc,wpc);//程序计数器PC//**********pipeirinst_reg(pc4,ins,clock,resetn,dpc4,inst,wpc);//IF级与ID级之间的寄存器,即指令寄存器IR//******pipeidid_stage(dpc4,inst,//指令译码ID级wrn,wdi,wwreg,clock,resetn,bpc,jpc,pcsource,dwreg,dm2reg,dwmem,daluc,daluimm,da,db,dimm,drn,dshift,djal,z,ern,mrn,ewreg,mwreg,idadepend,idbdepend,em2reg,wpc);pipederegde_reg(dwreg,dm2reg,dwmem,daluc,daluimm,da,db,dimm,drn,dshift,djal,dpc4,clock,resetn,ewreg,em2reg,ewmem,ealuc,ealuimm,ea,eb,eimm,ern0,eshift,ejal,epc4,idadepend,idbdepend,exeadepend,exebdepend,pcsource);//ID级与EXE级之间的寄存器pipeexeexe_stage(ealuc,ealuimm,ea,eb,eimm,eshift,ern0,epc4,//指令执行EXE级ejal,ern,ealu,z,malu,wmo,exeadepend,exebdepend);(五)在id_stage的cu中添加判断冒险的代码,并计算出a_depend和b_depend的值,cu的完整代码如下modulepipeidcu(rsrtequ,func,op,wreg,m2reg,wmem,aluc,regrt,aluimm,sext,pcsource,shift,jal,/*数据前推加入的参数*/exe_rd,mem_rd,exe_wreg,mem_wreg,idadepend,idbdepend,rs,rt,rd,exe_m2reg,wpc);input[4:0]exe_rd,mem_rd,rs,rt,rd;inputexe_wreg,mem_wreg,exe_m2reg;output[1:0]idadepend,idbdepend;outputwpc;//wreg是否写寄存器//dm2reg为1时将存储器数据写入寄存器,否则将ALU结果写入寄存器//dwmem为1时写存储器,否则不写////daluimm为1时ALUb输入端使用立即数//dshift为1时ALUa输入端使用移位位数//djal为1时执行jal指令,否则不是inputrsrtequ;input[5:0]func,op;outputwreg,m2reg,wmem,regrt,aluimm,sext,shift,jal;output[4:0]aluc;output[1:0]pcsource;wirei_add,i_sub,i_mul,i_and,i_or,i_xor,i_sll,i_srl,i_sra,i_jr;//对指令进行译码8/13wirei_addi,i_muli,i_andi,i_ori,i_xori,i_lw,i_sw,i_beq,i_bne,i_lui,i_j,i_jal;and(i_add,~op[5],~op[4],~op[3],~op[2],~op[1],~op[0],~func[2],~func[1],func[0]);and(i_sub,~op[5],~op[4],~op[3],~op[2],~op[1],~op[0],~func[2],func[1],~func[0]);and(i_mul,~op[5],~op[4],~op[3],~op[2],~op[1],~op[0],~func[2],func[1],func[0]);and(i_and,~op[5],~op[4],~op[3],~op[2],~op[1],op[0],~func[2],~func[1],func[0]);and(i_or,~op[5],~op[4],~op[3],~op[2],~op[1],op[0],~func[2],func[1],~func[0]);and(i_xor,~op[5],~op[4],~op[3],~op[2],~op[1],op[0],func[2],~func[1],~func[0]);and(i_sra,~op[5],~op[4],~op[3],~op[2],op[1],~op[0],~func[2],~func[1],func[0]);and(i_srl,~op[5],~op[4],~op[3],~op[2],op[1],~op[0],~func[2],func[1],~func[0]);and(i_sll,~op[5],~op[4