基于Verilog的单周期CPU设计

目录

1             实验项目名称:... 5

2             实验内容和目标... 5

2.1        实验内容... 5

2.2        实验目标... 5

3             实验器材原件设备... 5

4             实验原理:... 5

4.1        单周期CPU.. 5

4.2        MIPS指令格式... 6

4.3        指令集... 6

4.4        指令处理流程... 7

5             实验内容... 8

5.1        PC寄存器... 8

5.2        PCadd. 10

5.3        指令存储器INSTMEM... 11

5.4        控制单元CONUNIT.. 13

5.5        寄存器堆REGFILE.. 16

5.6        ALU.. 22

5.7        数据存储器DATAMEM... 24

5.8        选择器MUX.. 26

5.8.1      MUX4X32. 26

5.8.2      MUX2X5. 27

5.8.3      MUX2X32. 28

5.8.4      MUX32X32. 30

5.9        移位器SHIFTER.. 32

5.9.1      SHIFTER32_L2. 32

5.9.2      SHIFTER_FOR_J 33

5.10      扩展模块EXT16T32. 34

5.11      SingleCycleCPU(整合部件) 35

6             指令分析... 35

6.1        代码及元件图... 35

6.2        根据波形图分析指令... 38

6.2.1      Addi 38

6.2.2      Add. 39

6.2.3      Sub. 40

6.2.4      And. 41

6.2.5      Or 42

6.2.6      Andi 43

6.2.7      Ori 44

6.2.8      J 45

6.2.9      Beq. 46

6.2.10    Bne. 46

6.2.11    Sw.. 46

6.2.12    Lw.. 47

7             封装... 48

7.1        代码和测试文件... 48

7.1.1      代码... 48

7.1.2      测试文件... 50

7.2        元件图... 50

7.3        波形图... 51

8             结论和体会... 51

8.1        体会感悟... 51

8.2        对本实验过程及方法、手段的改进建议... 51

  1. 实验项目名称

单周期CPU的设计

  1. 实验内容和目标
    1. 实验内容
  1. 本实例所设计CPU的指令格式的拟定;
  2. 基本功能部件的设计与实现;
  3. CPU各主要功能部件的设计与实现;
  4. CPU的封装;
  5. 对各个单元组合而成的CPU进行指令测试,配合使用模拟仿真,了解指令和数据在各个单元中的传输过程及方向。
    1. 实验目标

至少支持addsubandoraddiandiorilwswbeqbnej十二条指令。

  1. 实验器材原件设备
  1. 操作系统:Win 10;
  2. 开发平台:Vivado 2019.2;
  3. 编程语言:VerilogHDL硬件描述语言
  1. 实验原理:
    1. 单周期CPU

单周期(Single Cycle)CPU是指CPU从取出1条指令到执行完该指令只需1个时钟周期。电平从低到高变化的瞬间称为时钟上升沿,两个相邻时钟上升沿之间的时间间隔称为一个时钟周期。时钟周期一般也称振荡周期(如果晶振的输出没有经过分频就直接作为CPU的工作时钟,则时钟周期就等于振荡周期。若振荡周期经二分频后形成时钟脉冲信号作为CPU的工作时钟,这样,时钟周期就是振荡周期的两倍。)

    1. MIPS指令格式

MIPS指令系统结构有MIPS-32和MIPS-64两种。本实验的MIPS指令选用MIPS-32。以下所说的MIPS指令均指MIPS-32。MIPS的指令格式为32位,以下为MIPS指的3种格式。

R型指令的op均为0,具体操作由func指定。rs和rt是源寄存器号,rd是目的寄存器号。移位指令中使用sa指定移位位数。

I型指令的低16位是立即数,计算时需扩展到32位,依指令的不同需进行零扩展和符号扩展。

J型指令的低26位是地址,是用于产生跳转的目标地址。

    1. 指令集

本实验中共需要完成12条指令,其中R指令4条,I指令7条,J指令1条,他们的指令结构如下图所示:

R型指令

指令

[31:26]

[25:21]

[20:16]

[15:11]

[10: 6]

[5:0]

功能

Add

000000

rs

rt

rd

000000

100000

寄存器加

Sub

000000

rs

rt

rd

000000

100010

寄存器减

And

000000

rs

rt

rd

000000

100100

寄存器与

Or

000000

rs

rt

rd

000000

100101

寄存器或

I型指令

Addi

001000

rs

rt

immediate

立即数加

Andi

001100

rs

rt

immediate

立即数与

Ori

001101

rs

rt

immediate

立即数或

Lw

100011

rs

rt

offset

取数据

Sw

101011

rs

rt

offset

存数据

Beq

000100

rs

rt

offset

相等转移

Bne

000101

rs

rt

offset

不等转移

J型指令

J

000010

address

跳转

    1. 指令处理流程

单周期CPU指从取出一条指令到执行该条指令只需要一个时钟周期。整个过程可以分为:取指、译码、执行、访问、写回,这五个阶段(并不是所有指令都会完全执行这五个阶段)

  1. 取指令(IF):根据程序计数器PC中的指令地址,从指令存储器中取出一条指令,同时PC根据指令字长度自动递增产生下一条指令所需要的指令地址,但遇到“地址转移”指令时,则控制器把“转移地址”送入PC,当然得到的“地址”需要做些变换才送入PC。
  2. 指令译码(ID):对取指令操作中得到的指令进行分析并译码,确定这条指令需要完成的操作,由指令的[15-12]位产生相应的操作控制信号,用于驱动执行状态中的各种操作。
  3. 指令执行(EXE):根据指令译码得到的操作控制信号,具体地执行指令动作,然后转移到结果写回状态。
  4. 存储器访问(MEM):所有需要访问存储器的操作都将在这个步骤中执行,该步骤给出存储器的数据地址,把数据写入到存储器中数据地址所指定的存储单元或者从存储器中得到数据地址单元中的数据。
  5. 结果写回(WB):指令执行的结果或者访问存储器中得到的数据写回相应的目的寄存器中。

  1. 实验内容

CPU的电路包括数据路径(Data path)和控制部件(Control Unit)两大部分。下面先给出单周期CPU的总体设计图,再分别介绍每个路径和控制部件的设计。

    1. PC寄存器

  1. 模块功能:

存储下一条要执行指令的地址。对于单周期CPU存储的是当前执行指令的地址。

  1. 实现思路:

为实现稳定输出,在时钟信号的上升沿更新,而且需要一个控制信号,在控制信号为0的时候初始化PC寄存器,即全部置零。

  1. 引脚及控制信号:

Clk:时钟周期,输入信号

Reset:控制信号,输入信号

Result目标地址,可能是跳转地址或者是下一条指令的地址,输入信号

Address:指令地址,输出信号

  1. 代码:
