# CPU 设计文档及思考题

# 设计草稿

# 思路

  • 在 P5 草稿基础上进行修订,加入了乘除模块和存储器外置,先画出流水线 CPU 的设计示意图如下。其中正三角表示转发的供给者,倒三角表示转发的需求者。部分控制信号连线省略了。
    图 2

# 工程化设计

  • 先对需要实现的指令进行分类,对每个类别设计数据通路。分成以下几类:Cal,immCal,irCal,Load,Store,Branch,jWrite,jUse,mdCal,mdSet,mdGet, 大多数沿用之前的通路。
    数据通路的设计如下,其中标记橙色部分为转发的需求者,需要替换为转发需求者多路选择器的输出:
    图 1
  • 控制信号的设计基本延续了之前的指令,添加了少量信号。
  • 暂停转发的设计如下 (延续 P5):
    • 暂停 / 转发的策略信号:rs-Tuse,rt-Tuse 是从存到 D 级开始,过多少周期用到。Tnew 是从存入当前寄存器开始,过多少周期更新数据。
    • 暂停信号例如 (rs-Tuse < Tnew_E && rs == A3@E && RegWrite@E && rs != 0)。其他同理,将两个子公式析取起来得到 rs_Stall,将 rs_Stall 和 rt_Stall 析取得到 Stall。
    • 转发信号的条件则是信号已经产生并且读写同一个寄存器、寄存器不为零号、写使能。由于采用集中式译码,因此 “信号已经产生” 的条件不是查看后面流水级寄存器得到的 T_new 为零,而是等到将要转发的那个周期中 T_new 为零。即 T_new_2D 为零,T_new_2E 小于等于一,T_new_2M 小于等于 2。
    • 添加 Stall_md 信号,当乘除模块 start 或 busy 时 D 级为乘除指令时暂停
    • 添加 E 级的转发选择信号,如果是 mfhi/mflo 指令应该转发 MDR.Out

# 模块设计

  • PC

    信号名方向描述
    ClkInput时钟
    ResetInput同步复位到 0x00003000
    InInput[31:0]npc
    OutOutput[31:0]pc
  • IM

    信号名方向描述
    A[31:0]Input指令的字节地址
    Out[31:0]Output指令
  • GRF

    信号名方向描述
    ClkInput时钟
    ResetInput同步复位
    WEInput写使能
    A1[4:0]Input操作数 1 地址
    A2[4:0]Input操作数 2 地址
    A3[4:0]Input操作数 3 地址
    WD[31:0]Input写入数据
    D1[31:0]Output读出数据 1
    D2[31:0]Output读出数据 2
  • NPC

    信号名方向描述
    BLOCK_NPCInput冻结信号
    NPCMode[2:0]Input运算模式
    PC_Out[31:0]InputF 级 PC
    PC_D[31;0]InputD 级 PC
    ImmInput[25:0]25 位立即数
    raInput[31:0]ra
    beqInputbeq 两数是否相等
    pc8Output[31:0]PC_D+8(PC_Out+4)
    npcOutput[31:0]NPC
  • EXT

    信号名方向描述
    EXTMode[1:0]Input模式
    Imm16Input待扩展立即数
    ext[31:0]Output结果
  • Controller

    信号名方向描述
    Opcode[5:0]Input
    Funct[5:0]Input
    rs[4:0]Input
    rt[4:0]Input
    T_new_E[2:0]Input
    T_new_D[2:0]Input
    T_new_M[2:0]Input
    RegWrite_EInput
    RegWrite_DInput
    RegWrite_MInput
    NPCModeOutput[2:0]NPC 模式
    EXTModeOutputEXT 使能和模式
    A3Src[1:0]OutputALU/DM 回写
    ALUSrc[1:0]OutputB 为寄存器 / 立即数
    ALUMode[3:0]OutputALU 模式
    MemoryWriteOutputDM 写使能
    WDSrc[1:0]OuputDM 选择
    RegWriteOutputGRF 写使能
    StallOutput暂停
    DFV1_DSrc[2:0]Output转发
    DFV2_DSrc[2:0]Output转发
    DFV1_ESrc[1:0]Output转发
    DFV2_ESrc[1:0]Output转发
    DFData_MSrc[1:0]Output转发
    T_new_D[2:0]Output产生结果时间
  • ALU

    信号名方向描述
    A[31:0]Input操作数 1
    B[31:0]Input操作数 2
    ALUMode[3:0]Input模式
    C[31:0]Output运算结果
  • D

    信号名方向描述
    CLKInput时钟信号
    ResetInput同步复位
    BLOCK_DInput冻结信号
    PC[31:0]Inputpc
    IR[31:0]Input指令
    PC_DOutput
    IR_()_DOuput指令的各个域

