计算机组成原理上机实验7 流水线CPU
2018-06-14

实验目的

设计一个MIPS指令集的CPU,

1、 基本要求:

a)    多周期
b)    包含16条指令
    i.    add addi addu sub subu 
    ii.
and andi or nor xor
    iii. bgtz bne j jr
    iv.    lw sw

2、 扩展要求

a)    在16条的基础上,增加其他指令
b)    实现中断功能
c)    实现流水
d)    实现下载

实验平台

EDA工具为ISE14.7,开发板型号为Digilent的Nexys3,FPGA型号为Spartan-6 XC6SLX16-CS324。

使用部件

开发板上的开关,7段数码管和按钮。

最终实现功能

1、实现五段流水线

2、实现完全转发和冒险检测

3、实现36条指令

4、实现下载并在数码管上动态显示内存

详细设计过程

1、 MIPS指令格式

MIPS-IV指令集标准下的指令长度为32位,分为R型、I型和J型指令


R-Tpye

I-Tpye

J-Tpye

它们的共同部分是26-31位的Op码,用来区分不同的指令,其中

(1)R-Type的指令Op=000000,再根据0-5位的Funct部分区分不同指令

(2)I-Type的指令0-15位为立即数,位扩展后用于直接计算或者访存和跳转偏移地址。Op=000001的I-Type指令,根据Rt的不同来区分。

(3)J-Type的指令除了Op之外,0-25位为立即数,位扩展后作为跳转地址偏移。
本次实现的36条指令,详细格式如下表

Instr 31-26 25-21 20-16 15-11 10-6 5-0 备注
op rs rt rd shamt func
SLL 000000 - $2 $1 shamt 000000 $1=$2<<shamt
SRL - $2 $1 shamt 000010 $1=$2>>shamt
SRA - $2 $1 shamt 000011 $1=$2>>>shamt
SLLV $3 $2 $1 - 000100 $1=$2<<$3
SRLV $3 $2 $1 - 000110 $1=$2>>$3
SRAV $3 $2 $1 - 000111 $1=$2>>>$3
JR $1 - - - 001000 PC=$1
MOVZ $3 $2 $1 001010 Set $t1 = $t2 if $t3 is zero
MOVN $3 $2 $1 001011 Set $t1 = $t2 if $t3 is not zero
ADD $2 $3 $1 - 100000 $1=$2+$3
ADDU $2 $3 $1 - 100001 $1=$2+$3(unsigned)
SUB $2 $3 $1 - 100010 $1=$2-$3
SUBU $2 $3 $1 - 100011 $1=$2-$3(unsigned)
AND $2 $3 $1 - 100100 $1=$2 and $3
OR $2 $3 $1 - 100101 $1=$2 or $3
XOR $2 $3 $1 - 100110 $1=$2 xor $3
NOR $2 $3 $1 - 100111 $1=$2 nor $3
SLT $2 $3 $1 - 101010 if($2<$3) $1=1 else $1=0
SLTU $2 $3 $1 - 101011 if($2<$3) $1=1 else $1=0(unsigned)
BLTZ 000001 $1 00000 offset if($1<0) PC=PC+4+(sign-extend)offset<<2
BGEZ $1 00001 offset if($1>=0) PC=PC+4+(sign-extend)offset<<2
J 000010 immediate PC=PC[31:28]+immediate<<2
BEQ 000100 $1 $2 offset if($1==$2) PC=PC+4+(sign-extend)offset<<2
BNE 000101 $1 $2 offset if($1!=$2) PC=PC+4+(sign-extend)offset<<2
BLEZ 000110 $1 - offset if($1<=0) PC=PC+4+(sign-extend)offset<<2
BGTZ 000111 $1 - offset if($1>0) PC=PC+4+(sign-extend)offset<<2
ADDI 001000 $2 $1 immediate $1=$2+(sign-extend)immediate
ADDIU 001001 $2 $1 immediate $1(unsigned)=$2+(sign-extend)immediate
SLTI 001010 $2 $1 immediate if($2<(sign-extend)immediate) $1=1 else $1=0
SLTIU 001011 $2 $1 immediate if($2<(sign-extend)immediate) $1=1 else $1=0
ANDI 001100 $2 $1 immediate $1=$2 and (zero-extend)immediate
ORI 001101 $2 $1 immediate $1=$2 or (zero-extend)immediate
XORI 001110 $2 $1 immediate $1=$2 xor (zero-extend)immediate
LUI 001111 - $1 immediate $1=immediate*65536
LW 100011 $2 $1 immediate $1=memory[$2+10]
SW 101011 $2 $1 immediate memory[$2+10]=$1

2、 整体设计

(1) 分为五个流水段IF、ID、EX、MEM、WB。

(2) clk上升沿为每段的开始,每段长度为一个clk。

(3) 每段的相关模块可以和clk上升沿同步也可以与clk下降沿同步。

(4) 甘特图如下

(5)详细数据通路图如下

3、 各模块设计,主要有以下几个模块

a) alu模块——算术逻辑单元

| alu.v | | | |
| — |
| 输入/输出 | 宽度 | 信号名 | 说明 |
| input | [31:0] | alu_a | 无符号型的操作数a,如果有负数,是以补码存储 |
| input | [31:0] | alu_b, | 无符号型的操作数b,如果有负数,是以补码存储 |
| input | [4:0] | alu_op, | 运算类型 |
| output | [31:0] | alu_out | 无符号型的运算结果,如果有负数,是以补码存储 |

alu模块完成大部分R型指令的计算,case判断alu_op的值,使用使用verilog语言中的各运算符完成运算。

这里应该注意的是add和addu的实现是一样的,这是因为当操作数都是补码表示时,符号可以直接参与运算,两条指令的区别在于,add视结果为有符号,因此如果要实现中断功能,就需要判断是否溢出。具体方法是alu_a与alu_b符号同号时,alu_out与它们异号,则表示溢出。

另外,还需要注意slt的实现,比较大小时,不能使用alu_a-alu_b>0,因为它们都是无符号数,相减结果认为无符号数,而无符号数>0是恒成立的。

b) regfile模块——寄存器文件

| regfile.v | | | |
| — |
| 输入/输出 | 宽度 | 信号名 | 说明 |
| input | [0:0] | clk | 时钟沿 |
| input | [0:0] | rst_n | 复位信号,低电平有效 |
| input | [4:0] | rAddr1 | 读地址1 |
| output | [31:0] | rDout1 | 读数据1 |
| input | [4:0] | rAddr2 | 读地址2 |
| output | [31:0] | rDout2 | 读数据2 |
| input | [4:0] | wAddr | 写地址 |
| input | [31:0] | wDin | 写数据 |
| input | [0:0] | wEna | 写使能,高电平有效 |

这个模块实现了一个32个32位宽的寄存器组,有两个读端口,一个写端口,读取数据为异步读,写入数据在时钟上升沿且写使能有效,使用非阻塞赋值。

c) mux模块和mux4模块——2路和4路选择器

| mux.v | | | |
| — |
| 输入/输出 | 宽度 | 信号名 | 说明 |
| input | [0:0] | sel | 选择信号 |
| input | [WIDTH-1:0] | d0 | 选择数据1 |
| input | [WIDTH-1:0] | d1 | 选择数据2 |
| output | [WIDTH-1:0] | out | 输出 |

d) IP核生成的DMem模块和IMem模块——存放数据段和代码段

Mem是同步读,同步写,且由指定coe文件初始化,coe文件的内容是16进制文本,由Mars编译汇编代码生成。

e) IFID模块——IF段和ID段之间的寄存器

| IFID.v | | | |
| — |
| 输入/输出 | 宽度 | 信号名 | 说明 |
| input | [0:0] | clk | 时钟沿 |
| input | [0:0] | en | 使能信号,高电平有效 |
| input | [0:0] | flush | 清空信号,高电平有效 |
| input | [31:0] | PCPlus_in | IF段的PC+4 |
| input | [31:0] | IMemout_in | IF段的Imem输出 |
| output | [31:0] | PCPlus_out | ID段的PC+4 |
| output | [31:0] | IMemout_out | ID段的Imem输出 |

f) IDEX模块——ID段和EX段之间的寄存器