`timescale 1ns / 1ps

module PC(Clk,Reset,Result,Address); 

    input Clk;//时钟

    input Reset;//是否重置地址。0-初始化PC,否则接受新地址      

    input[31:0] Result;

    output reg[31:0] Address;

    //reg[31:0] Address;

    initial begin

        Address  <= 0;

    end

    //将时钟信号的上升沿和控制信号Reset作为变量

    //使得pc在上升沿的时候发生改变或被重置。

    always @(posedge Clk or negedge Reset)

        begin 

            if (!Reset) //如果为0则初始化PC,否则接受新地址

                begin 

                    Address <= 0; 

                end 

            else  

                begin

                    Address =  Result; 

                end 

         end 

endmodule
    1. PCadd

  1. 模块功能

作为PC寄存器的更新信号。

  1. 实现思路

由于每条指令32位,所以增加一个32位加法器,固定与32位的立即数4进行相加,且得到的结果在当前时钟信号的上升沿更新进PC寄存器。

  1. 引脚及控制信号

PC_0:前指令地址,输入端口

PCadd4:一条指令地址,输出端口

  1. 代码
`timescale 1ns / 1ps



module PCadd4(PC_0,PCadd4);

    input [31:0] PC_0;//偏移量

    output [31:0] PCadd4;//新指令地址

    CLA_32 cla32_for_pcadd(PC_0,4,0, PCadd4, Cout);//在ALU模块

endmodule
    1. 指令存储器INSTMEM

  1. 模块功能:

依据当前pc,读取指令寄存器中相对应地址Addr[6:2]的指令。

  1. 实现思路:

将pc的输入作为敏感变量,当pc发生改变的时候,则进行指令的读取,根据相关的地址,输出指令寄存器中相对应的指令,且设计指令时,要用到给出的指令且尽量合理。

  1. 引脚及控制信号:

address: 指令地址,输入信号

inst:指令编码,输出信号

  1. 代码
`timescale 1ns / 1ps

module INSTMEM(

      input  [31:0] address,

      output [31:0] inst

    );

    wire [31:0] ram [0:18];



 assign ram[5'h00] = 32'b001000_00001_00001_0000000000000000;

//不写的码,仅占位用(andi R1 , R1 , 0x0000)

assign ram[5'h01] = 32'b001000_00001_00001_0001001000110100;

//addi R1 , R1 , 0x1234   

assign ram[5'h02] = 32'b001000_00010_00010_0001000100010001;

//addi R2 , R2 , 0x1111

 assign ram[5'h03] = 32'b000000_00001_00010_00011_00000_100000;

//add R3 , R1 , R2 0x2345

assign ram[5'h04] = 32'b000000_00001_00010_00100_00000_100010;

//sub R4 , R1 , R2 0x0123

assign ram[5'h05] = 32'b000000_00001_00010_00011_00000_100100;

//and R3 , R1 , R2  0x1010

assign ram[5'h06] = 32'b000000_00001_00010_00100_00000_100101;

//or R4 , R1 , R2 0x1335

assign ram[5'h07] = 32'b001100_00001_00101_0010001000100010;

//andi R5 , R1 , 0x2222

assign ram[5'h08] = 32'b001101_00001_00101_0011001100110011;

//ori R5 , R1 , 0x3333

assign ram[5'h09] = 32'b000010_00000000000000000000001100;

//j   跳转到beq行  0800000C

assign ram[5'h0a] = 32'b000000_00000_00010_00011_00100_000010;

//不用

assign ram[5'h0b] = 32'b000000_00000_00010_00011_00100_000011;

//不用

assign ram[5'h0c] = 32'b000100_00001_00011_0000000000000011;

//beq R1 , R2 , 3 不跳 10230003

assign ram[5'h0d] = 32'b000101_00001_00011_0000000000000010;

//bne R1 , R2 , 2 跳至sw行  14230002

assign ram[5'h0e] = 32'b001101_00001_00011_0000000011101111;

//不用

assign ram[5'h0f] = 32'b001110_00001_00011_0000000011101111;

//不用

assign ram[5'h10] = 32'b101011_00100_00101_0000000000000001;

//sw R5 , 1(R4)  AC850001 r5=r4=0x1335

assign ram[5'h11] = 32'b100011_00100_00110_0000000000000001;

// lw R6 , 1(R4)  8C860001

assign ram[5'h12] = 32'b000010_00000000000000000000000001;

//j 跳回开头  08000001

assign inst = ram[address[6:2]];

endmodule
    1. 控制单元CONUNIT

  1. 模块功能

控制器是作为CPU控制信号产生的器件,通过解析op得到该指令的各种控制信号,使其他器件有效或无效。

  1. 实现思路

参照引脚和控制信号设计

  1. 引脚

Func:输入信号

Op :输入信号

Z:零标志信号,对Pcsrc有影响,输入信号

Aluc :控制ALU的计算种类,输出信号

Aluqb:控制ALU的Y端口的输入值,输出信号

Pcsrc :控制目标指令地址,输出信号

Regrt:控制输入寄存器的Wr端口,输出信号

Reg2reg:控制REHFILE更新值的来源

Se:控制扩展模块,输出信号

Wmem:控制数据存储器的写使能信号,输出信号

Wreg:控制寄存器端的写使能信号,输出信号

  1. 控制信号

输入端口

输出端口

Op

Func

备注

Z

Regrt

Se

Wreg

Aluqb

Aluc

Wmem

Pcsrc

Reg2reg

[5:0]

[5:0]

[1:0]

[1:0]

000000

100000

add

X

0

0

1

1

00

0

00

1

000000

100010

sub

X

0

0

1

1

01

0

00

1

000000

100100

and

X

0

0

1

1

10

0

00

1

000000

100101

or

X

0

0

1

1

11

0

00

1

001000

-

addi

X

1

1

1

0

00

0

00

1

001100

-

andi

X

1

0

1

0

10

0

00

1

001101

-

ori

X

1

0

1

0

11

0

00

1

100011

-

lw

X

1

1

1

0

00

0

00

0

101011

-

sw

X

1

1

0

0

00

1

00

1

000100

-

beq

0

1

1

0

1

01

0

00

1

000100

-

beq

1

1

1

0

1

01

0

10

1

000101

-

bne

0

1

1

0

1

01

0

10

1

000101

-

bne

1

1

1

0

1

01

0

00

1

000010

-

j

X

1

0

0

1

00

0

11

1

根据这些值,我们可以写出真值表,就得到下边的代码。

  1. 代码
`timescale 1ns / 1ps

