P6-design-document

P6-Design-Document 一.数据通路图示 ​ P6在P5的基础上新增乘除槽单元、字节使能单元、数据扩展单元,在顶层模块中增加与testbench的接口,将IM与DM外移到testbench中。 二.功能模块定义 1. MCU 1.端口定义列表 名称 方向 位宽 描述 D_opcode I 6 操作码 D_funct I 6 功能码 SelA3_D O 2 选择写入寄存器地址 RegWrite_D O 1 寄存器堆写入地址 EXTOp_D O 1 立即数扩展信号 SelEMout_D O 1 选择E、M级转发数据 SelWout_D O 2 选择W级转发数据/写入GRF数据 SeLALUB_D O 1 选择ALU_B端口输入数据 SelALUS_D O 1 选择ALU_S端口移位数据 check_D O 1 判定是否为新信号(课上扩展使用) mf_D O 1 mfhi/mflo指令,选择E级输出数据 start_D O 1 乘除类导致延迟的指令信号,只有md类需要 CMPOp_D O 3 B类跳转指令操作码 NPCOp_D O 3 跳转地址选择 ALUOp_D O 4 ALU计算操作 MDUOp_D O 4 乘除类计算操作 DMOp_D O 4 存取指令操作 T_rs_use_D O 2 位于D级用到rs寄存器中值的周期数 T_rt_use_D O 2 位于D级用到rt寄存器中值的周期数 T_new_D O 2 位于D级产生新信号的周期数 注:由于BE模块产生字节使能信号,将原内存写入使能信号MemWrite省去 ...

November 28, 2023 · 11 min · sudo

P5课上测试

