Featured image of post 北航CO_P4-verilog搭建单周期CPU

北航CO_P4-verilog搭建单周期CPU


单周期CPU设计方案

概述

本次课下主要依靠P3的logisim进行翻译,不过我也对P3设计的一系列不合理之处进行了调整。我首先根据logisim电路图搭建各个模块,随后在mips.v文件中把各个模块合理地连接起来,最后添加controller模块产生控制信号。

指令说明

本文实现的CPU在P3基础上添加了2条指令,即jaljr

R型指令

  • add
  • sub
  • jr

实际上实现的指令相当于addusubu,因为题目明确指出不考虑溢出

I型指令

  • ori
  • lw
  • sw
  • beq
  • lui

J型指令

  • jal

空指令

  • nop

功能模块设计

IFU

将PC和IM合二为一。IM使用$readmehn语句读取code.txt文件里的指令。

信号名 方向 描述 位宽
clk input 时钟信号 1
reset input 复位信号 1
next_pc input 下一个指令地址 32
PC output 当前指令地址寄存器 32
instr output 当前指令 32

NPC

用组合逻辑计算出下一个指令的地址,通常是变为PC+4。根据控制信号不同会执行beq/jal/jr三种跳转。

信号名 方向 描述 位宽
PC input 当前指令地址 32
offset input 分支偏移量 16
instr_26 input 指令中26位目标地址 26
ra_data input 从GRF返回的地址数据 32
zero input 分支条件标志 1
NPCOp input 下一地址选择操作码 3
next_pc output 计算后的下一指令地址 32
pc_add_4 output 当前地址加4的值 32

GRF

寄存器堆,同样是翻译即可。注意要display否则无法评测。

信号名 方向 描述 位宽
clk input 时钟信号 1
reset input 复位信号 1
RegWrite input 寄存器写入使能信号 1
RA1 input 读寄存器1地址 5
RA2 input 读寄存器2地址 5
WA input 写寄存器地址 5
WD input 写入数据 32
RD1 output 读寄存器1数据 32
RD2 output 读寄存器2数据 32
PC input 当前程序计数器地址 32

DM

数据存储器,朴实无华的读写功能。同样需要display

信号名 方向 描述 位宽
PC input 当前程序计数器地址 32
clk input 时钟信号 1
reset input 复位信号 1
MemWrite input 内存写入使能信号 1
WA input 写地址 32
WD input 写入数据 32
RD output 读取数据 32

ALU

本文需要的指令只需要四个运算:加、减、或。相等的判断用减法实现。

信号名 方向 描述 位宽
ALUOp input ALU操作选择信号 3
A input ALU第一个操作数 32
B input ALU第二个操作数 32
zero output 结果是否为零标志 1
ALU_res output ALU运算结果 32

MUX

这个模块的设计和每个人自身的电路设计高度相关,我认为先建立mips.v顶层文件再构造更好。具体内容在下一部分讲解。

信号名 方向 描述 位宽
rt input 源寄存器1地址 5
rd input 源寄存器2地址 5
WAOp input 写地址选择操作码 2
MemRD input 从内存读取的数据 32
offset input 偏移量 16
ALU_res input ALU运算结果 32
pc_add_4 input 当前PC加4的值 32
WDOp input 写数据选择操作码 2
RD2 input 第二个读寄存器的数据 32
Ext_imm16 input 扩展的16位立即数 32
BOp input B选择信号 1
B output 选择的第二个操作数 32
WD output 写入的数据 32
A3 output 写入的目标寄存器地址 5

组建数据通路

verilog组建数据通路的难度远大于logisim,因为不够直观,很容易漏接某些线。我认为比较不容易出错的设计方式如下:

  1. 将除MUX外的所有模块放入mips.v文件中并接上所有的I/O端口。为防出错,所有的接口都用同名wire连接,可以先连好再定义这些wire型变量。
  2. 观察不同模块的接口,根据自己整理的表格/P3的电路图确定哪些端口之间是一对一连接的。使用assign语句把连接到对应端口的wire变量链接在一起。
  3. 对于需要用到多路选择器的端口,我们建立一个统一的MUX模块,这个模块负责处理所有需要进行信号选择的端口。我们将所有可能的输入信号和一会用Controller.v产生的选择信号全部作为MUX的输入,所有输出信号作为输出,内部使用条件判断语句为各个输出信号赋上对应的值。随后将其添加到mips.v中。