module CONUNIT(Op,Func,Z,Regrt,Se,Wreg,Aluqb,Aluc,Wmem,Pcsrc,Reg2reg);

    input [5:0] Op,Func;

    input Z;

    //输入指令的op和func字段(对应[31:26][5:0])

    //输入的z是alu的运算结果 全为零则z为零

    output Regrt,Se,Wreg,Aluqb,Wmem,Reg2reg;

    output [1:0] Aluc,Pcsrc;

    //regrt决定是读取立即数还是寄存器1/0

    //se决定是符号扩展还是0扩展 1/0

    //wreg寄存器的写信号

    //aluqb选择alu读取的扩展后的 还是qb寄存器输出的0/1

    //wmem 数据储存器的写信号1有效

    //reg2regd 输入寄存器D的是运算R还是dmem的dout

    //aluc 00+ 01- 10& 11|

    //pcsrs 0 0pc+4 2分支语句 3j指令

    wire R_type=~|Op;

   

    wire I_add=R_type&Func[5]&~Func[4]&~Func[3]&~Func[2]&~Func[1]&~Func[0];

    wire I_sub=R_type&Func[5]&~Func[4]&~Func[3]&~Func[2]&Func[1]&~Func[0];

    wire I_and=R_type&Func[5]&~Func[4]&~Func[3]&Func[2]&~Func[1]&~Func[0];

    wire I_or=R_type&Func[5]&~Func[4]&~Func[3]&Func[2]&~Func[1]&Func[0];

    wire I_addi=~Op[5]&~Op[4]&Op[3]&~Op[2]&~Op[1]&~Op[0];

    wire I_andi=~Op[5]&~Op[4]&Op[3]&Op[2]&~Op[1]&~Op[0];

    wire I_ori=~Op[5]&~Op[4]&Op[3]&Op[2]&~Op[1]&Op[0];

    wire I_lw=Op[5]&~Op[4]&~Op[3]&~Op[2]&Op[1]&Op[0];

    wire I_sw=Op[5]&~Op[4]&Op[3]&~Op[2]&Op[1]&Op[0];

    wire I_beq=~Op[5]&~Op[4]&~Op[3]&Op[2]&~Op[1]&~Op[0];

    wire I_bne=~Op[5]&~Op[4]&~Op[3]&Op[2]&~Op[1]&Op[0];

    wire I_J=~Op[5]&~Op[4]&~Op[3]&~Op[2]&Op[1]&~Op[0];

   

    assign Regrt=I_addi|I_andi|I_ori|I_lw|I_sw|I_beq|I_bne|I_J;

    assign Se=I_addi|I_lw|I_sw|I_beq|I_bne;

    assign Wreg=I_add|I_sub|I_and|I_or|I_addi|I_andi|I_ori|I_lw;

    assign Aluqb=I_add|I_sub|I_and|I_or|I_beq|I_bne|I_J;

    assign Aluc[1]=I_and|I_or|I_andi|I_ori;

    assign Aluc[0]=I_sub|I_or|I_ori|I_beq|I_bne;

    assign Wmem=I_sw;

    assign Pcsrc[1]=I_beq&Z|I_bne&~Z|I_J;

    assign Pcsrc[0]=I_J;

    assign Reg2reg=I_add|I_sub|I_and|I_or|I_addi|I_andi|I_ori|I_sw|I_beq|I_bne|I_J;

   

endmodule   
    1. 寄存器堆REGFILE

  1. 模块功能

给出要读取的两个寄存器编号和要写入的寄存器编号,然后由Qa和Qb端口更新Ra和Rb端口的输入编号分别输入其值。

  1. 实现思路

两个D锁存器构成一个D触发器,我们紧接着为D触发器增加使能端,并利用带有使能的1位触发器,构成一个32位的触发器,然后32个触发器构成了一个寄存器堆,我们写顶层文件增加两个端口用于接收要读取的两个寄存器编号,另一个端口用于接收要写入的寄存器的编号,在时钟上升沿将D写入。

  1. 引脚及控制信号

Clk:时钟周期,输入信号

Clrn:清零信号,输入信号

D:寄存器更新值,输入信号

Ra:读取寄存器编号1,输入信号

Rb:读取寄存器编号2或立即数,输入信号

We:写入寄存器编号3,输入信号

Wr:写使能信号,为0的时候不能写入,D值不更新,为1的时候能写入,D值更新(当时钟边沿到来时,将𝐷𝑎𝑡𝑎端口送来的数据写入𝑅𝑤指定的寄存器)入信号

Qa:输出寄存器1的值,输出信号

Qb:输出寄存器2的值,输出信号

  1. 代码