P5课上测试 一.增添指令一般步骤 明确指令RTL,可以使用课上提供的.class文件进行模拟 明确非转发数据通路:可以在单周期中思考新指令的行为,构思出数据通路 考虑转发: 考虑GRF的五位写入地址是否正确,一般在第二步已经调整完毕,但是像lwer,lhso等条件存储类指令只有在M级中从DM中取出数据后才能明确写入地址,需要在M级将写入地址再次修改 明确新指令的T_rs_use,T_rt_use,T_new,用不到的寄存器设置为T_rs/rt_use = 3 二.课上测试题型分析 1.计算指令——在E级就可以完成所有操作 ​ 新增的计算指令一般只需要增加ALU的功能,但是计算行为会稍稍复杂一些,Tnew和Tuse与calc_R或calc_I型保持一致即可 例如 循环左移 1 2 if(B[4:0] == 5'b0) out = A; else out = A << B[4:0] | A >> (6'd32 - B[4:0]) 2.条件跳转类——在D级就可以完成所有操作 条件跳转 + 无条件链接 条件跳转 + 条件链接 条件跳转 + 条件(无条件)链接 + 不跳转时清空延迟槽 1.条件跳转 增加CMP中的判断功能 2.无条件链接 D级将RegWrite置为1,跟随流水并更改A3(31),最后在W级选择PC8作为写入数据 3.条件链接 在D级根据CMP模块的输出结果判断RegWrite是否有效 设置一个check信号用以确定当前指令是否为新指令,进行流水 1 2 3 4 5 6 7 wire D_RegWrite_new = check_D ? (D_CMP_out ? 1'b1 : 1'b0) : RegWrite_D; //判断是否为新信号 如果是就已经隐含着regwrite=1,只需对CMP结果进行判断 //如果不是就保留原信号 //uut E_reg top_e_reg( .RegWrite_D(D_RegWrite_new), ); 4.不跳转时清空延迟槽 根据CMP输出结果判断是否要清空D级流水寄存器(这就是hyggge学长P5教程中D级clr信号接口的原因QAQ) 注意:当前为stall状态则不能清空延迟槽,此时传入CMP模块的不是新值,判断无效 1 2 3 4 5 wire D_reg_clr = check_D & ~D_CMP_out & ~stall //新信号 & 不跳转 & 不暂停 D_reg top_d_reg( .D_clr(D_reg_clr) //加入到同步复位列表中 ); 3.条件存储类——在M级就可以完成所有操作 在原来基础上修改stall控制信号,如果D级的指令要读寄存器,而且后面的新指令可能要写这个寄存器,那么就stall 修改写入A3地址并传入W_reg和HCU 1. condition1 condition成立 :将DM中值写入A condition不成立 :将DM中值写入B 1 2 3 4 5 6 7 8 9 10 11 12 13 14 //第一种题型(eg:condition满足向rt号写,否则写31号) assign stall_rs_E = (D_A1 != 5'd0) & (check_E ? (D_A1 == E_A3 | D_A1 == 5'd31) : D_A1 == E_A3) & (RFWrite_E) & (Tuse_rs < Tnew_E); assign stall_rs_M = (D_A1 != 5'd0) & (check_M ? (D_A1 == M_A3 | D_A1 == 5'd31) : D_A1 == M_A3) & (RFWrite_M) & (Tuse_rs < Tnew_M); assign stall_rt_E = (D_A2 != 5'd0) & (check_E ? (D_A2 == E_A3 | D_A2 == 5'd31) : D_A2 == E_A3) & (RFWrite_E) & (Tuse_rt < Tnew_E); assign stall_rt_M = (D_A2 != 5'd0) & (check_M ? (D_A2 == M_A3 | D_A2 == 5'd31) : D_A2 == M_A3) & (RFWrite_M) & (Tuse_rt < Tnew_M); //更改写入寄存器地址(eg:condition满足向rt号写,否则写31号) wire M_A3_new = check_M ? (condition ? `rt : 5'd31) : M_A3; //uut W_reg top_w_reg( .M_A3(M_A3_new), ); HCU top_hcu( .M_A3(M_A3_new), ); 2.condition2 conditon成立 :向31号写 condition不成立 :不写(写入0号寄存器) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 //按照第一种题型以写成 (check_M ? (D_A2 == 5'd31 | D_A2 == 5'd0): D_A2 == M_A3),因为前面有条件 D_A2 != 5'd0,所以可以简化 assign stall_rs_E = (D_A1 != 5'd0) & (check_E ? D_A2 == 5'd31 : D_A2 == M_A3) & (RFWrite_E) & (Tuse_rs < Tnew_E); assign stall_rs_M = (D_A1 != 5'd0) & (check_M ? D_A2 == 5'd31 : D_A2 == M_A3) & (RFWrite_M) & (Tuse_rs < Tnew_M); assign stall_rt_E = (D_A2 != 5'd0) & (check_E ? D_A2 == 5'd31 : D_A2 == M_A3) & (RFWrite_E) & (Tuse_rt < Tnew_E); assign stall_rt_M = (D_A2 != 5'd0) & (check_M ? D_A2 == 5'd31 : D_A2 == M_A3) & (RFWrite_M) & (Tuse_rt < Tnew_M); //更改写入寄存器地址(eg:condition满足向31号写,否则不写) wire M_A3_new = check_M ? (condition ? 5'd31 : 5'd0) : M_A3; //uut W_reg top_w_reg( .M_A3(M_A3_new), ); HCU top_hcu( .M_A3(M_A3_new), ); 3.condition3 condition满足时写入DM中值的低五位 这种情况下可能写入所有的寄存器,直接暂停 1 2 3 4 5 6 7 8 9 10 11 12 13 14 //第三种题型 (eg:condition满足时写入位置为DM的读取值的低五位) assign stall_rs_E = (D_A1 != 5'd0) & (check_E ? 1'b1 : D_A2 == M_A3) & (RFWrite_E) & (Tuse_rs < Tnew_E); assign stall_rs_M = (D_A1 != 5'd0) & (check_M ? 1'b1 : D_A2 == M_A3) & (RFWrite_M) & (Tuse_rs < Tnew_M); assign stall_rt_E = (D_A2 != 5'd0) & (check_E ? 1'b1 : D_A2 == M_A3) & (RFWrite_E) & (Tuse_rt < Tnew_E); assign stall_rt_M = (D_A2 != 5'd0) & (check_M ? 1'b1 : D_A2 == M_A3) & (RFWrite_M) & (Tuse_rt < Tnew_M); //更改写入寄存器地址(eg:写入位置为DM的读取值的低五位) wire M_A3_new = check_M ? DM_out[4:0] : M_A3; //uut W_reg top_w_reg( .M_A3(M_A3_new), ); HCU top_hcu( .M_A3(M_A3_new), );

