【CO】P4课下——单周期CPU(Verilog实现)

本文为P4课下单周期CPU设计思路与具体细节,仅供参考

P4课下–单周期CPU的Verilog实现

总体设计方案

指令集合

课下提交要求实现的指令包括add(u),sub(u),ori,lui,beq,lw,sw,nop,j,jal,jr。在此基础上添加了移位指令sll。分类如下:

R型指令 I型指令 J型指令
add(u),sub(u),sll(nop),jr ori,lui,beq,lw,sw j,jal

模块设计

从模块划分上看,P4与P3基本相同,包括PC,NPC,IM,GRF,ALU,DM,EXT,CTRL等模块。这些模块分别作为项目下的一个.v文件。为了将这些模块能更清晰地进行实例化,添加了datapath.v和mips.v作为上层模块来进行实例化。

整体架构

整体架构

具体模块设计

PC

端口说明

端口 功能
next_pc 接收来自NPC模块指示的下一条指令地址,并在时钟上升沿更新
clk 时钟信号
reset 异步复位信号,为1’b1时将当前指令设置为起始位置
pc 输出当前pc

实现

时序逻辑实现对寄存器exe_pc的更新,assign对pc输出端口进行连接。

NPC

端口说明

端口 功能
pc 接收当前指向的指令地址
pc_add_four 输出当前pc+4的值,便于jal指令跳转时将pc+4的值存入$ra中
IMM 接收当前J型指令的26位转移地址
beq_offset 接收beq指令中16位偏移量
RA 接收读出的jr指令转移至$31指向的地址(仅设计了jr $ra)
zero 接收ALU返回的rs rt寄存器值是否相等的信号,用于beq
NPCOp 接收CTRL返回的下一个PC所指向的位置类型控制信号
npc 向PC传递经NPC模块得到的下一条执行指令的位置

其中,控制信号NPCOp的功能具体如下:

信号 功能
2’b00 npc为pc+4
2’b01 npc为j/jal指向的指令
2’b10 npc为$ra指向的指令
2’b11 npc为pc+4/beq分支的指令,取决于zero的输入

实现

利用多目运算符和assign语句对端口进行赋值即可。

注意:

  1. 使用某中间变量(不准确的称呼)时,首先应声明为wire型,同时要注意端口位宽的匹配。
  2. 此处pc接收的应未减0x00003000,并且输出npc前减去0x00003000以保持与pc的配合。

IM

利用initial块中的$readmemh("code.txt",rom);语句即可实现从当前目录下读取文件并放入指定位置。

注意:
在声明寄存器阵列作为IM时,应当从低到高,如:reg [31:0] rom [0:4096];。这样做可以保证ISE和VCS仿真结果是相同的。

GRF

端口说明

端口 功能
A1 读取的第一个寄存器编号(rs)
A2 读取的第二个寄存器编号(rt)
WR 写入数据的目标寄存器(rd/rt/$ra)
WD 写入目标寄存器的数据
RD1 A1中的数据
RD2 A2中的数据
clk 时钟信号
reset 异步复位信号
WE 写入信号
pc 当前pc,以便于输出信息

实现

在时序逻辑中实现对内容的更新。注意不要对零寄存器进行更新,也不要输出信息。在判断条件中应当注意这一点。

ALU

端口说明

端口 功能
A 进行运算的第一个数
B 进行运算的第二个数
sll 进行sll操作需要左移的位数
ALUOp 控制信号,选择ALU进行的操作
zero 输出两个寄存器中的值是否相等,为beq指令时的NPC提供是否分支的依据
ans 输出运算结果

其中,控制信号ALUOp的功能具体如下:

信号 功能
3’b000 A+B
3’b001 A-B
3’b010 A
3’b011 B<<sll
3’b100 B<<16

实现电路

同样使用assign和多目运算符配合实现。Verilog中默认为无符号数,所以这里不需要考虑符号带来的问题。Verilog中4’b0认为是无符号数,但是0认为是有符号数。

DM

和IM几乎相同。P3中我的DMOp非常别扭,DM出的问题(只接了ld没有接str)也导致了P3课上挂掉:sob:,P4中对DMOp对应功能进行了更改,使其不再那么别扭。

信号 功能
1’b0 读出数据
1’b1 写入数据

EXT

选择0扩展或符号扩展,一条简单的assign语句。

信号 功能
1’b0 0扩展
1’b1 符号扩展

CTRL

端口说明

端口 功能
OpCode 获取指令的操作码
FuncCode 获取指令的功能码
NPCOp 模块控制信号,控制NPC操作
ALUOp 模块控制信号,控制ALU操作
WE 模块控制信号,控制GRF读写
DMOp 模块控制信号,控制DM读写
WRSlt 数据通路控制信号,控制写入的寄存器来源
WDSlt 数据通路控制信号,控制写入的数据来源
ALUSlt 数据通路控制信号,控制进入ALU的第二个数来源
EXTOp 模块控制信号,控制EXT扩展方式

实现

