BUAA-CO-P6 基于verilog的流水线MIPS架构CPU(2)

任务

  • 将P5中支持{add,sub,ori,lui,lw,sw,beq,jr,jal,nop}十条指令的CPU扩展,要求支持以下指令集:

          add,sub,and,or,slt,sltu,
          addi,andi,ori,lui,
          mult,multu,div,divu,mfhi,mflo,mthi,mtlo,
          lw,lb,lh,sw,sb,sh,
          beq,bne,jr,jal,
          nop
    
  • 不再自行模拟IM和DM,而由提供的外设传递instruction和相关数据给CPU

前提

  1. 不会连续出现乘除指令
  2. 乘除指令之后一定是mfhi/mflo

需要改动的地方

IM和DM

不用自己实现IM和DM了。

  • 相当于给出了IM和DM的外置设备,只需要考虑怎么将cpu和IM、DM连接起来
  • 在MEM阶段设置BE模块来负责数据交互
    • 原MemWrite变为byteEn总开关
    • 原MemRead现在没有意义了

MDU

在EX阶段新增乘除模块来实现乘除相关指令

  • 我们只需要把乘除法相关指令阻塞在D
  • 在HCU中增加MD_stall的判断

Control

指令集规模扩大,为提高添加指令的效率和conrol代码的简洁性,我们对指令进行分类,可大致分为: - R_cal - I_cal - MD - Load & Save - Branch & Jump