`timescale 1ns / 1ps

module REGFILE(Ra,Rb,D,Wr,We,Clk,Clrn,Qa,Qb);//使用的寄存器堆

    input [4:0]Ra,Rb,Wr;

    input [31:0]D;

    input We,Clk,Clrn;

    output [31:0]Qa,Qb;

    //调用了一个3t32的译码器

//负责将输入的要写入的寄存器号转换为对应的的寄存器号

    wire [31:0]Y_mux;

    DEC5T32E dec(Wr,We,Y_mux);

    //调用一个reg32

    wire [31:0]Q31_reg32,Q30_reg32,Q29_reg32,Q28_reg32,Q27_reg32,Q26_reg32,Q25_reg32,Q24_reg32,Q23_reg32,Q22_reg32,Q21_reg32,Q20_reg32,Q19_reg32,Q18_reg32,Q17_reg32,Q16_reg32,Q15_reg32,Q14_reg32,Q13_reg32,Q12_reg32,Q11_reg32,Q10_reg32,Q9_reg32,Q8_reg32,Q7_reg32,Q6_reg32,Q5_reg32,Q4_reg32,Q3_reg32,Q2_reg32,Q1_reg32,Q0_reg32;  

    REG32  A(D,Y_mux,Clk,Clrn,Q31_reg32,Q30_reg32,Q29_reg32,Q28_reg32,Q27_reg32,Q26_reg32,Q25_reg32,Q24_reg32,Q23_reg32,Q22_reg32,Q21_reg32,Q20_reg32,Q19_reg32,Q18_reg32,Q17_reg32,Q16_reg32,Q15_reg32,Q14_reg32,Q13_reg32,Q12_reg32,Q11_reg32,Q10_reg32,Q9_reg32,Q8_reg32,Q7_reg32,Q6_reg32,Q5_reg32,Q4_reg32,Q3_reg32,Q2_reg32,Q1_reg32,Q0_reg32);

    //因为reg输出的是32个信号 所以要用2个32选1的复用器选择那个是最后的输出

    MUX32X32 select1(Q0_reg32,Q1_reg32,Q2_reg32,Q3_reg32,Q4_reg32,Q5_reg32,Q6_reg32,Q7_reg32,Q8_reg32,Q9_reg32,Q10_reg32,Q11_reg32,Q12_reg32,Q13_reg32,Q14_reg32,Q15_reg32,Q16_reg32,Q17_reg32,Q18_reg32,Q19_reg32,Q20_reg32,Q21_reg32,Q22_reg32,Q23_reg32,Q24_reg32,Q25_reg32,Q26_reg32,Q27_reg32,Q28_reg32,Q29_reg32,Q30_reg32,Q31_reg32,Ra,Qa);

    MUX32X32 select2(Q0_reg32,Q1_reg32,Q2_reg32,Q3_reg32,Q4_reg32,Q5_reg32,Q6_reg32,Q7_reg32,Q8_reg32,Q9_reg32,Q10_reg32,Q11_reg32,Q12_reg32,Q13_reg32,Q14_reg32,Q15_reg32,Q16_reg32,Q17_reg32,Q18_reg32,Q19_reg32,Q20_reg32,Q21_reg32,Q22_reg32,Q23_reg32,Q24_reg32,Q25_reg32,Q26_reg32,Q27_reg32,Q28_reg32,Q29_reg32,Q30_reg32,Q31_reg32,Rb,Qb);

endmodule



module DEC5T32E(X,En,Y);

   input [4:0]X;

   input En;

   output [31:0]Y;

    wire [31:0]Y1,En_32;

    assign Y1=1<<X;

    assign En_32={32{En}};

    assign Y=Y1&En_32;

endmodule





module REG32(D,En,Clk,Clrn,Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0);

    //参数的输入顺序和课本保持一致

    //32位寄存器堆 共有32个32位的输出接口 一个32位的使能信号

    input [31:0]D,En;

    input Clk,Clrn;

    output [31:0]Q0,Q1,Q2,Q3,Q4,Q5,Q6,Q7,Q8,Q9,Q10,Q11,Q12,Q13,Q14,Q15,Q16;

    output [31:0]Q17,Q18,Q19,Q20,Q21,Q22,Q23,Q24,Q25,Q26,Q27,Q28,Q29,Q30,Q31;

    D_FFEC32 q1(D,Clk,En[1],Clrn,Q1);

    D_FFEC32 q2(D,Clk,En[2],Clrn,Q2);

    D_FFEC32 q3(D,Clk,En[3],Clrn,Q3);

    D_FFEC32 q4(D,Clk,En[4],Clrn,Q4);

    D_FFEC32 q5(D,Clk,En[5],Clrn,Q5);

    D_FFEC32 q6(D,Clk,En[6],Clrn,Q6);

    D_FFEC32 q7(D,Clk,En[7],Clrn,Q7);

    D_FFEC32 q8(D,Clk,En[8],Clrn,Q8);

    D_FFEC32 q9(D,Clk,En[9],Clrn,Q9);

    D_FFEC32 q10(D,Clk,En[10],Clrn,Q10);

    D_FFEC32 q11(D,Clk,En[11],Clrn,Q11);

    D_FFEC32 q12(D,Clk,En[12],Clrn,Q12);

    D_FFEC32 q13(D,Clk,En[13],Clrn,Q13);

    D_FFEC32 q14(D,Clk,En[14],Clrn,Q14);

    D_FFEC32 q15(D,Clk,En[15],Clrn,Q15);

    D_FFEC32 q16(D,Clk,En[16],Clrn,Q16);

    D_FFEC32 q17(D,Clk,En[17],Clrn,Q17);

    D_FFEC32 q18(D,Clk,En[18],Clrn,Q18);

    D_FFEC32 q19(D,Clk,En[19],Clrn,Q19);

    D_FFEC32 q20(D,Clk,En[20],Clrn,Q20);

    D_FFEC32 q21(D,Clk,En[21],Clrn,Q21);

    D_FFEC32 q22(D,Clk,En[22],Clrn,Q22);

    D_FFEC32 q23(D,Clk,En[23],Clrn,Q23);

    D_FFEC32 q24(D,Clk,En[24],Clrn,Q24);

    D_FFEC32 q25(D,Clk,En[25],Clrn,Q25);

    D_FFEC32 q26(D,Clk,En[26],Clrn,Q26);

    D_FFEC32 q27(D,Clk,En[27],Clrn,Q27);

    D_FFEC32 q28(D,Clk,En[28],Clrn,Q28);

    D_FFEC32 q29(D,Clk,En[29],Clrn,Q29);

    D_FFEC32 q30(D,Clk,En[30],Clrn,Q30);

    D_FFEC32 q31(D,Clk,En[31],Clrn,Q31);

    assign Q0=0;

endmodule



module D_FFEC32(D,Clk,En,Clrn,Q,Qn);//32位的带有使能端的D触发器 用了32个1位的D_FFEC

    input [31:0]D;

    input Clk,En,Clrn;

    output [31:0]Q,Qn;

    D_FFEC d0(D[0],Clk,En,Clrn,Q[0],Qn[0]);

    D_FFEC d1(D[1],Clk,En,Clrn,Q[1],Qn[1]);

    D_FFEC d2(D[2],Clk,En,Clrn,Q[2],Qn[2]);

    D_FFEC d3(D[3],Clk,En,Clrn,Q[3],Qn[3]);

    D_FFEC d4(D[4],Clk,En,Clrn,Q[4],Qn[4]);

    D_FFEC d5(D[5],Clk,En,Clrn,Q[5],Qn[5]);

    D_FFEC d6(D[6],Clk,En,Clrn,Q[6],Qn[6]);

    D_FFEC d7(D[7],Clk,En,Clrn,Q[7],Qn[7]);

    D_FFEC d8(D[8],Clk,En,Clrn,Q[8],Qn[8]);

    D_FFEC d9(D[9],Clk,En,Clrn,Q[9],Qn[9]);

    D_FFEC d10(D[10],Clk,En,Clrn,Q[10],Qn[10]);

    D_FFEC d11(D[11],Clk,En,Clrn,Q[11],Qn[11]);

    D_FFEC d12(D[12],Clk,En,Clrn,Q[12],Qn[12]);

    D_FFEC d13(D[13],Clk,En,Clrn,Q[13],Qn[13]);

    D_FFEC d14(D[14],Clk,En,Clrn,Q[14],Qn[14]);

    D_FFEC d15(D[15],Clk,En,Clrn,Q[15],Qn[15]);

    D_FFEC d16(D[16],Clk,En,Clrn,Q[16],Qn[16]);

    D_FFEC d17(D[17],Clk,En,Clrn,Q[17],Qn[17]);

    D_FFEC d18(D[18],Clk,En,Clrn,Q[18],Qn[18]);

    D_FFEC d19(D[19],Clk,En,Clrn,Q[19],Qn[19]);

    D_FFEC d20(D[20],Clk,En,Clrn,Q[20],Qn[20]);

    D_FFEC d21(D[21],Clk,En,Clrn,Q[21],Qn[21]);

    D_FFEC d22(D[22],Clk,En,Clrn,Q[22],Qn[22]);

    D_FFEC d23(D[23],Clk,En,Clrn,Q[23],Qn[23]);

    D_FFEC d24(D[24],Clk,En,Clrn,Q[24],Qn[24]);

    D_FFEC d25(D[25],Clk,En,Clrn,Q[25],Qn[25]);

    D_FFEC d26(D[26],Clk,En,Clrn,Q[26],Qn[26]);

    D_FFEC d27(D[27],Clk,En,Clrn,Q[27],Qn[27]);

    D_FFEC d28(D[28],Clk,En,Clrn,Q[28],Qn[28]);

    D_FFEC d29(D[29],Clk,En,Clrn,Q[29],Qn[29]);

    D_FFEC d30(D[30],Clk,En,Clrn,Q[30],Qn[30]);

    D_FFEC d31(D[31],Clk,En,Clrn,Q[31],Qn[31]);

endmodule



module D_FFEC(D,Clk,En,Clrn,Q,Qn);//带有使能端的1位D触发器

    input D,Clk,En,Clrn;

    output Q,Qn;

    wire Y0,Y_C;

    MUX2X1 m0(Q,D,En,Y0);

    and i0(Y_C,Y0,Clrn);

    D_FF d0(Y_C,Clk,Q,Qn);

endmodule



module MUX2X1(A0,A1,S,Cout);//在D_FFEC中用到的选择器

    input A0,A1,S;

    output Cout;

    function Select;

        input A0,A1,S;

        case(S)

            0:Select=A0;

            1:Select=A1;

        endcase

    endfunction

    assign Cout=Select(A0,A1,S);

endmodule



module D_FF(D,Clk,Q,Qn);//1位D触发器

    input D,Clk;

    output Q,Qn;

    wire Clkn,QO,Qn0;

    not i0(Clkn,Clk);

    D_Latch d0 (D,Clkn,Q0,Qn0);

    D_Latch d1 (Q0,Clk,Q,Qn);

endmodule



module D_Latch(D,En,Q,Qn);//一个d锁存器

    input D,En;

    output Q,Qn;

    wire Sn,Rn,Dn;

    not i0(Dn,D);

    nand i1(Sn,D,En);

    nand i2(Rn,En,Dn);

    nand i3(Q,Sn,Qn);

    nand i4(Qn,Q,Rn);

endmodule
    1. ALU

  1. 模块功能

算数逻辑部件,需要实现加,减,按位与,按位或。

  1. 实现思路

需要2位控制信号控制运算类型,核心部件是32位加法器ADDSUB_32。

  1. 引脚及控制信号

Aluc:控制信号

X:寄存器1的值,输入信号

Y:寄存器2的值或立即数,输入信号

Cout:输入寄存器端口D的计算结果,输出信号

Z:当值为1时代表两个输入信号值相等,当值为0时代表两个输入信号不等,输出信号。

  1. 代码

ALU

`timescale 1ns / 1ps