November 27, 2023 · 3 min · sudo

P5-pipeline-CPU-Design-Document

P5-pipeline-CPU-Design-Document 零.数据通路描述 ​ MIPS架构的五级流水线CPU数据通路有以下五个阶段: 取指阶段F(Fetch):从指令存储器中读取指令 译码阶段D(Decode):从寄存器文件中读取源操作数并对指令译码得到控制信号 执行阶段E(Execute):使用ALU执行计算 存储阶段M(Memory):读或写数据存储 写回阶段(Writeback):将结果写回到寄存器文件 ​ 通过在五个阶段之间加入寄存器来保存前面周期产生的数据(最后一个阶段的寄存器即为GRF),形成五级流水线CPU,数据通路如下图表示: 一.数据命名规范(checkstyle) 在控制信号连线名末尾加上“_阶段字母”,如NPCOp_D,用于区分所属的阶段 在流水线寄存器输出信号前加上“阶段字母_",如:D_A1,用以区分所属阶段 功能部件输出信号前加上功能部件名称 常规MUX命名采用"MUX-部件名-端口名”的命名方式,选择信号命名为"Sel+部件名+端口名" 转发MUX命名采用"HMUX-部件名-端口名"的命名方式,选择信号命名为"Fwd+部件名+端口名" 二.部件设计 1.MCU(main control unit) 1.端口定义列表 ​ 主控制单元模块负责对指令进行译码以及产生控制信号,本CPU的设计采用集中式译码,在P5中需要完成的指令及其需要的控制信号列表如下,其中有些控制信号对应多路选择器的选择。 名称 方向 位宽 描述 D_opcode I 6 D级输出opcode D_funct I 6 D级输出funct SelA3_D I 2 选择当前指令要写入的寄存器,写入E级流水线寄存器 RegWrite_D O 1 GRF写入使能 MemWrite_D O 1 DM写入使能 EXTOp_D O 1 对立即数进行符号扩展还是0扩展 SelEMout_D O 1 选择E,M级转发数据信号 SelWout_D O 2 选择W级转发数据信号 SelALUB_D O 1 ALU的B端口rt数据与32位立即数的选择 CMPOp_D O 3 D级B类指令比较选择 NPCOp_D O 3 写入NPC选择 ALUOp_D O 3 ALU操作信号 DMOp_D O 3 DM操作信号 T_rs_use_D O 2 指令处在D级时还有多少个周期需要使用rs寄存器的值 T_rt_use_D O 2 指令处在D级时还有多少个周期需要使用rt寄存器的值 T_new_D O 2 指令处在D级时还有多少个周期可以产生写入寄存器的新值 2.分指令梳理控制信号 指令 取高电平的控制信号和ALUOp add SelA3_D = 2’b01, RegWrite, ALUOp = 3’b000, NPCOp = 3’b000; sub SelA3_D = 2’b01, RegWrite, ALUOp = 3’b001, NPCOp = 3’b000; ori RegWrite,ALUSrc, SelALUB, ALUOp=3’b010, NPCOp = 3’b000; lui RegWrite,ALUSrc, SelALUB, ALUOp=3’b011, NPCOp = 3’b000; lw RegWrite, SelWout = 2’b01, SelALUB, EXTOp, ALUOp=3’b000, NPCOp = 3’b000; sw MemWrite, SelALUB, EXTOp, ALUOp=3’b000, NPCOp = 3’b000; beq CMPOp = 3’b000,EXTOp, NPCOp = 3’b001, CMPOp = 3’b000; jal RegWrite, SelA3_D = 2’b10, NPCOp = 3’b010; jr NPCOp = 3’b11; nop 相当于sll $0,$0,0,不需要进行特殊处理 3.译码方式的改动 ​ 在P4中,我才用使用reg类型变量记录指令识别的形式,在always块中每次都为代表所有信号的寄存器赋初值0,这样会导致pc一直自增下去,程序不断向后跑(虽然在上机时无影响),在蒋老师的测评机上会出现多输出的情况。在P5中,使用wire类型进行指令的识别,当识别的指令为x时,所有指令对应的wire都是不定值x,导致MCU中输出的控制信号为x,使得pc停下来,可以正常在蒋老师的测评机上跑结果。 ...