| IDEX.v | | | |
| — |
| 输入/输出 | 宽度 | 信号名 | 说明 |
| input | [0:0] | clk | 时钟沿 |
| input | [0:0] | en | 使能信号高电平有效 |
| input | [0:0] | flush | 清空信号高电平有效 |
| input | [31:0] | PCPlus_in | ID段的PCPlus |
| input | [31:0] | RegRdout1_in | ID段的RegRdout1 |
| input | [31:0] | RegRdout2_in | ID段的RegRdout2 |
| input | [31:0] | IMMSignExtended_in | ID段的IMMSignExtended |
| input | [31:0] | IMMZeroExtended_in | ID段的IMMZeroExtended |
| input | [31:0] | ShamtZeroExtended_in | ID段的ShamtZeroExtended |
| input | [4:0] | Rs_in | ID段的Rs |
| input | [4:0] | Rt_in | ID段的Rt |
| input | [4:0] | RegWtaddr_in | ID段的RegWtaddr |
| output | [31:0] | PCPlus_out | EX段的PCPlus |
| output | [31:0] | RegRdout1_out | EX段的RegRdout1 |
| output | [31:0] | RegRdout2_out | EX段的RegRdout2 |
| output | [31:0] | IMMSignExtended_out | EX段的IMMSignExtended |
| output | [31:0] | IMMZeroExtended_out | EX段的IMMZeroExtended |
| output | [31:0] | ShamtZeroExtended_out | EX段的ShamtZeroExtended |
| output | [4:0] | Rs_out | EX段的Rs |
| output | [4:0] | Rt_out | EX段的Rt |
| output | [4:0] | RegWtaddr_out | EX段的RegWtaddr |
| input | [0:0] | RegDst_in | ID段的RegDst |
| input | [0:0] | ALUSrcASel_in | ID段的ALUSrcASel |
| input | [1:0] | ALUSrcBSel_in | ID段的ALUSrcBSel |
| input | [4:0] | ALUControl_in | ID段的ALUControl |
| input | [0:0] | DMemRead_in | ID段的DMemRead |
| input | [0:0] | DMemWrite_in | ID段的DMemWrite |
| input | [0:0] | DMemtoReg_in | ID段的DMemtoReg |
| input | [0:0] | RegWrite_in | ID段的RegWrite |
| output | [0:0] | RegDst_out | EX段的RegDst |
| output | [0:0] | ALUSrcASel_out | EX段的ALUSrcASel |
| output | [1:0] | ALUSrcBSel_out | EX段的ALUSrcBSel |
| output | [4:0] | ALUControl_out | EX段的ALUControl |
| output | [0:0] | DMemRead_out | EX段的DMemRead |
| output | [0:0] | DMemWrite_out | EX段的DMemWrite |
| output | [0:0] | DMemtoReg_out | EX段的DMemtoReg |
| output | [0:0] | RegWrite_out | EX段的RegWrite |

g) EXMEM模块——EX段和MEM段之间的寄存器

| EXMEM.v | | | |
| — |
| 输入/输出 | 宽度 | 信号名 | 说明 |
| input | [0:0] | clk | 时钟沿 |
| input | [0:0] | en | 使能信号高电平有效 |
| input | [0:0] | flush | 清空信号高电平有效 |
| input | [31:0] | ALUResult_in | EX段的ALUResult |
| input | [31:0] | DMemin_in | EX的Dmemin |
| input | [4:0] | RegWtaddr_in | EX的RegWtaddr |
| output | [31:0] | ALUResult_out | MEM段的ALUResult |
| output | [31:0] | DMemin_out | MEM的Dmemin |
| output | [4:0] | RegWtaddr_out | MEM的RegWtaddr |
| input | [0:0] | DMemRead_in | EX段的DMemRead |
| input | [0:0] | DMemWrite_in | EX段的DMemWrite |
| input | [0:0] | DMemtoReg_in | EX段的DMemtoReg |
| input | [0:0] | RegWrite_in | EX段的RegWrite |
| output | [0:0] | DMemRead_out | MEM段的DMemRead |
| output | [0:0] | DMemWrite_out | MEM段的DMemWrite |
| output | [0:0] | DMemtoReg_out | MEM段的DMemtoReg |
| output | [0:0] | RegWrite_out | MEM段的RegWrite |

h) MEMWB模块——MEM段和WB段之间的寄存器

| MEMWB.v | | | |
| — |
| 输入/输出 | 宽度 | 信号名 | 说明 |
| input | [0:0] | clk | 时钟沿 |
| input | [0:0] | en | 使能信号高电平有效 |
| input | [0:0] | flush | 清空信号高电平有效 |
| input | [31:0] | ALUResult_in | MEM段的ALUResult |
| input | [31:0] | DMemout_in | MEM段的Dmemout |
| input | [4:0] | RegWtaddr_in | MEM段的RegWtaddr |
| output | [31:0] | ALUResult_out | WB段的ALUResult |
| output | [31:0] | DMemout_out | WB段的DMemout |
| output | [4:0] | RegWtaddr_out | WB段的RegWtaddr |
| input | [0:0] | DMemtoReg_in | MEM段的DMemtoReg |
| input | [0:0] | RegWrite_in | MEM段的RegWrite |
| output | [0:0] | DMemtoReg_out | WB段的DMemtoReg |
| output | [0:0] | RegWrite_out | WB段的RegWrite |

i) dff模块——D触发器,用于各个段寄存器

| dff.v | | | |
| — |
| 输入/输出 | 宽度 | 信号名 | 说明 |
| input | [0:0] | clk | 时钟沿 |
| input | [0:0] | en | 使能信号高电平有效 |
| input | [0:0] | rst | 复位信号高电平有效 |
| input | [WIDTH-1:0] | datain | 输入数据 |
| output | [WIDTH-1:0] | dataout | 输出数据 |

这是一个通用的D触发器,位数作为参数可根据需要变化,用一个always在时钟沿触发来实现非阻塞赋值。

j) compare模块——用于分支信号的判断

| dff.v | | | |
| — |
| 输入/输出 | 宽度 | 信号名 | 说明 |
| input | [31:0] | a, | 有符号数a,需加前缀signed |
| input | [31:0] | b, | 有符号数b,需加前缀signed |
| output | [1:0] | res | 比较结果 |

a等于b返回2’b01,a小于b返回2’b00,a大于b返回2’b10,比较均是基于有符号数。

k) SignExtended模块——立即数符号扩展

过于简单,直接在top中实现了。

l) ZeroExtended模块——立即数无符号扩展

过于简单,直接在top中实现了。

m) forward模块——旁路前推模块,实现转发

| forward.v | | | |
| — |
| 输入/输出 | 宽度 | 信号名 | 说明 |
| input | [4:0] | Rs_EX | EX段的Rs |
| input | [4:0] | Rt_EX | EX段的Rt |
| input | [0:0] | RegWrite_MEM | MEM段的RegWrite |
| input | [0:0] | RegWrite_WB | WB段的RegWrite |
| input | [4:0] | RegWtaddr_MEM | MEM段的RegWtaddr |
| input | [4:0] | RegWtaddr_WB | WB段的RegWtaddr |
| output | [1:0] | RegRdout1Sel_Forward_EX | EX段的RegRdout1Sel_Forward |
| output | [1:0] | RegRdout2Sel_Forward_EX | EX段的RegRdout2Sel_Forward |

这个模块实现从DMem输出到ALU输入以及ALU输出到ALU输入的转发。

n) hazard模块——冒险检测模块,实现插入气泡

| hazard.v | | | |
| — |
| 输入/输出 | 宽度 | 信号名 | 说明 |
| input | [4:0] | Rs_ID | ID段的Rs |
| input | [4:0] | Rt_ID | ID段的Rt |
| input | [4:0] | RegWtaddr_EX | EX段的RegWtaddr |
| input | [0:0] | DMemRead_EX | EX段的DMemRead |
| output | [0:0] | PCEn | 允许PC更新,高电平有效 |
| output | [0:0] | IF_ID_En | 允许IFID更新,高电平有效 |
| output | [0:0] | ID_EX_Flush | IDEX清空,高电平有效 |

lw之后R-Type需要使用访存结果,这在时间上是倒流的,不可能,只能插入一个气泡,清空IDEX寄存器,不更新IFID寄存器,不更新PC。

o) control模块——产生控制信号

| control.v | | | |
| — |
| 输入/输出 | 宽度 | 信号名 | 说明 |
| input | [0:0] | clk | 时钟沿 |
| input | [0:0] | rst | 复位信号高电平有效 |
| input | [5:0] | Op | |
| input | [4:0] | Rt | |
| input | [5:0] | Funct | |
| input | [1:0] | RsCMPRt | Rs和Rt寄存器比较结果 |
| input | [1:0] | RsCMPZero | Rs寄存器和0比较结果 |
| output | [1:0] | PCSrc | 0:+4,1:Branch,2:J,3:JR |
| output | [0:0] | RegDst | 0:RegWtaddr=rt,1:RegWtaddr=rd |
| output | [0:0] | ALUSrcASel | 0:RegRdout1,1:ShamtZeroExtended |
| output | [1:0] | ALUSrcBSel | 0:RegRdout2,1:IMMSignExtended,2:IMMZeroExtended |
| output | [4:0] | ALUControl | |
| output | [0:0] | DMemRead | 1:En |
| output | [0:0] | DMemWrite | 1:En |
| output | [0:0] | DMemtoReg | 0:Aluout,1:DMemout |
| output | [0:0] | RegWrite | 1:En |