module ALU(X,Y,Aluc,Cout,Z);

    //00+ 01- 10& 11|

    input [31:0]X,Y;

    input [1:0]Aluc;

    output [31:0]Cout;

    output Z;

   

    wire[31:0]out_as,out_and,out_or,out_and_or;

   

    ADDSUB_32 as(X,Y,Aluc[0],out_as);

   

    assign out_and=X&Y;

    assign out_or=X|Y;

   

    MUX2X32 and_or_select(out_and,out_or,Aluc[0],out_and_or);//0& 1|

    MUX2X32 as_ao_selete(out_as,out_and_or,Aluc[1],Cout);//0+- 1&|

   

    assign Z=~|Cout;

endmodule

32位加法器ADDSUB_32

`timescale 1ns / 1ps

//顶层文件 输入两个数 选择(进位)

//输出两个 结果 进位

module ADDSUB_32(X, Y, Sub, S, Cout);// 32位加法器ADDSUB_32

    //sub 0是加法 1是减法

    input [31:0] X;

    input [31:0] Y;

    input Sub;

    output [31:0] S;

    output Cout;//输出进位信息

    //对输入的sub进行扩展 如果是加法的话0的话异或还是原数

    //如果是减的话做异或并且让进位信号为1

    CLA_32 add0 (X, Y^{32{Sub}}, Sub, S, Cout); 

endmodule



module CLA_32(X, Y, Cin, S, Cout);

    input [31:0] X, Y;

    input Cin;//输入进位信息

    output [31:0] S;

    output Cout;

    //32位分成8个CLA

    wire Cout0, Cout1, Cout2, Cout3, Cout4, Cout5, Cout6;



    CLA_4 add0 (X[3:0], Y[3:0], Cin, S[3:0], Cout0);

    CLA_4 add1 (X[7:4], Y[7:4], Cout0, S[7:4], Cout1);

    CLA_4 add2 (X[11:8], Y[11:8], Cout1, S[11:8], Cout2);

    CLA_4 add3 (X[15:12], Y[15:12], Cout2, S[15:12], Cout3);

    CLA_4 add4 (X[19:16], Y[19:16], Cout3, S[19:16], Cout4);

    CLA_4 add5 (X[23:20], Y[23:20], Cout4, S[23:20], Cout5);

    CLA_4 add6 (X[27:24], Y[27:24], Cout5, S[27:24], Cout6);

    CLA_4 add7 (X[31:28], Y[31:28], Cout6, S[31:28], Cout);

endmodule



module CLA_4(X,Y,Cin,S,Cout);

    input [3:0]X,Y;

    input Cin;

    output [3:0]S;

    output Cout;

    wire cout0,cout1,cout2,cout3;

    assign cout0=X[0]&Y[0]|((X[0]|Y[0])&Cin);

    assign cout1=X[1]&Y[1]|((X[1]|Y[1])&cout0);

    assign cout2=X[2]&Y[2]|((X[2]|Y[2])&cout1);

    assign Cout=X[3]&Y[3]|((X[3]|Y[3])&cout2);

    assign S[0]=X[0]^Y[0]^Cin;

    assign S[1]=X[1]^Y[1]^cout0;

    assign S[2]=X[2]^Y[2]^cout1;

    assign S[3]=X[3]^Y[3]^cout2;

endmodule
    1. 数据存储器DATAMEM

  1. 模块功能

数据存储器,通过控制信号,对数据寄存器进行读或者写操作,并且此处模块额外合并了输出DB的数据选择器,此模块同时输出写回寄存器组的数据DB。

  1. 实现思路

由于需要支持取数/存数指令,所以要在指令储存器的基础上增加写入数据的数据写入端口,写使能信号。又因为写操作在时钟信号的上升沿,所以要增加时钟信号。

  1. 引脚及控制信号

Clock:时钟周期,输入信号

addr:访存地址,输入信号

datain:输入的值,输入信号

we:写使能信号,We为1时,进行sw指令操作,此时Din端口输入信号实际为rt,Addr端口输入信号为rs和偏移量相加的地址,在时钟周期上升沿将rt的值写入改地址的储存单元;We为0时,进行lw指令操作,此时Addr端口输入信号为rs和偏移量相加的地址,Dout为读取该地址储存器的内容,输入信号

dataout:读取的值,输出信号

  1. 代码
`timescale 1ns / 1ps

module DATEMEM(

      input [31:0] addr,

      input [31:0] datain,

      input Clock,

      input        we,

        output [31:0] dataout

    );

   reg [31:0] ram [31:0];

   assign dataout = ram[addr[6:2]];

   always @ (posedge Clock) begin

          if (we) ram[addr[6:2]] <= datain;

   end

   integer i;

   initial begin

          for ( i = 0 ; i <= 31 ; i = i + 1) ram [i] = i * i;

   end   

endmodule
    1. 选择器MUX
      1. MUX4X32

  1. 模块功能

实现目标地址的选择,四选一多路选择器,主要选择从输入的4个口中的具体选择哪一个口子数据输出

  1. 实现思路

目标地址可能是PC+4,也可能是beq和bne的跳转地址或是J型跳转地址,所以采用一个32位四选一多路选择器,因为只有4个来源,所以只要使用两位的s来进行选择就行。代码功能就是从a1,a2,a3,a4中通过s选一个输出到y,s的内容一般由控制器译码模块决定

  1. 引脚及控制信号

A0:PC+4的地址,输入信号

A1:空位,输入信号

A2:beq和bne指令的跳转地址,输入信号

A3:J指令的跳转地址,输入信号

S:对地址进行选择的控制信号,输入信号

Y:目标地址,输出信号

  1. 主要实现代码