November 12, 2023 · 24 min · sudo

支持40条指令P4_Verilog_CPU

P4-Verilog-CPU-Design-Document 零.数据通路描述(P4设计文档中最好加上P3图) ​ 本次设计文档基于P3-logisim-CPU的设计文档完成,P4的主要任务即为将logisim电路映射到verilog代码(看图写话?),对上次P3中实现的每个模块单独建立module.v,利用verilog语言对其行为进行建模,注意本次CPU设计中,不同于P3的异步复位,P4在各模块中使用同步复位!!!,verilog语言表示同步复位与异步复位如下代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 //同步复位表示方法 always@(posedge clk) begin if(reset) begin PC <= 32'h0000_3000; end else begin PC <= npc; end end //异步复位表示方法 将复位信号加入敏感信号列表 always@(posedge clk or posedge reset) begin if(reset) begin PC <= 32'h0000_3000; end else begin PC <= npc; end end 一.模块设计 1.IFU取指令单元 ​ IFU中存储单元有PC寄存器和存储指令的ROM。IFU中存储指令要求的ROM大小为16KiB,即4096*32bit。在verilog语言中使用寄存器数组实现对ROM的模拟,即4096个32位的寄存器。使用系统命令$readmemh("code.txt,ROM")将code.txt中的16进制代码读入名为ROM的数组中,由于PC同步复位到32’h0000_3000,而ROM中对应存储第一条指令的地址为0,即PC寄存器中存储的地址与真实ROM地址存在0x0000_3000差量,故设计reg [31:0] tmp存储实际的地址,即pc-0x0000_3000,进而读出指令的地址为tmp[13:2]。 ...

November 10, 2023 · 11 min · sudo

支持40条指令P3_logisim_CPU

CPU Design Document 1. CPU Design 1.总体设计 ​ 通过对于《计算机组成与设计》一书的阅读,对于P3的CPU设计我有如下思考,设计MIPS架构的CPU的本质在于从指令存储器中读出32位MIPS指令,并对MIPS指令进行解析进而完成指令中要求的操作。在本次实验中,要求实现的指令有add,sub,ori,lw,sw,beq,lui,nop其中,R型指令有add,sub,I型指令有ori,lw,sw,beq,lui,特殊的有nop指令,32位全为0。 RIJ类型指令图: 此次实验中要求实现的基本指令: R型 Operation Op-Code rs rt rd shamt(移位) func add 000000 00000 100000 sub 000000 00000 100010 I型 Operation Op-Code rs rt 16-bit-immediate-value ori 001101 lw 100011 sw 101011 beq 000100 lui 001111 nop ​ 32’b0 ​ 将我们要实现的CPU分为几个模块:IFU(取指令单元),GRF(寄存器堆),ALU(算术逻辑单元),DM(数据存储器),EXT(扩展单元),Controller(控制器)。 ...