控制信号

建立一个新的Controller.v模块,因为CPU操作只跟指令有关,所以输入只需要opcodefunct。对于其内部结构,我喜欢先为每一条指令单独定义一个wire型变量,再以这些变量为条件去编写控制信号的生成逻辑。

信号名 方向 描述 位宽
opcode input 指令操作码 6
funct input 功能码,用于区分操作类型 6
NPCOp output 下一地址选择操作码 3
RegWrite output 寄存器写使能信号 1
ALUOp output ALU操作码 3
MemWrite output 内存写使能信号 1
ExtOp output 立即数扩展操作选择信号 1
WAOp output 写地址选择操作码 2
WDOp output 写数据选择操作码 2
BOp output 第二操作数选择信号 1

思考题

  1. 阅读下面给出的 DM 的输入示例中(示例 DM 容量为 4KB,即 32bit × 1024字),根据你的理解回答,这个 addr 信号又是从哪里来的?地址信号 addr 位数为什么是 [11:2] 而不是 [9:0] ? alt text 答:addr是ALU通过将寄存器值与偏移量offset相加得到的地址信号,之所以取[11:2]是因为DM的存储是以32bit为单位,而地址信号是以8bit为单位,故略去后两位使地址信号与DM内的存储地址一致。
  2. 思考上述两种控制器设计的译码方式,给出代码示例,并尝试对比各方式的优劣 答:指令对应的控制信号如何取值:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    case (opcode)
         6'b000000: begin // R型指令,funct决定操作
             case (funct)
                 6'b100000: begin // add
                     RegWrite = 1;
                     ALUOp = 3'b000;
                     WAOp = 2'b01;
                     NPCOp = 3'b0;
                     MemWrite = 0;
                     ExtOp = 0;
                     WDOp = 2'b0;
                     BOp = 0;
                 end
                 6'b100010: begin // sub
                     RegWrite = 1;
                     ALUOp = 3'b001;
                     WAOp = 2'b01;
                     NPCOp = 3'b0;
                     MemWrite = 0;
                     ExtOp = 0;
                     WDOp = 2'b0;
                     BOp = 0;
                 end
                 //略
             endcase
          //略
     endcase
    
    控制信号每种取值时对应的指令:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    assign NPCOp = beq ? 3'b001:
                    jal ? 3'b010:
                    jr  ? 3'b011:
                    3'b0;
     assign RegWrite = add || sub || ori || lw || lui || jal;
     assign ALUOp = add || lw || sw ? 3'b000:
                    sub || beq ? 3'b001:
                    ori ? 3'b010:
                    3'b111;
     assign MemWrite = sw;
     assign ExtOp = ori;
     assign WAOp = add || sub || jr ? 2'b01:
                   jal ? 2'b10:
                   2'b0;
     assign WDOp = lw ? 2'b01:
                   lui? 2'b10:
                   jal? 2'b11:
                   2'b0;
     assign BOp = ori || lw || sw;
    
  3. 在相应的部件中,复位信号的设计都是同步复位,这与 P3 中的设计要求不同。请对比同步复位与异步复位这两种方式的 reset 信号与 clk 信号优先级的关系。 答:同步复位中clk信号优先级更高,只有clk上升沿到来时reset信号才有影响,而异步复位二者地位相当,二者都可以随时生效。
  4. C 语言是一种弱类型程序设计语言。C 语言中不对计算结果溢出进行处理,这意味着 C 语言要求程序员必须很清楚计算结果是否会导致溢出。因此,如果仅仅支持 C 语言,MIPS 指令的所有计算指令均可以忽略溢出。 请说明为什么在忽略溢出的前提下,addiaddiu 是等价的,addaddu 是等价的。 答:以addaddu为例,从RTL描述中可以看出,addu就是在add的基础上增加了如果运算结果最高位和次高位不相等就抛出报错,所以如果忽略这个溢出,二者就是等价的。addiaddiu同理。
使用 Hugo 构建
主题 StackJimmy 设计