精华内容
下载资源
问答
  • Verilog单周期CPU设计(超详细)

    万次阅读 多人点赞 2019-01-19 12:27:24
    实验 单周期CPU一、设计目的与目标实验内容实验要求二、课程设计器材硬件平台软件平台三、 CPU逻辑设计总体方案指令模块MIPS指令格式指令处理流程数据通路总体结构图设计流程逻辑图四、模块详细设计PCAdd4...

    下篇:Verilog流水线CPU设计(超详细)
    本篇完整工程下载链接

    一、设计目的与目标

    实验内容

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

    实验要求

    至少支持add、sub、and、or、addi、andi、ori、lw、sw、beq、bne和j十二条指令。

    二、课程设计器材

    硬件平台

    软件平台

    1. 操作系统:Win 10。
    2. 开发平台:Vivado 2017.2。
    3. 编程语言:VerilogHDL硬件描述语言。

    三、 CPU逻辑设计总体方案

    单周期CPU可以看成由数据通路和和控制部件两大部分组成。数据通路是指在指令执行过程中,数据所经过的路径和路径上所涉及的功能部件。而控制部件则根据每条指令的不同功能,生成对不同数据通路的不同控制信号,正确地控制指令的执行流程。因此,要设计处理器,首先需要确定处理器的指令集和指令编码,然后确定每条指令的数据通路,最后确定数据通路的控制信号。
    图3-0 CPU宏观设计方案

    指令模块

    单周期(Single Cycle)CPU是指CPU从取出1条指令到执行完该指令只需1个时钟周期。
    一条指令的执行过程包括:取指令→分析指令→执行指令→保存结果(如果有的话)。对于单周期CPU来说,这些执行步骤均在一个时钟周期内完成。

    时钟周期和单周期CPU指令的执行

    MIPS指令格式

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

    MIPS指令格式
    本实验只选取了12条典型的MIPS指令来描述CPU逻辑电路的设计方法。下表列出了本实验的所涉及到的12条MIPS指令。

    R型指令指令[31:26][25:21][20:16][15:11][10:6][5:0]功能Add000000rsrtrd000000100000寄存器加Sub000000rsrtrd000000100010寄存器减And000000rsrtrd000000100100寄存器与Or000000rsrtrd000000100101寄存器或Xor000000rsrtrd000000100110寄存器异或I型指令Addi001000rsrtimmediate立即数加Andi001100rsrtimmediate立即数与Ori001101rsrtimmediate立即数或Lw100011rsrtoffset取数据Sw101011rsrtoffset存数据Beq000100rsrtoffset相等转移Bne000101rsrtoffset不等转移J型指令J000010     address                            跳转
    R型指令的op均为0,具体操作由func指定。rs和rt是源寄存器号,rd是目的寄存器号。移位指令中使用sa指定移位位数。
    I型指令的低16位是立即数,计算时需扩展到32位,依指令的不同需进行零扩展和符号扩展。
    J型指令的低26位是地址,是用于产生跳转的目标地址。

    指令处理流程

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

    数据通路

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

    总体结构图

    在这里插入图片描述
    上图是一个简单的基本上能够在单周期上完成所要求设计的指令功能的数据通路和必要的控制线路图。其中指令储存在指令储存器,数据储存在数据存储器。访问存储器时,先给出地址,然后由读/写信号控制。对于寄存器组,读操作时,先给出地址,输出端直接输出相应数据;而在写操作时,在 We使能信号为1时,在时钟边沿触发写入。

    设计流程逻辑图

    根据实验原理中的单周期CPU总体结构图,我们可以清楚的知道单周期CPU的设计应包括PC,PCAdd4,INSTMEM,CONUNIT,REGFILE,ALU,DATAMEM, EXT16T32这几个核心模块,其中PCAdd4模块需要用到32位加法器CLA_32。此外还需要左移处理模块SHIFTER_COMBINATION,一个固定左移两位的移位器SHIFT32_L2,一个四选一多路选择器MUX4X32,两个32位二选一多路选择器MUX2X32,一个5位二选一多路选择器MUX2X5,一个数据扩展器EXT16T32。其中为了运行整个CPU还需要加入一个顶层模块(SingleCycleCPU)来调用这些模块,所以自然地,这些模块为顶层模块的子模块。
    设计流程逻辑图如下(左边为拓展模块,右边为核心模块)。

    在这里插入图片描述

    四、模块详细设计

    PCAdd4

    • 所处位置
      在这里插入图片描述
    • 模块功能

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

    • 实现思路

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

    • 引脚及控制信号

    Addr:当前指令地址,输入端口
    PCadd4:下一条指令地址,输出端口

    • 主要实现代码
    module PCadd4(PC_o,PCadd4);
    input [31:0] PC_o;//偏移量
    output [31:0] PCadd4;//新指令地址
    CLA_32 cla32(PC_o,4,0, PCadd4, Cout);
    endmodule
    
    module CLA_32(X, Y, Cin, S, Cout);
    input [31:0] X, Y; 
    input Cin;   
    output [31:0] S;
    output Cout;
    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;
    input [3:0] Y;
    input Cin;
    output [3:0] S;
    output Cout;
    and get_0_0_0(tmp_0_0_0, X[0], Y[0]);
    or get_0_0_1(tmp_0_0_1, X[0], Y[0]);
    and get_0_1_0(tmp_0_1_0, X[1], Y[1]);
    or get_0_1_1(tmp_0_1_1, X[1], Y[1]);
    and get_0_2_0(tmp_0_2_0, X[2], Y[2]);
    or get_0_2_1(tmp_0_2_1, X[2], Y[2]);
    and get_0_3_0(tmp_0_3_0, X[3], Y[3]);
    or get_0_3_1(tmp_0_3_1, X[3], Y[3]);
    and get_1_0_0(tmp_1_0_0, ~tmp_0_0_0, tmp_0_0_1);
    xor getS0(S0, tmp_1_0_0, Cin);
    and get_1_1_0(tmp_1_1_0, ~tmp_0_1_0, tmp_0_1_1);
    not get_1_1_1(tmp_1_1_1, tmp_0_0_0);
    nand get_1_1_2(tmp_1_1_2, Cin, tmp_0_0_1);
    nand get_2_0_0(tmp_2_0_0, tmp_1_1_1, tmp_1_1_2);
    xor getS1(S1, tmp_1_1_0, tmp_2_0_0);
    and get_1_2_0(tmp_1_2_0, ~tmp_0_2_0, tmp_0_2_1);
    not get_1_2_1(tmp_1_2_1, tmp_0_1_0);
    nand get_1_2_2(tmp_1_2_2, tmp_0_1_1, tmp_0_0_0);
    nand get_1_2_3(tmp_1_2_3, tmp_0_1_1, tmp_0_0_1, Cin);
    nand get_2_1_0(tmp_2_1_0, tmp_1_2_1, tmp_1_2_2, tmp_1_2_3);
    xor getS2(S2, tmp_1_2_0, tmp_2_1_0);
    and get_1_3_0(tmp_1_3_0, ~tmp_0_3_0, tmp_0_3_1);
    not get_1_3_1(tmp_1_3_1, tmp_0_2_0);
    nand get_1_3_2(tmp_1_3_2, tmp_0_2_1, tmp_0_1_0);
    nand get_1_3_3(tmp_1_3_3, tmp_0_2_1, tmp_0_1_1, tmp_0_0_0);
    nand get_1_3_4(tmp_1_3_4, tmp_0_2_1, tmp_0_1_1, tmp_0_0_1, Cin); 
    nand get_2_2_0(tmp_2_2_0, tmp_1_3_1, tmp_1_3_2, tmp_1_3_3, tmp_1_3_4);
    xor getS3(S3, tmp_1_3_0, tmp_2_2_0);
    not get_1_4_0(tmp_1_4_0, tmp_0_3_0);
    nand get_1_4_1(tmp_1_4_1, tmp_0_3_1, tmp_0_2_0);
    nand get_1_4_2(tmp_1_4_2, tmp_0_3_1, tmp_0_2_1, tmp_0_1_0);
    nand get_1_4_3(tmp_1_4_3, tmp_0_3_1, tmp_0_2_1, tmp_0_1_1, tmp_0_0_0);
    nand get_1_4_4(tmp_1_4_4, tmp_0_3_1, tmp_0_2_1, tmp_0_1_1, tmp_0_0_1, Cin);
    nand getCout(Cout, tmp_1_4_0, tmp_1_4_1, tmp_1_4_2, tmp_1_4_3,tmp_1_4_4);
    assign S = {S3,S2,S1,S0};
    endmodule
    
    

    PC

    • 所处位置
      在这里插入图片描述

    • 模块功能
      用于给出指令在指令储存器中的地址。

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

    • 引脚及控制信号
      Clk:时钟周期,输入信号
      Reset:控制信号,输入信号
      Result目标地址,可能是跳转地址或者是下一条指令的地址,输入信号
      Addr:指令地址,输出信号

    • 主要实现代码

    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
    always @(posedge Clk or negedge Reset)  
    begin  
    if (!Reset) //如果为0则初始化PC,否则接受新地址
    begin  
    Address <= 0;  
    end  
    else   
    begin
    Address =  Result;  
    end  
    end  
    endmodule
    

    INSTMEM

    • 所处位置
      在这里插入图片描述

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

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

    • 引脚及控制信号
      Addr:指令地址,输入信号
      Inst:指令编码,输出信号

    • 主要实现代码

    module INSTMEM(Addr,Inst);//指令存储器
    input[31:0]Addr;
    //input InsMemRW;//状态为'0',写指令寄存器,否则为读指令寄存器
    output[31:0]Inst;
    wire[7:0]Rom[31:0];
    assign Rom[5'h00]=32'h20010008;//addi $1,$0,8 $1=8
    assign Rom[5'h01]=32'h3402000C;//ori $2,$0,12 $2=12
    assign Rom[5'h02]=32'h00221820;//add $3,$1,$2 $3=20
    assign Rom[5'h03]=32'h00412022;//sub $4,$2,$1 $4=4
    assign Rom[5'h04]=32'h00222824;//and $5,$1,$2
    assign Rom[5'h05]=32'h00223025;//or $6,$1,$2
    assign Rom[5'h06]=32'h14220002;//bne $1,$2,2
    assign Rom[5'h07]=32'hXXXXXXXX;
    assign Rom[5'h08]=32'hXXXXXXXX;
    assign Rom[5'h09]=32'h10220002;// beq $1,$2,2
    assign Rom[5'h0A]=32'h0800000D;// J 0D 
    assign Rom[5'h0B]=32'hXXXXXXXX;
    assign Rom[5'h0C]=32'hXXXXXXXX;
    assign Rom[5'h0D]=32'hAD02000A;// sw $2 10($8) memory[$8+10]=12
    assign Rom[5'h0E]=32'h8D04000A;//lw $4 10($8) $4=12
    assign Rom[5'h0F]=32'h10440003;//beq $2,$4,3
    assign Rom[5'h10]=32'hXXXXXXXX;
    assign Rom[5'h11]=32'hXXXXXXXX;
    assign Rom[5'h12]=32'hXXXXXXXX;
    assign Rom[5'h13]=32'h30470009;//andi $2,9,$7
    assign Rom[5'h14]=32'hXXXXXXXX;
    assign Rom[5'h15]=32'hXXXXXXXX;
    assign Rom[5'h16]=32'hXXXXXXXX;
    assign Rom[5'h17]=32'hXXXXXXXX;
    assign Rom[5'h18]=32'hXXXXXXXX;
    assign Rom[5'h19]=32'hXXXXXXXX;
    assign Rom[5'h1A]=32'hXXXXXXXX;
    assign Rom[5'h1B]=32'hXXXXXXXX;
    assign Rom[5'h1C]=32'hXXXXXXXX;
    assign Rom[5'h1D]=32'hXXXXXXXX;
    assign Rom[5'h1E]=32'hXXXXXXXX;
    assign Rom[5'h1F]=32'hXXXXXXXX;
    assign Inst=Rom[Addr[6:2]];
    endmodule
    
    

    DATAMEM

    • 所处位置
      在这里插入图片描述

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

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

    • 引脚及控制信号

      引脚控制信号作用状态“0”状态“1”
      Addr(输入)R访存地址
      Din(输入)Qb输入的值
      Clk(输入)Clk时钟周期
      We(输入)Wmem写使能信号信号无效信号有效
      Dout(输出)Dout读取的值
      当We为1时,进行sw指令操作,此时Din端口输入信号实际为rt,Addr端口输入信号为rs和偏移量相加的地址,在时钟周期上升沿将rt的值写入改地址的储存单元。
      当We为0时,进行lw指令操作,此时Addr端口输入信号为rs和偏移量相加的地址,Dout为读取该地址储存器的内容。

    • 主要实现代码

    module DATAMEM(Addr,Din,Clk,We,Dout);
    input[31:0]Addr,Din;
    input Clk,We;
    output[31:0]Dout;
    reg[31:0]Ram[31:0];
    assign Dout=Ram[Addr[6:2]];
    always@(posedge Clk)begin
    if(We)Ram[Addr[6:2]]<=Din;
    end
    integer i;
    initial begin
    for(i=0;i<32;i=i+1)
    Ram[i]=0;
    end
    endmodule
    

    SHIFTER32_L2

    • 所处位置
      在这里插入图片描述

    • 模块功能
      一个固定左移两位的移位器

    • 实现思路
      使用32位移位器SHIFTER32,固定左移两位即可

    • 引脚及控制信号
      EXTIMM:指令中的偏移量,输入信号
      EXTIMML2:偏移量左移后的结果,输出信号

    • 主要实现代码

    module SHIFTER32_L2(X,Sh);
    input [31:0] X;
    output [31:0] Sh;
    parameter z=2'b00;
    assign Sh={X[29:0],z};
    endmodule
    

    SHIFTER_COMBINATION

    • 所处位置
      在这里插入图片描述

    • 模块功能
      J指令中用以产生跳转的目标地址

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

    • 引脚及控制信号
      Inst[26:0]:指令编码的低26位字段,输入信号。
      PCadd4:PC+4的32位字段,输入信号。
      InstL2:32位转移目标地址,输出信号。

    • 主要实现代码

    module SHIFTER_COMBINATION(X,PCADD4,Sh);
    input [26:0] X;
    input [31:0] PCADD4;
    output [31:0] Sh;
    parameter z=2'b00;
    assign Sh={PCADD4[3:0],X[26:0],z};
    endmodule
    

    MUX4X32

    • 所处位置
      在这里插入图片描述

    • 模块功能
      实现目标地址的选择。

    • 实现思路
      目标地址可能是PC+4,也可能是beq和bne的跳转地址或是J型跳转地址,所以采用一个32位四选一多路选择器。

    • 引脚及控制信号
      PCadd4:PC+4的地址,输入信号
      0:空位,输入信号
      mux4x32_2:beq和bne指令的跳转地址,输入信号
      InstL2:J指令的跳转地址,输入信号
      Pcsrc:对地址进行选择的控制信号,输入信号
      Result:目标地址,输出信号

    在这里插入代码片
    
    • 主要实现代码
    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)
    2'b00: select = A0;
    2'b01: select = A1;
    2'b10: select = A2;
    2'b11: select = A3;
    endcase
    endfunction
    assign Y = select (A0, A1, A2, A3, S);
    endmodule
    
    

    MUX2X5

    • 所处位置
      在这里插入图片描述

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

    • 实现思路
      R型指令Wr选择rd信号,I型指令Wr选择rt信号。

    • 引脚及控制信号
      Inst[15:11],:R型指令的rd信号,输入信号
      nst[20:16]:I型指令的rt信号,输入信号
      Regrt:选择指令的控制信号,输入信号
      Wr:Wr信号,输出信号

    • 主要实现代码

    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
    
    

    EXT16T32

    • 所处位置
      在这里插入图片描述

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

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

    • 引脚及控制信号
      Inst[15:0]:I型指令的立即数字段,输入信号。
      Se:选择零扩展或是符号扩展的控制模块,输入信号。
      EXTIMM:扩展后的立即数,输出信号。

    • 主要实现代码

    module EXT16T32 (X, Se, Y);
    input [15:0] X;
    input Se;
    output [31:0] Y;
    wire [31:0] E0, E1;
    wire [15:0] e = {16{X[15]}};
    parameter z = 16'b0;
    assign E0 = {z, X};
    assign E1 = {e, X};
    MUX2X32 i(E0, E1, Se, Y);
    endmodule   
    

    MUX2X32

    • 所处位置
      在这里插入图片描述

    • 模块功能一
      ALU的Y端输入信号种类根据指令的不同而不同。

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

    • 引脚及控制信号一
      EXTIMM:来自EXT16T32的信号,输入信号。
      Qb:来自REGFLE中Qb端口的信号,输入信号。
      Aluqb:控制信号。
      Y:输入ALU进行后续计算的信号,输出信号。

    • 模块功能二
      对写入寄存器的数据进行选择。

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

    • 引脚及控制信号二
      Dout: DATAMEM的输出值,输入信号
      R:ALU的输出值,输入信号
      Reg2reg:控制信号
      D::写入R寄存器堆D端的信号,输出信号

    • 主要实现代码

    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
    

    CONUNIT

    • 所处位置
      在这里插入图片描述

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

    • 实现思路
      参照引脚和控制信号设计

    • 引脚
      Inst[31:26]:Op,输入信号。
      Inst[5:0]:Func,输入信号。
      Z:零标志信号,对Pcsrc有影响,输入信号。
      Regrt:控制输入寄存器的Wr端口,输出信号。
      Se:控制扩展模块,输出信号。
      Wreg:控制寄存器端的写使能信号,输出信号。
      Aluqb:控制ALU的Y端口的输入值,输出信号。
      Aluc:控制ALU的计算种类,输出信号。
      Wmem:控制数据存储器的写使能信号,输出信号。
      Pcsrc:控制目标指令地址,输出信号。
      Reg2reg:控制REHFILE更新值的来源。

    • 控制信号

    输入端口输入端口输入端口输入端口输出端口输出端口输出端口输出端口输出端口输出端口输出端口输出端口
    Op[5:0]Func[5:0]备注ZRegrtSeWregAluqbAluc[1:0]WmemPcsrc[1:0]Reg2reg
    000000100000addX00110001
    000000100010subX00111001
    000000100100andX001110001
    000000100101orX001111001
    001000-addiX11100001
    001100-andiX101010001
    001101-oriX101011001
    100011-lwX11100000
    101011-swX11000101
    000100-beq011011001
    000100-beq1110110101
    000101-bne0110110101
    000101-bne111011001
    000010-jX100100111
    • 主要实现代码
    module CONUNIT(Op,Func,Z,Regrt,Se,Wreg,Aluqb,Aluc,Wmem,Pcsrc,Reg2reg);
    input[5:0]Op,Func;
    input Z;
    output Regrt,Se,Wreg,Aluqb,Wmem,Reg2reg;
    output[1:0]Pcsrc,Aluc;
    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    
    

    REGFILE

    • 所处位置
      在这里插入图片描述

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

    • 实现思路
      由32个寄存器组成,增加两个端口用于接收要读取的两个寄存器编号,另一个端口用于接收要写入的寄存器的编号。且在时钟上升沿将D写入。

    • 引脚及控制信号
      Inst[25:21]:读取寄存器编号1,输入信号。
      Inst[20:16]:读取寄存器编号2或立即数,输入信号。
      D:寄存器更新值,输入信号。
      Wr:写入寄存器编号3,输入信号。
      Wreg:写使能信号,为0的时候不能写入,D值不更新,为1的时候能写入,D值更新,输入信号。
      Clk:时钟周期,输入信号。
      Reset:清零信号,输入信号。
      Qa:输出寄存器1的值,输入信号。
      Qb:输出寄存器2的值,输入信号。

    • 主要实现代码

    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;
    wire [31:0]Y_mux,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;
    DEC5T32E dec(Wr,We,Y_mux);
    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);
    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
    
    

    ALU

    • 所处位置
      在这里插入图片描述

    • 模块功能
      算数逻辑部件,需要实现加,减,按位与,按位或。

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

    • 引脚及控制信号
      Qa:寄存器1的值。
      Y:寄存器2的值或立即数。
      Aluc:控制信号。
      R:输入寄存器端口D的计算结果,输出信号。
      Z:当值为1时代表两个输入信号值相等,当值为0时代表两个输入信号不等,输出信号。

    • 主要实现代码

    module ALU(X,Y,Aluc,R,Z);
    input [31:0]X,Y;
    input [1:0]Aluc;
    output [31:0]R;
    output Z;
    wire[31:0]d_as,d_and,d_or,d_and_or;
    ADDSUB_32 as(X,Y,Aluc[0],d_as);
    assign d_and=X&Y;
    assign d_or=X|Y;
    MUX2X32 select1(d_and,d_or,Aluc[0],d_and_or);
    MUX2X32 seleted(d_as,d_and_or,Aluc[1],R);
    assign Z=~|R;
    endmodule
    

    SingleCycleCPU(整合部件)

    • 所处位置
      在这里插入图片描述

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

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

    • 引脚及控制信号
      CLk:时钟周期,外部输入信号。
      Reset:清零信号,外部输入信号。

    • 主要实现代码

    module MAIN(Clk,Reset,Addr,Inst,Qa,Qb,ALU_R,NEXTADDR,D);
    input Clk,Reset;
    output [31:0] Inst,NEXTADDR,ALU_R,Qb,Qa,Addr,D;
    
    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;
    
    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_COMBINATION 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);
    DATAMEM 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
    
    

    Test(仿真代码)

    • 主要实现代码
    module TEST;
    reg Clk;
    reg Reset;
    wire [31:0] Addr,Inst,Qa,Qb,ALU_R,NEXTADDR;
    
    
    MAIN uut(
    .Clk(Clk),
    .Reset(Reset),
    .Addr(Addr),
    .Inst(Inst),
    .Qa(Qa),
    .Qb(Qb),
    .ALU_R(ALU_R),
    .NEXTADDR(NEXTADDR),
    .D(D)
    );
    
    

    五、仿真模拟分析

    仿真波形图

    在这里插入图片描述

    指令代码分析

    1. assign Rom[5’h00]=32’h20010008;
      二进制源码:001000 00000 00001 00000 00000 001000
      指令含义:addi $1,$0,8
      结果:$1=$0+8=8
      在这里插入图片描述

    2. assign Rom[5’h01]=32’h3402000C;
      二进制源码:001101 00000 00010 00000 00000 001100
      指令含义:ori $2,$0,12
      结果:$2=12
      在这里插入图片描述

    3. assign Rom[5’h02]=32’h00221820;
      二进制源码:000000 00001 00010 00011 00000 100000
      指令含义:add $3,$1,$2
      结果:$3=8+12=20
      在这里插入图片描述

    4. assign Rom[5’h03]=32’h00412022;
      二进制源码:000000 00010 00001 00100 00000 100010
      指令含义:sub $4,$2,$1
      结果:$4=12-8=4
      在这里插入图片描述

    5. assign Rom[5’h04]=32’h00222824;
      二进制源码:000000 00001 00010 00101 00000 100100
      指令含义:and $5,$1,$2
      结果:$5=b1000&b1100=b1000=h8
      在这里插入图片描述

    6. assign Rom[5’h05]=32’h00223025;
      二进制源码:000000 00001 00010 00110 00000 100101
      指令含义:or $1,$2,$6
      结果:$6=b1000|b1100=b1100=12=hc
      在这里插入图片描述

    7. assign Rom[5’h06]=32’h14220002;
      二进制源码:000101 00001 00010 0000000000000010
      指令含义:bne $1,$2,2
      结果:跳转到本地址+2的地址位置
      1):偏移量扩展到32位。
      2):左移两位。
      3):加b1000。
      4):加PC地址得到Addr。
      5):取Addr[6:2]作为下一个地址。
      在这里插入图片描述

    8. assign Rom[5’h07]=32’hXXXXXXXX;

    9. assign Rom[5’h08]=32’hXXXXXXXX;

    10. assign Rom[5’h09]=32’h10220002;
      二进制源码:000100 00001 00010 0000000000000010
      指令含义:beq $1,$2,2
      结果:没有跳转
      在这里插入图片描述

    11. assign Rom[5’h0A]=32’h0800000D;
      二进制源码:000010 00000000000000000000001101
      指令含义:J OD
      结果:跳转到第14条指令的地址位置
      在这里插入图片描述

    12. assign Rom[5’h0B]=32’hXXXXXXXX;

    13. assign Rom[5’h0C]=32’hXXXXXXXX;

    14. assign Rom[5’h0D]=32’hAD02000A;
      二进制源码:101011 01000 00010 0000000000001010
      y指令含义:sw $2 10($8)
      结果:将$2的值写入10+$8的储存单元。

    15. assign Rom[5’h0E]=32’h8D04000A;
      二进制源码:100011 01000 00100 0000000000001010
      指令含义:lw $4 10($8) 数据储存器中$8和偏移量相加的地址读取到$4。
      结果: $4=$2=12
      在这里插入图片描述

    16. assign Rom[5’h0F]=32’h10440003;
      二进制源码:000100 00010 00100 0000000000000011
      指令含义:beq $2,$4,3
      结果:跳转到本地址+3的地址位置
      在这里插入图片描述

    17. assign Rom[5’h10]=32’hXXXXXXXX;

    18. assign Rom[5’h11]=32’hXXXXXXXX;

    19. assign Rom[5’h12]=32’hXXXXXXXX;

    20. assign Rom[5’h13]=32’h30470009;
      二进制源码:001100 00010 00111 0000000000001001
      指令含义:andi $2,9,$7
      结果:$7=b1100&b1001=b1000=8
      在这里插入图片描述

    21. assign Rom[5’h14]=32’hXXXXXXXX;

    22. assign Rom[5’h15]=32’hXXXXXXXX;

    23. assign Rom[5’h16]=32’hXXXXXXXX;

    24. assign Rom[5’h17]=32’hXXXXXXXX;

    25. assign Rom[5’h18]=32’hXXXXXXXX;

    26. assign Rom[5’h19]=32’hXXXXXXXX;

    27. assign Rom[5’h1A]=32’hXXXXXXXX;

    28. assign Rom[5’h1B]=32’hXXXXXXXX;

    29. assign Rom[5’h1C]=32’hXXXXXXXX;

    30. assign Rom[5’h1D]=32’hXXXXXXXX;

    31. assign Rom[5’h1E]=32’hXXXXXXXX;

    32. assign Rom[5’h1F]=32’hXXXXXXXX;

    六、结论和体会

    体会感悟

    通过此次的CPU设计实验,让我对CPU内部组成以及指令在CPU部件上如何运作有了一个更深的理解。在实验过程中,我们遇到了各种问题,一开始老师布置下来的CPU任务的时候,完全是懵的,因为CPU器件和指令运算只在课本上学习,从来没有真正实践过,现在需要自己设计CPU的各个部件,而且要将指令在器件上运行,感觉很复杂。但在接下来的日子,我们没有因为不会而放弃,而是努力专心去设计好每个部件,对每个部件的功能进行模拟仿真,确保这一部件模块不出错,在设计过程中,感觉慢慢可以理清思路,也明白了下一步需要设计的东西通过此次实验,让我们对CPU有了更深的理解,而不只是纸上谈兵。

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

    1.过程中,应该每个部件都分别进行调试之后再组装在一起。
    2.各部件尽量再拆分为更小的部件组合而成。

    展开全文
  • 单周期CPU设计

    万次阅读 多人点赞 2016-05-24 21:43:44
    首先要理解什么叫单周期CPU(与后面多周期CPU对比)单周期CPU指的是一条指令的执行在一个时钟周期内完成,然后开始下一条指令的执行,即一条指令用一个时钟周期完成。 单周期CPU的功能:能够实现一些指令功能操作...

    终于有点时间了,恰好多周期的设计也已经完成,其实只想写写多周期的,无奈单周期补上才好,哈哈哈~

    —————+—————黄金分割线—————+—————  

    首先要理解什么叫单周期CPU(与后面多周期CPU对比)单周期CPU指的是一条指令的执行在一个时钟周期内完成,然后开始下一条指令的执行,即一条指令用一个时钟周期完成。  

    单周期CPU的功能:能够实现一些指令功能操作。需设计的指令与格式如下:


    ==>算术运算指令

    (1)add rd , rs, rt  (说明:以助记符表示,是汇编指令;以代码表示,是机器指令

       000000   

       rs(5位)   

       rt(5位)   

      rd(5位)   

      reserved  

    功能:rd←rs + rt。reserved为预留部分,即未用,一般填“0”。

    (2)addi rt , rs ,immediate 

       000001   

       rs(5位)   

       rt(5位)   

       immediate(16位)   

    功能:rt←rs + (sign-extend)immediate;immediate符号扩展再参加“加”运算。

        (3)sub rd , rs , rt

       000010  

      rs(5位)    

        rt(5位)   

       rd(5位)   

       reserved  

    完成功能:rd←rs - rt

        ==> 逻辑运算指令

    (4)ori rt , rs ,immediate 

       010000  

       rs(5位)  

      rt(5位)  

       immediate(16位)        

    功能:rt←rs | (zero-extend)immediate;immediate做“0”扩展再参加“或”运算。

    (5)and rd , rs , rt

      010001  

      rs(5位)   

       rt(5位)  

       rd(5位)  

        reserved    

    功能:rd←rs & rt;逻辑与运算。

        (6)or rd , rs , rt

       010010  

      rs(5位)  

      rt(5位)   

      rd(5位)   

       reserved    

    功能:rd←rs | rt;逻辑或运算。

        ==> 传送指令

        (7)move  rd , rs  

       100000  

       rs(5位)  

      00000   

      rd(5位)   

       reserved   

    功能:rd←rs + $0 ;$0=$zero=0。

    ==> 存储器读/写指令

    (8)sw rt ,immediate(rs) 写存储器

      100110  

      rs(5位)  

       rt(5位)   

        immediate(16位)      

        功能:memory[rs+ (sign-extend)immediate]←rt;immediate符号扩展再相加。

    (9) lw  rt , immediate(rs)读存储器

       100111  

      rs(5位)  

      rt(5位)  

        immediate(16位)       

    功能:rt ← memory[rs + (sign-extend)immediate];immediate符号扩展再相加。

     ==> 分支指令

        (10)beq rs,rt,immediate     

      110000  

       rs(5位)  

      rt(5位)  

    immediate(位移量,16位)

    功能:if(rs=rt) pc←pc +4 + (sign-extend)immediate <<2;

    特别说明:immediate是从PC+4地址开始和转移到的指令之间指令条数。immediate符号扩展之后左移2位再相加。为什么要左移2位?由于跳转到的指令地址肯定是4的倍数(每条指令占4个字节),最低两位是“00”,因此将immediate放进指令码中的时候,是右移了2位的,也就是以上说的“指令之间指令条数”。

    ==>停机指令

    (11)halt

      111111  

     00000000000000000000000000(26位)  

    功能:停机;不改变PC的值,PC保持不变。


     设计原理


    CPU在处理指令时,一般需要经过以下几个步骤:

       (1) 取指令(IF):根据程序计数器PC中的指令地址,从存储器中取出一条指令,同时,PC根据指令字长度自动递增产生下一条指令所需要的指令地址,但遇到“地址转移”指令时,则控制器把“转移地址”送入PC,当然得到的“地址”需要做些变换才送入PC。

       (2) 指令译码(ID):对取指令操作中得到的指令进行分析并译码,确定这条指令需要完成的操作,从而产生相应的操作控制信号,用于驱动执行状态中的各种操作。

       (3) 指令执行(EXE):根据指令译码得到的操作控制信号,具体地执行指令动作,然后转移到结果写回状态。

       (4) 存储器访问(MEM):所有需要访问存储器的操作都将在这个步骤中执行,该步骤给出存储器的数据地址,把数据写入到存储器中数据地址所指定的存储单元或者从存储器中得到数据地址单元中的数据。

       (5) 结果写回(WB):指令执行的结果或者访问存储器中得到的数据写回相应的目的寄存器中。

       单周期CPU,是在一个时钟周期内完成这五个阶段的处理。


    MIPS32的指令的三种格式:

    R类型:

    31       26 25       21 20      16 15       11 10        6 5       0

           op             

              rs            

             rt            

              rd            

            sa            

           func    

      6位         5位       5位       5位        5位        6位

     

    I类型:

    31        26 25         21 20        16 15                       0

            op              

              rs              

               rt              

                  immediate                 

    6位         5位          5位                16位

     

    J类型:

    31        26 25                                                0

              op           

                             address                                                

    6位                            26位

    其中,

    op:为操作码;

    rs:为第1个源操作数寄存器,寄存器地址(编号)是00000~11111,00~1F;

    rt:为第2个源操作数寄存器,或目的操作数寄存器,寄存器地址(同上);

    rd:为目的操作数寄存器,寄存器地址(同上);

    sa:为位移量(shift amt),移位指令用于指定移多少位;

    func:为功能码,在寄存器类型指令中(R类型)用来指定指令的功能;

    immediate:为16位立即数,用作无符号的逻辑操作数、有符号的算术操作数、数据加载(Laod)/数据保存(Store)指令的数据地址字节偏移量和分支指令中相对程序计数器(PC)的有符号偏移量;

    address:为地址。

     

    图2是一个简单的基本上能够在单周期上完成所要求设计的指令功能的数据通路和必要的控制线路图。其中指令和数据各存储在不同存储器中,即有指令存储器和数据存储器。访问存储器时,先给出地址,然后由读/写信号控制(1-写,0-读。当然,也可以由时钟信号控制,但必须在图上标出)。对于寄存器组,读操作时,先给出地址,输出端就直接输出相应数据;而在写操作时,在 WE使能信号为1时,在时钟边沿触发写入。图中控制信号作用如表1所示,表2是ALU运算功能表。

    表1 控制信号的作用

    控制信号名

    状态“0”

    状态“1”

    PCWre

    PC不更改,相关指令:halt

    PC更改,相关指令:除指令halt外

    ALUSrcB

    来自寄存器堆data2输出,相关指令:add、sub、or、and、move、beq

    来自sign或zero扩展的立即数,相关指令:addi、ori、sw、lw

    ALUM2Reg

    来自ALU运算结果的输出,相关指令:add、addi、sub、ori、or、and、move

    来自数据存储器(Data MEM)的输出,相关指令:lw

    RegWre

    无写寄存器组寄存器,相关指令:

    sw、halt

    寄存器组写使能,相关指令:add、addi、sub、ori、or、and、move、lw

    InsMemRW

    读指令存储器(Ins. Data),初始化为0

    写指令存储器

    DataMemRW

    读数据存储器,相关指令:lw

    写数据存储器,相关指令:sw

    ExtSel

    相关指令:ori,(zero-extend)immediate(0扩展

    相关指令:addi、sw、lw、beq,

    (sign-extend)immediate(符号扩展

    PCSrc

    PC←PC+4,相关指令:add、sub、ori、or、and、move、sw、lw、beq(zero=0)

    PC←PC+4+(sign-extend)immediate,同时zero=1,相关指令:beq

    RegOut

    写寄存器组寄存器的地址,来自rt字段,相关指令:addi、ori、lw

    写寄存器组寄存器的地址,来自rd字段,相关指令:add、sub、and、or、move

    ALUOp[2..0]

    ALU 8种运算功能选择(000-111),看功能表

    相关部件及引脚说明:

    InstructionMemory指令存储器

            Iaddr,指令存储器地址输入端口

            IDataIn,指令存储器数据输入端口(指令代码输入端口)

            IDataOut,指令存储器数据输出端口(指令代码输出端口)

            RW,指令存储器读写控制信号,为1写,为0读

    DataMemory数据存储器

            Daddr,数据存储器地址输入端口

            DataIn,数据存储器数据输入端口

            DataOut,数据存储器数据输出端口

            RW,数据存储器读写控制信号,为1写,为0读

    RegisterFile:(寄存器组)

            Read Reg1,rs寄存器地址输入端口

            Read Reg2,rt寄存器地址输入端口

            Write Reg,将数据写入的寄存器端口,其地址来源rt或rd字段

            Write Data,写入寄存器的数据输入端口

            Read Data1,rs寄存器数据输出端口

            Read Data2,rt寄存器数据输出端口

            WE,写使能信号,为1时,在时钟上升沿写入

    ALU

            result,ALU运算结果

            zero,运算结果标志,结果为0输出1,否则输出0

     

    表2 ALU运算功能表       

    ALUOp[2..0]

    功能

    描述

    000

    A + B

    001

    A – B

    010

    B – A

    011

    A ∨ B

    100

    A ∧ B

    101

    /A ∧ B

    A非与B

    110

    A Å B

    异或

    111

    A ⊙ B

    同或

     

    需要说明的是根据要实现的指令功能要求画出以上数据通路图,和确定ALU的运算功能(当然,以上指令没有完全用到提供的ALU所有功能,但至少必须能实现以上指令功能操作)。从数据通路图上可以看出控制单元部分需要产生各种控制信号,当然,也有些信号必须要传送给控制单元。从指令功能要求和数据通路图的关系得出以上表1,这样,从表1可以看出各控制信号与相应指令之间的相互关系,根据这种关系就可以得出控制信号与指令之间的关系表(如下),再根据关系表可以写出各控制信号的逻辑表达式,这样控制单元部分就可实现了。

    表3 控制信号与指令的关系表

     

    控制信号

    指令

    z

    PCWre

    ALUSrcB

    ALUM2Reg

    RegWre

    InsMemRW

    DataMemRW

    ExtSel

    PCSrc

    RegOut

    ALUOp[2..0]

    add

    x

    1

    0

    0

    1

    0

    x

    x

    0

    1

    000

    addi

    x

    1

    1

    0

    1

    0

    x

    1

    0

    0

    000

    sub

    x

    1

    0

    0

    1

    0

    x

    x

    0

    1

    001

    ori

    x

    1

    1

    0

    1

    0

    x

    0

    0

    0

    011

    and

    x

    1

    0

    0

    1

    0

    x

    x

    0

    1

    100

    or

    x

    1

    0

    0

    1

    0

    x

    x

    0

    1

    011

    move

    x

    1

    0

    0

    1

    0

    x

    x

    0

    1

    000

    sw

    x

    1

    1

    x

    0

    0

    1

    1

    0

    x

    000

    lw

    x

    1

    1

    1

    1

    0

    0

    1

    0

    0

    000

    beq

    0

    1

    0

    x

    0

    0

    x

    1

    0

    x

    001

    1

    1

    0

    x

    0

    0

    x

    1

    1

    x

    001

    halt

    x

    0

    x

    x

    x

    0

    x

    x

    x

    x

    xxx



    分析与设计


    根据实验原理中的单周期CPU数据通路和控制线路图,我们可以清楚的知道单周期CPU的设计应包括controlUnit,RegisterFile, ALU, DataMemory, instructionMemory, PC, signZeroExtend这几个模块,其中为了运行整个CPU还需要加入一个顶层模块(singleCycleCPU)来调用这七个模块,所以自然地,这七个模块为顶层模块的子模块。设计流程逻辑图如下:

     

     

     1、控制单元(controlUnit.v)

    根据数据通路图可以知道,控制单元的功能是接收一个6位的操作码(opCode)和一个标志符(zero)作为输入,输出PCWre、ALUSrcB等控制信号,各控制信号的作用见实验原理的控制信号作用表(表1),从而达到控制各指令的目的。其中模块内部实现则根据实验原理中控制信号与指令的关系表(表3)列出各信号的逻辑表达式从而实现各信号的输出。比如:

    ALUOp的表达式为:ALUOp[2]=i_and

    ALUOp[1]=i_ori | i_or

    ALUOp[0]=i_sub | i_ori | i_or | i_beq

    所以其实现为:assignALUOp[2] = (opCode == 6'b010001)? 1 : 0;

                  assignALUOp[1] = (opCode == 6'b010000 || opCode == 6'b010010)? 1 : 0;

                assign ALUOp[0] = (opCode == 6'b000010 || opCode == 6'b010000|| opCode == 6'b010010 || opCode == 6'b110000)? 1 : 0;

    整个模块设计如下:

    </pre><pre name="code" class="plain">`timescale 1ns / 1ps
    
    module controlUnit(opCode, zero, PCWre, ALUSrcB, ALUM2Reg, RegWre, InsMemRW, DataMemRW, ExtSel, PCSrc, RegOut, ALUOp);
        input [5:0] opCode;
    	 input zero;
    	 output PCWre, ALUSrcB, ALUM2Reg, RegWre, InsMemRW, DataMemRW, ExtSel, PCSrc, RegOut;
    	 output[2:0] ALUOp;
    	 
    	 assign PCWre = (opCode == 6'b111111)? 0 : 1;
    	 assign ALUSrcB = (opCode == 6'b000001 || opCode == 6'b010000 || opCode == 6'b100110 || opCode == 6'b100111)? 1 : 0;
    	 assign ALUM2Reg = (opCode == 6'b100111)? 1 : 0;
    	 assign RegWre = (opCode == 6'b100110 || opCode == 6'b111111)? 0 : 1;
    	 assign InsMemRW = 0;
    	 assign DataMemRW = (opCode == 6'b100111)? 0 : 1;
    	 assign ExtSel = (opCode == 6'b010000)? 0 : 1;
    	 assign PCSrc = (opCode == 6'b110000 && zero == 1)? 1 : 0;
    	 assign RegOut = (opCode == 6'b000001 || opCode == 6'b010000 || opCode == 6'b100111)? 0 : 1;
    	 assign ALUOp[2] = (opCode == 6'b010001)? 1 : 0;
    	 assign ALUOp[1] = (opCode == 6'b010000 || opCode == 6'b010010)? 1 : 0;
    	 assign ALUOp[0] = (opCode == 6'b000010 || opCode == 6'b010000 || opCode == 6'b010010 || opCode == 6'b110000)? 1 : 0; 
    	 
    endmodule
    

    2、算术运算单元(ALU.v)
    模块ALU接收寄存器的数据和控制信号作为输入,将结果输出,具体设计如下:
    `timescale 1ns / 1ps
    
    module ALU(ReadData1, ReadData2, inExt, ALUSrcB, ALUOp, zero, result);
        input [31:0] ReadData1, ReadData2, inExt;
    	 input ALUSrcB;
    	 input [2:0] ALUOp;
    	 output zero;
    	 output [31:0] result;
    	 
    	 reg zero;
    	 reg [31:0] result;
    	 wire [31:0] B;
    	 assign B = ALUSrcB? inExt : ReadData2;
    	 
    	 always @(ReadData1 or ReadData2 or inExt or ALUSrcB or ALUOp or B)
    	     begin
    		      case(ALUOp)
    				    // A + B
    					 3'b000: begin
    					     result = ReadData1 + B;
    						  zero = (result == 0)? 1 : 0;
    					 end
    					 // A - B
    					 3'b001: begin
    					     result = ReadData1 - B;
    						  zero = (result == 0)? 1 : 0;
    					 end
    					 // B - A
    					 3'b010: begin
    					     result = B - ReadData1;
    						  zero = (result == 0)? 1 : 0;
    					 end
    					 // A ∨ B
    					 3'b011: begin
    					     result = ReadData1 | B;
    						  zero = (result == 0)? 1 : 0;
    					 end
    					 // A ∧ B
    					 3'b100: begin
    					     result = ReadData1 & B;
    						  zero = (result == 0)? 1 : 0;
    					 end
    					 // /A ∧ B
    					 3'b101: begin
    					     result = (~ReadData1) & B;
    						  zero = (result == 0)? 1 : 0;
    					 end
    					 // A ⊕ B
    					 3'b110: begin
    					     result = ReadData1 ^ B;
    						  zero = (result == 0)? 1 : 0;
    					 end
    					 // A ⊙ B
    					 3'b111: begin
    					     result = ReadData1 ^~ B;
    						  zero = (result == 0)? 1 : 0;
    					 end
    		      endcase
    		  end
    endmodule
    

    3、PC单元(PC.v)

    PC单元以时钟信号clk、重置标志Reset、立即数以及PCWreck和PCSrc两个信号控制为输入,输出当前PC地址,具体设计如下:

    `timescale 1ns / 1ps
    
    module PC(clk, Reset, PCWre, PCSrc, immediate, Address);
        input clk, Reset, PCWre, PCSrc;
    	 input [31:0] immediate;
    	 output [31:0] Address;
    	 reg [31:0] Address;
    	 
    	 /*initial begin
    	     Address = 0;
    	 end*/
    	 
    	 always @(posedge clk or negedge Reset)
    	     begin
    		      if (Reset == 0) begin
    				    Address = 0;
    				end
    				else if (PCWre) begin
    				    if (PCSrc) Address = Address + 4 + immediate*4;
    					 else Address = Address + 4;
    				end
    		  end
    
    endmodule
    


    4、 扩展单元(signZeroExtend.v)

    扩展单元的设计比较简单,其功能就是将一个16位的立即数扩展到32位,具体模块设计如下:

    `timescale 1ns / 1ps
    
    module signZeroExtend(immediate, ExtSel, out);
        input [15:0] immediate;
    	 input ExtSel;
    	 output [31:0] out;
    	 
    	 assign out[15:0] = immediate;
    	 assign out[31:16] = ExtSel? (immediate[15]? 16'hffff : 16'h0000) : 16'h0000;
    
    endmodule
    


    5、数据存储单元(DataMemory.v)

    数据存储单元的功能是读取数据,根据数据通路图可以有如下模块设计:

    `timescale 1ns / 1ps
    
    module dataMemory(DAddr, DataIn, DataMemRW, DataOut);
        input [31:0] DAddr, DataIn;
    	 input DataMemRW;
    	 output reg [31:0] DataOut;
    	 reg [31:0] memory[0:31];
    	 
    	 // read data
    	 always @(DataMemRW) begin
    	 if (DataMemRW == 0) assign DataOut = memory[DAddr];
    	 end
    	 
    	 
    	 // write data
    	 integer i;
    	 initial begin
    	     for (i = 0; i < 32; i = i+1) memory[i] <= 0;
    	 end
    	 always @(DataMemRW or DAddr or DataIn)
    	     begin
    		      if (DataMemRW) memory[DAddr] = DataIn;
    		  end
    
    endmodule
    


    6、指令存储单元(instructionMemory.v)

    根据当前的PC地址得到对应的op,rs,rt,rd以及immediate.

    内部实现:

    将需要测试的汇编指令程序转化为指令代码,当然,每个人都可以有自己的测试指令,能正确转化为指令代码就好。为了书写的简便,我们可以将32位的二进制指令代码转化为16进制。具体测试表设计如下:

    地址

    汇编程序

    指令代码

    op(6)

    rs(5)

    rt(5)

    rd(5)/immediate (16)

    16进制数代码

    0x00000004

    addi  $1,$0,8

    000001

    00000

    00001

    0000 0000 0000 1000

    =

    04010008

    0x00000008

    ori  $2,$0,12

    010000

    00000

    00010

    0000 0000 0000 1100

    =

    4002000C

    0x0000000C

    add  $3,$1,$2

    000000

    00001

    00010

    00011 00000000000

    =

    00221800

    0x00000010

    sub  $4,$2,$1

    000010

    00010

    00001

    00100 00000000000

    =

    08412000

    0x00000014

    and  $5,$1,$2

    010001

    00001

    00010

    00101 00000000000

    =

    44222800

    0x00000018

    or  $6,$1,$2

    010010

    00001

    00010

    00110 00000000000

    =

    48223000

    0x0000001C

    beq  $1,$2,4 (转030)

    110000

    00001

    00010

    0000 0000 0000 0100

    =

    C0220004

    0x00000020

    move  $7,$1

    100000

    00001

    00000

    00111 00000000000

    =

    80203800

    0x00000024

    sw  $1,1($7)

    100110

    00111

    00001

    0000 0000 0000 0001

    =

    98E10001

    0x00000028

    lw  $2,0($1)

    100111

    00001

    00010

    0000 0000 0000 0000

    =

    9C220000

    0x0000002C

    beq $2,$7,-5 (转01C)

    110000

    00010

    00111

    1111 1111 1111 1011

    =

    C047FFFB

    0x00000030

    halt

    111111

    00000

    00000

    0000000000000000

    =

    FC000000

    其次,为了存储这些指令代码,可以申请一个32位的二进制数组来存储它们,最后根据PC地址得到对应的op,rs,rt,immediate等,具体模块设计如下:

    `timescale 1ns / 1ps
    
    module instructionMemory(
        input [31:0] pc,
        input InsMemRW,
    	 output [5:0] op, 
    	 output [4:0] rs, rt, rd,
    	 output [15:0] immediate);
    	 
    	 wire [31:0] mem[0:15];
    	 
    	 assign mem[0] = 32'h00000000;
        // addi  $1,$0,8
    	 assign mem[1] = 32'h04010008;
    	 // ori  $2,$0,12
    	 assign mem[2] = 32'h4002000C;
    	 // add  $3,$1,$2
    	 assign mem[3] = 32'h00221800;
    	 // sub  $4,$2,$1
    	 assign mem[4] = 32'h08412000;
    	 // and  $5,$1,$2
    	 assign mem[5] = 32'h44222800;
    	 // or  $6,$1,$2
    	 assign mem[6] = 32'h48223000;
    	 // beq  $1,$2,4 (转030)
    	 assign mem[7] = 32'hC0220004;
    	 // move  $7,$1
    	 assign mem[8] = 32'h80203800;
    	 // sw  $1,1($7)
    	 assign mem[9] = 32'h98E10001;
    	 // lw  $2,0($1)
    	 assign mem[10] = 32'h9C220000;
    	 // beq $2,$7,-5 (转01C)
    	 assign mem[11] = 32'hC047FFFB;
    	 // halt
    	 assign mem[12] = 32'hFC000000;
    	 
    	 assign mem[13] = 32'h00000000;
    	 assign mem[14] = 32'h00000000;
    	 assign mem[15] = 32'h00000000;
    	 
    	 // output
    	 assign op = mem[pc[5:2]][31:26];
    	 assign rs = mem[pc[5:2]][25:21];
    	 assign rt = mem[pc[5:2]][20:16];
    	 assign rd = mem[pc[5:2]][15:11];
    	 assign immediate = mem[pc[5:2]][15:0];
    
    endmodule
    


    7、寄存器文件单元(registerFile.v)

    寄存器文件单元的功能是接收instructionMemory中的rs,rt,rd作为输入,输出对应寄存器的数据,从而达到取寄存器里的数据的目的。需要注意的是,在其内部实现的过程中,为了防止0号寄存器写入数据需要在writeReg的时候多加入一个判断条件,即writeReg不等于0时写入数据。具体设计如下:

    `timescale 1ns / 1ps
    
    module registerFile(clk, RegWre, RegOut, rs, rt, rd, ALUM2Reg, dataFromALU, dataFromRW, Data1, Data2);
        input clk, RegOut, RegWre, ALUM2Reg;
    	 input [4:0] rs, rt, rd;
    	 input [31:0] dataFromALU, dataFromRW;
    	 output [31:0] Data1, Data2;
    	 
    	 wire [4:0] writeReg;
    	 wire [31:0] writeData;
    	 assign writeReg = RegOut? rd : rt;
    	 assign writeData = ALUM2Reg? dataFromRW : dataFromALU;
    	 
    	 reg [31:0] register[0:31];
    	 integer i;
    	 initial begin
    	     for (i = 0; i < 32; i = i+1) register[i] <= 0;
    	 end
    	 
    	 // output
    	 assign Data1 = register[rs];
    	 assign Data2 = register[rt];
    	 
    	 // Write Reg
    	 always @(posedge clk or RegOut or RegWre or ALUM2Reg or writeReg or writeData) begin
    	     if (RegWre && writeReg) register[writeReg] = writeData;  // 防止数据写入0号寄存器
    	 end
    
    endmodule
    


    8、顶层模块(singleStyleCPU)

    顶层模块(singleStyleCPU)是整个CPU的控制模块,通过连接各个子模块来达到运行CPU的目的,整个模块设计可以如下:

    `include "controlUnit.v"
    `include "dataMemory.v"
    `include "ALU.v"
    `include "instructionMemory.v"
    `include "registerFile.v"
    `include "signZeroExtend.v"
    `include "PC.v"
    `timescale 1ns / 1ps
    
    module SingleCycleCPU(
        input clk, Reset,
    	 output wire [5:0] opCode,
    	 output wire [31:0] Out1, Out2, curPC, Result);
    	 
    	 wire [2:0] ALUOp;
    	 wire [31:0] ExtOut, DMOut;
    	 wire [15:0] immediate;
    	 wire [4:0] rs, rt, rd;
    	 wire zero, PCWre, PCSrc, ALUSrcB, ALUM2Reg, RegWre, InsMemRW, DataMemRW, ExtSel, RegOut;
    	 
    	 // module ALU(ReadData1, ReadData2, inExt, ALUSrcB, ALUOp, zero, result);
    	 ALU alu(Out1, Out2, ExtOut, ALUSrcB, ALUOp, zero, Result);
    	 // module PC(clk, Reset, PCWre, PCSrc, immediate, Address);
    	 PC pc(clk, Reset, PCWre, PCSrc, ExtOut, curPC);
    	 // module controlUnit(opCode, zero, PCWre, ALUSrcB, ALUM2Reg, RegWre, InsMemRW, DataMemRW, ExtSel, PCSrc, RegOut, ALUOp);
    	 controlUnit control(opCode, zero, PCWre, ALUSrcB, ALUM2Reg, RegWre, InsMemRW, DataMemRW, ExtSel, PCSrc, RegOut, ALUOp);
    	 // module dataMemory(DAddr, DataIn, DataMemRW, DataOut);
    	 dataMemory datamemory(Result, Out2, DataMemRW, DMOut);
    	 /* module instructionMemory(
        input [31:0] pc,
        input InsMemRW,
    	 input [5:0] op, 
    	 input [4:0] rs, rt, rd,
    	 output [15:0] immediate);*/
    	 instructionMemory ins(curPC, InsMemRW, opCode, rs, rt, rd, immediate);
    	 // module registerFile(clk, RegWre, RegOut, rs, rt, rd, ALUM2Reg, dataFromALU, dataFromRW, Data1, Data2);
    	 registerFile registerfile(clk, RegWre, RegOut, rs, rt, rd, ALUM2Reg, Result, DMOut, Out1, Out2);
        // module signZeroExtend(immediate, ExtSel, out);
    	 signZeroExtend ext(immediate, ExtSel, ExtOut);
    
    
    endmodule
    


    
    最后就是测试程序(testSCPU.v)
    

    从顶层模块中可以看出整个CPU的输入只有时钟信号clk和重置信号Reset,所以测试程序代码比较简单。(说明:下面是在测试文件中额外加的,因为其他初始化数据ISE已经自动生成,点赞)

    Reset = 1; //初始化PC地址,为0

            forever #100 clk = ~clk;



     一个简单的单周期就设计完成了,重点是要学会其中涉及到的模块化思想,这中模块化的分解思想应用极其广泛,所以,最好学会熟练使用。

    表脸粘一下仿真结果好了:


    单周期CPU卒,见多周期CPU,2333~

     



    展开全文
  • 单周期CPU,多周期CPU

    2021-05-29 21:06:48
    单周期CPU:一个时钟周期完成一条指令,如果一个程序有多条指令,则时钟周期的时间根据执行时间最长的那条指令为主。执行一条指令就需要一个时钟周期则CPI为1。 多周期CPU:一条指令被分成了若干个阶段,假设为n个,...
    1. 单周期CPU:一个时钟周期完成一条指令,如果一个程序有多条指令,则时钟周期的时间根据执行时间最长的那条指令为主。执行一条指令就需要一个时钟周期则CPI为1。
    2. 多周期CPU:一条指令被分成了若干个阶段,假设为n个,每执行一条指令需要花费n个时钟周期,所以执行一条指令就需要n个时钟周期CPI为n。
      多周期CPU比单周期CPU的优势在于:因为一个程序的不同指令所需要的执行时间是不同的,所以如果按照单周期处理的话,无论什么指令我都按照最长的那条指令去处理,可能我只要占用CPU1s,但是你给了我100s,其中99sCPU都在等待,闲着没事干,这完全是在浪费CPU。多周期CPU就是程序中的每一条指令要多少时间我就给你多少时间,比如第一条指令要是2s,那我就给你2s的CPU,第二条指令要5s,我就给你5s,多周期CPU完成这2条指令一共是7s,如果是单周期总时间就需要10s,多周期的CPU的效率比单周期高吧。但是多周期CPU也有缺点,就是同一时间不能运行多条指令无法实现CPU并行工作,因为有的时候一个程序执行的不同指令可能用的是CPU中的不同部件,如果可以让CPU中的所有部件都能不闲着那效率不就更高了,所以就有了指令流水线。
    展开全文
  • Verilog 单周期CPU

    热门讨论 2012-04-14 10:43:19
    自己设计的单周期CPU,可以直接运行查看结果。
  • (Verilog)单周期CPU设计

    万次阅读 多人点赞 2017-05-23 23:53:54
    (Verilog)单周期CPU设计首先是基础资料部分(借用学校资料):一.实验目的(1) 掌握单周期CPU数据通路图的构成、原理及其设计方法; (2) 掌握单周期CPU的实现方法,代码实现方法; (3) 认识和掌握指令与CPU的关系;...

    (Verilog)单周期CPU设计


    首先是基础资料部分(借用学校资料):

    一.实验内容

    设计一个单周期CPU,该CPU至少能实现以下指令功能操作。需设计的指令与格式如下:

    ==> 算术运算指令

    (1)add rd , rs, rt (说明:以助记符表示,是汇编指令;以代码表示,是机器指令)

    功能:rd←rs + rt。reserved为预留部分,即未用,一般填“0”。
    (2)addi rt , rs ,immediate

    功能:rt←rs + (sign-extend)immediate;immediate符号扩展再参加“加”运算。
    (3)sub rd , rs , rt

    完成功能:rd←rs - rt

    ==> 逻辑运算指令

    (4)ori rt , rs ,immediate

    功能:rt←rs | (zero-extend)immediate;immediate做“0”扩展再参加“或”运算。
    (5)and rd , rs , rt

    功能:rd←rs & rt;逻辑与运算。
    (6)or rd , rs , rt

    功能:rd←rs | rt;逻辑或运算。

    ==> 传送指令

    (7)move rd , rs

    功能:rd←rs + $0 ;$0=$zero=0。

    ==> 存储器读/写指令

    (8)sw rt ,immediate(rs) 写存储器

    功能:memory[rs+ (sign-extend)immediate]←rt;immediate符号扩展再相加。
    (9) lw rt , immediate(rs) 读存储器

    功能:rt ← memory[rs + (sign-extend)immediate];immediate符号扩展再相加。

    ==> 分支指令

    (10)beq rs,rt,immediate

    功能:if(rs=rt) pc←pc + 4 + (sign-extend)immediate <<2;
    特别说明:immediate是从PC+4地址开始和转移到的指令之间指令条数。immediate符号扩展之后左移2位再相加。为什么要左移2位?由于跳转到的指令地址肯定是4的倍数(每条指令占4个字节),最低两位是“00”,因此将immediate放进指令码中的时候,是右移了2位的,也就是以上说的“指令之间指令条数”。

    ==> 停机指令

    (11)halt

    功能:停机;不改变PC的值,PC保持不变。


    二.实验原理

    单周期CPU指的是一条指令的执行在一个时钟周期内完成,然后开始下一条指令的执行,即一条指令用一个时钟周期完成。电平从低到高变化的瞬间称为时钟上升沿,两个相邻时钟上升沿之间的时间间隔称为一个时钟周期。
    CPU在处理指令时,一般需要经过以下几个步骤:
    (1) 取指令(IF):根据程序计数器PC中的指令地址,从存储器中取出一条指令,同时,PC根据指令字长度自动递增产生下一条指令所需要的指令地址,但遇到“地址转移”指令时,则控制器把“转移地址”送入PC,当然得到的“地址”需要做些变换才送入PC。
    (2) 指令译码(ID):对取指令操作中得到的指令进行分析并译码,确定这条指令需要完成的操作,从而产生相应的操作控制信号,用于驱动执行状态中的各种操作。
    (3) 指令执行(EXE):根据指令译码得到的操作控制信号,具体地执行指令动作,然后转移到结果写回状态。
    (4) 存储器访问(MEM):所有需要访问存储器的操作都将在这个步骤中执行,该步骤给出存储器的数据地址,把数据写入到存储器中数据地址所指定的存储单元或者从存储器中得到数据地址单元中的数据。
    (5) 结果写回(WB):指令执行的结果或者访问存储器中得到的数据写回相应的目的寄存器中。
    单周期CPU,是在一个时钟周期内完成这五个阶段的处理。

    这里写图片描述

    图1 单周期CPU指令处理过程
    MIPS32的指令的三种格式:
    R类型:

    31-26 25-21 20-16 15-11 10-6 5-0
    op rs rt rd sa func
    6位 5位 5位 5位 5位 6位

    I类型:

    31-26 25-21 20-16 15-0
    op rs rt immediate
    6位 5位 5位 16位

    J类型:

    31-26 25-0
    op address
    6位 26位

    其中,
    op:为操作码;
    rs:为第1个源操作数寄存器,寄存器地址(编号)是00000~11111,00~1F;
    rt:为第2个源操作数寄存器,或目的操作数寄存器,寄存器地址(同上);
    rd:为目的操作数寄存器,寄存器地址(同上);
    sa:为位移量(shift amt),移位指令用于指定移多少位;
    func:为功能码,在寄存器类型指令中(R类型)用来指定指令的功能;
    immediate:为16位立即数,用作无符号的逻辑操作数、有符号的算术操作数、数据加载(Laod)/数据保存(Store)指令的数据地址字节偏移量和分支指令中相对程序计数器(PC)的有符号偏移量;
    address:为地址。
    这里写图片描述
    图2 单周期CPU数据通路和控制线路图

    图2是一个简单的基本上能够在单周期上完成所要求设计的指令功能的数据通路和必要的控制线路图。其中指令和数据各存储在不同存储器中,即有指令存储器和数据存储器。访问存储器时,先给出地址,然后由读/写信号控制(1-写,0-读。当然,也可以由时钟信号控制,但必须在图上标出)。对于寄存器组,读操作时,先给出地址,输出端就直接输出相应数据;而在写操作时,在 WE使能信号为1时,在时钟边沿触发写入。图中控制信号作用如表1所示,表2是ALU运算功能表。

    表1 控制信号的作用

    控制信号名 状态“0” 状态“1”
    PCWre PC不更改,相关指令:halt PC更改,相关指令:除指令halt外
    ALUSrcB 来自寄存器堆data2输出,相关指令:add、sub、or、and、move、beq 来自sign或zero扩展的立即数,相关指令:addi、ori、sw、lw
    ALUM2Reg 来自ALU运算结果的输出,相关指令:add、addi、sub、ori、or、and、move 来自数据存储器(Data MEM)的输出,相关指令:lw
    RegWre 无写寄存器组寄存器,相关指令:sw、halt 寄存器组写使能,相关指令:add、addi、sub、ori、or、and、move、lw
    InsMemRW 读指令存储器(Ins. Data),初始化为0 写指令存储器
    DataMemRW 读数据存储器,相关指令:lw 写数据存储器,相关指令:sw
    ExtSel 相关指令:ori,(zero-extend)immediate(0扩展) 相关指令:addi、sw、lw、beq,(sign-extend)immediate(符号扩展)
    PCSrc PC←PC+4,相关指令:add、sub、ori、or、and、move、sw、lw、beq(zero=0) PC←PC+4+(sign-extend)immediate,同时zero=1,相关指令:beq
    RegOut 写寄存器组寄存器的地址,来自rt字段,相关指令:addi、ori、lw 写寄存器组寄存器的地址,来自rd字段,相关指令:add、sub、and、or、move
    ALUOp[2..0] ALU 8种运算功能选择(000-111),看功能表

    相关部件及引脚说明:

    • Instruction Memory:指令存储器,
      • Iaddr,指令存储器地址输入端口
      • IDataIn,指令存储器数据输入端口(指令代码输入端口)
      • IDataOut,指令存储器数据输出端口(指令代码输出端口)
      • RW,指令存储器读写控制信号,为1写,为0读
    • Data Memory:数据存储器,
      • Daddr,数据存储器地址输入端口
      • DataIn,数据存储器数据输入端口
      • DataOut,数据存储器数据输出端口
      • RW,数据存储器读写控制信号,为1写,为0读
    • Register File:(寄存器组)
      • Read Reg1,rs寄存器地址输入端口
      • Read Reg2,rt寄存器地址输入端口
      • Write Reg,将数据写入的寄存器端口,其地址来源rt或rd字段
      • Write Data,写入寄存器的数据输入端口
      • Read Data1,rs寄存器数据输出端口
      • Read Data2,rt寄存器数据输出端口
      • WE,写使能信号,为1时,在时钟上升沿写入
    • ALU:
      • result,ALU运算结果
      • zero,运算结果标志,结果为0输出1,否则输出0

    表2 ALU运算功能表

    ALUOp[2..0] 功能 描述
    000 A + B
    001 A – B
    010 B – A
    011 A ∨ B
    100 A ∧ B
    101 /A ∧ B A非与B
    110 A ⊕ B 异或
    111 A ⊙ B 同或

    需要说明的是根据要实现的指令功能要求画出以上数据通路图,和确定ALU的运算功能(当然,以上指令没有完全用到提供的ALU所有功能,但至少必须能实现以上指令功能操作)。从数据通路图上可以看出控制单元部分需要产生各种控制信号,当然,也有些信号必须要传送给控制单元。从指令功能要求和数据通路图的关系得出以上表1,这样,从表1可以看出各控制信号与相应指令之间的相互关系,根据这种关系就可以得出控制信号与指令之间的关系表(留给学生完成),再根据关系表可以写出各控制信号的逻辑表达式,这样控制单元部分就可实现了。
    指令执行的结果总是在下个时钟到来前开始保存到寄存器、或存储器中,PC的改变也是在这个时候进行。另外,值得注意的问题,设计时,用模块化的思想方法设计,关于ALU设计、存储器设计、寄存器组设计等等,也是必须认真考虑的问题。可以参考其他资料文档,里面有相应的设计方法介绍。(资料文档:MIPS汇编与单周期CPU.ppt)。


    前提条件搞定,正文来了:

    先看数据通路图:
    这里写图片描述

    我遇到的一些基本问题:

    1. wire和reg是什么意思? —— 在verilog里面,变量有wire和reg两种类型,wire类型意为线,它不可储存中间结果,通过输入得出输出,类似纯函数,只要输入变化,输出立即变化,如果没有输入,自然就没有输出。reg类型意为寄存器,它可以赋初值,可以储存中间结果,只有当满足某种条件时(比如时钟上升沿),它才会变化,其他时间会一直保持最后一次变化的值。
    2. 指令怎么来? —— 在IM组件和RW组件分别开两个寄存器数组,用来模拟指令内存和数据内存,通过文件读取,从test.txt(test文件夹中)读指令到IM的指令内存(从0开始),IM组件通过输入的IAddr(数组地址下标),得到相应的指令。
    3. 指令怎么变化? —— 在PC端,有两个外部输入:CLK和Reset。其中PC内部有指令寄存器,每次CLK上升沿触发后,会改成新的指令,同时,当Reset=1时,指令寄存器也会置0。
    4. 模块和模块间怎么连接? —— 此时,需要一个顶层模块,相当于main函数,它会根据数据通路图,将一些下层模块的输出,连接到另一些下层模块的输入中。
    5. 写好的cpu怎么运行? —— 需要在顶层模块再添加一个测试文件,测试文件提供外部输入(CLK和Reset),然后模块就会自动运行得到相应的仿真结果。

    实现思路:

    每一个组件都能写成一个下层模块,实现相应的功能,并保证输入变化后输出跟着变化(不发生延迟)。
    顶层模块调用各个下层模块,根据数据通路图将模块之间连线,保证PC指令改变后,其他所有的模块都根据控制信号,发生对应的变化。
    测试模块控制CLK和Reset信号,控制PC指令的改变。


    具体代码:

    (懒得看的可以去我的github下载:链接点我点我点我)

    每个组件都能写成下层模块,下面把每个组件都写成模块:

    1. PC:CLK上升沿触发,更改指令地址
    `timescale 1ns / 1ps
    //////////////////////////////////////////////////////////////////////////////////
    // Company: 
    // Engineer: 
    // 
    // Create Date:    23:43:40 05/02/2017 
    // Design Name: 
    // Module Name:    PC 
    // Project Name: 
    // Target Devices: 
    // Tool versions: 
    // Description: 
    //
    // Dependencies: 
    //
    // Revision: 
    // Revision 0.01 - File Created
    // Additional Comments: 
    //
    //////////////////////////////////////////////////////////////////////////////////
    module PC(
        input CLK,                         // 时钟
        input Reset,                       // 重置信号
        input PCWre,                       // PC是否更改,如果为0,PC不更改
        input [31:0] newAddress,           // 新指令
        output reg[31:0] currentAddress    // 当前指令
        );
    
        initial begin
            currentAddress <= 0;  // 非阻塞赋值
        end
    
        always@(posedge CLK or posedge Reset)
         begin
            if (Reset == 1)  currentAddress <= 0;  // 如果重置,赋值为0
            else 
             begin
                if (PCWre)  currentAddress <= newAddress;
                else  currentAddress <= currentAddress;
             end
         end
    
    endmodule
    

    输入:CLK,Reset,PCWre,newAddress
    输出:currentAddress
    解释:由于指令地址存储在寄存器里,一开始需要赋currentAddress为0。Reset是重置信号,当为1时,指令寄存器地址重置。PCWre的作用为保留现场,如果PCWre为0,指令地址不变。

    2. InstructionMemory:储存指令,分割指令
    `timescale 1ns / 1ps
    //////////////////////////////////////////////////////////////////////////////////
    // Company: 
    // Engineer: 
    // 
    // Create Date:    00:10:27 05/03/2017 
    // Design Name: 
    // Module Name:    IM 
    // Project Name: 
    // Target Devices: 
    // Tool versions: 
    // Description: 
    //
    // Dependencies: 
    //
    // Revision: 
    // Revision 0.01 - File Created
    // Additional Comments: 
    //
    //////////////////////////////////////////////////////////////////////////////////
    module InstructionMemory(
         input InsMemRW,            // 读写控制信号,1为写,0位读
        input [31:0] IAddr,        // 指令地址输入入口
         //input IDataIn,             // 没用到 
    
        output [5:0] op,
        output [4:0] rs,
        output [4:0] rt,
        output [4:0] rd,
        output [15:0] immediate    // 指令代码分时段输出
        );
    
        reg[7:0] mem[0:63];  // 新建一个32位的数组用于储存指令
    
        initial 
         begin
            $readmemb("test/test.txt", mem);  //读取测试文档中的指令
         end
    
        // 从地址取值,然后输出
        assign op = mem[IAddr][7:2];
        assign rs[4:3] = mem[IAddr][1:0];
        assign rs[2:0] = mem[IAddr + 1][7:5];
        assign rt = mem[IAddr + 1][4:0];
        assign rd = mem[IAddr + 2][7:3];
        assign immediate[15:8] = mem[IAddr + 2];
        assign immediate[7:0] = mem[IAddr + 3];
    
    endmodule
    

    输入:InsMenRW,IAddr
    输出:op,rs,rt,rd,immediate
    解释:该部分为指令寄存器,通过一个64大小的8位寄存器数组来保存从文件输入的全部指令。然后通过输入的地址,找到相应的指令,并分割成op,rs,rt,rd,immediate输出。(由于寄存器地址+4,所以不用右移变换成真正的地址)

    3.RegisterFile:储存寄存器组,并根据地址对寄存器组进行读写
    `timescale 1ns / 1ps
    //////////////////////////////////////////////////////////////////////////////////
    // Company: 
    // Engineer: 
    // 
    // Create Date:    01:07:13 05/03/2017 
    // Design Name: 
    // Module Name:    RF 
    // Project Name: 
    // Target Devices: 
    // Tool versions: 
    // Description: 
    //
    // Dependencies: 
    //
    // Revision: 
    // Revision 0.01 - File Created
    // Additional Comments: 
    //
    //////////////////////////////////////////////////////////////////////////////////
    module RegisterFile(
         input CLK,                 // 时钟
         input RegWre,              // 写使能信号,为1时,在时钟上升沿写入
        input [4:0] rs,            // rs寄存器地址输入端口
        input [4:0] rt,            // rt寄存器地址输入端口
        input [4:0] WriteReg,      // 将数据写入的寄存器端口,其地址来源rt或rd字段
        input [31:0] WriteData,    // 写入寄存器的数据输入端口
         output [31:0] ReadData1,   // rs寄存器数据输出端口
        output [31:0] ReadData2    // rt寄存器数据输出端口
        );
    
    
        reg [31:0] register[0:15];  // 新建16个寄存器,用于操作
        // 初始时,将32个寄存器全部赋值为0
        integer i;
        initial 
         begin
            for(i = 0; i < 16; i = i + 1)  register[i] <= 0;
         end
    
        // 读寄存器
        assign ReadData1 = register[rs];
        assign ReadData2 = register[rt];
    
        // 写寄存器
        always@(negedge CLK)
         begin
            // 如果寄存器不为0,并且RegWre为真,写入数据
            if (RegWre && WriteReg != 0)  register[WriteReg] = WriteData;
         end 
    
    endmodule
    

    输入:CLK,RegWre,rs,rt,WriteReg,WriteData
    输出:ReadData1,ReadData2
    解释:该部分为寄存器读写单元,RegWre的作用是控制寄存器是否写入。同上,通过一个16大小的32位寄存器数组来模拟寄存器,开始时全部置0。通过访问寄存器的地址,来获取寄存器里面的值,并进行操作。(PS:由于$0恒为0,所以写入寄存器的地址不能为0)

    4.ALU(算术逻辑单元):用于逻辑指令计算和跳转指令比较
    `timescale 1ns / 1ps
    //
    // Company: 
    // Engineer: 
    // 
    // Create Date:    01:54:18 05/03/2017 
    // Design Name: 
    // Module Name:    ALU 
    // Project Name: 
    // Target Devices: 
    // Tool versions: 
    // Description: 
    //
    // Dependencies: 
    //
    // Revision: 
    // Revision 0.01 - File Created
    // Additional Comments: 
    //
    //
    module ALU(
         input [2:0] ALUOp,           // ALU操作控制
        input [31:0] A,              // 输入1
        input [31:0] B,              // 输入2
        output reg zero,             // 运算结果result的标志,result为0输出1,否则输出0
        output reg [31:0] result     // ALU运算结果
        );
    
        // 进行ALU计算
        always@(*)
         begin
            // 进行运算
            case (ALUOp)
                3'b000 : result = A + B;  // 加法
                3'b001 : result = A - B;  // 减法
                3'b010 : result = B - A;  // 减法
                3'b011 : result = A | B;  // 或
                3'b100 : result = A & B;  // 与
                3'b101 : result = ~A & B;  // A非与B
                3'b110 : result = A ^ B;  // 异或
                3'b111 : result = ~A ^ B;  // 同或
                default : result = 0;
            endcase
            // 设置zero
            if (result)  zero = 0;
            else  zero = 1;
         end
    
    endmodule
    

    输入:ALUOp,A,B
    输出:zero,result
    解释:ALUOp用于控制算数的类型,AB为输入数,result为运算结果,zero主要用于beq和bne指令的判断。

    5.SignZeroExtend:用于immediate的扩展
    `timescale 1ns / 1ps
    //
    // Company: 
    // Engineer: 
    // 
    // Create Date:    00:58:20 05/03/2017 
    // Design Name: 
    // Module Name:    EX 
    // Project Name: 
    // Target Devices: 
    // Tool versions: 
    // Description: 
    //
    // Dependencies: 
    //
    // Revision: 
    // Revision 0.01 - File Created
    // Additional Comments: 
    //
    //
    module SignZeroExtend(
        input ExtSel,                  // 控制补位,如果为1,进行符号扩展,如果为0,全补0
        input [15:0] immediate,        // 16位立即数
        output [31:0] extendImmediate   // 输出的32位立即数
        );
    
        // 进行扩展
        assign extendImmediate[15:0] = immediate;
        assign extendImmediate[31:16] = ExtSel ? (immediate[15] ? 16'hffff : 16'h0000) : 16'h0000;
    
    endmodule
    

    输入:ExtSel,immediate
    输出:extendImmediate
    解释:比较简单的一个模块。ExtSel为控制补位信号。判断后,将extendImmediate的前16位全补1或0即可。

    6.DataMemory:用于内存存储,内存读写
    `timescale 1ns / 1ps
    //////////////////////////////////////////////////////////////////////////////////
    // Company: 
    // Engineer: 
    // 
    // Create Date:    01:37:40 05/03/2017 
    // Design Name: 
    // Module Name:    DM 
    // Project Name: 
    // Target Devices: 
    // Tool versions: 
    // Description: 
    //
    // Dependencies: 
    //
    // Revision: 
    // Revision 0.01 - File Created
    // Additional Comments: 
    //
    //////////////////////////////////////////////////////////////////////////////////
    module DataMemory(
         input DataMemRW,            // 数据存储器读写控制信号,为1写,为0读
        input [31:0] DAddr,         // 数据存储器地址输入端口
        input [31:0] DataIn,        // 数据存储器数据输入端口
        output reg [31:0] DataOut   // 数据存储器数据输出端口
        );
    
        // 模拟内存,以8位为一字节存储,共64字节
        reg [7:0] memory[0:63];
    
        // 初始赋值
        integer i;
        initial
         begin
            for (i = 0; i < 64; i = i + 1)  memory[i] <= 0;
         end
    
        // 读写内存
        always@(DAddr)
         begin
    
         end
    
        always@(DAddr or DataIn)
         begin
            // 写内存
            if (DataMemRW)
             begin
               memory[DAddr] <= DataIn[31:24];
                memory[DAddr + 1] <= DataIn[23:16];
                memory[DAddr + 2] <= DataIn[15:8];
                memory[DAddr + 3] <= DataIn[7:0];
             end
            // 读内存
            else
             begin
                DataOut[31:24] <= memory[DAddr];
                DataOut[23:16] <= memory[DAddr + 1];
                DataOut[15:8] <= memory[DAddr + 2];
                DataOut[7:0] <= memory[DAddr + 3];
             end
         end
    
    endmodule
    

    输入:DataMenRW,DAddr,DataIn
    输出:DataOut
    解释:该部分控制内存存储。同上,用64大小的8位寄存器数组模拟内存(内存小主要是因为编译快),内存部分采用小端模式。DataMenRW控制内存读写。由于指令为真实地址,所以不需要*4。

    7.Multiplexer:5线和32线二路选择器
     module Multiplexer5(
         input control,
        input [4:0] in1,
        input [4:0] in0,
        output [4:0] out
        );
    
        // 5线多路选择器
        assign out = control ? in1 : in0;
    
    endmodule
    module Multiplexer32(
         input control,
        input [31:0] in1,
        input [31:0] in0,
        output [31:0] out
        );
    
        // 32线多路选择器
        assign out = control ? in1 : in0;
    
    endmodule
    

    输入:control,in1,in0
    输出:out
    解释:多路选择器,不用过多解释。

    8.最重要的ControlUnit:控制信号模块,通过解析op得到该指令的各种控制信号

    首先,需要得到控制信号表:
    这里写图片描述

    通过信号控制表,可以很轻松的写控制模块。

    `timescale 1ns / 1ps
    //
    // Company: 
    // Engineer: 
    // 
    // Create Date:    02:11:08 05/03/2017 
    // Design Name: 
    // Module Name:    CU 
    // Project Name: 
    // Target Devices: 
    // Tool versions: 
    // Description: 
    //
    // Dependencies: 
    //
    // Revision: 
    // Revision 0.01 - File Created
    // Additional Comments: 
    //
    //
    module ControlUnit(
        input [5:0] op,         // op操作符
        input zero,             // ALU的zero输出
    
         // 一堆控制信号
         output reg PCSrc,           // 多路选择器
        output reg PCWre,           // (PC)PC是否更改,如果为0,PC不更改
        output reg ALUSrcB,         // 多路选择器
        output reg ALUM2Reg,        // 多路选择器
        output reg RegWre,          // (RF)写使能信号,为1时,在时钟上升沿写入
        output reg InsMemRW,        // (IM)读写控制信号,1为写,0位读
        output reg DataMemRW,       // (DM)数据存储器读写控制信号,为1写,为0读
        output reg ExtSel,          // (EXT)控制补位,如果为1,进行符号扩展,如果为0,全补0
        output reg RegOut,          // 多路选择器
        output reg [2:0] ALUOp      // (ALU)ALU操作控制 
        );
    
        // 进行各种赋值
        initial 
         begin
            ExtSel = 0;
            PCWre = 1;
            InsMemRW = 1;
            RegOut = 1;
            RegWre = 0;
            ALUOp = 0;
            PCSrc = 0;
            ALUSrcB = 0;
            DataMemRW = 0;
            ALUM2Reg = 0;
        end
    
        always@(op or zero)
        begin  
          case(op) 
                // add
                6'b000000:
              begin   //以下都是控制单元产生的控制信号
                    PCWre = 1;
                    ALUSrcB = 0;
                    ALUM2Reg = 0;
                    RegWre = 1;
                    InsMemRW = 1;
                    DataMemRW = 0;
                    ExtSel = 0;
                    PCSrc = 0;
                    RegOut = 1;
                    ALUOp = 000;
                 end
                // addi
                6'b000001:
              begin   //以下都是控制单元产生的控制信号
                    PCWre = 1;
                    ALUSrcB = 1;
                    ALUM2Reg = 0;
                    RegWre = 1;
                    InsMemRW = 1;
                    DataMemRW = 0;
                    ExtSel = 1;
                    PCSrc = 0;
                    RegOut = 0;
                    ALUOp = 000;
                 end
                // sub
                6'b000010:
              begin   //以下都是控制单元产生的控制信号
                    PCWre = 1;
                    ALUSrcB = 0;
                    ALUM2Reg = 0;
                    RegWre = 1;
                    InsMemRW = 1;
                    DataMemRW = 0;
                    ExtSel = 0;
                    PCSrc = 0;
                    RegOut = 1;
                    ALUOp = 001;
                 end
                // ori
                6'b010000:
                 begin   //以下都是控制单元产生的控制信号
                    PCWre = 1;
                    ALUSrcB = 1;
                    ALUM2Reg = 0;
                    RegWre = 1;
                    InsMemRW = 1;
                    DataMemRW = 0;
                    ExtSel = 0;
                    PCSrc = 0;
                    RegOut = 0;
                    ALUOp = 011;
              end
               // and
             6'b010001:
              begin   //以下都是控制单元产生的控制信号
                    PCWre = 1;
                    ALUSrcB = 0;
                    ALUM2Reg = 0;
                    RegWre = 1;
                    InsMemRW = 1;
                    DataMemRW = 0;
                    ExtSel = 0;
                    PCSrc = 0;
                    RegOut = 1;
                    ALUOp = 100;
              end
                // or
                6'b010010:
              begin   //以下都是控制单元产生的控制信号
                    PCWre = 1;
                    ALUSrcB = 0;
                    ALUM2Reg = 0;
                    RegWre = 1;
                    InsMemRW = 1;
                    DataMemRW = 0;
                    ExtSel = 0;
                    PCSrc = 0;
                    RegOut = 1;
                    ALUOp = 011;
                 end
               // move
             6'b100000:
              begin   //以下都是控制单元产生的控制信号
                    PCWre = 1;
                    ALUSrcB = 0;
                    ALUM2Reg = 0;
                    RegWre = 1;
                    InsMemRW = 1;
                    DataMemRW = 0;
                    ExtSel = 0;
                    PCSrc = 0;
                    RegOut = 1;
                    ALUOp = 000;
              end
                // sw
                6'b100110:
              begin   //以下都是控制单元产生的控制信号
                    PCWre = 1;
                    ALUSrcB = 1;
                    ALUM2Reg = 0;
                    RegWre = 0;
                    InsMemRW = 1;
                    DataMemRW = 1;
                    ExtSel = 1;
                    PCSrc = 0;
                    RegOut = 0;
                    ALUOp = 000;
              end
             // lw
                6'b100111:
              begin   //以下都是控制单元产生的控制信号
                    PCWre = 1;
                    ALUSrcB = 1;
                    ALUM2Reg = 1;
                    RegWre = 1;
                    InsMemRW = 1;
                    DataMemRW = 0;
                    ExtSel = 1;
                    PCSrc = 0;
                    RegOut = 0;
                    ALUOp = 000;
              end
             // beq
               6'b110000:
              begin   //以下都是控制单元产生的控制信号
                    if (zero) begin
                        PCSrc = 1;
                    end else begin
                        PCSrc = 0;
                    end
                ALUM2Reg = 0;
                    PCWre = 1;
                    ALUSrcB = 0;
                    RegWre = 0;
                    InsMemRW = 1;
                    DataMemRW = 0;
                    ExtSel = 1;
                    RegOut = 0;
                    ALUOp = 001;
              end
               // halt
               6'b111111:
                 begin   //以下都是控制单元产生的控制信号
                    PCWre = 0;
                    ALUSrcB = 0;
                    ALUM2Reg = 0;
                    RegWre = 0;
                    InsMemRW = 0;
                    DataMemRW = 0;
                    ExtSel = 0;
                    PCSrc = 0;
                    RegOut = 0;
                    ALUOp = 000;
                 end
            endcase
         end
    
    endmodule
    

    输入:op,zero
    输出:各类控制信号
    解释:通过上表,可以将每个指令case到相应的控制信号上。


    然后,通过顶层模块,调用下层模块并将它们输入输出连在一起:

    SingleCPU:顶层连接模块
    `timescale 1ns / 1ps
    //////////////////////////////////////////////////////////////////////////////////
    // Company: 
    // Engineer: 
    // 
    // Create Date:    23:43:17 05/02/2017 
    // Design Name: 
    // Module Name:    SingleCPU 
    // Project Name: 
    // Target Devices: 
    // Tool versions: 
    // Description: 
    //
    // Dependencies: 
    //
    // Revision: 
    // Revision 0.01 - File Created
    // Additional Comments: 
    //
    //////////////////////////////////////////////////////////////////////////////////
    module SingleCPU(
        input CLK,
        input Reset,
        output [5:0] op,
         output [4:0] rs,
         output [4:0] rt,
         output [4:0] rd,
         output [15:0] immediate,
        output [31:0] ReadData1,
        output [31:0] ReadData2,
         output [31:0] WriteData,
         output [31:0] DataOut,
        output [31:0] currentAddress,
        output [31:0] result
        );
    
        // 各种临时变量
        wire [2:0] ALUOp; 
       wire [31:0] B, newAddress;
       wire [31:0] currentAddress_4, extendImmediate, currentAddress_immediate;   
       wire [4:0] WriteReg;  
       wire zero, PCSrc, PCWre, ALUSrcB, ALUM2Reg, RegWre, InsMemRW, DataMemRW, ExtSel, RegOut;
    
    
        /*module ControlUnit(
        input [5:0] op,         // op操作符
        input zero,             // ALU的zero输出
         // 一堆控制信号
         output PCSrc,           // 多路选择器
        output PCWre,           // (PC)PC是否更改,如果为0,PC不更改
        output ALUSrcB,         // 多路选择器
        output ALUM2Reg,        // 多路选择器
        output RegWre,          // (RF)写使能信号,为1时,在时钟上升沿写入
        output InsMemRW,        // (IM)读写控制信号,1为写,0位读
        output DataMemRW,       // (DM)数据存储器读写控制信号,为1写,为0读
        output ExtSel,          // (EXT)控制补位,如果为1,进行符号扩展,如果为0,全补0
        output RegOut,          // 多路选择器
        output [2:0] ALUOp      // (ALU)ALU操作控制 
        );*/
        ControlUnit cu(op, zero, PCSrc, PCWre, ALUSrcB, ALUM2Reg, RegWre, InsMemRW, DataMemRW, ExtSel, RegOut, ALUOp);
    
        /*module PC(
        input CLK,                         // 时钟
        input Reset,                       // 重置信号
        input PCWre,                       // PC是否更改,如果为0,PC不更改
        input [31:0] newAddress,           // 新指令
        output reg[31:0] currentAddress    // 当前指令
        );*/
        PC pc(CLK, Reset, PCWre, newAddress, currentAddress);
    
        /*module InstructionMemory(
         input InsMemRW,            // 读写控制信号,1为写,0位读
        input [31:0] IAddr,        // 指令地址输入入口
         //input IDataIn,             // 没用到 
        output [5:0] op,
        output [4:0] rs,
        output [4:0] rt,
        output [4:0] rd,
        output [15:0] immediate    // 指令代码分时段输出
        );*/
        InstructionMemory im(InsMemRW, currentAddress, op, rs, rt, rd, immediate);
    
        /*module RegisterFile(
         input CLK,                 // 时钟
         input RegWre,              // 写使能信号,为1时,在时钟上升沿写入
        input [4:0] rs,            // rs寄存器地址输入端口
        input [4:0] rt,            // rt寄存器地址输入端口
        input [4:0] WriteReg,      // 将数据写入的寄存器端口,其地址来源rt或rd字段
        input [31:0] WriteData,    // 写入寄存器的数据输入端口
        output [31:0] ReadData1,   // rs寄存器数据输出端口
        output [31:0] ReadData2    // rt寄存器数据输出端口
        );*/
        RegisterFile rf(CLK, RegWre, rs, rt, WriteReg, WriteData, ReadData1, ReadData2);
    
        /*module ALU(
         input [2:0] ALUOp,       // ALU操作控制
        input [31:0] A,          // 输入1
        input [31:0] B,          // 输入2
        output reg zero,             // 运算结果result的标志,result为0输出1,否则输出0
        output reg [31:0] result     // ALU运算结果
        );*/
        ALU alu(ALUOp, ReadData1, B, zero, result);
    
        /*module SignZeroExtend(
        input ExtSel,                  // 控制补位,如果为1,进行符号扩展,如果为0,全补0
        input [15:0] immediate,        // 16位立即数
        input [31:0] extendImmediate   // 输出的32位立即数
        );*/
        SignZeroExtend sze(ExtSel, immediate, extendImmediate);
    
        /*module DataMemory(
         input DataMemRW,            // 数据存储器读写控制信号,为1写,为0读
        input [31:0] DAddr,         // 数据存储器地址输入端口
        input [31:0] DataIn,        // 数据存储器数据输入端口
        output reg [31:0] DataOut   // 数据存储器数据输出端口
        );*/
        DataMemory dm(DataMemRW, result, ReadData2, DataOut);
    
        assign currentAddress_4 = currentAddress + 4;
        assign currentAddress_immediate = currentAddress_4 + (extendImmediate << 2);
        //ADD add1(currentAddress, 32'h00000004, currentAddress_4);
        //ADD add1(currentAddress_4, extendImmediate << 2, currentAddress_immediate);
    
        Multiplexer5 m5(RegOut, rd, rt, WriteReg);
    
        Multiplexer32 m321(ALUSrcB, extendImmediate, ReadData2, B);
        Multiplexer32 m322(ALUM2Reg, DataOut, result, WriteData);
        Multiplexer32 m323(PCSrc, currentAddress_immediate, currentAddress_4, newAddress);
    
    endmodule
    

    (PS:该模块只需要输入输出正确。)


    最后,加入测试单元,即可进行仿真模拟:

        // 部分测试单元代码
        initial begin
            CLK = 0;
            Reset = 1;
    
            // Wait 50 ns for global reset to finish
            #50; // 刚开始设置pc为0
             CLK = !CLK;  // 下降沿,使PC先清零
          #50;
             Reset = 0;  // 清除保持信号
          forever #50
             begin // 产生时钟信号,周期为50s
             CLK = !CLK;
           end
        end

    我使用的测试指令如下:

    //addi $1, $0, 4
    000001 00000 00001 0000000000000100
    //addi $2, $0, 8
    000001 00000 00010 0000000000001000
    //sw $2, 0($2)
    100110 00010 00010 0000000000000000
    //add $3, $2, $1
    000000 00010 00001 00011 00000000000
    //sub $3, $3, $1
    000010 00011 00001 00011 00000000000
    //beq $2, $3, -2
    110000 00010 00011 1111111111111110
    //ori $1, $1, 1
    010000 00001 00001 0000000000000001
    //or $3, $2, $1
    010010 00010 00001 00011 00000000000
    //move $3, $2
    100000 00010 00000 00011 00000000000
    //and $1, $3, $2
    010001 00011 00010 00001 00000000000
    //lw $4, 0($2)
    100111 00010 00100 0000000000000000
    //halt 
    111111 00000000000000000000000000
    00000100
    00000001
    00000000
    00000100
    00000100
    00000010
    00000000
    00001000
    10011000
    01000010
    00000000
    00000000
    00000000
    01000001
    00011000
    00000000
    00001000
    01100001
    00011000
    00000000
    11000000
    01000011
    11111111
    11111110
    01000000
    00100001
    00000000
    00000001
    01001000
    01000001
    00011000
    00000000
    10000000
    01000000
    00011000
    00000000
    01000100
    01100010
    00001000
    00000000
    10011100
    01000100
    00000000
    00000000
    11111100
    00000000
    00000000
    00000000
    

    测试结果如下:

    这里写图片描述

    可以看出,结果还是符合预期的。


    总结:

    其实自己做了一遍后,发现,并不是很难。。。感觉代码部分只要结构严谨,语法规范,并没有造成多大的麻烦,还有,最最关键的一点,判断==是不靠谱的,很容易产生竞争冒险的问题。

    展开全文
  • 单周期CPU设计(Verilog)

    万次阅读 多人点赞 2017-05-07 10:46:00
    2017/06/08: 当时单周期cpu写的比较仓促,没有深入的进行调试,我准备在放假的时候重构一下代码, 然后把博文改进一下,现在实在没有时间,很抱歉~ 不过多周期我有调试过的,所以有需要的可以移步到我的多周期cpu设计...
  • 单周期CPU设计实验

    千次阅读 2018-07-24 17:03:03
    (1) 掌握单周期CPU数据通路图的构成、原理及其设计方法; (2) 掌握单周期CPU的实现方法,代码实现方法; (3) 认识和掌握指令与CPU的关系; (4) 掌握测试单周期CPU的方法; (5) 掌握单周期CPU的实现方法。 ...
  • 单周期CPU设计与实现

    万次阅读 多人点赞 2018-06-07 15:26:00
    单周期CPU设计与实现 实验内容: 学校资料
  • (Verilog)单周期CPU设计 单周期CPU设计与实现原理分析
  • MIPS-单周期CPU设计

    万次阅读 多人点赞 2016-04-22 22:16:04
    MIPS-单周期CPU设计设计一个单周期CPU,该CPU至少能实现以下指令功能操作。需设计的指令与格式如下: 实验原理 单周期CPU指的是一条指令的执行在一个时钟周期内完成,然后开始下一条指令的执行,即一条指令用...
  • 单周期CPU仿真

    千次阅读 2018-06-09 20:17:27
    之前的几周我们做了单周期CPU仿真的实验,虽然一开始做得一脸懵逼,但最后还是成功实现了一个简单的CPU。 单周期CPU原理 单周期CPU指的是一条指令的执行在一个时钟周期内完成,无论是哪种指令。处理指令有...
  • 《计算机组成原理实验》 单周期CPU

    千次阅读 2019-08-26 22:44:32
    《计算机组成原理实验》 单周期CPU 前言 这是中山大学2018年计算机组成原理实验中单周期CPU的实验报告,仿真与写板的内容暂略,所有源代码(包括写板)已经上传至我的github当中,欢迎大家访问。 github个人主页:...
  • 单周期CPU设计与实现原理分析

    千次阅读 2018-07-28 20:35:36
    单周期CPU设计与实现原理分析 一.单周期CPU的设计思路 二.单周期CPU的模块实现 ① Instruction Memory指令存储器的设计 ② ALU算术逻辑单元的设计 ③ PC程序计数器的设计 ④ Control Unit控制单元 ⑤ Data ...
  • 实验四、单周期 CPU 设计与实现——单指令 CPU

    千次阅读 多人点赞 2019-06-21 21:47:17
    实验四、单周期CPU设计与实现——单指令CPU 一、实验目的: 通过设计并实现支持一条指令的CPU,理解和掌握CPU设计的基本原理和过程。 二、实验内容: 设计和实现一个支持加法指令的单周期CPU。要求该加法指令(表示...
  • 此为中山大学17级计算机组成与设计实验课题。为不影响老师的教学,...掌握单周期CPU数据通路图的构成、原理及其设计方法; 掌握单周期CPU的实现方法,代码实现方法; 认识和掌握指令与CPU的关系; 掌握测试单周期C...
  • MIPS单周期CPU设计(24条指令)

    万次阅读 多人点赞 2020-07-22 21:14:25
    MIPS单周期可执行24条指令CPU 实验要求 本实训项目帮助学生构建支持 24 条指令的 MIPS 单周期 CPU ,最终实现的处理器能运行 benchmark 测试程序。~~另外希望学有余力的同学能为自己的处理器增加中断处理机制,能...
  • 单周期CPU实验之实验介绍及分析

    千次阅读 2017-04-29 16:39:11
    单周期CPU实验内容及分析。这一篇,将对如何实现单周期CPU进行相关的分析。
  • 用Verilog语言设计的单周期CPU,资源里包含了源代码及单周期CPU结构图,与大家分享下。
  • 单周期CPU实验之代码实现及模拟

    千次阅读 2017-04-29 17:22:07
    单周期CPU代码实现。学习 了这么久,终于可以完整实现了单周期CPU了。以下是相关代码,供大家学习使用。
  • 单周期CPU——verilog语言实现

    万次阅读 多人点赞 2018-06-29 11:28:38
    单周期CPU的设计与实现 关于单周期CPU的设计问题,相信各位在课程上也有所学习,以下是个人的一些理解 整个项目的资源下载:这里写链接内容 实验内容 基本要求 PC和寄存器组写状态使用时钟触发,这是必须...
  • 31条指令单周期cpu设计(Verilog)-(二)总体设计

    万次阅读 多人点赞 2018-07-14 11:27:27
    31条指令单周期cpu设计(Verilog)-(一)相关软件 31条指令单周期cpu设计(Verilog)-(二)总体设计 31条指令单周期cpu设计(Verilog)-(三)指令分析 (重要) 31条指令单周期cpu设计(Verilog)-(四)数据输入输出关系表 ...
  • 单周期cpu设计与实现

    万次阅读 2016-05-02 17:44:42
    前两周终于把计组第二个实验完成,单周期cpu设计与实现。 环境用的是ise14.7,也许是不兼容的问题,导致写着写着代码突然间就秒退了,一度弄得人都要炸了。
  • 使用logisim布线完成的MIPS单周期CPU,可支持28条指令。跑马灯的代码已经装入了寄存器,可以直接开启时钟运行。
  • 单周期CPU实验之学习之旅

    千次阅读 2017-04-13 18:16:50
    单周期CPU实验设计。这一篇,我们先来学习一些基础知识,主要包括MIPS指令集和Verilog语言。
  • 设计和实现一个支持如下十条指令的单周期CPU。 非访存指令  清除累加器指令CLA  累加器取反指令COM  算术右移一位指令SHR:将累加器ACC中的数右移一位,结果放回ACC  循环左移一位指令CSL:对累加器中的数据...
  • 单周期CPU的实现

    千次阅读 2018-03-06 16:20:25
    0 本文主要总结计算机组成与结构中的大作业——单周期CPU的实现1 主要实现代码1.1 PC及PC+4module PC(Clk,Clrn,Result,Address);//Reset=0,重置 input Clk,Clrn; input[31:0] Result; output[31:0] Address; ...
  • 【计组大作业】单周期CPU

    千次阅读 2019-12-04 22:43:57
      实现基于MIPS 指令集的单周期CPU,可执行如下34 种指令:   用 vivado 编写 CPU 工程,编写完成后,通过运行一份包含所有指令的汇编代码(将二进制代码储存于指令内存中),观察仿真信号及 FPGA 显示来检验 ...
  • 设计一个简单的单周期CPU

    千次阅读 2020-08-27 16:53:39
    设计一个简单的单周期 CPU 1.1 设计单周期 CPU 时的总体思路 设计输入是指令系统规范,设计输出是一个数字逻辑电路,这个数字逻辑电路能够实现指令系统规范所定义的各项功能。 1.1.1 指令系统规范 指令系统规范是...
  • 制作单周期CPU(分析)

    万次阅读 多人点赞 2017-04-21 16:49:26
    单周期:单周期CPU指一条指令在一个时钟周期内完成并开始下一条指令的执行。由时钟上升、下降沿控制相关操作。两个相邻时钟上升沿或下降沿之间的时间间隔为一个时钟周期2.CPU如何处理指令:CPU处理指令的步骤如下: ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 190,360
精华内容 76,144
关键字:

单周期cpu