计算用组合逻辑积累

  1. 乘除相关
    1
    2
    3
    4
    //madd
    {temp_hi, temp_lo} <= {hi, lo} + $signed($signed(64'd0) + $signed(rs) * $signed(rt));
    // 或者
    {temp_hi, temp_lo} <= {hi, lo} + $signed({{32{rs[31]}}, rs[31:0]} * $signed({{32{rt[31]}}, rt[31:0]})); // 手动进行符号位扩展

模块设计

ID

HCU

  • 转发:新增了数据来源(M_HI/M_LO)
    转发到/数据来源 M锁存的ALUresult W_WD M_PC8 M_HI M_LO E_PC8
    ID stage RtData/RsData RtData/RsData RtData/RsData RtData/RsData RtData/RsData RtData/RsData
    EX stage RtData/RsData RtData/RsData RtData/RsData RtData/RsData RtData/RsData 不存在
    MEM stage 不存在 RtData 不存在 不存在 不存在 不存在
  • 暂停:增加了MD_stall
    • assign MD_stall = (D_isMDinstr & (E_MDUop != 3'b0 | E_Busy));
    • 即:当E中MDUstart/busy且D中为MD相关指令,stall

EX

MDU

  • 整体设计
    • 能够实现和乘除有关的功能
  • 模块规格
    信号名 方向 位宽 描述
    reset I 1 同步复位
    clk I 1 clk
    op I 3 MDUop
    000:未start
    001:mult start
    010:multu start
    011:div start
    100:divu start
    operand1 I 32 默认RsData
    operand2 I 32 默认RtData
    we_HI I 1 HI写使能信号
    we_LO I 1 LO写使能信号
    Busy O 1 MDU繁忙信号
    HI O 32 HI data
    LO O 32 LO data

MEM

BE

  • 模块设计
    • 将字节写使能信号传入外设DM
  • 模块规格
    信号名 方向 位宽 描述
    Address I 32 读/写地址,默认是ALUresult
    MemWrite I 1 写使能信号
    LoadType I 2 load模式选择信号
    SaveType I 2 Save模式选择信号
    MemWriteData I 32 要被处理准备被写入的数据
    MemReadData 从DM中读出的对应Word
    LoadData O 32 要Load到rt中的数据
    SaveData O 32 要Save到DM中的数据
    byteEn O 4 要Save到DM对应字的字节位置

思考题

  1. 为什么需要有单独的乘除法部件而不是整合进 ALU?为何需要有独立的 HI、LO 寄存器?

    • 乘除的实现时间远长于ALU中的简单运算,如果整合进去,那么EX阶段的时间就会被拉长,降低效率
    • 独立的HI、LO:
      1. 运算结果是64位/两个数,而通用寄存器只有32位;
      2. 可以简化寄存器堆的写回逻辑(否则GRF要实现同时写入两个数据,与现有GRF结构不同)
  2. 真实的流水线 CPU 是如何使用实现乘除法的?请查阅相关资料进行简单说明。

    • 真实的CPU实际上是通过专用硬件电路 + 内部流水线 + 乱序调度来实现乘法,而除法通过使用 SRT 算法来实现(搜索得到)
  3. 请结合自己的实现分析,你是如何处理 Busy 信号带来的周期阻塞的?

    • 我在MDU内部设计了count来计算阻塞周期数,实际上这就是一个有限状态机的实现。
    • 在HCU中增加了MD_stall(具体见上MDU)
  4. 请问采用字节使能信号的方式处理写指令有什么好处?(提示:从清晰性、统一性等角度考虑)

    • DM内部不再需要区分写入指令的方法(sw/sb/sh/…),它只需要根据Address和字节使能信号决定要不要写入。
  5. 请思考,我们在按字节读和按字节写时,实际从 DM 获得的数据和向 DM 写入的数据是否是一字节?在什么情况下我们按字节读和按字节写的效率会高于按字读和按字写呢?

    • 不是,是一个字
    • 当我们需要紧密地存储数据时,按字节读写效率会更高(比如处理字符串时)
  6. 为了对抗复杂性你采取了哪些抽象和规范手段?这些手段在译码和处理数据冲突的时候有什么样的特点与帮助?

    • 把控制信号合成为总线向后传,要用的时候再利用宏取出。当需要新增控制信号(如增加乘除法相关的 we_HI/LO)时,只需修改位宽定义和宏,而无需修改流水线寄存器(Reg_E, Reg_M等)的端口列表;
    • 在增加指令时把指令分为R-cal\I-cal\Branch and Jump\Load and Save\MD相关,再把它加入对应的控制信号表。填表的过程就是梳理指令全过程、思考待改变模块的过程,因此填完表之后基本就知道要怎么修改cpu。
    • 转发采取from - to的思路,只要增加的指令没有涉及到转发source的增加就不用改HCU。(如果要改,类似:添加MD相关指令之后,在HCU和顶层数据选择逻辑中加入了M_HI/M_LO,在WDsel的来源中增加了W_HI/W_LO)
  7. 在本实验中你遇到了哪些不同指令类型组合产生的冲突?你又是如何解决的?相应的测试样例是什么样的?

    • 转发:按照以下from - to表来列举
      转发到/数据来源 M锁存的ALUresult W_WD M_PC8 M_HI M_LO E_PC8
      ID stage R_cal/I_cal - nop - Branch R_cal/I_cal/Load - nop - nop - Branch jal - (jump处)nop - Branch mfhi - nop - Branch mflo - nop - Branch jal - (jump处)Branch
      EX stage R_cal/I_cal - R_cal/I_cal/MD相关 R_cal/I_cal/MD相关/jal/Load - nop - R_cal/I_cal/MD相关 jal - (jump处)R_cal/I_cal/MD相关 mfhi - R_cal/I_cal/MD相关 mflo - R_cal/I_cal/MD相关 不存在
      MEM stage 不存在 R_cal/I_cal/MD相关/jal/Load - Save 不存在 不存在 不存在 不存在
    • 暂停:
      • E_Conflict:
        1
        2
        3
        4
        ori $1,$0,3
        beq $1,$0,label
        label:
        nop
      • M_Conflict:
        1
        2
        3
        4
        5
        lw $1, 0($0)      
        nop
        beq $1, $0, label
        label:
        nop
  8. 如果你是手动构造的样例,请说明构造策略,说明你的测试程序如何保证覆盖了所有需要测试的情况;如果你是完全随机生成的测试样例,请思考完全随机的测试程序有何不足之处;如果你在生成测试样例时采用了特殊的策略,比如构造连续数据冒险序列,请你描述一下你使用的策略如何结合了随机性达到强测的效果。

    • 手动构造:
      • 根据7中表格覆盖每一种转发和暂停的场合
      • 加入 $0 寄存器作为读写目标,确保不会错误地覆盖 $0 的值

Control表

R_cal

控制信号\指令 add sub and or slt sltu
RegWrite 1 1 1 1 1 1
aluControlSel 000 000 000 000 000 000
ALUop 0000 0001 0010 0011 0101 0110
MemWrite 0 0 0 0 0 0
MemRead 0 0 0 0 0 0
EXTop 0 0 0 0 0 0
RegDst 000 000 000 000 000 000
WDsel 00 00 00 00 00 00
NPCop 000 000 000 000 000 000
MemWriteDataOp 00 00 00 00 00 00
ifB 0 0 0 0 0 0
ifJ 0 0 0 0 0 0
BranchOp 000 000 000 000 000 000
useRt 1 1 1 1 1 1
islink 0 0 0 0 0 0
isConditionalLink 0 0 0 0 0 0
isLikely 0 0 0 0 0 0
LoadType 0 0 0 0 0 0
SaveType 0 0 0 0 0 0
MDUop 000 000 000 000 000 000
we_HI 0 0 0 0 0 0
we_LO 0 0 0 0 0 0

I_cal

控制信号\指令 addi andi ori lui
RegWrite 1 1 1 1
aluControlSel 001 001 001 001
ALUop 0000 0010 0011 0100
MemWrite 0 0 0 0
MemRead 0 0 0 0
EXTop 1 0 0 0
RegDst 001 001 001 001
WDsel 00 00 00 00
NPCop 000 000 000 000
MemWriteDataOp 00 00 00 00
ifB 0 0 0 0
ifJ 0 0 0 0
BranchOp 000 000 000 000
useRt 0 0 0 0
islink 0 0 0 0
isConditionalLink 0 0 0 0
isLikely 0 0 0 0
LoadType 0 0 0 0
SaveType 0 0 0 0
MDUop 000 000 000 000
we_HI 0 0 0 0
we_LO 0 0 0 0

MD相关

控制信号\指令 mult multu div divu mfhi mflo mthi mtlo
RegWrite 0 0 0 0 1 1 0 0
aluControlSel 0 0 0 0 0 0 0 0
ALUop 0000 0000 0000 0000 0000 0000 0000
MemWrite 0 0 0 0 0 0 0 0
MemRead 0 0 0 0 0 0 0 0
EXTop 0 0 0 0 0 0 0 0
RegDst 000 000 000 000 000 000 000 000
WDsel 000 000 000 000 011 100 000 000
NPCop 000 000 000 000 000 000 000 000
MemWriteDataOp 00 00 00 00 00 00 00 00
ifB 0 0 0 0 0 0 0 0
ifJ 0 0 0 0 0 0 0 0
BranchOp 000 000 000 000 000 000 000 000
useRt 1 1 1 1 0 0 0 0
islink 0 0 0 0 0 0 0 0
isConditionalLink 0 0 0 0 0 0 0 0
isLikely 0 0 0 0 0 0 0 0
LoadType 0 0 0 0 0 0 0 0
SaveType 0 0 0 0 0 0 0 0
MDUop 001 010 011 100 000 000 000 000
we_HI 0 0 0 0 0 0 1 0
we_LO 0 0 0 0 0 0 0 1

Load & Save

控制信号\指令 lb lh lw sb sh sw
RegWrite 1 1 1 0 0 0
aluControlSel 001 001 001 001 001 001
ALUop 0000 0000 0000 0000 0000 0000
MemWrite 0 0 0 1 1 1
MemRead 1 1 1 0 0 0
EXTop 1 1 1 1 1 1
RegDst 001 001 001 000 000 000
WDsel 01 01 01 00 00 00
NPCop 000 000 000 000 000 000
MemWriteDataOp 00 00 00 00 00 00
ifB 0 0 0 0 0 0
ifJ 0 0 0 0 0 0
BranchOp 000 000 000 000 000 000
useRt 0 0 0 1 1 1
islink 0 0 0 0 0 0
isConditionalLink 0 0 0 0 0 0
isLikely 0 0 0 0 0 0
LoadType 010 100 000 000 000 000
SaveType 0000 0000 0000 0010 0001 0000
MDUop 000 000 000 000 000 000
we_HI 0 0 0 0 0 0
we_LO 0 0 0 0 0 0

Branch & Jump

控制信号\指令 beq jal jr bne
RegWrite 0 1 0 0
aluControlSel 000 000 000 000
ALUop 0000 0000 0000 0000
MemWrite 0 0 0 0
MemRead 0 0 0 0
EXTop 1 0 0 1
RegDst 000 011 000 000
WDsel 00 10 00 00
NPCop 001 010 011 001
MemWriteDataOp 00 00 00 00
ifB 1 0 0 1
ifJ 0 1 1 0
BranchOp 000 000 000 001
useRt 1 0 0 1
islink 0 1 0 0
isConditionalLink 0 0 0 0
isLikely 0 0 0 0
LoadType 000 000 000 000
SaveType 0000 0000 0000 0000
MDUop 000 000 000 000
we_HI 0 0 0 0
we_LO 0 0 0 0

Copyright © 2026 CRZ's Blog

Powered by Hexo • Theme Candelas