根据输入信号,判断指令类型,输出对应控制信号的取值。

p) debounce模块——去抖动

| debounce.v | | | |
| — |
| 输入/输出 | 宽度 | 信号名 | 说明 |
| input | [0:0] | clk | 时钟沿 |
| input | [0:0] | in | 输入信号,可能有抖动 |
| output | [0:0] | out | 输出信号,输入稳定后才变化 |

q) seg模块——7段数码管

| seg.v | | | |
| — |
| 输入/输出 | 宽度 | 信号名 | 说明 |
| input | [0:0] | clk | 时钟沿 |
| input | [0:0] | rst_n | 复位信号,低电平有效 |
| input | [31:0] | data32 | 要显示的数据 |
| output | [3:0] | sel | 选择当前显示位,低电平有效 |
| output | [6:0] | segments | 选择显示的数码管,高电平有效 |

r) top模块——实例化以上模块,连接各个信号

| top.v | | | |
| — |
| 输入/输出 | 宽度 | 信号名 | 说明 |
| input | [0:0] | clk | 时钟沿 |
| input | [7:0] | sw | 开关选择 |
| output | [6:0] | seg7 | 7段数码管选择 |
| output | [3:0] | an | 7段数码管位选择 |
| input | [0:0] | btns | 按钮中间,按下复位 |

4、 信号命名规则

(1) 信号名称由几个单词(或其简写)构成,每个单词首字母大写

(2) 一般先写该信号对应的部件名,然后紧跟操作或接口名。

(3) 如果该信号是另外一个信号的选择信号,则加上Sel后缀。

(4) 如果该信号涉及在不同段之间传递,则在对应段的信号后加上所在段的名称。

(5) 以上面方法命名基本不会引起混淆。

  • 比如:RegRdout1_EX表示寄存器文件读端口1读出的数据传递到EX段的信号。

5、 DMem和IMem的初始化

IP核选择Block Memory,设置为Single Port RAM,

宽度为32,深度为256

选中Load Init File设置,选择初始化coe文件的路径,

其中IMem_init_test.coe文件内容为

    memory_initialization_radix = 16;
    memory_initialization_vector =
    20110008
    01d17020
    21ceffff
    11c00001
    02200008
    20100001
    200f2000
    20080010
    00084040
    00084082
    ade80000
    21ef0004
    8de9fffc
    01295004
    3c090001
    ade90000
    21ef0004
    adea0000
    21ef0004
    01505022
    adea0000
    21ef0004
    010a4025
    01004024
    01084027
    ade80000
    21ef0004
    01485826
    000b5843
    020b5807
    adeb0000
    21ef0004
    000b5842
    020b5806
    000b5dc0
    016b5821
    adeb0000
    21ef0004
    01685823
    adeb0000
    21ef0004
    256b0001
    adeb0000
    21ef0004
    356b0001
    316b0003
    390b0001
    adeb0000
    21ef0004
    020b602a
    adec0000
    21ef0004
    020b602b
    adec0000
    21ef0004
    296c0001
    adec0000
    21ef0004
    2d6c0001
    adec0000
    21ef0004
    018c680a
    aded0000
    21ef0004
    0210680b
    aded0000
    21ef0004
    200e0000
    08000062
    08000066
    21ce0001
    adee0000
    21ef0004
    1560fffb
    21ce0001
    adee0000
    21ef0004
    1000fff8
    21ce0001
    adee0000
    21ef0004
    1800fff8
    21ce0001
    adee0000
    21ef0004
    0560fff8
    21ce0001
    adee0000
    21ef0004
    0401fff8
    21ce0001
    adee0000
    21ef0004
    1e00fff8
    21ce0001
    adee0000
    21ef0004
    1560fff8
    21ce0001
    adee0000
    21ef0004
    116bfff8
    21ce0001
    adee0000
    08000068;

DMem的初始化类似该过程,其coe文件初始全为0。

6、 测试的汇编代码

.text
    addi $17, $0, 8 #const $17=8
    add $14, $14, $17 #$14=8
    addi $14, $14, -1 #$14--
    beq $14, $0, begin
    jr $17 #jump to IMemAddr 8
begin:
    addi $16, $0, 1 #const 1
    addi $15, $0, 8192 #MemAddr
    addi $8, $0, 16 #$8=16
    sll $8, $8, 1 #$8=32
    srl $8, $8, 2 #$8=8
    sw $8, 0($15) #-------Save Value: 8------
    addi $15, $15, 4 #MemAddr++ 
    lw $9, -4($15) #$9=8(32'b00000000_00000001_00000000_00001000)
    sllv $10, $9, $9 #$10=2048(32'b00000000_00000000_00001000_00000000)
    lui $9, 1 #$9=32'b00000000_00000001_00000000_00000000
    sw $9, 0($15) #-------Save Value: 32'b00000000_00000001_00000000_00001000------
    addi $15, $15, 4 #MemAddr++ 
    sw $10, 0($15) #-------Save Value: 2048------
    addi $15, $15, 4 #MemAddr++
    sub $10, $10, $16 #$10=2047(32'b00000000_00000000_00000111_11111111)
    sw $10, 0($15) #-------Save Value: 2047------
    addi $15, $15, 4 #MemAddr++
    or $8, $8, $10 #$8=2047(32'b111_11111111)
    and $8, $8, $0 #$8=0
    nor $8, $8, $8 #$8=2^32-1
    sw $8, 0($15) #-------Save Value: 2^32-1------
    addi $15, $15, 4 #MemAddr++ 
    xor $11, $10, $8 #$11=32'b11111111_11111111_11111000_00000000
    sra $11, $11, 1 #$11=32'b11111111_11111111_11111100_00000000
    srav $11, $11, $16 #$11=32'b11111111_11111111_11111110_00000000
    sw $11, 0($15) #-------Save Value: 32'b11111111_11111111_11111110_00000000------
    addi $15, $15, 4 #MemAddr++ 
    srl $11, $11, 1 #$11=32'b01111111_11111111_11111111_00000000
    srlv $11, $11, $16 #$11=32'b00111111_11111111_11111111_10000000
    sll $11, $11, 23 #$11=32'b01000000_00000000_00000000_00000000
    addu $11, $11, $11 #$11=32'b10000000_00000000_00000000_00000000
    sw $11, 0($15) #-------Save Value: 32'b10000000_00000000_00000000_00000000------
    addi $15, $15, 4 #MemAddr++     
    subu $11, $11, $8 #$11=32'b10000000_00000000_00000000_00000001
    sw $11, 0($15) #-------Save Value: 32'b10000000_00000000_00000000_00000001------
    addi $15, $15, 4 #MemAddr++ 
    addiu $11, $11, 1 #$11=32'b10000000_00000000_00000000_00000010
    sw $11, 0($15) #-------Save Value: 32'b10000000_00000000_00000000_00000010------
    addi $15, $15, 4 #MemAddr++ 
    ori $11, $11, 1 #$11=32'b10000000_00000000_00000000_00000011
    andi $11, $11, 3 #$11=32'b00000000_00000000_00000000_00000011
    xori $11, $8, 1 #$11=32'b11111111_11111111_11111111_11111110
    sw $11, 0($15) #-------Save Value: 32'b11111111_11111111_11111111_11111100------
    addi $15, $15, 4 #MemAddr++ 
    slt $12, $16, $11 #$12=0
    sw $12, 0($15) #-------Save Value: 0------
    addi $15, $15, 4 #MemAddr++ 
    sltu $12, $16, $11 #$12=1
    sw $12, 0($15) #-------Save Value: 1------
    addi $15, $15, 4 #MemAddr++ 
    slti $12, $11, 1 #$12=1
    sw $12, 0($15) #-------Save Value: 1------
    addi $15, $15, 4 #MemAddr++ 
    sltiu $12, $11, 1 #$12=0
    sw $12, 0($15) #-------Save Value: 0------
    addi $15, $15, 4 #MemAddr++     
    movz $13, $12, $12 #because $12=0, set $13=$12,
    sw $13, 0($15) #-------Save Value: 0------
    addi $15, $15, 4 #MemAddr++         
    movn $13, $16, $16 #because $16=1(!=0), set $13=$16,
    sw $13, 0($15) #-------Save Value: 1------
    addi $15, $15, 4 #MemAddr++
    addi $14, $0, 0 #count
    j a