module MUX4X32(A0,A1,A2,A3,S,Y);

    input [31:0] A0,A1,A2,A3;

    input [1:0] S;

    output [31:0] Y;

    function [31:0] select;

        input [31:0] A0,A1,A2,A3;

        input [1:0]S;

            case(S)

                00:select=A0;

                01:select=A1;

                10:select=A2;

                11:select=A3;

            endcase

        endfunction

    assign Y=select(A0,A1,A2,A3,S);

endmodule
      1. MUX2X5

       

  1. 模块功能

R型指令和I行指令的Wr信号不同,所以需要一个5位二选一选择器进行选择。

  1. 实现思路

R型指令Wr选择rd信号,I型指令Wr选择rt信号。

  1. 引脚及控制信号

Inst[15:11],:R型指令的rd信号,输入信号

nst[20:16]:I型指令的rt信号,输入信号

Regrt:选择指令的控制信号,输入信号

Wr:Wr信号,输出信号

  1. 代码
module MUX2X5(A0,A1,S,Y);

    input [4:0] A0,A1;

    input S;

    output [4:0] Y;

    function [4:0] select;

        input [4:0] A0,A1;

        input S;

        case(S)

            0:select=A0;

            1:select=A1;

        endcase

    endfunction

    assign Y=select(A0,A1,S);

endmodule
      1. MUX2X32

mux2x32_for_dmem_alu

  1. 模块功能

对写入寄存器的数据进行选择

  1. 实现思路

在lw指令中,需要将DATAMEM中选中储存器的值保存到REGFILE的寄存器中,而其他会更新REGFILE的指令的更新信号来自于ALU的R输出端。所以需要一个二选一选择器进行选择

  1. 引脚及控制信号

Dout: DATAMEM的输出值,输入信号

R:ALU的输出值,输入信号

S:控制信号

Y:写入R寄存器堆D端的信号,输出信号

mux2x32_for_qb

  1. 模块功能

ALU的Y端输入信号种类根据指令的不同而不同

  1. 实现思路

在执行R型指令时,ALU的Y端输入信号可能来自Qb,在执行I型指令的addi,andi和ori指令时时,ALU的Y端输入信号来自EXT16T32,所以需要一个二选一选择器

  1. 引脚及控制信号

A1:来自EXT16T32的信号,输入信号

A1:来自REGFLE中Qb端口的信号,输入信号

S:控制信号

Y:输入ALU进行后续计算的信号,输出信号

  1. 代码
module MUX2X32(A0,A1,S,Y);

    input [31:0] A0,A1;

    input S;

    output [31:0] Y;

    function [31:0] select;

        input [31:0] A0,A1;

        input S;

            case(S)

                0:select=A0;

                1:select=A1;

            endcase

        endfunction

    assign Y=select(A0,A1,S);

endmodule
      1. MUX32X32
  1. 模块功能

由于有32个寄存器可供选择,所以必须有32位的32选1的多路选择器

  1. 实现思路

在寄存器堆中,因为reg输出的是32个信号 所以要用2个32选1的复用器选择最后的输出

  1. 引脚及控制信号

Q0-Q31:输入信号

S:控制信号

Y:输出信号

  1. 代码
module MUX32X32(Q0,Q1,Q2,Q3,Q4,Q5,Q6,Q7,Q8,Q9,Q10,Q11,Q12,Q13,Q14,Q15,Q16,Q17,Q18,Q19,Q20,Q21,Q22,Q23,Q24,Q25,Q26,Q27,Q28,Q29,Q30,Q31,S,Y);

    input[4:0] S;

    input[31:0] Q0,Q1,Q2,Q3,Q4,Q5,Q6,Q7,Q8,Q9,Q10,Q11,Q12,Q13,Q14,Q15,Q16,Q17,Q18,Q19,Q20,Q21,Q22,Q23,Q24,Q25,Q26,Q27,Q28,Q29,Q30,Q31;

    output[31:0] Y;

    function[31:0] select;

        input[31:0] Q0,Q1,Q2,Q3,Q4,Q5,Q6,Q7,Q8,Q9,Q10,Q11,Q12,Q13,Q14,Q15,Q16,Q17,Q18,Q19,Q20,Q21,Q22,Q23,Q24,Q25,Q26,Q27,Q28,Q29,Q30,Q31;

        input[31:0] S;

            case(S)

                5'b00000:select=Q0;

                5'b00001:select=Q1;

                5'b00010:select=Q2;

                5'b00011:select=Q3;

                5'b00100:select=Q4;

                5'b00101:select=Q5;

                5'b00110:select=Q6;

                5'b00111:select=Q7;

                5'b01000:select=Q8;

                5'b01001:select=Q9;

                5'b01010:select=Q10;

                5'b01011:select=Q11;

                5'b01100:select=Q12;

                5'b01101:select=Q13;

                5'b01110:select=Q14;

                5'b01111:select=Q15;

                5'b10000:select=Q16;

                5'b10001:select=Q17;

                5'b10010:select=Q18;

                5'b10011:select=Q19;

                5'b10100:select=Q20;

                5'b10101:select=Q21;

                5'b10110:select=Q22;

                5'b10111:select=Q23;

                5'b11000:select=Q24;

                5'b11001:select=Q25;

                5'b11010:select=Q26;

                5'b11011:select=Q27;

                5'b11100:select=Q28;

                5'b11101:select=Q29;

                5'b11110:select=Q30;

                5'b11111:select=Q31;

            endcase

        endfunction

    assign Y=select(Q0,Q1,Q2,Q3,Q4,Q5,Q6,Q7,Q8,Q9,Q10,Q11,Q12,Q13,Q14,Q15,Q16,Q17,Q18,Q19,Q20,Q21,Q22,Q23,Q24,Q25,Q26,Q27,Q28,Q29,Q30,Q31,S);

endmodule
    1. 移位器SHIFTER
      1. SHIFTER32_L2

  1. 模块功能

一个固定左移两位的移位器,用于beq指令的移位

  1. 实现思路

使用32位移位器SHIFTER32,固定左移两位即可

  1. 引脚及控制信号

X:指令中的偏移量,输入信号

Sh:偏移量左移后的结果,输出信号

  1. 代码
module SHIFTER32_L2(X,Sh);

    //用于beq指令的移位

    //beq指令的扩展之后左移两位即乘四

    input [31:0] X;

    output [31:0] Sh;

    parameter pa=2'b00;//一个局部的常量

    assign Sh={X[29:0],pa};

endmodule
      1. SHIFTER_FOR_J

  1. 模块功能

用于j指令的移位器,指令中用以产生跳转的目标地址

  1. 实现思路

跳转的目标地址采用拼接的方式形成,最高4位为PC+4的最高4位,中间26位为J型指令的26位立即数字段,最低两位为0.

  1. 引脚及控制信号

Inst[26:0]:指令编码的低26位字段,输入信号。

PCadd4:PC+4的32位字段,输入信号。

sh:32位转移目标地址,输出信号。

  1. 代码
module SHIFTER_FOR_J(X,PCADD4,Sh);

    //用于j指令的移位器 需要产生的前四位是pc+4的最高四位

    //加上指令的第26位 最低位是00

    //就是课本上最下边的移位器

    input [26:0] X;

    input [31:0] PCADD4;

    output [31:0] Sh;

    parameter pa=2'b00;

    assign Sh={PCADD4[3:0],X[26:0],pa};

