1、实验报告课程名称:计算机系统结构实验学 院:计算机科学与工程学院专 业:计算机科学与技术指导教师:好老师学生姓名:爱学习的小学生 2014666666666实验成绩:日 期:2017 年 5 月 19 日电子科技大学计算机学院实验中心1 / 14电 子 科 技 大 学实 验 报 告一、 实验项目名称:解决数据冒险二、 实验室名称:主楼 A2-412 实验时间:2017 年 5 月 19 日三、 实验目的在给出的流水线代码基础上,增加内部前推数据通路、暂停流水线数据通路和关闭写使能信号的数据通路,解决普通的数据冒险和 load 数据冒险,通过完成本次实验,更好地理解和掌握解决数据冒险的原理,学以
2、致用,增强编写程序的能力。四、 实验原理(一) 数据冒险的定义由于流水线上指令重叠执行,改变了原来串行执行的读/写操作数顺序,使得后面依赖前面指令结果的指令得不到准备好的数据,这样的现象叫做数据冒险(数据相关) 。回顾数据冒险的程序例子I1: add r1,r2,r3I2: sub r4,r1 ,r5I3: and r6,r7,r1I4: or r8,r1,r9I5: addi r10,r1,1002 / 14I1 下面有 3 条指令不能从寄存器 r1 读出正确的数据。(二) 数据冒险的解决方案1、 暂停流水线如上图所示,暂停流水线到最初的指令执行完毕,可以解决数据冒险,但是会涉及到两个问题,
3、即“如何检测出数据冒险”和“如何暂停流水线” 。如何检测数据冒险?a比较器;I1 指令写目的寄存器 rd,I2 和 I3 的源操作数是寄存器 rs1 或 rs2 中的数据, I2、I3 的 rs1 或 rs2 与 I1 的目的寄存器号 rd 相等时才有可能发生数据冒险。b操作码参与检测;由于指令格式中源寄存器号 rs2 与立即数部分重叠,而立即数是不会出现冒险的,因此,指令操作码必须要参与检测(区分是寄存器操作数还是立即数) 。cWREG 信号也应参与检测(实际上, WREG 也是从操作码中得出的) ;3 / 14如何暂停流水线?暂停条件判断电路 STALL 的输入包括OPCODE,ID_rs
4、l,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 写人数
5、据。2、 内部前推技术数据相关本质:一条指令执行时要用到上面指令的计算结果,但这个结果尚未被写入4 / 14寄存器堆。而实质上,此时结果已经由 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
6、= =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+storeID_rs2IsReg=and+or+add+sub(EXE_WREG= =1)表示 EXE 级的指令确实要更新寄存器,没被取
7、消(ID_rs1IsReg )条件是为了确认是寄存器,而不是立即数EXE_rd 表示 EXE 级的流水线目标寄存器 rdDEPENA_DEPEN + B_DEPENA_DEPENEXE_A_DEPEN + MEM_A_DEPENB_DEPENEXE_B_DEPEN + MEM_B_DEPEN5 / 14(三) load 指令的处理暂停与内部前推相结合ALU 指令在 EXE 级结束后,结果就出现在流水线寄存器 R 中,后续指令可以通过内部前推电路来直接使用它。但有一种情况是例外:load r1, 200(r2);add r3, r1, r4;load 指令在 EXE 级结束后,还在忙着访问存储器
8、。在 MEM 级结束后,结果才出现在流水线寄存器 D 中,见下图。这时,即使使用内部前推技术也无法消除 load 指令与它的下一条相关指令之间的第一个“气泡” 。我们的是由硬件负责检测与 load 指令的相关性。为了保证操作结果的正确性,我们采用暂停流水线一个周期的方法。第二个“气泡”用内部前推技术加以消除。我们给出下面的用于实现 load 流水线暂停的控制信号的表达式。这个信号是在 ID 级6 / 14产生,并且使用与暂停 ALU 流水线类似的方法:LOADDEPEN=EXE_A_DEPEN+EXE_B_DEPENEXE_A_DEPEN=(ID_rs1= =EXE_rd)(EXE_SLD=
9、=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。五、
10、 实验内容在给出的流水线代码基础上,增加内部前推数据通路、暂停流水线数据通路和关闭写使能信号的数据通路以解决普通的数据冒险和 load 数据冒险。六、 实验器材(设备、元器件)7 / 14ISE Design Suite 14.7 集成开发环境,编程语言:Verilog HDL 硬件描述语言七、 实验步骤(一) 创建工程 DataHazardAndForwarding(二) 将原工程文件导入(三) 在 pipelinedcpu 中添加下列变量:wire 1:0 idadepend,idbdepend;/id 级 a、b dependwire 1:0 exeadepend,exebdepend;
11、/ exe 级 a、b dependwire wpc;/写 pc为了解决数据冒险,需要将 depend 的值保存下来,所以需要两个 a、bdepend 值wpc 用于暂停流水线,从而实现 load 冒险时暂停指令留出以及写寄存器和写 mem(四) 为了实现数据通路,需要在下列模块的参数列表上增加一些参数增加后的代码如下pipepc prog_cnt (npc,clock,resetn,pc,wpc);/程序计数器 PC/*pipeir inst_reg (pc4,ins,clock,resetn,dpc4,inst,wpc);/IF 级与 ID 级之间的寄存器,即指令寄存器 IR/*pipei
12、d id_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); pipedereg de_reg (dwreg,dm2reg,dwmem,daluc,daluimm,da,db,dimm, drn,dshift,djal,dpc4,clock,resetn,ewreg,em2re
13、g,ewmem,ealuc,ealuimm,ea,eb,eimm,ern0,eshift,ejal,epc4,idadepend,idbdepend,exeadepend,exebdepend,pcsource); /ID 级与 EXE 级之间的寄存器pipeexe exe_stage (ealuc,ealuimm,ea,eb,eimm,eshift,ern0,epc4,/指令执行 EXE 级ejal,ern,ealu,z,malu,wmo,exeadepend,exebdepend);(五) 在 id_stage 的 cu 中添加判断冒险的代码,并计算出 a_depend 和 b_depen
14、d 的值,cu的完整代码如下module pipeidcu(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;8 / 14input exe_wreg,mem_wreg,exe_m2reg;output 1:0 idadepend,idbdepend;out
15、put wpc;/wreg 是否写寄存器 /dm2reg 为 1 时将存储器数据写入寄存器,否则将 ALU 结果写入寄存器 /dwmem 为 1 时写存储器,否则不写/daluimm 为 1 时 ALUb 输入端使用立即数/dshift 为 1 时 ALUa 输入端使用移位位数/djal 为 1 时执行 jal 指令,否则不是input rsrtequ; input 5:0 func,op;output wreg,m2reg,wmem,regrt,aluimm,sext,shift,jal;output 4:0 aluc;output 1:0 pcsource;wire i_add,i_sub
16、,i_mul,i_and,i_or,i_xor,i_sll,i_srl,i_sra,i_jr; /对指令进行译码wire i_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,op5,op4,op3,op2,op1,op0,func2,func1,func0);and(i_sub,op5,op4,op3,op2,op1,op0,func2,func1,func0);and(i_mul,op5,op4,op3,op2,op1,op0,func2,func1,func0);and(i_an
17、d,op5,op4,op3,op2,op1,op0,func2,func1,func0);and(i_or,op5,op4,op3,op2,op1,op0,func2,func1,func0);and(i_xor,op5,op4,op3,op2,op1,op0,func2,func1,func0);and(i_sra,op5,op4,op3,op2,op1,op0,func2,func1,func0);and(i_srl,op5,op4,op3,op2,op1,op0,func2,func1,func0);and(i_sll,op5,op4,op3,op2,op1,op0,func2,func
18、1,func0);and(i_jr,op5,op4,op3,op2,op1,op0,func2,func1,func0);and(i_addi,op5,op4,op3,op2,op1,op0);and(i_muli,op5,op4,op3,op2,op1,op0);and(i_andi,op5,op4,op3,op2,op1,op0);and(i_ori,op5,op4,op3,op2,op1,op0);and(i_xori,op5,op4,op3,op2,op1,op0);and(i_lw,op5,op4,op3,op2,op1,op0);and(i_sw,op5,op4,op3,op2,o
19、p1,op0);and(i_beq,op5,op4,op3,op2,op1,op0);and(i_bne,op5,op4,op3,op2,op1,op0);and(i_lui,op5,op4,op3,op2,op1,op0);and(i_j,op5,op4,op3,op2,op1,op0);and(i_jal,op5,op4,op3,op2,op1,op0);wire i_rs=i_add|i_sub|i_mul|i_and|i_or|i_xor|i_jr|i_addi|i_muli| i_andi|i_ori|i_xori|i_lw|i_sw|i_beq|i_bne;9 / 14wire i
20、_rt=i_add|i_sub|i_mul|i_and|i_or|i_xor|i_sra|i_srl|i_sll|i_sw|i_beq|i_bne;/控制信号的生成/assign wreg=(i_add|i_sub|i_mul|i_and|i_or|i_xor|i_sll| /wreg 为 1 时写寄存器堆中某一寄存器,否则不写i_srl|i_sra|i_addi|i_muli|i_andi|i_ori|i_xori|i_lw|i_lui|i_jal) assign regrt=i_addi|i_muli|i_andi|i_ori|i_xori|i_lw|i_lui; /regrt 为 1 时
21、目的寄存器是 rt,否则为 rdassign jal=i_jal; /为 1 时执行 jal 指令,否则不是assign m2reg=i_lw; /为 1 时将存储器数据写入寄存器,否则将 ALU 结果写入寄存器assign shift=i_sll|i_srl|i_sra;/为 1 时 ALUa 输入端使用移位位数assign aluimm=i_addi|i_muli|i_andi|i_ori|i_xori|i_lw|i_lui|i_sw;/为 1 时ALUb 输入端使用立即数assign sext=i_addi|i_muli|i_lw|i_sw|i_beq|i_bne;/为 1 时符号拓展,
22、否则零拓展assign aluc4=i_sra;/ALU 的控制码assign aluc3=i_sub|i_or|i_ori|i_xor|i_xori| i_srl|i_sra|i_beq|i_bne;/ALU 的控制码assign aluc2=i_sll|i_srl|i_sra|i_lui;/ALU 的控制码assign aluc1=i_and|i_andi|i_or|i_ori|i_xor|i_xori|i_beq|i_bne;/ALU 的控制码assign aluc0=i_mul|i_muli|i_xor|i_xori|i_sll|i_srl|i_sra|i_beq|i_bne;/ALU
23、 的控制码assign wmem=i_sw /为 1 时写存储器,否则不写/判断 rs1 是否为寄存器操作数assign rs1IsReg=i_and | i_andi | i_or | i_ori | i_add | i_addi | i_sub | i_lw | i_sw | i_sll | i_srl | i_sra;/判断 rs2 是否为寄存器操作数assign rs2IsReg=i_and | i_or | i_add | i_sub | i_sll | i_sra | i_srl; /计算 exe 级和 mem 级 adependassign exe_a_depen=(rs=exe
24、_rd) assign mem_a_depen=(rs=mem_rd) /计算 exe 级和 mem 级 bdependAssign exe_b_depen=(rt=exe_rd)assign mem_b_depen=(rt=mem_rd) /计算 adepend 和 bdependassign idadepend0= mem_a_depen;10 / 14assign idadepend1=mem_a_depen | exe_a_depen ;assign idbdepend0=mem_b_depen | aluimm ;assign idbdepend1=mem_b_depen | exe
25、_b_depen;/判断 load 指令冒险,如果是 wpc=0,stall 一个时钟周期assign wpc = (rs=exe_rd) assign pcsource1=i_jr|i_j|i_jal;/选择下一条指令的地址,00 选 PC+4,01 选转移地址,10 选寄存器内地址,11 选跳转地址assign pcsource0=i_beqendmodule(六) 接着修改多路选择器,将以前的两路转化成四路module mux4x32(a0,a1,a2,a3,s,y);input 31:0 a0,a1,a2,a3;input 1:0 s;output 31:0 y;assign y=(s
26、=2b00)?a0:(s=2b01)?a1:(s=2b10)?a2:a3;/四选一endmodule八、 实验数据及结果分析(一)画出修改后的流水线图;11 / 14(二)写出增加的多路选择器的选择信号逻辑函数增加的多路选择器信号逻辑函数如下:/加上数据前推之后变成四选一的多路器mux4x32 alu_ina (ea,sa,malu,wmo,exe_a_depend,alua);mux4x32 alu_inb (eb,eimm,malu,wmo,exe_b_depend,alub);多路选择器的定义如下:module mux4x32(a0,a1,a2,a3,s,y);input 31:0 a0
27、,a1,a2,a3;input 1:0 s;output 31:0 y;assign y=(s=2b00)?a0:(s=2b01)?a1:(s=2b10)?a2:a3;/四选一endmodule(三)流水线仿真结果,对仿真结果进行必要说明为了验证是否程序正确的完成了 forwarding 和数据冒险,下面将进行两组测试,并对测试结果进行详细分析解释测试一(非 load 指令数据冒险测试)首先不管是不是 load 指令,数据冒险都会用到数据前推,实验提供的代码中测试文件里面的例子就是一个很典型的数据冒险,下面直接用这组例子进行测试,代码如下/测试数据前推assign rom6h00=32h440
28、00001;/lui r1,0 0assign rom6h01=32h28014024;/ori r4,r1,80 80assign rom6h02=32h00202124;/sub r8,r9,r4 fffassign rom6h03=32h14001005;/addi r5,r0,4 4assign rom6h04=32h38000082;/store r2,0x0(r4) 80assign rom6h05=32h34000089;/load r9,0x0(r4) 80可以看到第二条指令和第三条指令关于 r4 冲突,如果没有解决数据冒险,sub 指令时r8 会写入 0(r9=r4=0) ,
29、解决数据冒险过后,应该等于-80。运行程序观察结果:12 / 14图 1:数据冒险测试截图从结果中我们可以看到,400ns 时 alu 输出结果为 ffffffb0(-80),这与我们预想数据冒险解决成功的结果是一致的,说明数据前推是正确的,成功解决了数据冒险。测试二 (load 指令数据冒险测试)仍然采用原来程序提供的测试例子,这次我们将 load 指令插到 sub 指令前面,代码如下/测试 load 指令的停顿assign rom6h00=32h44000001;/lui r1,0assign rom6h01=32h28014024;/ori r4,r1,80assign rom6h02=
30、32h34000089;/load r9,0x0(r4)assign rom6h03=32h00202124;/sub r8,r9,r4assign rom6h04=32h14001005;/addi r5,r0,4assign rom6h05=32h38000102;/store r2,0x0(r8)可以看到,load 指令和 sub 指令关于 r9 数据冒险,这时候除了使用前推,还必须将sub 指令暂停一个时钟周期,否则 r9 会得到-80 的结果,但如果解决了 load 数据冒险的话,最终的结果应该是 83(因为在内存中设置了 80 所在的位置内容为 163)运行程序,观察分析结果图 2
31、:load 数据冒险测试截图可以看出,在 400ns 时,pc 值没有发生改变,也就是暂停了一个时钟周期,在 600ns时,alu 结果为 53(十进制 83) ,验证了前面的分析,load 指令的数据冒险也已经成功解决九、 总结、改进建议及心得体会(一) 总结:经过对代码的改写,包括添加变量、模块参数、修改模块代码、增改连线、反复测试、修改等,成功地通过内部前推解决了普通的数据冒险,并通过暂停和前推,解决 load 指令13 / 14的数据冒险。(二) 改进建议:如果能够有板子可以跑一下,就更有意思了,像之前的计算机组成原理那样,实验的最后一步是下载到板子上运行。(三) 心得体会:1、 基础很重要,基础知识一定要扎实,才能进一步自己编程实现;2、 编写多组指令进行测试,并且注意查看内部寄存器的值,而不仅仅是查看结果。