精华内容
下载资源
问答
  • 单周期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~

     



    展开全文
  • 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设计与实现

    万次阅读 多人点赞 2018-06-07 15:26:00
    单周期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

    000000 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. sll rd, rt,sa

    011000 未用 rt(5位) rd(5位) sa reserved

    功能:rd<-rt<<(zero-extend)sa,左移sa位 ,(zero-extend)sa

    ==>比较指令
    8. slti rt, rs,immediate 带符号

    011011 rs(5位) rt(5位) immediate(16位)

    功能:if (rs <(sign-extend)immediate) rt =1 else rt=0, 具体请看表2 ALU运算功能表,带符号

    ==> 存储器读/写指令
    9. sw rt ,immediate(rs) 写存储器

    100110 rs(5位) rt(5位) immediate(16位)

    功能:memory[rs+ (sign-extend)immediate]←rt;immediate符号扩展再相加。即将rt寄存器的内容保存到rs寄存器内容和立即数符号扩展后的数相加作为地址的内存单元中。
    10. lw rt , immediate(rs) 读存储器

    100111 rs(5位) rt(5位) immediate(16位)

    功能:rt ← memory[rs + (sign-extend)immediate];immediate符号扩展再相加。
    即读取rs寄存器内容和立即数符号扩展后的数相加作为地址的内存单元中的数,然后保存到rt寄存器中。

    ==> 分支指令
    11. beq rs,rt,immediate

    110000 rs(5位) rt(5位) immediate(16位)

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

    110001 rs(5位) rt(5位) immediate(16位)

    功能:if(rs!=rt) pc←pc + 4 + (sign-extend)immediate <<2 else pc ←pc + 4
    特别说明:与beq不同点是,不等时转移,相等时顺序执行。

    ==>跳转指令
    13. j addr

    111000 addr[27…2]

    功能:pc <-{(pc+4)[31..28],addr[27..2],2{0}},无条件跳转。
    说明:由于MIPS32的指令代码长度占4个字节,所以指令地址二进制数最低2位均为0,将指令地址放进指令代码中时,可省掉!这样,除了最高6位操作码外,还有26位可用于存放地址,事实上,可存放28位地址了,剩下最高4位由pc+4最高4位拼接上。
    ==> 停机指令
    14. halt

    111111 00000000000000000000000000(26位)

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

    实验原理:

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

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

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

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

    MIPS指令的三种格式:
    这里写图片描述

    其中,

    • op:为操作码;
    • rs:只读。为第1个源操作数寄存器,寄存器地址(编号)是00000~11111,00~1F;
    • rt:可读可写。为第2个源操作数寄存器,或目的操作数寄存器,寄存器地址(同上);
    • rd:只写。为目的操作数寄存器,寄存器地址(同上);
    • sa:为位移量(shift amt),移位指令用于指定移多少位;
    • funct:为功能码,在寄存器类型指令中(R类型)用来指定指令的功能与操作码配合使用;
    • immediate:为16位立即数,用作无符号的逻辑操作数、有符号的算术操作数、数据加载(Load)/数据保存(Store)指令的数据地址字节偏移量和分支指令中相对程序计数器(PC)的有符号偏移量;
    • address:为地址。

    单周期CPU数据通路和控制线路图:

    这里写图片描述

    控制信号名 状态“0” 状态“1”
    Reset 初始化PC为0 PC接收新地址
    PCWre PC不更改,相关指令:halt PC更改,相关指令:除指令halt外
    ALUSrcA 来自寄存器堆data1输出,相关指令:add、sub、addi、or、and、ori、beq、bne、slti、sw、lw 来自移位数sa,同时,进行(zero-extend)sa,即 {{27{0}},sa},相关指令:sll
    ALUSrcB 来自寄存器堆data2输出,相关指令:add、sub、or、and、sll、beq、bne 来自sign或zero扩展的立即数,相关指令:addi、ori、slti、sw、lw
    DBDataSrc 来自ALU运算结果的输出,相关指令:add、addi、sub、ori、or、and、slti、sll 来自数据存储器(Data MEM)的输出,相关指令:lw
    RegWre 无写寄存器组寄存器,相关指令:beq、bne、sw、halt、j 寄存器组写使能,相关指令:add、addi、sub、ori、or、and、slti、sll、lw
    InsMemRW 写指令存储器 读指令存储器(Ins. Data)
    mRD 输出高阻态 读数据存储器,相关指令:lw
    mWR 无操作 写数据存储器,相关指令:sw
    RegDst 写寄存器组寄存器的地址,来自rt字段,相关指令:addi、ori、lw、slti 写寄存器组寄存器的地址,来自rd字段,相关指令:add、sub、and、or、sll
    ExtSel (zero-extend)immediate(0扩展),相关指令:ori (sign-extend)immediate(符号扩展),相关指令:addi、slti、sw、lw、beq、bne
    PCSrc[1..0] 00:pc<-pc+4,相关指令:add、addi、sub、or、ori、and、slti、sll、sw、lw、beq(zero=0)、bne(zero=1);01:pc<-pc+4+(sign-extend)immediate,相关指令:beq(zero=1)、bne(zero=0);10:pc<-{(pc+4)[31:28],addr[27:2],2{0}},相关指令:j;11:未用 -
    ALUOp[2..0] ALU 8种运算功能选择(000-111),看功能表
    相关部件及引脚说明:
    • Instruction Memory:指令存储器,
    • Iaddr,指令存储器地址输入端口
    • IDataIn,指令存储器数据输入端口(指令代码输入端口)
    • IDataOut,指令存储器数据输出端口(指令代码输出端口)
    • RW,指令存储器读写控制信号,为0写,为1读
    • Data Memory:数据存储器,
    • Daddr,数据存储器地址输入端口
    • DataIn,数据存储器数据输入端口
    • DataOut,数据存储器数据输出端口
    • /RD,数据存储器读控制信号,为0读
    • /WR,数据存储器写控制信号,为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,则zero=1;否则zero=0

    表2 ALU运算功能表

    ALUOp[2:0] 功能 描述
    000 Y = A + B
    001 Y = A – B
    010 Y = B << A B左移A位
    011 Y = A ∨ B
    100 Y = A ∧ B
    101 Y =(A < B)? 1: 0 比较A与B 不带符号
    110 比较A与B 带符号
    111 Y = A ⊕ B 异或

    实验过程与结果

    设计思路以及流程:

    完成控制信号与相对应指令之间相互关系的表格

    表3是依据表1控制信号的作用以及表2 ALU运算功能表完成的,某些指令无需用到部分模块,则相对应模块的使能控制信号与其无关。例如,对于跳转指令而言,其无需对数据寄存器进行读写操作,则数据寄存器相关的控制信号mRD,mWR设为0,防止修改里面的数据。部分指令执行不需要所有的模块都参与,故有些模块的控制信号与其没有直接关系,为了防止出现一些不必要的错误,统一将指令相对应的无关的使能控制信号默认设置为低电平(0),无需ALU运算的(例如跳转指令)默认将其操作变成(000)。**

    表3 控制信号与相对应指令之间的相互关系

    指 令 控制信号量
    PCWre ExtSel InsMemRW RegDst RegWre ALUSrcA ALUSrcB PCSrc(zero:0/1) ALUOp mRD mWR DBDataSrc
    addi 1 1 1 0 1 0 1 00 000 0 0 0
    ori 1 1 1 0 1 0 1 00 011 0 0 0
    add 1 0 1 1 1 0 0 00 000 0 0 0
    sub 1 1 1 1 1 0 0 00 001 0 0 0
    and 1 0 1 1 1 0 0 00 100 0 0 0
    or 1 0 1 1 1 0 0 00 011 0 0 0
    sll 1 0 1 1 1 1 0 00 010 0 0 0
    bne 1 1 1 X 0 0 0 01/ 00 001 0 0 0
    slti 1 1 1 0 1 0 1 00 101 0 0 0
    beq 1 1 1 X 0 0 0 00 / 01 001 0 0 0
    sw 1 1 1 X 0 0 1 00 000 0 1 0
    lw 1 1 1 0 1 0 1 00 000 1 0 1
    j 1 0 1 X 0 X X 10 000 0 0 0
    halt 0 0 0 X 0 X X 00 000 0 0 0

    完成控制信号与相对应指令之间的关系以后该表后,对于如何实现单周期依旧感到很模糊,不知道相对应的信号量具体的控制意义,因此尝试结合实验原理中的图2单周期CPU数据通路和控制线路图,思考三种类型的指令,R型、I型、J型指令的CPU处理过程。对于R型指令而言,主要是一些算术运算指令和逻辑运算,主要为取指令,解析指令,执行指令,将运算结果写回寄存器组,其不需要访问数据寄存器,下一条指令顺序下一条,即pc←pc+4,其中的一些运算则由控制单元得到指令的操作码以后,设置控制信号,控制各个模块执行不同操作或者数据选择器选择相对应的输入作为输出;对于I型指令,其包含指令种类比较多,存储器指令,需要对存储器进行读或写的操作,对于pc没有别的特别影响,而分支指令则下一个pc可能不是pc+4,需要依据其运算结果做相对应的跳转操作或者顺序执行操作;对于J型指令,其是跳转指令,跳转到指令中相对应的地址中,主要对pc进行操作。不同类型的指令,其进行的过程并非完成相同的,不同类型指令所使用的模块并不是一样的,所有的指令也不是都需要完整的五个处理阶段。结合CPU数据通路图以及指令相对应的控制信号后,对于每种指令的数据通路有了一个比较清晰的了解,对于每个控制信号与相对应的功能模块更加熟悉和了解,理清了如何设计单周期CPU,即将其模块化,并且在控制单元中依据指令的操作码,对各个模块的控制信号进行一定的设定,执行指令相对应的操作。

    CPU模块划分与实现

    依据图2 单周期CPU数据通路和控制线路图,将CPU划分为9个模块,没有完全依据单周期CPU数据通路图进行划分,主要依据数据通路图进行划分太冗余,因此将一些数据选择器合并进了部分功能模块中,实现简化。模块划分结果如图三所示。
    这里写图片描述

    pcAdd

    • 模块功能:根据控制信号PCSrc,计算获得下一个pc以及控制信号Reset重置。

    • 实现思路:首先先决定何时引起触发,决定敏感变量,该模块选择将时钟的下降沿以及控制信号Reset的下降沿作为敏感变量,主要是为了能够确保下一条pc能够正确得到。

    • 主要实现代码:

    `timescale 1ns / 1ps
    module pcAdd(
            input Reset,
            input CLK,               //时钟
            input [1:0] PCSrc,             //数据选择器输入
            input [31:0] immediate,  //偏移量
            input [25:0] addr,
            input [31:0] curPC,
            output reg[31:0] nextPC  //新指令地址
        );
    
        initial begin
            nextPC <= 0;
        end
    
        reg [31:0] pc;
    
        always@(negedge CLK or negedge Reset)
        begin
            if(!Reset) begin
                nextPC <= 0;
            end
            else begin
                pc <= curPC + 4;
                case(PCSrc)
                    2'b00: nextPC <= curPC + 4;
                    2'b01: nextPC <= curPC + 4 + immediate * 4;
                    2'b10: nextPC <= {pc[31:28],addr,2'b00};
                    2'b11: nextPC <= nextPC;
                endcase
            end
        end
    endmodule
    

    PC

    • 模块功能:根据控制信号PCWre,判断pc是否改变以及根据Reset信号判断是否重置

    • 实现思路:将时钟信号的上升沿和控制信号Reset作为敏感变量,使得pc在上升沿的时候发生改变或被重置。

    • 主要实现代码:
    `timescale 1ns / 1ps
    module PC(
           input CLK,               //时钟
           input Reset,             //是否重置地址。0-初始化PC,否则接受新地址
           input PCWre,             //是否接受新的地址。0-不更改;1-可以更改
           input [1:0] PCSrc,             //数据选择器输入
           input [31:0] nextPC,  //新指令地址
           output reg[31:0] curPC //当前指令的地址
        );
    
        initial begin
            curPC <= 0;
        end
    
        always@(posedge CLK or negedge Reset)
        begin
            if(!Reset) // Reset == 0, PC = 0
                begin
                    curPC <= 0;
                end
            else 
                begin
                    if(PCWre) // PCWre == 1
                        begin 
                            curPC <= nextPC;
                        end
                    else    // PCWre == 0, halt
                        begin
                            curPC <= curPC;
                        end
                end
        end
    endmodule
    

    InsMEM

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

    • 实现思路:将pc的输入作为敏感变量,当pc发生改变的时候,则进行指令的读取,根据相关的地址,输出指令寄存器中相对应的指令

    • 主要实现代码:

    `timescale 1ns / 1ps
    //ROM
    //instruction memory 指令寄存器
    module InsMEM(
            input [31:0] IAddr,
            input InsMemRW,             //状态为'0',写指令寄存器,否则为读指令寄存器
            output reg[31:0] IDataOut
        );
    
        reg [7:0] rom[128:0];  // 存储器定义必须用reg类型,存储器存储单元8位长度,共128个存储单元,可以存32条指令
    
        // 加载数据到存储器rom。注意:必须使用绝对路径
        initial 
        begin
            $readmemb("地址\\rom.txt", rom);
        end
    
        //大端模式
        always@(IAddr or InsMemRW)
        begin
            //取指令
            if(InsMemRW)
                begin
                    IDataOut[7:0] = rom[IAddr + 3];
                    IDataOut[15:8] = rom[IAddr + 2];
                    IDataOut[23:16] = rom[IAddr + 1];
                    IDataOut[31:24] = rom[IAddr];
                end 
                //$display("iaddr: %d insmemrw: %d inst; %d",IAddr, InsMemRW, IDataOut);
        end
    
    endmodule
    

    InstructionCut

    • 模块功能:对指令进行分割,获得相对应的指令信息

    • 实现思路:根据各种类型的指令结构,将指令分割,得到相对应的信息

    • 主要实现代码:

    `timescale 1ns / 1ps
    //指令分割
    module InstructionCut(
            input [31:0] instruction,
            output reg[5:0] op,
            output reg[4:0] rs,
            output reg[4:0] rt,
            output reg[4:0] rd,
            output reg[4:0] sa,
            output reg[15:0] immediate,
            output reg[25:0] addr
        );
    
        initial begin
            op = 5'b00000;
            rs = 5'b00000;
            rt = 5'b00000;
            rd = 5'b00000;
        end
    
        always@(instruction) 
        begin
            op = instruction[31:26];
            rs = instruction[25:21];
            rt = instruction[20:16];
            rd = instruction[15:11];
            sa = instruction[10:6];
            immediate = instruction[15:0];
            addr = instruction[25:0];
        end
    endmodule
    

    ControlUnit

    • 模块功能:控制单元,依据指令的操作码(op)以及标记符(ZERO),输出PCWre、ALUSrcB等控制信号,各控制信号的作用见实验原理的控制信号作用表(表3),从而达到控制各指令的目的.

    • 主要实现代码:

    `timescale 1ns / 1ps
    //Control Unit
    module ControlUnit(
            input zero,         //ALU运算结果是否为0,为0时候为1
            input [5:0] op,     //指令的操作码
            output reg PCWre,       //PC是否更改的信号量,为0时候不更改,否则可以更改
            output reg ExtSel,      //立即数扩展的信号量,为0时候为0扩展,否则为符号扩展
            output reg InsMemRW,    //指令寄存器的状态操作符,为0的时候写指令寄存器,否则为读指令寄存器
            output reg RegDst,      //写寄存器组寄存器的地址,为0的时候地址来自rt,为1的时候地址来自rd
            output reg RegWre,      //寄存器组写使能,为1的时候可写
            output reg ALUSrcA,     //控制ALU数据A的选择端的输入,为0的时候,来自寄存器堆data1输出,为1的时候来自移位数sa
            output reg ALUSrcB,     //控制ALU数据B的选择端的输入,为0的时候,来自寄存器堆data2输出,为1时候来自扩展过的立即数
            output reg [1:0]PCSrc,  //获取下一个pc的地址的数据选择器的选择端输入
            output reg [2:0]ALUOp,  //ALU 8种运算功能选择(000-111)
            output reg mRD,         //数据存储器读控制信号,为0读
            output reg mWR,         //数据存储器写控制信号,为0写
            output reg DBDataSrc    //数据保存的选择端,为0来自ALU运算结果的输出,为1来自数据寄存器(Data MEM)的输出        
        );
    
        initial begin
            InsMemRW = 1;
            PCWre = 1;
            mRD = 0;
            mWR = 0;
            DBDataSrc = 0;
        end
    
        always@(op or zero) 
        begin
            PCWre = (op == 6'b111111) ? 0 : 1;   //halt
            InsMemRW = (op == 6'b111111) ? 0 : 1;    
            mWR = (op == 6'b100110) ? 1 : 0;     //sw
            mRD = (op == 6'b100111) ? 1 : 0;     //lw
            DBDataSrc = (op == 6'b100111) ? 1 : 0;
    
            case(op)
                //addi
                6'b000001:
                    begin
                        ExtSel = 1;
                        RegDst = 0;
                        RegWre = 1;
                        ALUSrcA = 0;
                        ALUSrcB = 1;
                        PCSrc = 2'b00;
                        ALUOp = 3'b000;
                    end
                //ori
                6'b010000:
                    begin
                        ExtSel = 1;
                        RegDst = 0;
                        RegWre = 1;
                        ALUSrcA = 0;
                        ALUSrcB = 1;
                        PCSrc = 2'b00;
                        ALUOp = 3'b011;
                    end
                //add
                6'b000000:
                    begin
                        ExtSel = 0;
                        RegDst = 1;
                        RegWre = 1;
                        ALUSrcA = 0;
                        ALUSrcB = 0;
                        PCSrc = 2'b00;
                        ALUOp = 3'b000;
                    end
                //sub
                6'b000010:
                    begin
                        ExtSel = 1;
                        RegDst = 1;
                        RegWre = 1;
                        ALUSrcA = 0;
                        ALUSrcB = 0;
                        PCSrc = 2'b00;
                        ALUOp = 3'b001;
                    end
                //and
                6'b010001:
                    begin
                        ExtSel = 0;
                        RegDst = 1;
                        RegWre = 1;
                        ALUSrcA = 0;
                        ALUSrcB = 0;
                        PCSrc = 2'b00;
                        ALUOp = 3'b100;
                    end
                //or
                6'b010010:
                    begin
                        ExtSel = 0;
                        RegDst = 1;
                        RegWre = 1;
                        ALUSrcA = 0;
                        ALUSrcB = 0;
                        PCSrc = 2'b00;
                        ALUOp = 3'b011;
                    end
                //sll
                6'b011000:
                    begin
                        ExtSel = 0;
                        RegDst = 1;
                        RegWre = 1;
                        ALUSrcA = 1;
                        ALUSrcB = 0;
                        PCSrc = 2'b00;
                        ALUOp = 3'b010;
                    end
                //bne
                6'b110001:
                    begin
                        ExtSel = 1;
                        RegDst = 0;
                        RegWre = 0;
                        ALUSrcA = 0;
                        ALUSrcB = 0;
                        PCSrc = zero ? 2'b00 : 2'b01;
                        ALUOp = 3'b001;
                    end
                //slti
                6'b011011:
                    begin
                        ExtSel = 1;
                        RegDst = 0;
                        RegWre = 1;
                        ALUSrcA = 0;
                        ALUSrcB = 1;
                        PCSrc = 2'b00;
                        ALUOp = 3'b101;
                    end
                //beq
                6'b110000:
                    begin
                        ExtSel = 1;
                        RegDst = 0;
                        RegWre = 0;
                        ALUSrcA = 0;
                        ALUSrcB = 0;
                        PCSrc = zero ? 2'b01 : 2'b00;
                        ALUOp = 3'b001;
                    end
                //sw
                6'b100110:
                    begin
                        ExtSel = 1;
                        RegDst = 0;
                        RegWre = 0;
                        ALUSrcA = 0;
                        ALUSrcB = 1;
                        PCSrc = 2'b00;
                        ALUOp = 3'b000;
                    end
                //lw
                6'b100111:
                    begin
                        ExtSel = 1;
                        RegDst = 0;
                        RegWre = 1;
                        ALUSrcA = 0;
                        ALUSrcB = 1;
                        PCSrc = 2'b00;
                        ALUOp = 3'b000;
                    end
                //j
                6'b111000:
                    begin
                        ExtSel = 0;
                        RegDst = 0;
                        RegWre = 0;
                        ALUSrcA = 0;
                        ALUSrcB = 0;
                        PCSrc = 2'b10;
                        ALUOp = 3'b000;
                    end
                //halt
                6'b111111:
                    begin
                        ExtSel = 0;
                        RegDst = 0;
                        RegWre = 0;
                        ALUSrcA = 0;
                        ALUSrcB = 0;
                        PCSrc = 2'b11;
                        ALUOp = 3'b000;
                    end
            endcase
        end
    endmodule

    RegisterFile

    • 模块功能:寄存器组,通过控制单元输出的控制信号,进行相对应的读或写操作

    • 主要实现代码:

    `timescale 1ns / 1ps
    //寄存器组
    module RegisterFile(
            input CLK,                  //时钟
            input [4:0] ReadReg1,    //rs寄存器地址输入端口
            input [4:0] ReadReg2,    //rt寄存器地址输入端口
            input [31:0] WriteData,     //写入寄存器的数据输入端口
            input [4:0] WriteReg,       //将数据写入的寄存器端口,其地址来源rt或rd字段
            input RegWre,               //WE,写使能信号,为1时,在时钟边沿触发写入
            output reg[31:0] ReadData1,  //rs寄存器数据输出端口
            output reg[31:0] ReadData2   //rt寄存器数据输出端口
        );
    
        initial begin
            ReadData1 <= 0;
            ReadData2 <= 0;
        end
    
    
        reg [31:0] regFile[0:31]; //  寄存器定义必须用reg类型
        integer i;
        initial begin
            for (i = 0; i < 32; i = i+ 1) regFile[i] <= 0;  
        end
    
        always@(ReadReg1 or ReadReg2) 
        begin
            ReadData1 = regFile[ReadReg1];
            ReadData2 = regFile[ReadReg2];
            //$display("regfile %d %d\n", ReadReg1, ReadReg2);
        end
    
        always@(negedge CLK)
        begin
            //$0恒为0,所以写入寄存器的地址不能为0
            if(RegWre && WriteReg)
                begin
                    regFile[WriteReg] <= WriteData;
                end
        end
    endmodule
    

    ALU

    • 模块功能:算术逻辑单元,对两个输入依据ALUOp进行相对应的运算

    • 实现思路:依据实验原理中的ALU运算功能表(表2)完成操作码对应的操作

    • 主要实现代码:

    `timescale 1ns / 1ps
    module ALU(
            input ALUSrcA,
            input ALUSrcB,
            input [31:0] ReadData1,
            input [31:0] ReadData2,
            input [4:0] sa,
            input [31:0] extend,
            input [2:0] ALUOp,
            output reg zero,
            output reg[31:0] result
        );
    
        reg [31:0] A;
        reg [31:0] B;
    
        always@(ReadData1 or ReadData2 or ALUSrcA or ALUSrcB or ALUOp) 
        begin
            //定义两个输入端口
            A = (ALUSrcA == 0) ? ReadData1 : sa;
            B = (ALUSrcB == 0) ? ReadData2 : extend;
            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) ? 1 : 0;
                3'b110: result = (((ReadData1 < ReadData2) && (ReadData1[31] == ReadData2[31] )) ||( ( ReadData1[31] ==1 && ReadData2[31] == 0))) ? 1:0;
                3'b111: result = A ^ B;
            endcase
            zero = (result == 0) ? 1 : 0;
        end 
    endmodule
    

    DataMEM

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

    • 主要实现代码:

    `timescale 1ns / 1ps
    //RAM
    //data memory 数据存储器
    module DataMEM(
            /*
                Daddr,数据存储器地址输入端口
                DataIn,数据存储器数据输入端口
                DataOut,数据存储器数据输出端口
                mRD,数据存储器读控制信号,为0读
                mWR,数据存储器写控制信号,为0写
            */
            input mRD,
            input mWR,
            input CLK,
            input DBDataSrc,
            input [31:0] DAddr,
            input [31:0] DataIn,
            output reg[31:0] DataOut,
            output reg[31:0] DB
        );
    
        initial begin 
            DB <= 16'b0;
        end
    
         reg [7:0] ram [0:31];     // 存储器定义必须用reg类型    
    
        always@(mRD or DAddr or DBDataSrc)
        begin
            //读
            DataOut[7:0] = mRD ? ram[DAddr + 3] : 8'bz; // z 为高阻态     
            DataOut[15:8] = mRD ? ram[DAddr + 2] : 8'bz;     
            DataOut[23:16] = mRD ? ram[DAddr + 1] : 8'bz;     
            DataOut[31:24] = mRD ? ram[DAddr] : 8'bz;
    
            DB = (DBDataSrc == 0) ? DAddr : DataOut;
        end
    
        always@(negedge CLK)
        begin   
            //写
            if(mWR)
                begin
                    ram[DAddr] = DataIn[31:24];    
                    ram[DAddr + 1] = DataIn[23:16];
                    ram[DAddr + 2] = DataIn[15:8];     
                    ram[DAddr + 3] = DataIn[7:0];    
                end
            //$display("mwr: %d $12 %d %d %d %d", mWR, ram[12], ram[13], ram[14], ram[15]);
        end
    
    endmodule
    

    SignZeroExtend

    • 模块功能:根据指令相关的控制信号ExtSel,对立即数进行扩展。

    • 实现思路:根据控制信号ExtSel判断是0扩展还是符号扩展,然后进行相对应的扩展

    • 主要实现代码:

    `timescale 1ns / 1ps
    module SignZeroExtend(
            input wire [15:0] immediate,    //立即数
            input ExtSel,                   //状态'0',0扩展,否则符号位扩展
            output [31:0] extendImmediate
        );
    
        always@(extendImmediate)
        begin
            $display("%d", extendImmediate[31]);
        end
    
        assign extendImmediate[15:0] = immediate;
        assign extendImmediate[31:16] = ExtSel ? (immediate[15] ? 16'hffff : 16'h0000) : 16'h0000;
    endmodule
    

    顶层模块:SingleCycleCPU

    • 实现思路:在顶层模块中将各个已实现的底层模块进行实列,并且用verilog语言将各个模块用线连接起来
    • 代码
    `timescale 1ns / 1ps
    
    module SingleCycleCPU(
            input CLK,
            input Reset,
            output [31:0] curPC,
            output [31:0] nextPC,
            output [31:0] instruction,
            output [5:0] op,
            output [4:0] rs,
            output [4:0] rt,
            output [4:0] rd,
            output [31:0] DB,
            output [31:0] A,
            output [31:0] B,
            output [31:0] result,
            output [1:0] PCSrc,
            output zero,
            output PCWre,       //PC是否更改的信号量,为0时候不更改,否则可以更改
            output ExtSel,      //立即数扩展的信号量,为0时候为0扩展,否则为符号扩展
            output InsMemRW,    //指令寄存器的状态操作符,为0的时候写指令寄存器,否则为读指令寄存器
            output RegDst,      //写寄存器组寄存器的地址,为0的时候地址来自rt,为1的时候地址来自rd
            output RegWre,      //寄存器组写使能,为1的时候可写
            output ALUSrcA,     //控制ALU数据A的选择端的输入,为0的时候,来自寄存器堆data1输出,为1的时候来自移位数sa
            output ALUSrcB,     //控制ALU数据B的选择端的输入,为0的时候,来自寄存器堆data2输出,为1时候来自扩展过的立即数
            output [2:0]ALUOp,  //ALU 8种运算功能选择(000-111)
            output mRD,         //数据存储器读控制信号,为0读
            output mWR,         //数据存储器写控制信号,为0写
            output DBDataSrc    //数据保存的选择端,为0来自ALU运算结果的输出,为1来自数据寄存器(Data MEM)的输出  
        );
    
        wire [31:0] extend;
        wire [31:0] DataOut;
        wire[4:0] sa;
        wire[15:0] immediate;
        wire[25:0] addr;
    
        pcAdd pcAdd(.Reset(Reset),
                    .CLK(CLK),
                    .PCSrc(PCSrc),
                    .immediate(extend),
                    .addr(addr),
                    .curPC(curPC),
                    .nextPC(nextPC));
    
        PC pc(.CLK(CLK),
              .Reset(Reset),
              .PCWre(PCWre),
              .PCSrc(PCSrc),
              .nextPC(nextPC),
              .curPC(curPC));
    
        InsMEM InsMEM(.IAddr(curPC), 
                    .InsMemRW(InsMemRW), 
                    .IDataOut(instruction));
    
        InstructionCut InstructionCut(.instruction(instruction),
                                      .op(op),
                                      .rs(rs),
                                      .rt(rt),
                                      .rd(rd),
                                      .sa(sa),
                                      .immediate(immediate),
                                      .addr(addr));
    
        ControlUnit ControlUnit(.zero(zero),
                                .op(op),
                                .PCWre(PCWre),
                                .ExtSel(ExtSel),
                                .InsMemRW(InsMemRW),
                                .RegDst(RegDst),
                                .RegWre(RegWre),
                                .ALUSrcA(ALUSrcA),
                                .ALUSrcB(ALUSrcB),
                                .PCSrc(PCSrc),
                                .ALUOp(ALUOp),
                                .mRD(mRD),
                                .mWR(mWR),
                                .DBDataSrc(DBDataSrc));
    
        RegisterFile RegisterFile(.CLK(CLK),
                                  .ReadReg1(rs),
                                  .ReadReg2(rt),
                                  .WriteData(DB),
                                  .WriteReg(RegDst ? rd : rt),
                                  .RegWre(RegWre),
                                  .ReadData1(A),
                                  .ReadData2(B));
    
        ALU alu(.ALUSrcA(ALUSrcA),
                .ALUSrcB(ALUSrcB),
                .ReadData1(A),
                .ReadData2(B),
                .sa(sa),
                .extend(extend),
                .ALUOp(ALUOp),
                .zero(zero),
                .result(result));
    
        DataMEM DataMEM(.mRD(mRD),
                        .mWR(mWR),
                        .CLK(CLK),
                        .DBDataSrc(DBDataSrc),
                        .DAddr(result),
                        .DataIn(B),
                        .DataOut(DataOut),
                        .DB(DB));
    
        SignZeroExtend SignZeroExtend(.immediate(immediate),
                                      .ExtSel(ExtSel),
                                      .extendImmediate(extend));
    
    endmodule
    

    CPU正确性的验证

    仿真程序:

    `timescale 1ns / 1ps
    
    module TestSingleCycleCpu();
        // Inputs
        reg CLK;
        reg Reset;
    
        // Outputs
        wire [1:0] PCSrc;
        wire [5:0] op;
        wire [4:0] rs;
        wire [4:0] rt;
        wire [4:0] rd;
        wire [31:0] DB;
        wire [31:0] result;
        wire [31:0] curPC;
        wire [31:0] nextPC;
        wire [31:0] instruction;
        wire [31:0] A;
        wire [31:0] B;
        wire zero;
        wire PCWre;       //PC是否更改的信号量,为0时候不更改,否则可以更改
        wire ExtSel;      //立即数扩展的信号量,为0时候为0扩展,否则为符号扩展
        wire InsMemRW;    //指令寄存器的状态操作符,为0的时候写指令寄存器,否则为读指令寄存器
        wire RegDst;      //写寄存器组寄存器的地址,为0的时候地址来自rt,为1的时候地址来自rd
        wire RegWre;      //寄存器组写使能,为1的时候可写
        wire ALUSrcA;     //控制ALU数据A的选择端的输入,为0的时候,来自寄存器堆data1输出,为1的时候来自移位数sa
        wire ALUSrcB;     //控制ALU数据B的选择端的输入,为0的时候,来自寄存器堆data2输出,为1时候来自扩展过的立即数
        wire [2:0]ALUOp;  //ALU 8种运算功能选择(000-111)
        wire mRD;         //数据存储器读控制信号,为0读
        wire mWR;         //数据存储器写控制信号,为0写
        wire DBDataSrc;    //数据保存的选择端,为0来自ALU运算结果的输出,为1来自数据寄存器(Data MEM)的输出  
        // Instantiate the Unit Under Test (UUT)
        SingleCycleCPU uut (
            .CLK(CLK), 
            .Reset(Reset), 
            .curPC(curPC),
            .nextPC(nextPC),
            .instruction(instruction),
            .op(op), 
            .rs(rs),
            .rt(rt),
            .rd(rd),
            .DB(DB),
            .A(A),
            .B(B),
            .result(result),
            .PCSrc(PCSrc),
            .zero(zero),
            .PCWre(PCWre),
            .ExtSel(ExtSel),
            .InsMemRW(InsMemRW),
            .RegDst(RegDst),
            .RegWre(RegWre),
            .ALUSrcA(ALUSrcA),
            .ALUSrcB(ALUSrcB),
            .ALUOp(ALUOp),
            .mRD(mRD),
            .mWR(mWR),
            .DBDataSrc(DBDataSrc)
        );
    
        initial begin
            // Initialize Inputs
            CLK = 1;
            Reset = 0;
    
            CLK = !CLK;  // 下降沿,使PC先清零
            Reset = 1;  // 清除保持信号
            forever #5
            begin // 产生时钟信号,周期为50s
                 CLK = !CLK;
            end
        end
    endmodule
    
    程序代码测试
    地址 汇编程序 指令代码
    op(6) rs(5) rt(5) rd(5)/immediate (16)
    0x00000000 addi $1,$0,8 000001 00000 00001 0000 0000 0000 1000
    0x00000004 ori $2,$0,2 010000 00000 00010 0000 0000 0000 0010
    0x00000008 add $3,$2,$1 000000 00010 00001 0001 1000 0000 0000
    0x0000000C sub $5,$3,$2 000010 00011 00010 0010 1000 0000 0000
    0x00000010 and $4,$5,$2 010001 00101 00010 0010 0000 0000 0000
    0x00000014 or $8,$4,$2 010010 00100 00010 0100 0000 0000 0000
    0x00000018 sll $8,$8,1 011000 00000 01000 0100 0000 0100 0000
    0x0000001C bne $8,$1,-2 (≠,转18) 110001 01000 00001 1111 1111 1111 1110
    0x00000020 slti $6,$2,8 011011 00010 00110 0000 0000 0000 1000
    0x00000024 slti $7,$6,0 011011 00110 00111 0000 0000 0000 0000
    0x00000028 addi $7,$7,8 000001 00111 00111 0000 0000 0000 1000
    0x0000002C beq $7,$1,-2 (=,转28) 110000 00111 00001 1111 1111 1111 1110
    0x00000030 sw $2,4($1) 100110 00001 00010 0000 0000 0000 0100
    0x00000034 lw $9,4($1) 100111 00001 01001 0000 0000 0000 0100
    0x00000038 j 0x00000040 111000 00000 00000 0000 0000 0001 0000
    0x0000003C addi $10,$0,10 000001 00000 01010 0000 0000 0000 1010
    0x00000040 Halt 111111 00000 00000 0000 0000 0000 0000
    0x00000044
    0x00000048
    0x0000004C

    使用上面程序段进行测试CPU正确性,将其中的指令写入一个romData.txt文件中。
    在模块InsMEM中进行读入(使用的路径为绝对路径)

    (源码和实验报告)

    总结

    本次实验中遇到的问题比较多。首先是关于CPU的设计,其次就是verilog语言。一开始不知道如何实现,感觉无从下手。主要通过分析实验原理中的图2 单周期CPU数据通路和控制线路图,分析各种指令的处理过程,学会将CPU内各个部分模块化,各个模块分别实现一定的功能,然后通过相对应的控制信号连接起来,这样就实现cpu设计。完成模块的划分以后,按照先前对每个模块功能预设进行完成,但是每个模块的敏感信号的选择还是很重要的,有些模块程序要在时钟信号上升沿触发,而有些模块要在时钟信号的下降沿触发,有些则将电平信号作为敏感信号,每个模块里面的敏感信号的选择都十分的重要,一开始没有太过注意导致出现了很多的问题,后面重新仔细的想指令的处理过程,重新规定了各个模块always@里面的敏感信号。
    其次就是verilog里面的wire和reg两种变量类型,感觉这是比较大的坑。一开始不了解两者的区别,导致后面一堆报错。现在大致的清楚了二者的区别,wire主要起信号间连接的作用,例如顶层模块中,需要将各个模块连接起来,这时候只能用wire连接,不能使用reg,wire不保存状态,它的值的随时可以改变,不受时钟信号的影响,而reg则是寄存器的抽象表达,可以用于存储数值,例如指令寄存器和寄存器组以及数据寄存器里面的存储器必须为reg类型,用于保留数据。其次wire类型只能通过assign进行赋值,而reg类型只能在always里面被赋值,而涉及到always又有阻塞赋值和非阻塞赋值这个大坑,一开始也不知道怎么弄,就混用了,后面也是出现乱七八糟的问题,后面仔细学习了一下,敏感信号为电平信号的时候,采用阻塞赋值(=),而敏感信号为时序信号的时候,采用非阻塞赋值(<=)。
    再者就是烧板的时候的消抖问题。一开始没有进行消抖,然后总是按一下运行了几条指令,后面上网学习了一下如何消抖,顺利的解决了该问题。
    还有比较疑惑的问题就是使用vivado进行Implemention的时候,有时候进行Running place_design这一部分的时候就一直在此处运行,没有任何进度了,网上也没有合理的解释,然后新新建个项目,将里面的代码复制进去又可以正常的运行了,这个问题目前尚未解决。
    本次单周期CPU设计实验,将计组理论课上所讲的指令处理过程自己重复并实现了单周期CPU的设计,加深了CPU处理指令过程理解,之前由于计组理论学的不是特别清楚,本次实验加深了印象,也更加了解每条指令的处理过程以及单周期CPU是如何工作的,同时本次实验也更加了解verilog语言,之前学的懵懵懂懂的,最重要的是学会模块化,将一项工作分成多个模块进行完成,先简化成小部分,然后再将其组合起来。

    展开全文
  • (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
    

    测试结果如下:

    这里写图片描述

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


    总结:

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

    展开全文
  • MIPS-单周期CPU设计

    万次阅读 多人点赞 2016-04-22 22:16:04
    MIPS-单周期CPU设计设计一个单周期CPU,该CPU至少能实现以下指令功能操作。需设计的指令与格式如下: 实验原理 单周期CPU指的是一条指令的执行在一个时钟周期内完成,然后开始下一条指令的执行,即一条指令用...
  • 为不影响老师的教学,本文已经删去所有实现代码,完整含代码版本将在本学期结束时发布在我的个人博客(https://wu-kan.github.io/posts/计算机组成原理/单周期CPU设计) 实验目的 掌握单周期CPU数据通路图的构成、...
  • 单周期CPU设计与实现原理分析

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

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

    千次阅读 多人点赞 2019-06-21 21:47:17
    实验四、单周期CPU设计与实现——单指令CPU 一、实验目的: 通过设计并实现支持一条指令的CPU,理解和掌握CPU设计的基本原理和过程。 二、实验内容: 设计和实现一个支持加法指令的单周期CPU。要求该加法指令(表示...
  • 单周期cpu设计与实现

    万次阅读 2016-05-02 17:44:42
    前两周终于把计组第二个实验完成,单周期cpu设计与实现。 环境用的是ise14.7,也许是不兼容的问题,导致写着写着代码突然间就秒退了,一度弄得人都要炸了。
  • 单周期CPU设计(Verilog)

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

    千次阅读 2018-07-24 17:03:03
    (1) 掌握单周期CPU数据通路图的构成、原理及其设计方法; (2) 掌握单周期CPU的实现方法,代码实现方法; (3) 认识和掌握指令与CPU的关系; (4) 掌握测试单周期CPU的方法; (5) 掌握单周期CPU的实现方法。 ...
  • 实验二 : 单周期CPU设计与实现 一.实验目的 二.实验内容 三.实验原理 1.相关部件及引脚说明(根据个人设计有所修改): 2.FPGA板的端口与功能设计: 四.实验器材 五.仿真代码实现 1.单周期CPU模块代码 ...
  • 单周期CPU设计(一)

    2020-05-23 19:39:57
    单周期CPU设计(一) 设计过程分两步,首先,根据所设计的所有汇编指令的功能及指令格式,完成CPU的数据通路设计;其次,根据指令功能和数据通路设计控制部件。 数据通路的一般设计方法: 1.根据指令的功能,确定每条...
  • MIPS单周期CPU设计(24条指令)

    万次阅读 多人点赞 2020-07-22 21:14:25
    MIPS单周期可执行24条指令CPU 实验要求 本实训项目帮助学生构建支持 24 条指令的 MIPS 单周期 CPU ,最终实现的处理器能运行 benchmark 测试程序。~~另外希望学有余力的同学能为自己的处理器增加中断处理机制,能...
  • 设计和实现一个支持如下十条指令的单周期CPU。 非访存指令  清除累加器指令CLA  累加器取反指令COM  算术右移一位指令SHR:将累加器ACC中的数右移一位,结果放回ACC  循环左移一位指令CSL:对累加器中的数据...
  • 16位单周期CPU设计

    千次阅读 2016-05-17 22:11:14
    Verilog之CPU设计——16位单周期

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 117,104
精华内容 46,841
关键字:

单周期cpu设计