endmodule
    1. 扩展模块EXT16T32

  1. 模块功能

I指令的addi需要对立即数进行符号拓展,andi和ori需要对立即数进行零扩展,所以需要一个扩展模块。

  1. 实现思路

采用一个16位扩展成32位的扩展模块EXT16T32,实现零扩展和符号扩展

  1. 引脚及控制信号

select:选择零扩展或是符号扩展的控制模块,输入信号

X:I型指令的立即数字段,输入信号

Y:扩展后的立即数,输出信号。

  1. 代码
`timescale 1ns / 1ps

module EXT16T32 (X, Select, Y);

    //要移位的信号x 选择是0扩展还是符号扩展的select

    input [15:0] X;

    input Select;

    output [31:0] Y;

   

    wire [31:0] E0, E1;//e0是零扩展 e1是符号扩展

    wire [15:0] e = {16{X[15]}};//e是x[15]的符号位*16

   

    parameter z = 16'b0;//用于0扩展

   

    assign E0 = {z, X};

    assign E1 = {e, X};

   

    MUX2X32 select_for_ext16t32(E0, E1, Select, Y);//e0是零扩展 e1是符号扩展

   

endmodule
    1. SingleCycleCPU(整合部件)
  1. 模块功能

实现CPU的封装,设计输出信号使得在方正时便于观察其波形图

  1. 实现思路

调用各个下层模块并将他们的输入和输出连接到一起。

  1. 引脚及控制信号

Clk:时钟周期,外部输入信号。

Reset:清零信号,外部输入信号。

  1. 代码

见第四部分指令分析

  1. 指令分析

在这部分,我们要完成的是对于CPU的测试,检查结果是否正确,所所以我们要首先完成的是检查输出,我们构造顶层文件,输出所有的值,检查对应的结果并分析指令。

随后当检查结果正确后,我们将对CPU进行封装。

    1. 代码及元件图
  1. 代码
module CPU(Clk,Reset,Inst,Qa,Qb,Addr

,Result,PCadd4,EXTIMM,InstL2,EXTIMML2,D,Y,Dout,mux4x32_2,R,

Z,Regrt,Se,Wreg,Aluqb,Reg2reg,Cout,Wmem,

Aluc,Pcsrc,Wr

);



input Clk,Reset;

output [31:0] Inst,Qb,Qa,Addr;



output [31:0]Result,PCadd4,EXTIMM,InstL2,EXTIMML2,D,Y,Dout,mux4x32_2,R;

output Z,Regrt,Se,Wreg,Aluqb,Reg2reg,Cout,Wmem;

output [1:0]Aluc,Pcsrc;

output [4:0]Wr;



PC pc(Clk,Reset,Result,Addr);

PCadd4 pcadd4(Addr,PCadd4);

INSTMEM instmem(Addr,Inst);



CONUNIT conunit(Inst[31:26],Inst[5:0],Z,Regrt,Se,Wreg,Aluqb,Aluc,Wmem,Pcsrc,Reg2reg);

MUX2X5 mux2x5(Inst[15:11],Inst[20:16],Regrt,Wr);

EXT16T32 ext16t32(Inst[15:0],Se,EXTIMM);

SHIFTER_FOR_J shifter1(Inst[26:0],PCadd4,InstL2);

SHIFTER32_L2 shifter2(EXTIMM,EXTIMML2);

REGFILE regfile(Inst[25:21],Inst[20:16],D,Wr,Wreg,Clk,Reset,Qa,Qb);

MUX2X32 mux2x321(EXTIMM,Qb,Aluqb,Y);

ALU alu(Qa,Y,Aluc,R,Z);

DATEMEM datamem(R,Qb,Clk,Wmem,Dout);

MUX2X32 mux2x322(Dout,R,Reg2reg,D);

CLA_32 cla_32(PCadd4,EXTIMML2,0,mux4x32_2, Cout);

MUX4X32 mux4x32(PCadd4,0,mux4x32_2,InstL2,Pcsrc,Result);

//assign NEXTADDR=Result;

//assign ALU_R=R;



endmodule
  1. 测试文件
`timescale 1ns / 1ps

module TEST;

//module CPU(Clk,Reset,Inst,Qb,Qa,Addr

//,Result,PCadd4,EXTIMM,InstL2,EXTIMML2,D,Y,Dout,mux4x32_2,R,

//Z,Regrt,Se,Wreg,Aluqb,Reg2reg,Cout,Wmem,

//Aluc,Pcsrc,Wr

//);



reg Clk,Reset;

wire [31:0] Inst,Qb,Qa,Addr;



wire [31:0]Result,PCadd4,EXTIMM,InstL2,EXTIMML2,D,Y,Dout,mux4x32_2,R;

wire Z,Regrt,Se,Wreg,Aluqb,Reg2reg,Cout,Wmem;

wire [1:0]Aluc,Pcsrc;

wire [4:0]Wr;



CPU test(

.Clk(Clk),

.Reset(Reset),

.Inst(Inst),

.Qa(Qa),

.Qb(Qb),

.Addr(Addr),

.Result(Result),

.PCadd4(PCadd4),

.EXTIMM(EXTIMM),

.InstL2(InstL2),

.EXTIMML2(EXTIMML2),

.D(D),

.Y(Y),

.Dout(Dout),

.mux4x32_2(mux4x32_2),

.R(R),

.Z(Z),

.Regrt(Regrt),

.Se(Se),

.Wreg(Wreg),

.Aluqb(Aluqb),

.Reg2reg(Reg2reg),

.Cout(Cout),

.Wmem(Wmem),

.Aluc(Aluc),

.Pcsrc(Pcsrc),

.Wr(Wr)

);

    initial begin

        Clk=0;Reset=0;

        #10;

        Clk=1;Reset=0;

        #10;

        Reset=1;

        Clk=0;

        forever #20 Clk=~Clk;

    end