和P3中的设计思路一样,分为与逻辑(识别)和或逻辑(赋值)。这里要比Logisim中简单的多。

数据通路分析

和P3的设计一样,下面再次给出:

WRSlt对应的选择器选择进行写入操作的寄存器编号:

WRSlt 来源
2’b00 rd
2’b01 rt
2’b10 $ra

WDSlt对应的选择器选择写入寄存器的数据:

WDSlt 来源
2’b00 pc+4
2’b01 ALU计算得到的ans
2’b10 DM读取的数据

ALUSlt对应的选择器选择进入ALU模块的第二个数:

ALUSlt 来源
1’b0 RD2
1’b1 EXT扩展后的结果

综上,给出每条指令对应的各个控制信号:

指令 NPCOp WE ALUOp DMOp WRSlt WDSlt ALUSlt EXTOp
add(u) 2’b00 1’b1 3’b000 1’b0 2’b00 2’b01 1’b0 1’b0
sub(u) 2’b00 1’b1 3’b001 1’b0 2’b00 2’b01 1’b0 1’b0
sll 2’b00 1’b1 3’b011 1’b0 2’b00 2’b01 1’b0 1’b0
ori 2’b00 1’b1 3’b010 1’b0 2’b01 2’b01 1’b1 1’b0
lui 2’b00 1’b1 3’b100 1’b0 2’b01 2’b01 1’b1 1’b0
beq 2’b11 1’b0 3’b001 1’b0 2’b00 2’b00 1’b0 1’b0
lw 2’b00 1’b1 3’b000 1’b0 2’b01 2’b10 1’b1 1’b1
sw 2’b00 1’b0 3’b000 1’b1 2’b00 2’b00 1’b1 1’b1
j 2’b01 1’b0 3’b000 1’b0 2’b10 2’b00 1’b0 1’b0
jal 2’b01 1’b1 3’b000 1’b0 2’b00 2’b00 1’b0 1’b0
jr 2’b10 1’b0 3’b000 1’b0 2’b10 2’b00 1’b0 1’b0

datapath和mips

实例化以上模块即可,注意位宽的匹配和实例化过程中变量的对应。

思考题

  1. 阅读下面给出的 DM 的输入示例中(示例 DM 容量为 4KB,即 32bit × 1024字),根据你的理解回答,这个 addr 信号又是从哪里来的?地址信号 addr 位数为什么是 [11:2] 而不是 [9:0] ?

    示例

答:addr信号来自于ALU模块,位数是[11:2]因为DM中按字存储,每个字有4个字节,所以左移两位才对应着DM中储存的字的位置。

  1. 思考上述两种控制器设计的译码方式,给出代码示例,并尝试对比各方式的优劣。

    答:
    指令对应的控制信号如何取值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    always@(*) begin 
    case(OpCode)
    6’b101011: begin
    DMOp=1’b1
    ...
    end
    ...
    endcase
    end

    控制信号每种取值所对应的指令:

    1
    2
    3
    4
    5
    assign add=(OpCode==6'b000000&&FuncCode==6'b100000)?1:0;
    assign NPCOp=(add|sub|sll|ori|lui|lw|sw)?2'b00:
    (j|jal)?2'b01:
    (jr)?2'b10:
    2'b11;

    记录指令对应的控制信号如何取值便于观察每条指令的运作,与其他指令可以独立开来,添加指令时更方便。
    记录控制信号每种取值所对应的指令便于对控制信号进行观察,检查控制信号是否按照预期的设计输出。

  2. 在相应的部件中,复位信号的设计都是同步复位,这与 P3 中的设计要求不同。请对比同步复位异步复位这两种方式的 reset 信号与 clk 信号优先级的关系。

    答:同步复位中reset信号优先级低于clk信号;
    异步复位中reset信号优先级高于clk信号。

  3. C 语言是一种弱类型程序设计语言。C 语言中不对计算结果溢出进行处理,这意味着 C 语言要求程序员必须很清楚计算结果是否会导致溢出。因此,如果仅仅支持 C 语言,MIPS 指令的所有计算指令均可以忽略溢出。 请说明为什么在忽略溢出的前提下,addi 与 addiu 是等价的,add 与 addu 是等价的。提示:阅读《MIPS32® Architecture For Programmers Volume II: The MIPS32® Instruction Set》中相关指令的 Operation 部分。

    答:add和addi都会对溢出进行检查,如果溢出,则抛出SignalException(IntegerOverflow)异常,而addu和addiu不进行溢出检查,所以在忽略溢出的前提下,两种指令是等价的。

测试方案

此次自动化测试相对比较简单,原因在于ISE可以通过命令行操作编译项目后直接获得控制台输出,同时在模块设计过程中已实现了向控制台的输出。我们只需要捕获这些输出并且与编译后的C程序输出进行对拍即可。


【CO】P4课下——单周期CPU(Verilog实现)
http://example.com/2024/11/03/【CO】P4课下--单周期CPU(Verilog实现)/
作者
mRNA
发布于
2024年11月3日
许可协议