i:  j end
h:  addi $14, $14, 1 #count++
    sw $14, 0($15) #-------Save Value: $14------
    addi $15, $15, 4 #MemAddr++     
    bne $11, $0, i #to i    
g:  addi $14, $14, 1 #count++
    sw $14, 0($15) #-------Save Value: $14------
    addi $15, $15, 4 #MemAddr++     
    beq $0, $0, h #to h 
f:  addi $14, $14, 1 #count++
    sw $14, 0($15) #-------Save Value: $14------
    addi $15, $15, 4 #MemAddr++     
    blez $0, g #to g
e:  addi $14, $14, 1 #count++
    sw $14, 0($15) #-------Save Value: $14------
    addi $15, $15, 4 #MemAddr++     
    bltz $11, f #to f
d:  addi $14, $14, 1 #count++
    sw $14, 0($15) #-------Save Value: $14------
    addi $15, $15, 4 #MemAddr++     
    bgez $0, e #to e
c:  addi $14, $14, 1 #count++
    sw $14, 0($15) #-------Save Value: $14------
    addi $15, $15, 4 #MemAddr++     
    bgtz $16, d #to d
b:  addi $14, $14, 1 #count++
    sw $14, 0($15) #-------Save Value: $14------
    addi $15, $15, 4 #MemAddr++     
    bne $11,$0, c #to c
a:  addi $14, $14, 1 #count++
    sw $14, 0($15) #-------Save Value: $14------
    addi $15, $15, 4 #MemAddr++     
    beq $11, $11, b #to b
end:addi $14, $14, 1 #count++
    sw $14, 0($15) #-------Save Value: $14------
out:j out

完整汇编代码如上,36条指令均测试到了,每条语句的执行结果在该语句后面给出了注释,每测试几条指令就将结果写入内存,方便查看,并且3种相关有包括在上面的代码中。

实验结果

(1)仿真结果

汇编代码在Mars编译运行得到Data段为

而ISE仿真结果显示DMem为

汇编代码在Mars编译运行得到结束时寄存器数据和仿真结果的regfile数据

可见测试程序在编写的MIPS CPU上运行结果与Mars运行结果一致,并且符合上面注释的的预期结果。

(2)下载结果

按下按钮中键复位,拨动开关选择地址,数码管显示对应地址内存。

由于数据为32位,4个数码管只能显示16位,为此还实现了动态显示,数字会向左移动循环显示。

附录:

一、 编写过程中遇到问题和一些发现

1、如果位拼接作为左值,单个变量也要花括号,如

{{RegDst},{ALUSrc},{MemtoReg},{RegWrite},{MemWrite},{Branch},{ALUOp[1:0]},{Jump}}=9'b100100100;

2、”==”会严格匹配每一位,比如module内5位数字比较(一个常量,一个input),但外部传入的是3位的,高位自动拓展好像是x,就导致“==”不成立,这是一个不好发现的问题。

3、仿真遇到高阻态,多半是这个信号不存在,经常是拼写错了,有时候ise检查不出来。

4、在top里的wire信号,如果通过control模块(或其他模块)进行控制,那么这个信号不能直接在top里赋初值,要到仿真模块里赋值,不然control里改变这个信号的值时,会变成不确定。

5、在clk边缘用<=对某信号进行赋值时,如果同时刻需要对该信号进行判断(如状态机,根据状态以完成对应操作),则判断时信号的值是本次改变前的。(非阻塞赋值的特性体现)

6、 srl是逻辑右移,高位直接补零,而sra是算术右移,高位补符号,在Verilog中,既可以自行实现,也可以这样实现

`$signed(alu_a) >>> alu_b`

应该注意, >>> 是算术右移,但对于无符号数,高位仍然补0,所以需要用$signed()转为有符号数

`$signed(alu_a) >> alu_b `

这样写也不行,>>不具有算术右移的功能

7、reset的时候应该把各段寄存器的各信号置0,否则将导致forward,hazard模块输出的值不确定进而使相关的选择信号不确定(比如PCEn不确定,这样一开始就不能更新PC)。

二、 模块源代码

top.v