endmodule
  1. 元件图

    1. 根据波形图分析指令
      1. Addi

    assign ram[5'h01] = 32'b001000_00001_00001_0001001000110100;   

指令含义 addi R1 , R1 , 0x1234

对应的Inst 20211234 对应的Addr 00000004

波形图

由于是立即数相加,而原先的寄存器中没有数据,所以Qa,Qb都是0,最终的运算结果和预期吻合,也相当于赋值操作

      1. Add

    assign ram[5'h03] = 32'b000000_00001_00010_00011_00000_100000;

指令含义 add R3 , R1 , R2

对应的Inst 00221802 对应的Addr 0000000c

波形图

经过先前的赋值,R1=0x1234 R2=0x1111 所以R3=0x2345

      1. Sub

assign ram[5'h04] = 32'b000000_00001_00010_00100_00000_100010;

指令含义 sub R4 , R1 , R2

对应的Inst 00222022 对应的Addr 00000010

波形图

经过先前的赋值,R1=0x1234 R2=0x1111 所以R4=0x0123,与预期结果吻合。

      1. And

assign ram[5'h05] = 32'b000000_00001_00010_00011_00000_100100;

指令含义 and R3 , R1 , R2

对应的Inst 00221824 对应的Addr 00000014

波形图

经过先前的赋值,R1=0x1234 R2=0x1111 所以按位与之后R3=0x1111,与预期结果吻合。

      1. Or

assign ram[5'h06] = 32'b000000_00001_00010_00100_00000_100101;

指令含义 or R4 , R1 , R2 0x1335

对应的Inst 00222025 对应的Addr 00000018

波形图

经过先前的赋值,R1=0x1234 R2=0x1111 所以按位或之后R3=0x1335,与预期结果吻合。

      1. Andi

assign ram[5'h07] = 32'b001100_00001_00101_0010001000100010;

指令含义 andi R5 , R1 , 0x2222

对应的Inst 3025222 对应的Addr 0000001c

波形图

经过先前的赋值,R1=0x1234 所以与扩展后的立即数,也就是上图中的Y=0x2222相与之后R4=0x0200,与预期结果吻合。

      1. Ori

    assign ram[5'h08] = 32'b001101_00001_00101_0011001100110011;

指令含义 ori R5 , R1 , 0x3333

对应的Inst 20211234 对应的Addr 00000004

波形图

经过先前的赋值,R1=0x1234 所以与扩展后的立即数,也就是上图中的Y=0x2222相与之后R4=0x0200,与预期结果吻合。

      1. J

assign ram[5'h09] = 32'b000010_00000000000000000000001100;

指令含义 j指令 跳转两行到beq行

对应的Inst 0800000C 对应的Addr 00000024

波形图

J指令的高4位是pc+4的高四位,然后取偏移量的26位,最后两位补零,经过计算,偏移后的地址,也就是上图中的NXTADDR为原地址加12,Pcsrc的值是3,下一次的地址也就是0x0030,与预期的结果是吻合的。

      1. Beq

assign ram[5'h0c] = 32'b000100_00001_00011_0000000000000011;

指令含义 beq  R1 , R2 , 3

对应的Inst 10230003 对应的Addr 00000030

波形图

经过先前的赋值,R1=0x1234 R2=0x1111 所以两者不等,不发生跳转,Pcsrc的值是0,下一次的地址还是pc+4.

      1. Bne

assign ram[5'h0d] = 32'b000101_00001_00011_0000000000000010;

指令含义 bne R1 , R2 , 2

对应的Inst 14230002 对应的Addr 00000034

波形图

经过先前的赋值,R1=0x1234 R2=0x1111 所以两者不等,发生跳转,Pcsrc的值是2,下一次的地址是0x0040

      1. Sw

assign ram[5'h10] = 32'b101011_00100_00101_0000000000000001;

指令含义 sw R5 , R4,1

对应的Inst ac850001 对应的Addr 00000040

波形图

Sw是存数,存进去的数是1,也就是Y。

      1. Lw

assign ram[5'h11] = 32'b100011_00100_00110_0000000000000001;

指令含义  lw R6 , R4,1 

对应的Inst 8c860001 对应的Addr 00000044

波形图

Lw是取数指令,从地址为R4+1的存储单元取一个数到数据存储器。

  1. 封装

在经过了4的测试之后,我们确定自己的代码没有问题,要将CPU封装成形,下面是我们的封装代码,测试文件和代码。

    1. 代码和测试文件
      1. 代码

CPU

`timescale 1ns / 1ps

module FINAL(Clk,Clrn);

    input Clk,Clrn;

    wire [31:0] Qb,R,Addr;

    wire Wmem;

    wire [31:0] Inst,D;

    CPU cpu(Clk,Clrn,Inst,D,Addr,Qb,R,Wmem);

    DATEMEM datamem(R,Qb,Clk,Wmem,D);

    INSTMEM instmem(Addr,Inst);

endmodule



module CPU(Clk,Reset,Inst,D,Addr,Qb,R,Wmem);

    input Clk,Reset;

    input [31:0] Inst,D;

    output [31:0] Qb,R,Addr;

    output Wmem;

    wire [31:0] Result,Qa;

    wire [31:0]PCadd4;

    PC pc(Clk,Reset,Result,Addr);

    PCadd4 pcadd4(Addr,PCadd4);

    wire Z;//alu的输出z

    wire Regrt,Se,Wreg,Aluqb,Reg2reg;

    wire [1:0] Aluc,Pcsrc;

    CONUNIT conunit(Inst[31:26],Inst[5:0],Z,Regrt,Se,Wreg,Aluqb,Aluc,Wmem,Pcsrc,Reg2reg);

    wire [4:0]Wr;

    MUX2X5 mux2x5(Inst[15:11],Inst[20:16],Regrt,Wr);

    wire [31:0] Extout;

    EXT16T32 ext16t32(Inst[15:0],Se,Extout);

    wire [31:0] InstjL2;

    SHIFTER_FOR_J j_shifter(Inst[26:0],PCadd4,InstjL2);

    wire [31:0]D;//最后一个mux2x32的输出

    REGFILE regfile (Inst[25:21],Inst[20:16],D,Wr,Wreg,Clk,Reset,Qa,Qb);

    wire [31:0]Y;//输入alu的第二个值;

    MUX2X32 mux2x32_for_qb(Extout,Qb,Aluqb,Y);

    ALU alu(Qa,Y,Aluc,R,Z);//R在output定义

    wire [31:0]Dout;

    MUX2X32 mux2x32_for_dmem_alu(Dout,R,Reg2reg,D);

    wire [31:0]ExtoutL2;

    SHIFTER32_L2 shifter_l2(Extout,ExtoutL2);

    wire Cout;//反正也用不上

    wire [31:0]mux4x32_2;

    CLA_32 cla_for_last(ExtoutL2,PCadd4,0,mux4x32_2,Cout);

    MUX4X32 mux4x32(PCadd4,0,mux4x32_2,InstL2,Pcsrc,Result);

   

endmodule
      1. 测试文件
`timescale 1ns / 1ps

module FINAL_TEST;

    reg Clk;

    reg Reset;

    wire [31:0] Addr,Inst,Qa,Qb,R,Result,D;

    FINAL test(

    .Clk(Clk),

    .Clrn(Reset)

);

    initial begin

        Clk=0;Reset=0;

        #10;

        Clk=1;Reset=0;

        #10;

        Reset=1;

        Clk=0;

        forever #20 Clk=~Clk;

    end

endmodule
    1. 元件图

    1. 波形图

  1. 结论和体会
    1. 体会感悟

通过仿真测试,以上指令均成功实现,在本次实验过程中,从一开始不会写代码,到深入了解什么是单周期CPU,怎么设计一个CPU,到最后完成整个单周期CPU的实验,经历了一段很长的历程,过程是艰难的,却让我们学到很多,不管是设计指令阶段,从功能级到门级的修改阶段,还是最后的调整仿真阶段,都让我们学会了合作,取舍,关注细节,以及面对各种突发情况的应对能力。

    1. 对本实验过程及方法、手段的改进建议

实验过程中,由于各个部件复杂并且环环相扣,所以可以将每个部件都分别进行调试之后再组装在一起,避免因为出现小错误牵一发而动全身,各部件尽量再拆分为更小的部件组合而成,这样在出现问题时能够清晰的找到出错点,并进行改进。

  • 8
    点赞
  • 111
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值