November 6, 2023 · 6 min · sudo

OOpre总结

OOpre课程结课总结 ——22373362杜启嵘 ​ 经历九周的OOpre课程的学习,我对面向对象编程有了基本的认识。从类、对象、方法等的基本概念,到最后一次迭代作业涉及到的设计模式,我循序渐进地学习到基本的面向对象编程思想(虽然我的代码仍然不够面向对象QAQ)。 一.代码的最终架构和过程中的重构 ​ 在五次作业的迭代中,我进行了多次代码的重构,前几次作业中的重构我认为是合理的,最后一次作业中的重构是把超过500行的类中的部分方法强行抽离出来放进静态方法类,并把参数分成几行避免行字数超过100字(面向OO-checkstyle的重构)。 1.第一次重构 ​ 在第三次作业中进行了实际上是两次重构,第一次在面临主类中的主方法行数超过60行,对方法中分指令执行的代码抽离出来声明为单独的方法,在编写JUnit时进行了第二次重构,由于第一版代码中解析输入逻辑与代码执行逻辑杂糅,而在编写JUnit时无法对输入进行操作,改为使用课程组提供的利用“二维数组”在统一存储输入内容,在inputhandler类中读取二维数组进行指令解析,这样的架构就能编写满足覆盖率的JUnit,下图为简单的架构图。 二.第二次重构 ​ 第四次作业中新增了战斗日志的概念,我一开始的处理中并没有对fightlog建类,只是在Adventure中建立容器来存储代表战斗日志的字符串,导致处理逻辑比较复杂,结果因为一个方法中的错误逻辑挂了强测,在修改强测的过程中完成了对代码的重构,新增fightlog类,架构图如下 三.第三次重构 ​ 在最后一次迭代开发中,由于类的行数限制,我不得不将一个类拆成两个类,在静态方法类中进行传参,属于是一次很丑陋的重构。 2.使用JUnit的心得体会 ​ 使用JUnit可以在提交测评机之前进行本地测试,可以通过构造数据计算预期结果比对程序输出结果判断程序的正确性,在第六次作业完成过程中涉及到很多计算还有精度的问题,使用JUnit在本地进行测试可以找出一些问题。编写JUnit过程中达到分支覆盖对于验证正确性也有很大帮助,第六次作业中继承关系中覆盖不同子类进行测试帮助我找到了一些bug。在当下阶段使用JUnit的不足在于构造数据过于简单,没有对边界条件进行测试(毕竟手搓复杂数据真的很难绷QWQ),总体来说在几次作业的迭代中,我通过使用JUnit实现了对于程序的本地测试,并且能够发现一些bug,使第一次提交至少通过数据点多了一些。 3.学习OOpre的心得体会 ​ 从面向过程到面向对象的编程思维的转变对于我来说还是有一些难度,我的代码中的很多编写也不够面向对象,导致方法行数爆炸,类行数爆炸,处理逻辑复杂。但是在几次迭代中,我也对面向对象有了基本的认识 理解面向对象的核心概念:面向对象编程是一种基于对象的思维方式。它的核心概念包括类、对象、封装、继承和多态。要想掌握面向对象编程,首先要理解这些概念的含义和关系 不断练习和总结经验:面向对象编程是一种需要不断实践和经验积累的编程方式。通过不断地练习和实践,才能更好地理解和应用面向对象编程的技巧。同时,还要及时总结经验教训,找到自己的不足之处并加以改进 阅读和理解优秀的面向对象代码:课程结束后学习优秀代码 4.对OOpre课程的简单建议 提高中测强度,尽量中测程度的数据过了就不要挂强测(强测挂了真的好压力) 指导书中部分内容可以进行细化,尤其是第七次作业的指导书,对于不同设计模式的解释可以再细致一点(?)

November 4, 2023 · 1 min · sudo