# 测试方案

  • 进行一定的手动测试。其中可以先进行单条指令的测试。这里使用了部分 P5 的测试点,并构造了一些其他点测试新指令,如:
.data

.text
  ori $1,$0,12
  sw $1,0($0)
  ori $2,$0,15
  sb $2,1($0)
  ori $3,$0,1376
  sh $3,2($0)
  ori $4,$0,0xffff
  sh $4,4($0)
  lui $5,0xffff
  sb $5,7($0)
  slt $10,$5,$4
  sh $10,6($0)
  slt $11,$1,$2
  sb $11,8($0)
  slt $10,$3,$1
  slt $11,$1,$4
  sltu $10,$5,$4
  sltu $11,$1,$2
  sltu $10,$3,$1
  sltu $11,$1,$4
  and $1,$2,$1
  sb $1,10($0)
  and $2,$3,$3
  sh $2,14($0)
  and $5,$5,$3
  lw $4,0($0)
  and $4,$5,$4
  lh $3,0($0)
  or $1,$2,$1
  lb $3,3($0)
  or $2,$3,$3
  lh $5,4($0)
  or $5,$5,$3
  lb $4,5($0)
  or $4,$5,$4
  • 多条指令之间的暂停转发的测试。为了进行充分覆盖,将指令分成不同的类型,类型内和类型之间进行测试。包括前一条指令与后一条指令数据相关,隔一条指令数据相关,隔两条指令数据相关。测试样例如下 (算数指令和立即数指令内和之间):
.data

.text
  ori $1,$0,12
  addi $2,$0,15
  ori $3,$0,16
  addi $4,$0,17
  add $1,$2,$3
  sub $2,$3,$1
  add $1,$2,$3
  sub $3,$1,$2
  add $1,$2,$3
  nop
  or $3,$1,$2
  add $1,$2,$3
  nop
  sub $3,$2,$1
  and $1,$2,$3
  nop
  nop
  slt $3,$1,$2
  add $1,$2,$3
  nop
  nop
  sltu $3,$2,$1
  add $1,$2,$3
  andi $3,$1,15
  add $1,$2,$3
  nop
  ori $3,$1,13
  add $1,$2,$3
  nop
  nop
  ori $3,$1,15
  and $1,$2,$3
  ori $3,$1,15
  or $1,$3,$2
  ori $3,$1,15
  nop
  add $1,$3,$2
  ori $3,$1,15
  nop
  add $1,$2,$3
  ori $3,$1,15
  nop
  nop
  or $1,$3,$2
  ori $3,$1,15
  nop
  nop
  or $1,$2,$3
  lui $1,0xff
  add $1,$2,$1
  lui $1,0xff
  add $1,$1,$1
  lui $1,0xff
  nop
  add $1,$2,$1
  lui $1,0x1
  nop
  add $2,$1,$2
  lui $1,0x11
  nop
  nop
  add $2,$1,$2
  lui $1,0x1
  nop
  nop
  add $2,$2,$1