module top(
    input clk,
    input [7:0] sw,
    output [6:0] seg7,
    output [3:0] an,
    input btnr,btnl,btns,btnd,btnu
    );
    //_后缀表示该信号所在的流水段
    wire ALUSrcASel_ID;
    wire ALUSrcASel_EX;
    wire [1:0] ALUSrcBSel_ID;//alu B在regout2和imm之间选择
    wire [1:0] ALUSrcBSel_EX;
    wire [31:0] ALUSrcA_EX;
    wire [31:0] ALUSrcB_EX;
    wire [4:0] ALUControl_ID;
    wire [4:0] ALUControl_EX;
    wire [31:0] ALUResult_EX;
    wire [31:0] ALUResult_MEM;
    wire [31:0] ALUResult_WB;
    wire [1:0] RsCMPZero;
    wire [1:0] RsCMPRt;
    wire [31:0] IMMSignExtended_ID;
    wire [31:0] IMMSignExtended_EX;
    wire [31:0] IMMZeroExtended_ID;
    wire [31:0] IMMZeroExtended_EX;
    wire [31:0] ShamtZeroExtended_ID;
    wire [31:0] ShamtZeroExtended_EX;
    wire [1:0] RegRdout1Sel_Forward_EX;//旁路单元产生的选择信号
    wire [1:0] RegRdout2Sel_Forward_EX;
    wire [31:0] RegRdout1_Forward_EX;//旁路数据
    wire [31:0] RegRdout2_Forward_EX;
    wire [4:0] RegRdaddr1_ID;
    wire [31:0] RegRdout1_ID;
    wire [31:0] RegRdout1_EX;
    wire [4:0] RegRdaddr2_ID;
    wire [31:0] RegRdout2_ID;
    wire [31:0] RegRdout2_EX;
    wire [4:0] RegWtaddr_ID;
    wire [4:0] RegWtaddr_EX;    
    wire [4:0] RegWtaddr_MEM;
    wire [4:0] RegWtaddr_WB;
    wire [31:0] RegWtin_WB;
    wire RegWrite_ID;
    wire RegWrite_EX;
    wire RegWrite_MEM;
    wire RegWrite_WB;
    wire RegDst_ID;
    wire [31:0] IMemaddr;
    wire [31:0] IMemout;
    wire [31:0] DMemaddr_MEM;
    wire [31:0] DMemin_MEM;
    wire DMemRead_MEM;
    wire [31:0] DMemout_MEM;
    wire [31:0] DMemout_WB;
    wire DMemWrite_MEM;
    wire DMemtoReg_EX;
    wire DMemtoReg_MEM;
    wire DMemtoReg_WB;
    wire [31:0] PC;
    wire [31:0] PCPlus_IF;
    wire [31:0] PCPlus_ID;
    wire [31:0] PCPlus_EX;
    wire [31:0] EPC;
    wire [31:0] nextPC;
    wire PCEn;
    wire [1:0] PCSrc_ID;//Control输出的,0:+4,1:Branch,2:J,3:JR
    wire IF_ID_En;
    wire IF_ID_Flush;
    wire ID_EX_Flush;
    wire [31:0] PCJump_ID;
    wire [31:0] PCJR_ID;
    wire [31:0] PCBranch_ID;
    wire [31:0] Instr;  
    wire [5:0] Funct;
    wire [4:0] Shamt;
    wire [15:0] IMM16;
    wire [4:0] Rd;
    wire [4:0] Rt;
    wire [4:0] Rs;
    wire [5:0] Op;
    wire [4:0] Rt_EX;//为了旁路判断
    wire [4:0] Rs_EX;//为了旁路判断
    wire [25:0] JumpIMM;
    wire [31:0] IMMSignExtendedShiftLeft2;
    wire btns_d;
    debounce debounce(clk,btns,btns_d);//中键去抖动
    reg rst;
    assign Led = rst;
    always @(posedge btns_d) rst=~rst;
    //--------------regfile_copy_DMem是DMem的一份复制--------------
    wire [4:0] addr_show;
    wire [31:0] out_show;
    assign addr_show = sw;//开关输输入要显示的地址
    seg seg1(clk,1'b1,out_show,an,seg7);//显示数据
    regfile_copy_DMem regfile_copy_DMem(clk,~rst,addr_show,out_show,DMemaddr_MEM >> 2,DMemin_MEM,DMemWrite_MEM);
    //---------------方便显示内存-------------------------
    //=======================IF========================
    mux4 MUXPC(
        .sel(PCSrc_ID),
        .d0(PCPlus_IF),//+4直接用IF的
        .d1(PCBranch_ID),
        .d2(PCJump_ID),
        .d3(PCJR_ID),
        .out(nextPC)
    );
    dff DFFPC(
        .clk(~clk),//下降沿更新PC
        .en(PCEn),
        .rst(rst),
        .datain(nextPC),
        .dataout(PC)
    );  
    alu ALUPCPlus(PC,4,5'd01,PCPlus_IF);
    assign IMemaddr = PC >> 2;//>>2是因为这里IMem是每个地址存储4字节,和实际上的(一地址一字节)不一样
    IMem IMem(clk,1'b0,IMemaddr,32'b0,IMemout);//上升沿读指令
    //======================IFID========================
    IFID IFID(
        .clk(~clk),
        .en(IF_ID_En),
        .flush(IF_ID_Flush || rst),
        .PCPlus_in(PCPlus_IF),
        .IMemout_in(IMemout),
        .PCPlus_out(PCPlus_ID),
        .IMemout_out(Instr)
    );
    //=======================ID==========================
    assign JumpIMM = Instr[25:0];
    assign Funct = Instr[5:0];
    assign Shamt = Instr[10:6];
    assign IMM16 = Instr[15:0];
    assign Rd = Instr[15:11];
    assign Rt = Instr[20:16];
    assign Rs = Instr[25:21];
    assign Op = Instr[31:26];
    //*******Control*******
    control control(
        //in
        .clk(clk),
        .rst(rst),
        .Op(Op),
        .Rt(Rt),
        .Funct(Funct),
        .RsCMPRt(RsCMPRt),
        .RsCMPZero(RsCMPZero),
        //out
        .PCSrc(PCSrc_ID), 
        //ID
        .RegDst(RegDst_ID),
        //EX
        .ALUSrcASel(ALUSrcASel_ID),
        .ALUSrcBSel(ALUSrcBSel_ID), 
        .ALUControl(ALUControl_ID),
        //MEM
        .DMemRead(DMemRead_ID),
        .DMemWrite(DMemWrite_ID),
        //WB
        .DMemtoReg(DMemtoReg_ID),
        .RegWrite(RegWrite_ID)
    );
    //*******Control*******
    assign RegRdaddr1_ID = Rs;
    assign RegRdaddr2_ID = Rt;
    mux #(5) MUXRegWtaddr(RegDst_ID,Rt,Rd,RegWtaddr_ID);
    assign ShamtZeroExtended_ID = {{27{1'b0}},Shamt};
    assign IMMSignExtended_ID = {{16{IMM16[15]}},IMM16};
    assign IMMZeroExtended_ID = {{16{1'b0}},IMM16};
    assign IMMSignExtendedShiftLeft2 = IMMSignExtended_ID << 2;
    alu BranchALU(PCPlus_ID,IMMSignExtendedShiftLeft2,5'd01,PCBranch_ID);
    assign PCJump_ID = {{PCPlus_ID[31:28]},{{2'b00,JumpIMM}<<2}};
    assign PCJR_ID = RegRdout1_ID;
    assign IF_ID_Flush = (PCSrc_ID != 2'b00);//有跳转则清空IF_ID寄存器
    regfile regfile(clk,~rst,RegRdaddr1_ID,RegRdout1_ID,RegRdaddr2_ID,RegRdout2_ID,RegWtaddr_WB,RegWtin_WB,RegWrite_WB);
    compare compare1(RegRdout1_ID,RegRdout2_ID,RsCMPRt);//for beq,bne
    compare compare2(RegRdout1_ID,0,RsCMPZero);//for movz,movn,blez,bgtz,bltz,bgez
    hazard hazard(Rs,Rt,RegWtaddr_EX,DMemRead_EX,PCEn,IF_ID_En,ID_EX_Flush);
    //======================IDEX=========================
    IDEX IDEX(
        .clk(~clk),
        .en(1'b1),  
        .flush(ID_EX_Flush || rst),
        //data
        //in
        .PCPlus_in(PCPlus_ID),
        .RegRdout1_in(RegRdout1_ID),
        .RegRdout2_in(RegRdout2_ID),
        .IMMSignExtended_in(IMMSignExtended_ID),
        .IMMZeroExtended_in(IMMZeroExtended_ID),
        .ShamtZeroExtended_in(ShamtZeroExtended_ID),
        .Rs_in(Rs),
        .Rt_in(Rt),
        .RegWtaddr_in(RegWtaddr_ID),
        //out
        .PCPlus_out(PCPlus_EX),
        .RegRdout1_out(RegRdout1_EX),
        .RegRdout2_out(RegRdout2_EX),
        .IMMSignExtended_out(IMMSignExtended_EX),
        .IMMZeroExtended_out(IMMZeroExtended_EX),
        .ShamtZeroExtended_out(ShamtZeroExtended_EX),
        .Rs_out(Rs_EX),
        .Rt_out(Rt_EX),
        .RegWtaddr_out(RegWtaddr_EX),
        //control sign
        //in
        .RegDst_in(RegDst_ID),
        .ALUSrcASel_in(ALUSrcASel_ID),
        .ALUSrcBSel_in(ALUSrcBSel_ID), 
        .ALUControl_in(ALUControl_ID),
        .DMemRead_in(DMemRead_ID),
        .DMemWrite_in(DMemWrite_ID),
        .DMemtoReg_in(DMemtoReg_ID),
        .RegWrite_in(RegWrite_ID),
        //out
        .RegDst_out(RegDst_EX),
        .ALUSrcASel_out(ALUSrcASel_EX),
        .ALUSrcBSel_out(ALUSrcBSel_EX), 
        .ALUControl_out(ALUControl_EX),
        .DMemRead_out(DMemRead_EX),
        .DMemWrite_out(DMemWrite_EX),
        .DMemtoReg_out(DMemtoReg_EX),
        .RegWrite_out(RegWrite_EX)
    );
    //========================EX=========================
    forward forward(Rs_EX,Rt_EX,RegWrite_MEM,RegWrite_WB,RegWtaddr_MEM,RegWtaddr_WB,RegRdout1Sel_Forward_EX,RegRdout2Sel_Forward_EX);
    mux4 MUXRegRdout1FW(RegRdout1Sel_Forward_EX,RegRdout1_EX,RegWtin_WB,ALUResult_MEM,0,RegRdout1_Forward_EX);//forward
    mux4 MUXRegRdout2FW(RegRdout2Sel_Forward_EX,RegRdout2_EX,RegWtin_WB,ALUResult_MEM,0,RegRdout2_Forward_EX);//forward
    mux MUXALUSrcA(ALUSrcASel_EX,RegRdout1_Forward_EX,ShamtZeroExtended_EX,ALUSrcA_EX);
    mux4 MUXALUSrcB(ALUSrcBSel_EX,RegRdout2_Forward_EX,IMMSignExtended_EX,IMMZeroExtended_EX,0,ALUSrcB_EX);
    alu alu(ALUSrcA_EX,ALUSrcB_EX,ALUControl_EX,ALUResult_EX);
    //======================EXMEM========================
    EXMEM EXMEM(
        .clk(~clk),
        .en(1'b1),
        .flush(rst),
        //data
        //in
        .ALUResult_in(ALUResult_EX),
        .DMemin_in(RegRdout2_Forward_EX),
        .RegWtaddr_in(RegWtaddr_EX),
        //out
        .ALUResult_out(ALUResult_MEM),
        .DMemin_out(DMemin_MEM),
        .RegWtaddr_out(RegWtaddr_MEM),
        //control sign
        //in
        .DMemRead_in(DMemRead_EX),
        .DMemWrite_in(DMemWrite_EX),
        .DMemtoReg_in(DMemtoReg_EX),
        .RegWrite_in(RegWrite_EX),
        //out
        .DMemRead_out(DMemRead_MEM),
        .DMemWrite_out(DMemWrite_MEM),
        .DMemtoReg_out(DMemtoReg_MEM),
        .RegWrite_out(RegWrite_MEM)
    );
    //=======================MEM=========================   
    assign DMemaddr_MEM = ALUResult_MEM;
    DMem DMem(clk,DMemWrite_MEM,DMemaddr_MEM >> 2,DMemin_MEM,DMemout_MEM);  
    //======================MEMWB========================
    MEMWB MEMWB(
        .clk(~clk),
        .en(1'b1),
        .flush(rst),
        //data
        //in
        .ALUResult_in(ALUResult_MEM),
        .DMemout_in(DMemout_MEM),
        .RegWtaddr_in(RegWtaddr_MEM),
        //out
        .ALUResult_out(ALUResult_WB),
        .DMemout_out(DMemout_WB),
        .RegWtaddr_out(RegWtaddr_WB),
        //control sign
        //in
        .DMemtoReg_in(DMemtoReg_MEM),
        .RegWrite_in(RegWrite_MEM),
        //out
        .DMemtoReg_out(DMemtoReg_WB),
        .RegWrite_out(RegWrite_WB)
    );
    //========================WB=========================
    mux MUXDMemtoReg(DMemtoReg_WB,ALUResult_WB,DMemout_WB,RegWtin_WB);
endmodule

alu.v

`define A_NOP 5'd00 //nop
`define A_ADD 5'd01 //signed_add
`define A_SUB 5'd02 //signed_sub
`define A_AND 5'd03 //and
`define A_OR  5'd04 //or
`define A_XOR 5'd05 //xor
`define A_NOR 5'd06 //nor
`define A_ADDU 5'd07 //unsigned_add
`define A_SUBU 5'd08 //unsigned_sub
`define A_SLT 5'd09 //slt
`define A_SLTU 5'd10 //unsigned_slt
`define A_SLL 5'd11 //sll
`define A_SRL 5'd12 //srl
`define A_SRA 5'd13 //sra
`define A_MOV 5'd14 //movz,movn
`define A_LUI 5'd15 //lui
module alu(
    input [31:0] alu_a,//无符号型的,如果有负数,是以补码存储
    input [31:0] alu_b,
    input [4:0] alu_op,
    output reg [31:0] alu_out

    );
    always@(*)
        case (alu_op)
            `A_NOP: alu_out = 0;
            `A_ADD: alu_out = alu_a + alu_b;
            `A_SUB: alu_out = alu_a - alu_b;
            `A_AND: alu_out = alu_a & alu_b;
            `A_OR : alu_out = alu_a | alu_b;
            `A_XOR: alu_out = alu_a ^ alu_b;
            `A_NOR: alu_out = ~(alu_a | alu_b);
            `A_ADDU: alu_out = alu_a + alu_b;
            `A_SUBU: alu_out = alu_a - alu_b;
            `A_SLT: //a<b(signed) return 1 else return 0;
                begin
                    if(alu_a[31] == alu_b[31]) alu_out = (alu_a < alu_b) ? 32'b1 : 32'b0;
//对于不加signed的变量类型,运算和比较视为无符号,但依然可以存储有符号数,这里相当于自行根据首位判断
//首位相等,即同号情况,直接比较,如果同正,后面31位大的,原数就大,如果同负,后面31位(补码)大的,依然是原数大
                    else alu_out = (alu_a[31] < alu_b[31]) ? 32'b0 : 32'b1;
//异号情况,直接比较符号
                end
            `A_SLTU: alu_out = (alu_a < alu_b) ? 32'b1 : 32'b0;
            `A_SLL: alu_out = alu_b << alu_a;
            `A_SRL: alu_out = alu_b >> alu_a;
            `A_SRA: alu_out = $signed(alu_b) >>> alu_a;
//使用>>>为算术右移,高位补符号,应该注意,如果是无符号数,>>>仍是逻辑右移,故应该$signed
            `A_MOV: alu_out = alu_b;
//原样输出,相当于reg[rt],mov本不需要通过alu,但因为是RType格式,故统一
            `A_LUI: alu_out = alu_b << 16;
            default: ;
        endcase
endmodule

regfile.v

module regfile(
    input clk,
    input rst_n,
    input [4:0] rAddr1,//读地址1
    output [31:0] rDout1,//读数据1
    input [4:0] rAddr2,//读地址2
    output [31:0] rDout2,//读数据2
    input [4:0] wAddr,//写地址
    input [31:0] wDin,//写数据
    input wEna//写使能
    
);
    reg [31:0] data [0:31];
    integer i;
    assign rDout1=data[rAddr1];//读1
    assign rDout2=data[rAddr2];//读2
    always@(posedge clk or negedge rst_n)//写和复位
        if(~rst_n)
        begin
            for(i=0; i<32; i=i+1) data[i]<=0;
        end
        else
        begin
            if(wEna)
                data[wAddr]<=wDin;
        end
endmodule

dff.v

module dff #(parameter WIDTH = 32) ( //Data Flip-Flop 
    input clk,
    input en,
    input rst,
    input [WIDTH-1:0] datain,
    output reg [WIDTH-1:0] dataout
    );
    always@(posedge clk)
    begin
        if(rst)
            dataout <= 0;
        else if(en)
            dataout <= datain;
    end
endmodule

mux.v

module mux #(parameter WIDTH = 32)( //2路选择器
    input sel,
    input [WIDTH-1:0] d0,
    input [WIDTH-1:0] d1,
    output [WIDTH-1:0] out
    );
    assign out = (sel == 1'b1 ? d1 : d0);
endmodule

mux4.v

module mux4 #(parameter WIDTH = 32)( //4路选择器
    input [1:0] sel,
    input [WIDTH-1:0] d0,
    input [WIDTH-1:0] d1,
     input [WIDTH-1:0] d2,
     input [WIDTH-1:0] d3,
    output reg [WIDTH-1:0] out
    );
    always@(*)
        case(sel)
            2'b00: out=d0;
            2'b01: out=d1;
            2'b10: out=d2;
            2'b11: out=d3;
            default:;
        endcase
endmodule

IFID.v

module IFID(
    input clk,
    input en,
    input flush,
    input [31:0] PCPlus_in,
    input [31:0] IMemout_in,
    output [31:0] PCPlus_out,
    output [31:0] IMemout_out
    );
     dff dff1(clk,en,flush,PCPlus_in,PCPlus_out);
     dff dff2(clk,en,flush,IMemout_in,IMemout_out);
endmodule

IDEX.v

module IDEX(
    input clk,
    input en,
    input flush,//flush for stall or start
    input [31:0] PCPlus_in,
    input [31:0] RegRdout1_in,
    input [31:0] RegRdout2_in,
    input [31:0] IMMSignExtended_in,
    input [31:0] IMMZeroExtended_in,
    input [31:0] ShamtZeroExtended_in,
    input [4:0] Rs_in,
    input [4:0] Rt_in,
    input [4:0] RegWtaddr_in,
    output [31:0] PCPlus_out,
    output [31:0] RegRdout1_out,
    output [31:0] RegRdout2_out,
    output [31:0] IMMSignExtended_out,
    output [31:0] IMMZeroExtended_out,
    output [31:0] ShamtZeroExtended_out,
    output [4:0] Rs_out,
    output [4:0] Rt_out,
    output [4:0] RegWtaddr_out,
    //control
    input RegDst_in,
    input ALUSrcASel_in,
    input [1:0] ALUSrcBSel_in, 
    input [4:0] ALUControl_in,
    input DMemRead_in,
    input DMemWrite_in,
    input DMemtoReg_in,
    input RegWrite_in,
    output RegDst_out,
    output ALUSrcASel_out,
    output [1:0] ALUSrcBSel_out, 
    output [4:0] ALUControl_out,
    output DMemRead_out,
    output DMemWrite_out,
    output DMemtoReg_out,
    output RegWrite_out
    );
    dff dff1(clk,en,flush,PCPlus_in,PCPlus_out);
    dff dff2(clk,en,flush,RegRdout1_in,RegRdout1_out);
    dff dff3(clk,en,flush,RegRdout2_in,RegRdout2_out);
    dff dff4(clk,en,flush,IMMSignExtended_in,IMMSignExtended_out);
    dff dff5(clk,en,flush,IMMZeroExtended_in,IMMZeroExtended_out);
    dff dff6(clk,en,flush,ShamtZeroExtended_in,ShamtZeroExtended_out);
    dff #(5) dff7(clk,en,flush,Rs_in,Rs_out);
    dff #(5) dff8(clk,en,flush,Rt_in,Rt_out);
    dff #(5) dff9(clk,en,flush,RegWtaddr_in,RegWtaddr_out);
    
    dff #(1) dff10(clk,en,flush,RegDst_in,RegDst_out);
    dff #(1) dff11(clk,en,flush,ALUSrcASel_in,ALUSrcASel_out);
    dff #(2) dff12(clk,en,flush,ALUSrcBSel_in,ALUSrcBSel_out);  
    dff #(5) dff13(clk,en,flush,ALUControl_in,ALUControl_out);  
    dff #(1) dff14(clk,en,flush,DMemRead_in,DMemRead_out);
    dff #(1) dff15(clk,en,flush,DMemWrite_in,DMemWrite_out);
    dff #(1) dff16(clk,en,flush,DMemtoReg_in,DMemtoReg_out);
    dff #(1) dff17(clk,en,flush,RegWrite_in,RegWrite_out);  
endmodule

EXMEM.v

module EXMEM(
    input clk,
     input en,
     input flush,
     input [31:0] ALUResult_in,
     input [31:0] DMemin_in,
     input [4:0] RegWtaddr_in,
     output [31:0] ALUResult_out,
     output [31:0] DMemin_out,
     output [4:0] RegWtaddr_out,
     //control
     input DMemRead_in,
     input DMemWrite_in,
     input DMemtoReg_in,
     input RegWrite_in,
     output DMemRead_out,
     output DMemWrite_out,
     output DMemtoReg_out,
     output RegWrite_out
    );
     dff dff1(clk,en,flush,ALUResult_in,ALUResult_out);
     dff dff2(clk,en,flush,DMemin_in,DMemin_out);
     dff #(5) dff3(clk,en,flush,RegWtaddr_in,RegWtaddr_out);

     dff #(1) dff14(clk,en,flush,DMemRead_in,DMemRead_out);
     dff #(1) dff15(clk,en,flush,DMemWrite_in,DMemWrite_out);
     dff #(1) dff16(clk,en,flush,DMemtoReg_in,DMemtoReg_out);
     dff #(1) dff17(clk,en,flush,RegWrite_in,RegWrite_out);

endmodule

MEMWB.v

module MEMWB(
     input clk,
     input en,
     input flush,
     input [31:0] ALUResult_in,
     input [31:0] DMemout_in,
     input [4:0] RegWtaddr_in,
     output [31:0] ALUResult_out,
     output [31:0] DMemout_out,
     output [4:0] RegWtaddr_out,
     //control
     input DMemtoReg_in,
     input RegWrite_in,
     output DMemtoReg_out,
     output RegWrite_out
    );
     dff dff1(clk,en,flush,ALUResult_in,ALUResult_out);
     dff dff2(clk,en,flush,DMemout_in,DMemout_out);
     dff #(5) dff3(clk,en,flush,RegWtaddr_in,RegWtaddr_out);
     dff #(1) dff16(clk,en,flush,DMemtoReg_in,DMemtoReg_out);
     dff #(1) dff17(clk,en,flush,RegWrite_in,RegWrite_out);
endmodule

compare.v

`define LESS 2'b00
`define EQUAL 2'b01
`define GREATER 2'b10
module compare(//为了判断分支
     input signed [31:0] a,
     input signed [31:0] b,
     output reg [1:0] res
    );
     always @(*)
        if(a == b) res = 2'b01;
        else if(a < b) res = 2'b00;
        else if(a > b) res = 2'b10;
endmodule

forward.v

module forward(//前推
    input [4:0] Rs_EX,
    input [4:0] Rt_EX,
    input RegWrite_MEM,
    input RegWrite_WB,
    input [4:0] RegWtaddr_MEM,
    input [4:0] RegWtaddr_WB,
    output reg [1:0] RegRdout1Sel_Forward_EX,
    output reg [1:0] RegRdout2Sel_Forward_EX
    );
    always @(*) begin
        RegRdout1Sel_Forward_EX[0] = RegWrite_WB && (RegWtaddr_WB != 0) && (RegWtaddr_MEM != Rs_EX) && (RegWtaddr_WB == Rs_EX);
        RegRdout1Sel_Forward_EX[1] = RegWrite_MEM && (RegWtaddr_MEM != 0) && (RegWtaddr_MEM == Rs_EX);
        RegRdout2Sel_Forward_EX[0] = RegWrite_WB && (RegWtaddr_WB != 0) && (RegWtaddr_MEM != Rt_EX) && (RegWtaddr_WB == Rt_EX);
        RegRdout2Sel_Forward_EX[1] = RegWrite_MEM && (RegWtaddr_MEM != 0) && (RegWtaddr_MEM == Rt_EX);  
    end
endmodule

hazard.v

module hazard(//上一条指令是LW指令且当前指令ID级读的是同一个寄存器,则插入bubble
     input [4:0] Rs_ID,
     input [4:0] Rt_ID, 
     input [4:0] RegWtaddr_EX,
     input DMemRead_EX,
     output PCEn,
     output IF_ID_En,
     output ID_EX_Flush
    );
    assign ID_EX_Flush = ((RegWtaddr_EX == Rs_ID) || (RegWtaddr_EX == Rt_ID)) && DMemRead_EX;//条件成立则为1,清空
    assign IF_ID_En = ~ID_EX_Flush;//条件成立则为0,保持
    assign PCEn = ~ID_EX_Flush;//条件成立则为0,保持
endmodule

control.v

module control(
    input clk,rst,
    input [5:0] Op, //instr[31:26]
    input [4:0] Rt, //instr[20:16]

    input [5:0] Funct,//instr[5:0]
    input [1:0] RsCMPRt,
    input [1:0] RsCMPZero,
    output reg [1:0] PCSrc, //0:+4,1:Branch,2:J,3:JR
    //ID
    output reg RegDst,//0:RegWtaddr=rt,1:RegWtaddr=rd
    //EX
    output reg ALUSrcASel,//0:RegRdout1,1:ShamtZeroExtended
    output reg [1:0] ALUSrcBSel, //0:RegRdout2,1:IMMSignExtended,2:IMMZeroExtended
    output reg [4:0] ALUControl,
    //MEM
    output reg DMemRead,//1:En
    output reg DMemWrite,//1:En
    //WB
    output reg DMemtoReg,//0:Aluout,1:DMemout
    output reg RegWrite//1:En
    );
    
    reg [1:0] tmpsrc;
    
    always @(*)
    begin
    if(rst) 
        begin
            {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_0_0_00},{`A_NOP},{4'b0001}};
        end
    else
        case(Op)
            6'b000000: //R-Type
                case(Funct)
                    //SLL的rs rt rd shamt全0时是nop,本来nop没有对应的
                    6'b000000: //SLL,注意Alu_a来自Shamt的无符号拓展,即ALUSrcASel=1,下面两个同理
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_1_1_00},{`A_SLL},{4'b0001}};
                    //6'b000001: //MOVCI
                    6'b000010: //SRL
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_1_1_00},{`A_SRL},{4'b0001}};
                    6'b000011: //SRA
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_1_1_00},{`A_SRA},{4'b0001}};
                    6'b000100: //SLLV,注意Alu_a来自reg[rs],即ALUSrcASel=0,下面两个同理
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_1_0_00},{`A_SLL},{4'b0001}};
                    //6'b000101: //*
                    6'b000110: //SRLV
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_1_0_00},{`A_SRL},{4'b0001}};
                    6'b000111: //SRAV
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_1_0_00},{`A_SRA},{4'b0001}};
                    6'b001000: //JR
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b11_z_z_zz},{`A_NOP},{4'b00z0}};
                    //6'b001001: //JALR
                    
                    6'b001010: //MOVZ,如果 reg[rs]=0 则 reg[rd]=reg[rt],此时RsCMPZero=01,所以 RegWrite=RsCMPZero[0]
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_1_0_00},{`A_MOV},{3'b000},{RsCMPZero[0]}};
                    6'b001011: //MOVN,如果 reg[rs]!=0 则 reg[rd]=reg[rt],此时RsCMPZero=00或10,所以 RegWrite=~RsCMPZero[0]
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_1_0_00},{`A_MOV},{3'b000},{~RsCMPZero[0]}};

                    6'b100000: //ADD
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_1_0_00},{`A_ADD},{4'b0001}};
                    6'b100001: //ADDU
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_1_0_00},{`A_ADDU},{4'b0001}};
                    6'b100010: //SUB
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_1_0_00},{`A_SUB},{4'b0001}};
                    6'b100011: //SUBU
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_1_0_00},{`A_SUB},{4'b0001}};
                    6'b100100: //AND
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_1_0_00},{`A_AND},{4'b0001}};
                    6'b100101: //OR
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_1_0_00},{`A_OR},{4'b0001}};
                    6'b100110: //XOR
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_1_0_00},{`A_XOR},{4'b0001}};
                    6'b100111: //NOR
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_1_0_00},{`A_NOR},{4'b0001}};
                  
                    6'b101010: //SLT
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_1_0_00},{`A_SLT},{4'b0001}};
                    6'b101011: //SLTU
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_1_0_00},{`A_SLTU},{4'b0001}};
                endcase
            6'b000001: 
                case(Rt)
                    6'b00000: //BLTZ,Reg[rs]<0则跳转
                    begin
                        if(RsCMPZero == `LESS) tmpsrc = 2'b01; else tmpsrc = 2'b00;
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{tmpsrc},{4'bz_z_zz},{`A_NOP},{4'b00z0}};
                    end
                    6'b00001: //BGEZ,Reg[rs]>=0则跳转
                    begin
                        if(RsCMPZero == `GREATER || RsCMPZero == `EQUAL) tmpsrc = 2'b01; else tmpsrc = 2'b00;
                        {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{tmpsrc},{4'bz_z_zz},{`A_NOP},{4'b00z0}};
                    end
                endcase
            6'b000010: //J,无条件跳转
                {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b10_z_z_zz},{`A_NOP},{4'b00z0}};
            //6'b000011: //JAL
            6'b000100: //BEQ,Reg[rs]==Reg[rt]则跳转,RsCMPRt=01(==),则PCSrc=01,否则PCSrc=00(不跳转)
                {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{{1'b0},{RsCMPRt[0]}},{4'bz_z_zz},{`A_NOP},{4'b00z0}};
            6'b000101: //BNE,Reg[rs]!=Reg[rt]则跳转,RsCMPRt=00(<)或10(>),则PCSrc=01,否则PCSrc=00(不跳转)
                {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{{1'b0},{~RsCMPRt[0]}},{4'bz_z_zz},{`A_NOP},{4'b00z0}};
            6'b000110: //BLEZ,Reg[rs]<=0则跳转
                begin
                if(RsCMPZero == `LESS || RsCMPZero == `EQUAL) tmpsrc = 2'b01; else tmpsrc = 2'b00;
                {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{tmpsrc},{4'bz_z_zz},{`A_NOP},{4'b00z0}};
                end
            6'b000111: //BGTZ,Reg[rs]>0则跳转
                begin
                if(RsCMPZero == `GREATER) tmpsrc = 2'b01; else tmpsrc = 2'b00;
                {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{tmpsrc},{4'bz_z_zz},{`A_NOP},{4'b00z0}};
                end
            6'b001000: //ADDI,注意RegDst=0,AluBSrcSel=01(IMMSignExtended),下面三个同理
                {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_0_0_01},{`A_ADD},{4'b0001}};
            6'b001001: //ADDIU
                {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_0_0_01},{`A_ADDU},{4'b0001}};
            6'b001010: //SLTI
                {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_0_0_01},{`A_SLT},{4'b0001}};
            6'b001011: //SLTIU
                {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_0_0_01},{`A_SLTU},{4'b0001}};
            6'b001100: //ANDI,注意RegDst=0,AluBSrcSel=10(IMMZeroExtended),下面三个同理
                {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_0_0_10},{`A_AND},{4'b0001}};
            6'b001101: //ORI
                {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_0_0_10},{`A_OR},{4'b0001}};
            6'b001110: //XORI
                {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_0_0_10},{`A_XOR},{4'b0001}};
            6'b001111: //LUI
                {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_0_0_10},{`A_LUI},{4'b0001}};

            6'b100011: //LW,注意RegDst=0(写到Reg[rt]),AluBSrcSel=01(IMMSignExtended),DMemtoReg=1(来自DMem),
                {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_0_0_01},{`A_ADD},{4'b1011}};

            6'b101011: //SW,注意RegDst=x(不写Reg),AluBSrcSel=01(IMMSignExtended)
                {{PCSrc},{RegDst},{ALUSrcASel},{ALUSrcBSel},{ALUControl},{DMemRead},{DMemWrite},{DMemtoReg},{RegWrite}}={{6'b00_z_0_01},{`A_ADD},{4'b01z0}};

            default: ;
        endcase     
    end
endmodule

debounce.v

module debounce(//去抖动
     input clk,
    input in,
    output reg out=0
      
    );
    reg [31:0] cnt=0;
    always@(posedge clk)
        begin
            if(in!=out)
                begin
                    cnt=cnt+1;
                    
                    if(cnt==100000)
                    begin
                        out=~out;
                        cnt=0;
                    end
                    
                end
            else cnt=0;
            
        end
endmodule

seg.v

module seg(
    input clk,
    input rst_n,
    input [31:0] data32,
    output reg [3:0] sel,
    output reg [6:0] segments
    );
    integer clk_25=0;//4位数码管循环显示用
    integer clk_50000000=0;//2hz,移动显示用
    reg [1:0] cnt;
    reg [3:0] cnt2;
    reg [15:0] data16;//data32的16bit
    reg [3:0] data4;//data16的4bit
    reg [3:0] empty;//空白位
    always@(*)//组合逻辑,控制七段数码管
        begin
            if(!rst_n)
                segments = 7'b000_0000;
            else
                case(data4)
                    0: segments =       ~7'b011_1111;//0
                    1: segments =       ~7'b000_0110;//1
                    2: segments =       ~7'b101_1011;//2
                    3: segments =       ~7'b100_1111;//3
                    4: segments =       ~7'b110_0110;//4
                    5: segments =       ~7'b110_1101;//5
                    6: segments =       ~7'b111_1101;//6
                    7: segments =       ~7'b000_0111;//7
                    8: segments =       ~7'b111_1111;//8
                    9: segments =       ~7'b110_1111;//9
                    10:segments =       ~7'b111_0111;//A
                    11:segments =       ~7'b111_1100;//b
                    12:segments =       ~7'b011_1001;//C
                    13:segments =       ~7'b101_1110;//d
                    14:segments =       ~7'b111_1001;//E
                    15:segments =       ~7'b111_0001;//F
                    default: segments = 7'b000_0000; // required
                endcase
        end
        
    always@(posedge clk)//时序逻辑,产生位选择信号段选择信号
        begin
            //if(!rst_n)
                //cnt = 2'b00;
            //else 
                if(clk_25==400000)
                    begin
                        clk_25=0;
                        cnt = cnt + 2'b01;
                    end
                else
                    clk_25=clk_25+1;
                    
                if(clk_50000000==50000000)//
                    begin
                        clk_50000000=0;
                        cnt2=cnt2+1;
                        if(cnt2==4'b1010) cnt2=4'b0000;
                    end
                else
                    clk_50000000=clk_50000000+1;
            end

    always@(*)//组合逻辑,选择当前显示段
        begin
            case(cnt2)
                4'b0000:begin data16={8'bzzzzzzzz,data32[31:24]}; empty=4'b1100; end
                4'b0001:begin data16={4'bzzzz,data32[31:20]}; empty=4'b1000; end
                4'b0010:begin data16=data32[31:16]; empty=4'b0000; end
                4'b0011:begin data16=data32[27:12]; empty=4'b0000; end
                4'b0100:begin data16=data32[23:8]; empty=4'b0000; end
                4'b0101:begin data16=data32[19:4]; empty=4'b0000; end
                4'b0110:begin data16=data32[15:0]; empty=4'b0000; end
                4'b0111:begin data16={data32[11:0],4'bzzzz}; empty=4'b0001; end
                4'b1000:begin data16={data32[7:0],8'bzzzzzzzz}; empty=4'b0011; end
                4'b1001:begin data16={data32[3:0],8'bzzzzzzzz,data32[31:28]}; empty=4'b0110; end
                default:;
            endcase
        end
            
    always@(*)//组合逻辑,选择当前显示位
        begin
            case(cnt)
                2'b00:sel=4'b1110 | empty;
                2'b01:sel=4'b1101 | empty;
                2'b10:sel=4'b1011 | empty;
                2'b11:sel=4'b0111 | empty;
                default:sel=4'b1110;
            endcase
        end
        
    always@(*)//组合逻辑,选择当前显示位的数据
        begin
            case(cnt)
                2'b00:data4=data16[3:0];
                2'b01:data4=data16[7:4];
                2'b10:data4=data16[11:8];
                2'b11:data4=data16[15:12];
                default:data4=16'b0;
            endcase
        end 
endmodule

test.v

module test;
    // Inputs
    reg clk;
    reg rst;
    // Instantiate the Unit Under Test (UUT)
    top uut (
        .clk(clk), 
        .rst(rst),
    );
    
    initial begin
        // Initialize Inputs
        clk = 1;

        rst = 0;
        #100;
        
        rst = 1;
        
        // Wait 100 ns for global reset to finish
        #100;
        clk=~clk;
        #10;
        clk=~clk;
      rst = 0;
        
        forever begin
            #10;
            clk=~clk;
        end
        
        // Add stimulus here
    end
      
endmodule
搜索
背景设置