# 思考题

  • 思考题 1:
    • 乘除法需要较长的计算时间,独立出来进行计算,与不用到乘除模块的指令并行进行能够显著提高效率;并且部件用到了大量的加法器,规模较大,无法简单复用 ALU 模块。
    • 为了保证乘除模块的独立性,不至于和其他的指令产生数据相关和冒险,设置专用寄存器 hi/lo。
  • 思考题 2:
    • 流水线乘法器(除法器)可以采用横向进位的方式,然后在每一级(若干位)插入一个流水线寄存器,将整个组合电路分成 N 个流水级,N 个周期算出结果。
  • 思考题 3:
    • 将 busy 和 start 信号传入 D 级中,如果 start || busy 且 D 级的指令使用到了乘除模块,则 D 级指令暂停,否则继续执行。
  • 思考题 4:
    • 按字节使能的方式来处理数据,要存入哪几个字节一目了然,相比于使用其它控制信号,代码易读且易修改,将来有其它的写入方式也容易添加。
    • 通过字节使能信号的标记,能清晰地看出要将待写入的有效字节放在哪几位上,和 Mem 中写入的位统一。
  • 思考题 5:
    • 实际读写时写入和读出的数据都是一个字而不是一个字节。
    • 如果数据是按照字节存储的,例如 char,那么按照字节读取的效率高于按字读取的效率。
  • 思考题 6:
    • 延续和扩展了之前使用的一些规范手段,例如采用宏定义标记多路选择器的选择信号;合理命名指令译码信号、控制信号和选择器等等。
    • 采用了一些抽象手段,例如给指令进行分类,将若干个信号析取合成一个指令类信号,例如Cal=addsubandor;Cal = add | sub | and | or; 等。释放相应控制信号尽可能采用抽象类信号。
    • 如上所述,在译码过程中采用的是控制信号驱动的译码。在记录相应指令信号时采用指令类的信号使得代码量减小且易读。
    • 处理数据冲突时先记录 T_use_rs 和 T_use_rt T_new,再根据大小关系判断暂停和转发信号,在此次设计中同样将 T_use_rs 和 T_use_rt T_new 信号的记录换成指令类,其他信号几乎无修改即可实现冲突处理。
  • 思考题 7:
    • 在本分类当中,产生冲突的组合有
        Cal            Cal
        immCal         irCal
        irCal          Load
        Load     +     Store
        jWrite         Branch
        mdGet          jUse
                       mdCal
    
    以及mdCal+mdCal/mdGet/mdSetmdCal + mdCal/mdGet/mdSet
    • 解决方案为记录相应的 T-use-rs 和 T-use-rt T-new,通过比较判断暂停和转发情况。特别的,乘除模块的冲突使用 busy/start 信号判断是否需要暂停,始终无需转发
    • 对每个类型构造相邻 / 相隔一条指令 / 相隔两条指令来测试暂停和转发的正确性,样例同测试方案样例 (展示了 Cal 内部测试):
.data

.text
  ori $1,$0,12
  addi $2,$0,15
  ori $3,$0,16
  addi $4,$0,17
  add $1,$2,$3
  sub $2,$3,$1
  add $1,$2,$3
  sub $3,$1,$2
  add $1,$2,$3
  nop
  or $3,$1,$2
  add $1,$2,$3
  nop
  sub $3,$2,$1
  and $1,$2,$3
  nop
  nop
  slt $3,$1,$2
  add $1,$2,$3
  nop
  nop
  sltu $3,$2,$1
  add $1,$2,$3
  andi $3,$1,15
  add $1,$2,$3
  nop
  ori $3,$1,13
  add $1,$2,$3
  nop
  nop
  ori $3,$1,15
  and $1,$2,$3
  ori $3,$1,15
  or $1,$3,$2
  ori $3,$1,15
  nop
  add $1,$3,$2
  ori $3,$1,15
  nop
  add $1,$2,$3
  ori $3,$1,15
  nop
  nop
  or $1,$3,$2
  ori $3,$1,15
  nop
  nop
  or $1,$2,$3
  lui $1,0xff
  add $1,$2,$1
  lui $1,0xff
  add $1,$1,$1
  lui $1,0xff
  nop
  add $1,$2,$1
  lui $1,0x1
  nop
  add $2,$1,$2
  lui $1,0x11
  nop
  nop
  add $2,$1,$2
  lui $1,0x1
  nop
  nop
  add $2,$2,$1
  • 思考题 8:
    • 对于单条指令的测试,与 P3/P4 基本相同,即构造不同类别、不同大小、边界条件的数据,以保证测试的覆盖性。
    • 对于冲突和冒险的测试,根据上面思考题 7 提到的方案进行构造。对于寄存器冲突的测试,只需要测试相邻 / 相隔一个周期 / 相隔两个周期即可,因为相隔三个周期及以上已存入通用寄存器下一个周期才读取并存入流水线寄存器,不会产生数据冒险。对于乘除模块,则需要测试相隔高达 5/10 个周期。
更新于

请我喝[茶]~( ̄▽ ̄)~*

Zeng Huaxu 微信支付

微信支付

Zeng Huaxu 支付宝

支付宝