精华内容
下载资源
问答
  • 多周期CPU控制单元

    2018-12-29 20:13:08
    多周期CPU设计的时候弄的控制单元里各个控制信号的取值……可能会有点小问题,但是设计时没有出现大错误
  • 单周期CPU,多周期CPU

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

    2020-06-22 19:08:32
    单周期CPU.zip
  • 单周期CPU设计

    万次阅读 多人点赞 2016-05-24 21:43:44
    终于有点时间了,恰好周期的设计也已经完成,其实只想写写周期的,无奈单周期补上才好,哈哈哈~ —————+—————黄金分割线—————+————— 首先要理解什么叫单周期CPU(与后面多周期CPU对比)单...

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

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

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

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


    ==>算术运算指令

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

       000000   

       rs(5位)   

       rt(5位)   

      rd(5位)   

      reserved  

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

    (2)addi rt , rs ,immediate 

       000001   

       rs(5位)   

       rt(5位)   

       immediate(16位)   

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

        (3)sub rd , rs , rt

       000010  

      rs(5位)    

        rt(5位)   

       rd(5位)   

       reserved  

    完成功能:rd←rs - rt

        ==> 逻辑运算指令

    (4)ori rt , rs ,immediate 

       010000  

       rs(5位)  

      rt(5位)  

       immediate(16位)        

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

    (5)and rd , rs , rt

      010001  

      rs(5位)   

       rt(5位)  

       rd(5位)  

        reserved    

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

        (6)or rd , rs , rt

       010010  

      rs(5位)  

      rt(5位)   

      rd(5位)   

       reserved    

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

        ==> 传送指令

        (7)move  rd , rs  

       100000  

       rs(5位)  

      00000   

      rd(5位)   

       reserved   

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

    ==> 存储器读/写指令

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

      100110  

      rs(5位)  

       rt(5位)   

        immediate(16位)      

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

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

       100111  

      rs(5位)  

      rt(5位)  

        immediate(16位)       

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

     ==> 分支指令

        (10)beq rs,rt,immediate     

      110000  

       rs(5位)  

      rt(5位)  

    immediate(位移量,16位)

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

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

    ==>停机指令

    (11)halt

      111111  

     00000000000000000000000000(26位)  

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


     设计原理


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

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

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

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

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

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

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


    MIPS32的指令的三种格式:

    R类型:

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

           op             

              rs            

             rt            

              rd            

            sa            

           func    

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

     

    I类型:

    31        26 25         21 20        16 15                       0

            op              

              rs              

               rt              

                  immediate                 

    6位         5位          5位                16位

     

    J类型:

    31        26 25                                                0

              op           

                             address                                                

    6位                            26位

    其中,

    op:为操作码;

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

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

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

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

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

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

    address:为地址。

     

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

    表1 控制信号的作用

    控制信号名

    状态“0”

    状态“1”

    PCWre

    PC不更改,相关指令:halt

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

    ALUSrcB

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

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

    ALUM2Reg

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

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

    RegWre

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

    sw、halt

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

    InsMemRW

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

    写指令存储器

    DataMemRW

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

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

    ExtSel

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

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

    (sign-extend)immediate(符号扩展

    PCSrc

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

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

    RegOut

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

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

    ALUOp[2..0]

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

    相关部件及引脚说明:

    InstructionMemory指令存储器

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

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

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

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

    DataMemory数据存储器

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

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

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

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

    RegisterFile:(寄存器组)

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

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

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

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

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

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

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

    ALU

            result,ALU运算结果

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

     

    表2 ALU运算功能表       

    ALUOp[2..0]

    功能

    描述

    000

    A + B

    001

    A – B

    010

    B – A

    011

    A ∨ B

    100

    A ∧ B

    101

    /A ∧ B

    A非与B

    110

    A Å B

    异或

    111

    A ⊙ B

    同或

     

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

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

     

    控制信号

    指令

    z

    PCWre

    ALUSrcB

    ALUM2Reg

    RegWre

    InsMemRW

    DataMemRW

    ExtSel

    PCSrc

    RegOut

    ALUOp[2..0]

    add

    x

    1

    0

    0

    1

    0

    x

    x

    0

    1

    000

    addi

    x

    1

    1

    0

    1

    0

    x

    1

    0

    0

    000

    sub

    x

    1

    0

    0

    1

    0

    x

    x

    0

    1

    001

    ori

    x

    1

    1

    0

    1

    0

    x

    0

    0

    0

    011

    and

    x

    1

    0

    0

    1

    0

    x

    x

    0

    1

    100

    or

    x

    1

    0

    0

    1

    0

    x

    x

    0

    1

    011

    move

    x

    1

    0

    0

    1

    0

    x

    x

    0

    1

    000

    sw

    x

    1

    1

    x

    0

    0

    1

    1

    0

    x

    000

    lw

    x

    1

    1

    1

    1

    0

    0

    1

    0

    0

    000

    beq

    0

    1

    0

    x

    0

    0

    x

    1

    0

    x

    001

    1

    1

    0

    x

    0

    0

    x

    1

    1

    x

    001

    halt

    x

    0

    x

    x

    x

    0

    x

    x

    x

    x

    xxx



    分析与设计


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

     

     

     1、控制单元(controlUnit.v)

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

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

    ALUOp[1]=i_ori | i_or

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

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

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

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

    整个模块设计如下:

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

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

    3、 PC单元(PC.v)

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

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


    4、 扩展单元(signZeroExtend.v)

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

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


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

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

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


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

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

    内部实现:

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

    地址

    汇编程序

    指令代码

    op(6)

    rs(5)

    rt(5)

    rd(5)/immediate (16)

    16进制数代码

    0x00000004

    addi  $1,$0,8

    000001

    00000

    00001

    0000 0000 0000 1000

    =

    04010008

    0x00000008

    ori  $2,$0,12

    010000

    00000

    00010

    0000 0000 0000 1100

    =

    4002000C

    0x0000000C

    add  $3,$1,$2

    000000

    00001

    00010

    00011 00000000000

    =

    00221800

    0x00000010

    sub  $4,$2,$1

    000010

    00010

    00001

    00100 00000000000

    =

    08412000

    0x00000014

    and  $5,$1,$2

    010001

    00001

    00010

    00101 00000000000

    =

    44222800

    0x00000018

    or  $6,$1,$2

    010010

    00001

    00010

    00110 00000000000

    =

    48223000

    0x0000001C

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

    110000

    00001

    00010

    0000 0000 0000 0100

    =

    C0220004

    0x00000020

    move  $7,$1

    100000

    00001

    00000

    00111 00000000000

    =

    80203800

    0x00000024

    sw  $1,1($7)

    100110

    00111

    00001

    0000 0000 0000 0001

    =

    98E10001

    0x00000028

    lw  $2,0($1)

    100111

    00001

    00010

    0000 0000 0000 0000

    =

    9C220000

    0x0000002C

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

    110000

    00010

    00111

    1111 1111 1111 1011

    =

    C047FFFB

    0x00000030

    halt

    111111

    00000

    00000

    0000000000000000

    =

    FC000000

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

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


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

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

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


    8、顶层模块(singleStyleCPU)

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

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


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

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

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

            forever #100 clk = ~clk;



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

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


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

     



    展开全文
  • 单周期CPU实验

    2018-11-18 12:26:30
    计算机组成原理课程设计,附报告,单周期CPU设计,运行截图
  • 单周期CPU Vivado

    2018-07-25 12:24:10
    用Vivado实现一个单周期CPU,不包含烧电路板内容,根据2018年的计算机组成原理及接口技术的实验课程要求。
  • 用 Verilog 实现的单周期 MIPS 指令集的 CPU
  • MIPS32位单周期CPU 32位MIPS单周期CPU 可以实现16条指令
  • Logisim单周期CPU

    2018-07-07 10:13:29
    Logisim单周期CPU 已通过仿真测试 可以运行小规模程序
  • 单周期cpu实现

    2015-04-20 14:30:14
    一个系统结构里面 关于 MIPS 指令的单周期CPU
  • Verilog单周期CPU

    2018-07-07 09:54:27
    Verilog 单周期CPU设计 能通过仿真 相关测试文件已经放在压缩包
  • 单周期cpu代码.zip

    2020-06-22 19:13:04
    单周期cpu代码.zip
  • 单周期CPU设计verilog

    2017-05-10 22:42:35
    单周期CPU设计verilog,课程设计
  • Verilog 单周期CPU

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

    2018-05-08 16:21:27
    北航计算机组成课程设计单周期CPU的Verilog代码实现,内包含源代码和相应的测试文件
  • 单周期CPU电路设计

    2018-05-08 16:19:18
    北航计算机组成原理课程设计的单周期CPU电路设计实验,内包含设计电路图以及相应的测试文件
  • ZJU的“ Computer Organization And Design课程中设计了周期和单周期CPU。 -单周期CPU -多周期CPU 实施了-15条MIPS指令 管道CPU是在ZJU的“ Computer Architecture课程中设计的。 -实施转发-实现了分支延迟插槽-31...
  • 中山大学单周期CPU源代码计算机组成原理
  • Verilog单周期CPU设计(超详细)

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

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

    一、设计目的与目标

    实验内容

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

    实验要求

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

    二、课程设计器材

    硬件平台

    软件平台

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

    三、 CPU逻辑设计总体方案

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

    指令模块

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

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

    MIPS指令格式

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

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

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

    指令处理流程

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

    数据通路

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

    总体结构图

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

    设计流程逻辑图

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

    在这里插入图片描述

    四、模块详细设计

    PCAdd4

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

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

    • 实现思路

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

    • 引脚及控制信号

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

    • 主要实现代码
    module PCadd4(PC_o,PCadd4);
    input [31:0] PC_o;//偏移量
    output [31:0] PCadd4;//新指令地址
    CLA_32 cla32(PC_o,4,0, PCadd4, Cout);
    endmodule
    
    module CLA_32(X, Y, Cin, S, Cout);
    input [31:0] X, Y; 
    input Cin;   
    output [31:0] S;
    output Cout;
    wire Cout0, Cout1, Cout2, Cout3, Cout4, Cout5, Cout6;    
    CLA_4 add0 (X[3:0], Y[3:0], Cin, S[3:0], Cout0);
    CLA_4 add1 (X[7:4], Y[7:4], Cout0, S[7:4], Cout1);
    CLA_4 add2 (X[11:8], Y[11:8], Cout1, S[11:8], Cout2);
    CLA_4 add3 (X[15:12], Y[15:12], Cout2, S[15:12], Cout3);
    CLA_4 add4 (X[19:16], Y[19:16], Cout3, S[19:16], Cout4);
    CLA_4 add5 (X[23:20], Y[23:20], Cout4, S[23:20], Cout5);
    CLA_4 add6 (X[27:24], Y[27:24], Cout5, S[27:24], Cout6);
    CLA_4 add7 (X[31:28], Y[31:28], Cout6, S[31:28], Cout);
    Endmodule
    
    module CLA_4(X, Y, Cin, S, Cout);
    input [3:0] X;
    input [3:0] Y;
    input Cin;
    output [3:0] S;
    output Cout;
    and get_0_0_0(tmp_0_0_0, X[0], Y[0]);
    or get_0_0_1(tmp_0_0_1, X[0], Y[0]);
    and get_0_1_0(tmp_0_1_0, X[1], Y[1]);
    or get_0_1_1(tmp_0_1_1, X[1], Y[1]);
    and get_0_2_0(tmp_0_2_0, X[2], Y[2]);
    or get_0_2_1(tmp_0_2_1, X[2], Y[2]);
    and get_0_3_0(tmp_0_3_0, X[3], Y[3]);
    or get_0_3_1(tmp_0_3_1, X[3], Y[3]);
    and get_1_0_0(tmp_1_0_0, ~tmp_0_0_0, tmp_0_0_1);
    xor getS0(S0, tmp_1_0_0, Cin);
    and get_1_1_0(tmp_1_1_0, ~tmp_0_1_0, tmp_0_1_1);
    not get_1_1_1(tmp_1_1_1, tmp_0_0_0);
    nand get_1_1_2(tmp_1_1_2, Cin, tmp_0_0_1);
    nand get_2_0_0(tmp_2_0_0, tmp_1_1_1, tmp_1_1_2);
    xor getS1(S1, tmp_1_1_0, tmp_2_0_0);
    and get_1_2_0(tmp_1_2_0, ~tmp_0_2_0, tmp_0_2_1);
    not get_1_2_1(tmp_1_2_1, tmp_0_1_0);
    nand get_1_2_2(tmp_1_2_2, tmp_0_1_1, tmp_0_0_0);
    nand get_1_2_3(tmp_1_2_3, tmp_0_1_1, tmp_0_0_1, Cin);
    nand get_2_1_0(tmp_2_1_0, tmp_1_2_1, tmp_1_2_2, tmp_1_2_3);
    xor getS2(S2, tmp_1_2_0, tmp_2_1_0);
    and get_1_3_0(tmp_1_3_0, ~tmp_0_3_0, tmp_0_3_1);
    not get_1_3_1(tmp_1_3_1, tmp_0_2_0);
    nand get_1_3_2(tmp_1_3_2, tmp_0_2_1, tmp_0_1_0);
    nand get_1_3_3(tmp_1_3_3, tmp_0_2_1, tmp_0_1_1, tmp_0_0_0);
    nand get_1_3_4(tmp_1_3_4, tmp_0_2_1, tmp_0_1_1, tmp_0_0_1, Cin); 
    nand get_2_2_0(tmp_2_2_0, tmp_1_3_1, tmp_1_3_2, tmp_1_3_3, tmp_1_3_4);
    xor getS3(S3, tmp_1_3_0, tmp_2_2_0);
    not get_1_4_0(tmp_1_4_0, tmp_0_3_0);
    nand get_1_4_1(tmp_1_4_1, tmp_0_3_1, tmp_0_2_0);
    nand get_1_4_2(tmp_1_4_2, tmp_0_3_1, tmp_0_2_1, tmp_0_1_0);
    nand get_1_4_3(tmp_1_4_3, tmp_0_3_1, tmp_0_2_1, tmp_0_1_1, tmp_0_0_0);
    nand get_1_4_4(tmp_1_4_4, tmp_0_3_1, tmp_0_2_1, tmp_0_1_1, tmp_0_0_1, Cin);
    nand getCout(Cout, tmp_1_4_0, tmp_1_4_1, tmp_1_4_2, tmp_1_4_3,tmp_1_4_4);
    assign S = {S3,S2,S1,S0};
    endmodule
    
    

    PC

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

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

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

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

    • 主要实现代码

    module PC(Clk,Reset,Result,Address);  
    input Clk;//时钟
    input Reset;//是否重置地址。0-初始化PC,否则接受新地址       
    input[31:0] Result;
    output reg[31:0] Address;
    //reg[31:0] Address;
    initial begin
    Address  <= 0;
    end
    always @(posedge Clk or negedge Reset)  
    begin  
    if (!Reset) //如果为0则初始化PC,否则接受新地址
    begin  
    Address <= 0;  
    end  
    else   
    begin
    Address =  Result;  
    end  
    end  
    endmodule
    

    INSTMEM

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

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

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

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

    • 主要实现代码

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

    DATAMEM

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

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

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

    • 引脚及控制信号

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

    • 主要实现代码

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

    SHIFTER32_L2

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

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

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

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

    • 主要实现代码

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

    SHIFTER_COMBINATION

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

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

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

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

    • 主要实现代码

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

    MUX4X32

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

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

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

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

    在这里插入代码片
    
    • 主要实现代码
    module MUX4X32 (A0, A1, A2, A3, S, Y);
    input [31:0] A0, A1, A2, A3;
    input [1:0] S;
    output [31:0] Y;
    function [31:0] select;
    input [31:0] A0, A1, A2, A3;
    input [1:0] S;
    case(S)
    2'b00: select = A0;
    2'b01: select = A1;
    2'b10: select = A2;
    2'b11: select = A3;
    endcase
    endfunction
    assign Y = select (A0, A1, A2, A3, S);
    endmodule
    
    

    MUX2X5

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

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

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

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

    • 主要实现代码

    module MUX2X5(A0,A1,S,Y);
    input [4:0] A0,A1;
    input S;
    output [4:0] Y;
    function [4:0] select;
    input [4:0] A0,A1;
    input S;
    case(S)
    0:select=A0;
    1:select=A1;
    endcase
    endfunction
    assign Y=select(A0,A1,S);
    endmodule
    
    

    EXT16T32

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

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

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

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

    • 主要实现代码

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

    MUX2X32

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

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

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

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

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

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

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

    • 主要实现代码

    module MUX2X32(A0,A1,S,Y);
    input [31:0] A0,A1;
    input S;
    output [31:0] Y;
    function [31:0] select;
    input [31:0] A0,A1;
    input S;
    case(S)
    0:select=A0;
    1:select=A1;
    endcase
    endfunction
    assign Y=select(A0,A1,S);
    endmodule
    

    CONUNIT

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

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

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

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

    • 控制信号

    输入端口输入端口输入端口输入端口输出端口输出端口输出端口输出端口输出端口输出端口输出端口输出端口
    Op[5:0]Func[5:0]备注ZRegrtSeWregAluqbAluc[1:0]WmemPcsrc[1:0]Reg2reg
    000000100000addX00110001
    000000100010subX00111001
    000000100100andX001110001
    000000100101orX001111001
    001000-addiX11100001
    001100-andiX101010001
    001101-oriX101011001
    100011-lwX11100000
    101011-swX11000101
    000100-beq011011001
    000100-beq1110110101
    000101-bne0110110101
    000101-bne111011001
    000010-jX100100111
    • 主要实现代码
    module CONUNIT(Op,Func,Z,Regrt,Se,Wreg,Aluqb,Aluc,Wmem,Pcsrc,Reg2reg);
    input[5:0]Op,Func;
    input Z;
    output Regrt,Se,Wreg,Aluqb,Wmem,Reg2reg;
    output[1:0]Pcsrc,Aluc;
    wire R_type=~|Op;
    wire I_add=R_type&Func[5]&~Func[4]&~Func[3]&~Func[2]&~Func[1]&~Func[0];
    wire I_sub=R_type&Func[5]&~Func[4]&~Func[3]&~Func[2]&Func[1]&~Func[0];
    wire I_and=R_type&Func[5]&~Func[4]&~Func[3]&Func[2]&~Func[1]&~Func[0];
    wire I_or=R_type&Func[5]&~Func[4]&~Func[3]&Func[2]&~Func[1]&Func[0];
    wire I_addi=~Op[5]&~Op[4]&Op[3]&~Op[2]&~Op[1]&~Op[0];
    wire I_andi=~Op[5]&~Op[4]&Op[3]&Op[2]&~Op[1]&~Op[0];
    wire I_ori=~Op[5]&~Op[4]&Op[3]&Op[2]&~Op[1]&Op[0];
    wire I_lw=Op[5]&~Op[4]&~Op[3]&~Op[2]&Op[1]&Op[0];
    wire I_sw=Op[5]&~Op[4]&Op[3]&~Op[2]&Op[1]&Op[0];
    wire I_beq=~Op[5]&~Op[4]&~Op[3]&Op[2]&~Op[1]&~Op[0];
    wire I_bne=~Op[5]&~Op[4]&~Op[3]&Op[2]&~Op[1]&Op[0];
    wire I_J=~Op[5]&~Op[4]&~Op[3]&~Op[2]&Op[1]&~Op[0];
    assign Regrt=I_addi|I_andi|I_ori|I_lw|I_sw|I_beq|I_bne|I_J;
    assign Se=I_addi|I_lw|I_sw|I_beq|I_bne;
    assign Wreg=I_add|I_sub|I_and|I_or|I_addi|I_andi|I_ori|I_lw;
    assign Aluqb=I_add|I_sub|I_and|I_or|I_beq|I_bne|I_J;
    assign Aluc[1]=I_and|I_or|I_andi|I_ori;
    assign Aluc[0]=I_sub|I_or|I_ori|I_beq|I_bne;
    assign Wmem=I_sw;
    assign Pcsrc[1]=I_beq&Z|I_bne&~Z|I_J;
    assign Pcsrc[0]=I_J;
    assign Reg2reg=I_add|I_sub|I_and|I_or|I_addi|I_andi|I_ori|I_sw|I_beq|I_bne|I_J;
    endmodule    
    

    REGFILE

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

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

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

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

    • 主要实现代码

    module REGFILE(Ra,Rb,D,Wr,We,Clk,Clrn,Qa,Qb);
    input [4:0]Ra,Rb,Wr;
    input [31:0]D;
    input We,Clk,Clrn;
    output [31:0]Qa,Qb;
    wire [31:0]Y_mux,Q31_reg32,Q30_reg32,Q29_reg32,Q28_reg32,Q27_reg32,Q26_reg32,Q25_reg32,Q24_reg32,Q23_reg32,Q22_reg32,Q21_reg32,Q20_reg32,Q19_reg32,Q18_reg32,Q17_reg32,Q16_reg32,Q15_reg32,Q14_reg32,Q13_reg32,Q12_reg32,Q11_reg32,Q10_reg32,Q9_reg32,Q8_reg32,Q7_reg32,Q6_reg32,Q5_reg32,Q4_reg32,Q3_reg32,Q2_reg32,Q1_reg32,Q0_reg32;
    DEC5T32E dec(Wr,We,Y_mux);
    REG32  A(D,Y_mux,Clk,Clrn,Q31_reg32,Q30_reg32,Q29_reg32,Q28_reg32,Q27_reg32,Q26_reg32,Q25_reg32,Q24_reg32,Q23_reg32,Q22_reg32,Q21_reg32,Q20_reg32,Q19_reg32,Q18_reg32,Q17_reg32,Q16_reg32,Q15_reg32,Q14_reg32,Q13_reg32,Q12_reg32,Q11_reg32,Q10_reg32,Q9_reg32,Q8_reg32,Q7_reg32,Q6_reg32,Q5_reg32,Q4_reg32,Q3_reg32,Q2_reg32,Q1_reg32,Q0_reg32);
    MUX32X32 select1(Q0_reg32,Q1_reg32,Q2_reg32,Q3_reg32,Q4_reg32,Q5_reg32,Q6_reg32,Q7_reg32,Q8_reg32,Q9_reg32,Q10_reg32,Q11_reg32,Q12_reg32,Q13_reg32,Q14_reg32,Q15_reg32,Q16_reg32,Q17_reg32,Q18_reg32,Q19_reg32,Q20_reg32,Q21_reg32,Q22_reg32,Q23_reg32,Q24_reg32,Q25_reg32,Q26_reg32,Q27_reg32,Q28_reg32,Q29_reg32,Q30_reg32,Q31_reg32,Ra,Qa);
    MUX32X32 select2(Q0_reg32,Q1_reg32,Q2_reg32,Q3_reg32,Q4_reg32,Q5_reg32,Q6_reg32,Q7_reg32,Q8_reg32,Q9_reg32,Q10_reg32,Q11_reg32,Q12_reg32,Q13_reg32,Q14_reg32,Q15_reg32,Q16_reg32,Q17_reg32,Q18_reg32,Q19_reg32,Q20_reg32,Q21_reg32,Q22_reg32,Q23_reg32,Q24_reg32,Q25_reg32,Q26_reg32,Q27_reg32,Q28_reg32,Q29_reg32,Q30_reg32,Q31_reg32,Rb,Qb);
    Endmodule
    
    

    ALU

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

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

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

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

    • 主要实现代码

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

    SingleCycleCPU(整合部件)

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

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

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

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

    • 主要实现代码

    module MAIN(Clk,Reset,Addr,Inst,Qa,Qb,ALU_R,NEXTADDR,D);
    input Clk,Reset;
    output [31:0] Inst,NEXTADDR,ALU_R,Qb,Qa,Addr,D;
    
    wire [31:0]Result,PCadd4,EXTIMM,InstL2,EXTIMML2,D,Y,Dout,mux4x32_2,R;
    wire Z,Regrt,Se,Wreg,Aluqb,Reg2reg,Cout,Wmem;
    wire [1:0]Aluc,Pcsrc;
    wire [4:0]Wr;
    
    PC pc(Clk,Reset,Result,Addr);
    PCadd4 pcadd4(Addr,PCadd4);
    INSTMEM instmem(Addr,Inst);
    
    CONUNIT conunit(Inst[31:26],Inst[5:0],Z,Regrt,Se,Wreg,Aluqb,Aluc,Wmem,Pcsrc,Reg2reg);
    MUX2X5 mux2x5(Inst[15:11],Inst[20:16],Regrt,Wr);
    EXT16T32 ext16t32(Inst[15:0],Se,EXTIMM);
    SHIFTER_COMBINATION shifter1(Inst[26:0],PCadd4,InstL2);
    SHIFTER32_L2 shifter2(EXTIMM,EXTIMML2);
    REGFILE regfile(Inst[25:21],Inst[20:16],D,Wr,Wreg,Clk,Reset,Qa,Qb);
    MUX2X32 mux2x321(EXTIMM,Qb,Aluqb,Y);
    ALU alu(Qa,Y,Aluc,R,Z);
    DATAMEM datamem(R,Qb,Clk,Wmem,Dout);
    MUX2X32 mux2x322(Dout,R,Reg2reg,D);
    CLA_32 cla_32(PCadd4,EXTIMML2,0,mux4x32_2, Cout);
    MUX4X32 mux4x32(PCadd4,0,mux4x32_2,InstL2,Pcsrc,Result);
    assign NEXTADDR=Result;
    assign ALU_R=R;
    endmodule
    
    

    Test(仿真代码)

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

    五、仿真模拟分析

    仿真波形图

    在这里插入图片描述

    指令代码分析

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    六、结论和体会

    体会感悟

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

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

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

    展开全文
  • 多周期CPU设计

    万次阅读 多人点赞 2016-05-24 22:47:01
    和单周期CPU的设计相同,都是为了实现一系列的指令功能,但需要指出的是何为周期(注意与前面写道的单周期的区别,这也是设计的关键之处) 多周期CPU指的是将整个CPU的执行过程分成几个阶段,每个阶段用一个时钟去...

    ------更新一下bug(测试代码有毒)-------

    和单周期CPU的设计相同,都是为了实现一系列的指令功能,但需要指出的是何为多周期(注意与前面写道的单周期的区别,这也是设计的关键之处)

    多周期CPU指的是将整个CPU的执行过程分成几个阶段,每个阶段用一个时钟去完成,然后开始下一条指令的执行,而每种指令执行时所用的时钟数不尽相同,这就是所谓的多周期CPU。

    理解完多周期与单周期的区别后,开始我们的多周期CPU设计之路(可以随时对应单周期的设计,注意联系与区别)。

    需要设计的指令及格式如下:

    ==>算术运算指令

    (1)add rd, rs, rt

    000000

    rs(5位)

    rt(5位)

    rd(5位)

    reserved

    功能:rd<-rs + rt

        (2)sub rd, rs, rt

    000001

    rs(5位)

    rt(5位)

    rd(5位)

    reserved

    完成功能:rd<-rs - rt

    (3)addi  rt, rs, immediate

    000010

    rs(5位)

    rt(5位)

    immediate(16位)

    功能:rt<-rs + (sign-extend)immediate

     

    ==>逻辑运算指令

    (4)or rd, rs, rt

    010000

    rs(5位)

    rt(5位)

    rd(5位)

    reserved

    功能:rd<-rs | rt

    (5)and rd, rs, rt

    010001

    rs(5位)

    rt(5位)

    rd(5位)

    reserved

    功能:rd<-rs & rt

    (6)ori rt, rs, immediate

    010010

    rs(5位)

    rt(5位)

    immediate

    功能:rt<-rs | (zero-extend)immediate

     

    ==>移位指令

    (7)sll rd, rs,sa

    011000

    rs(5位)

    未用

    rd(5位)

    sa

    reserved

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

     

    ==>传送指令

        (8)move  rd, rs

    100000

    rs(5位)

    00000

    rd(5位)

    reserved

    功能:rd<-rs + $0

     

    ==>比较指令

    (9) slt rd, rs, rt

    100111

    rs(5位)

    rt(5位)

    rd(5位)

    reserved

    功能:如果(rs<rt),则rd=1;  否则 rd=0

     

    ==>存储器读写指令

    (10)sw rt, immediate(rs)

    110000

    rs(5位)

    rt(5位)

    immediate(16位)

        功能:memory[rs+ (sign-extend)immediate]<-rt

    (11)lw rt, immediate(rs)

    110001

    rs(5位)

    rt(5位)

    immediate(16位)

    功能:rt <- memory[rs + (sign-extend)immediate]

     

    ==>分支指令

        (12)beq rs,rt, immediate (说明:immediate是从pc+4开始和转移到的指令之间间隔条数)    

    110100

    rs(5位)

    rt(5位)

    immediate(16位)

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

     

    ==>跳转指令

    (13)j addr    

    111000

    addr[27..2]

    功能:pc <{pc[31..28],addr[27..2],0,0},转移

    (14)jr rs    

    111001

    rs(5位)

    未用

    未用

    reserved

    功能:pc<-rs,转移

     

    ==>调用子程序指令

    (15)jal addr    

    111010

    addr[27..2]

    功能:调用子程序,pc <- {pc[31..28],addr[27..2],0,0};$31<-pc+4,返回地址设置;子程序返回,需用指令 jr  $31。

    ==>停机指令

    (16)halt (停机指令)

    111111

    00000000000000000000000000(26位)

    不改变pc的值,pc保持不变。


    设计原理

        (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:为地址。


    状态的转移有的是无条件的,例如从IF状态转移到ID 和 EXE状态就是无条件的;有些是有条件的,例如ID 或 EXE状态之后不止一个状态,到底转向哪个状态由该指令功能,即指令操作码决定。每个状态代表一个时钟周期。


    图3是多周期CPU控制部件的电路结构,三个D触发器用于保存当前状态,是时序逻辑电路,RST用于初始化状态“000“,另外两个部分都是组合逻辑电路,一个用于产生下一个阶段的状态,另一个用于产生每个阶段的控制信号。从图上可看出,下个状态取决于指令操作码和当前状态;而每个阶段的控制信号取决于指令操作码、当前状态和反映运算结果的状态zero标志等。

     

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

    特别提示,图上增加IR指令寄存器,目的是使指令代码保持稳定,还有pc增加写使能控制信号pcWre,也是确保pc适时修改,原因都是和多周期工作的CPU有关。ADR、BDR、ALUout、ALUM2DR四个寄存器不需要写使能信号,其作用是切分数据通路,将大组合逻辑切分为若干个小组合逻辑,大延时变为多个分段小延时。

    表1 控制信号作用

    控制信号名

    状态“0”

    状态“1”

    PCWre

    PC不更改,相关指令:halt

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

    ALUSrcB

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

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

    ALUM2Reg

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

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

    RegWre

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

    beq、j、sw、jr、halt

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

    WrRegData

    写入寄存器组寄存器的数据来自pc+4(pc4),相关指令:jal,写$31

    写入寄存器组寄存器的数据来自存储器、寄存器组寄存器和ALU运算结果,相关指令:add、addi、sub、or、and、ori、slt、sll、move、lw

    InsMemRW

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

    写指令存储器

    DataMemRW

    读数据存储器(Data MEM),相关指令:lw

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

    IRWre

    IR(指令寄存器)不更改

    IR寄存器写使能。向指令存储器发出读指令代码后,这个信号也接着发出,在时钟上升沿,IR接收从指令存储器送来的指令代码。与每条指令都相关。

     

    ALUOp[2..0]

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

    PCSrc[1..0]

    00:pc<-pc+4,相关指令:add、addi、sub、or、ori、and、move、

        slt、sll、sw、lw、beq(zero=0)

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

    10:pc<-rs,相关指令:jr

    11:pc<-pc(31..28],addr,0,0  ,相关指令:j、jal

    RegOut[1..0]

    写寄存器组寄存器的地址,来自:

    00:0x1F($31),相关指令:jal,用于保存返回地址($31<-pc+4)

    01:rt字段,相关指令:addi、ori、lw

    10:rd字段,相关指令:add、sub、or、and、move、slt、sll

    11:未用

    ExtSel[1..0]

    00:(zero-extend)sa,相关指令:sll

    01:(zero-extend)immediate,相关指令:ori

    10:(sign-extend)immediate,相关指令:addi、lw、sw、beq

    11:未用

    相关部件及引脚说明:

    InstructionMemory指令存储器

            Iaddr,指令地址输入端口

            DataIn,存储器数据输入端口

            DataOut,存储器数据输出端口

            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时,在时钟上升沿写入

    IR:    指令寄存器,用于存放正在执行的指令代码

    ALU

            result,ALU运算结果

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

     

    表2 ALU运算功能表       

    ALUOp[2..0]

    功能

    描述

    000

    Y = A + B

    001

    Y = A – B

    010

    if (A<B)

    Y = 1; else Y = 0;

    比较A与B

    011

    Y = A>>B

    A右移B位

    100

    Y = A<<B

    A左移B位

    101

    Y = A ∨ B

    110

    Y = A ∧ B

    111

    Y = A ⊕ B

    异或

     


    分析与设计

    此次实验是在上次单周期CPU基础上的改进,基本框架是相同的,但是相比单周期CPU的设计最大的不同就是“多周期”,何为多周期,在实现上与单周期又有何区别?简单的来说就是每个时钟周期内只执行一个阶段,而不是像单周期那样一个时钟周期就执行完整个指令,这就是单周期与多周期的主要区别。当然,由此也衍生出了其他的几个区别,比如,数据传输的延迟问题,增加的跳转指令等使得数据通路图变得复杂了很多。

    根据这些区别,就可以开始在单周期CPU基础进行改进了。具体如下:

    首先,确定每个指令的状态转化关系,具体转化图见上面原理分析,例如指令add的指令状态转化是IF(000)->ID(001)->EXE(110)->WB(111)->IF;所以,需要设置两个3位的状态变量(stage)和(next_stage)来表示状态的转变。由于指令是用来控制指令执行的,所以需要把指令状态的转变实现发在控制单元(controlUnit)中。

    其次,就是数据传输延迟的问题,从数据通路图中可以看出,寄存器(RegisterFile)输出处存在两个延迟(ADR)和(BDR),计算单元(ALU)的输出处存在一个延迟,数据存储器(DataMemory)输出存在一个延迟,指令寄存器(InsMemory)输出处存在一个延迟,当然这里延迟需要控制信号IRWre的额外控制。综上来看,前四个延迟可以设计一个叫DataLate的简单模板模块(因为它们的输入、输出完全相同),具体实现如下。最后一个延迟可以放在INSMemory模块中。

    module DataLate(input [31:0] i_data,

                    input clk,

                    output reg [31:0] o_data);

     

       always @(negedge clk) begin

           o_data = i_data;

        end

    endmodule

    最后就是融入增加的跳转指令,比如根据数据通路图增添了一个如下的地址模块


    另外,其他增加的数据线的增加就具体加入到相应模块中作为输入、输出。

    所以,现在在单周期CPU的基础上,可以画出整个多周期CPU的逻辑图。


    一、         控制单元(controlUnit.v)

    相比单周期的CU,多周期的CU在输入输出上大致相同,但具体控制内容、存在比较大的差别。

    1、指令状态转化的实现,前面已经提到。

    2、控制信号的赋值。由于多周期指令信号控制状态的不同而可能改变,所以这里实现各控制信号的时候不再像单周期那样单纯利用操作码来实现。

    类似的,写出控制信号与指令、指令状态的关系表,如下:

    Stage

    Ins

    Zero

    PCWre

    ALUSrcB

    ALUM2Reg

    RegWre

    WrRegData

    InsMemRW

    DataMemRW

    IRWre

    ExtSel[1..0]

    PCSrc

    [1..0]

    RegOut

    [1..0]

    ALUOp[2..0]

    sif

    (000)

    x

    x

    1

    x

    x

    0

    x

    1

    0

    1

    xx

    xx

    xx

    xxx

    sid

    (001)

    j

    x

    0

    x

    x

    0

    x

    x

    0

    0

    xx

    11

    xx

    xxx

    jal

    x

    0

    x

    x

    1

    0

    x

    0

    0

    xx

    11

    00

    xxx

    jr

    x

    0

    x

    x

    0

    x

    x

    0

    0

    xx

    10

    xx

    xxx

    halt

    x

    0

    x

    x

    0

    x

    x

    0

    0

    xx

    xx

    xx

    xxx

    exe1

    (110)

    add

    x

    0

    0

    x

    0

    x

    x

    0

    0

    xx

    xx

    xx

    000

    sub

    x

    0

    0

    x

    0

    x

    x

    0

    0

    xx

    xx

    xx

    001

    addi

    x

    0

    1

    x

    0

    x

    x

    0

    0

    10

    xx

    xx

    000

    or

    x

    0

    0

    x

    0

    x

    x

    0

    0

    xx

    xx

    xx

    101

    and

    x

    0

    0

    x

    0

    x

    x

    0

    0

    xx

    xx

    xx

    110

    ori

    x

    0

    1

    x

    0

    x

    x

    0

    0

    01

    xx

    xx

    101

    move

    x

    0

    0

    x

    0

    x

    x

    0

    0

    xx

    xx

    xx

    000

    slt

    x

    0

    0

    x

    0

    x

    x

    0

    0

    xx

    xx

    xx

    010

    sll

    x

    0

    1

    x

    0

    x

    x

    0

    0

    00

    xx

    xx

    100

    exe2

    (101)

    beq

    0

    0

    0

    x

    0

    x

    x

    0

    0

    10

    00

    xx

    001

    beq

    1

    0

    0

    x

    0

    x

    x

    0

    0

    10

    01

    xx

    001

    exe3

    (010)

    sw

    x

    0

    1

    x

    0

    x

    x

    0

    0

    10

    xx

    xx

    000

    lw

    x

    0

    1

    x

    0

    x

    x

    0

    0

    10

    xx

    xx

    000

    smem

    (011)

    sw

    x

    0

    x

    x

    0

    x

    x

    1

    0

    10

    00

    xx

    xxx

    lw

    x

    0

    x

    x

    0

    x

    x

    0

    0

    10

    xx

    xx

    xxx

    wb1

    (111)

    add

    x

    0

    x

    0

    1

    1

    x

    0

    0

    xx

    00

    10

    xxx

    sub

    x

    0

    x

    0

    1

    1

    x

    0

    0

    xx

    00

    10

    xxx

    addi

    x

    0

    x

    0

    1

    1

    x

    0

    0

    xx

    00

    01

    xxx

    or

    x

    0

    x

    0

    1

    1

    x

    0

    0

    xx

    00

    10

    xxx

    and

    x

    0

    x

    0

    1

    1

    x

    0

    0

    xx

    00

    10

    xxx

    ori

    x

    0

    x

    0

    1

    1

    x

    0

    0

    xx

    00

    01

    xxx

    move

    x

    0

    x

    0

    1

    1

    x

    0

    0

    xx

    00

    10

    xxx

    slt

    x

    0

    x

    0

    1

    1

    x

    0

    0

    xx

    00

    10

    xxx

    sll

    x

    0

    x

    0

    1

    1

    x

    0

    0

    xx

    00

    10

    xxx

    wb2

    (100)

    lw

    x

    0

    x

    1

    1

    1

    x

    0

    0

    xx

    00

    01

    xxx

     

    根据以上关系表,写出对应控制信号的实现。

    `timescale 1ns / 1ps
    
    
    module controlUnit(input [5:0] opcode, 
                       input zero, clk, Reset,
    		   output reg PCWre, InsMemRW, IRWre, WrRegData, RegWre, ALUSrcB, ALUM2Reg, DataMemRW,
    		   output reg [1:0] ExtSel, RegOut, PCSrc,
    						 output reg [2:0] ALUOp, state_out);
        parameter [2:0] sif = 3'b000,   // IF state
    	                 sid = 3'b001,   // ID state
    						  exe1 = 3'b110,  // add、sub、addl、or、and、ori、move、slt、sll
    						  exe2 = 3'b101,  // beq
    						  exe3 = 3'b010,  // sw、lw
    						  smem = 3'b011,  // MEM state
    						  wb1 = 3'b111,   // add、sub、addl、or、and、ori、move、slt、sll
    						  wb2 = 3'b100;   // lw
    						  
    	 parameter [5:0] addi = 6'b000010,
                        ori = 6'b010010,
                        sll = 6'b011000,
                        add = 6'b000000,
                        sub = 6'b000001,
                        move = 6'b100000,
                        slt = 6'b100111,
                        sw = 6'b110000,
                        lw = 6'b110001,
                        beq = 6'b110100,
                        j = 6'b111000,
                        jr = 6'b111001,
                        Or = 6'b010000,
                        And = 6'b010001,
                        jal = 6'b111010,
                        halt = 6'b111111;
    								 
    	 reg [2:0] state, next_state;
    	
    	initial begin
    	   PCWre = 0;
    		InsMemRW = 0;
    		IRWre = 0;
    		WrRegData = 0;
    		RegWre = 0;
    		ALUSrcB = 0;
    		ALUM2Reg = 0;
    		DataMemRW = 0;
    		ExtSel = 2'b11;
    		RegOut = 2'b11;
    		PCSrc = 2'b00;
    		ALUOp = 0;
    		state = sif;
    		state_out = state;
    	end
    	
    	always @(posedge clk) begin
    	     if (Reset == 0) begin
    		      state <= sif;
    		  end else begin
    		      state <= next_state;
    		  end
    		  state_out = state;
    	 end
    	
    	always @(state or opcode) begin
    	case(state)
    	    sif: next_state = sid;
    		 sid: begin
    		     case (opcode[5:3])
    			      3'b111: next_state = sif; // j, jal, jr, halt等指令
                   3'b110: begin
                       if (opcode == 6'b110100) next_state = exe2; // beq指令
                       else next_state = exe3; // sw, lw指令
                   end
                   default: next_state = exe1; // add, sub, slt, sll等指令
               endcase
    		 end
    		 exe1: next_state = wb1;
           exe2: next_state = sif;
           exe3: next_state = smem;
    		 smem: begin
    		     if (opcode == 6'b110001) next_state = wb2; // lw指令
                    else next_state = sif; // sw指令
           end
           wb1: next_state = sif;
           wb2: next_state = sif;
           default: next_state = sif;
    	endcase
    	end
    		 
    	always @(state) begin
    	
            // 确定PCWre的值
            if (state == sif && opcode != halt) PCWre = 1;
            else PCWre = 0;
    		  
            // 确定InsMemRW的值
            InsMemRW = 1;
    		  
            // 确定IRWre的值
            if (state == sif) IRWre = 1;
            else IRWre = 0;
    		  
            // 确定WrRegData的值
            if (state == wb1 || state == wb2) WrRegData = 1;
            else WrRegData = 0;
    		  
            // 确定RegWre的值
            if (state == wb1 || state == wb2 || opcode == jal) RegWre = 1;
            else RegWre = 0;
            
    		  // 确定ALUSrcB的值
            if (opcode == addi || opcode == ori || opcode == sll || opcode == sw || opcode == lw) ALUSrcB = 1;
            else ALUSrcB = 0;
            
    		  // 确定DataMemRW的值
            if (state == smem && opcode == sw) DataMemRW = 1;
            else DataMemRW = 0;
            
    		  // 确定ALUM2Reg的值
            if (state == wb2) ALUM2Reg = 1;
            else ALUM2Reg = 0;
            
    		  // 确定ExtSel的值
            if (opcode == ori) ExtSel = 2'b01;
            else if (opcode == sll) ExtSel = 2'b00;
            else ExtSel = 2'b10;
            
    		  // 确定RegOut的值
            if (opcode == jal) RegOut = 2'b00;
            else if (opcode == addi || opcode == ori || opcode == lw) RegOut = 2'b01;
            else RegOut = 2'b10;
            
    		  // 确定PCSrc的值
            case(opcode)
                j: PCSrc = 2'b11;
                jal: PCSrc = 2'b11;
                jr: PCSrc = 2'b10;
                beq: begin
                    if (zero) PCSrc = 2'b01;
                    else PCSrc = 2'b00;
                end
                default: PCSrc = 2'b00;
            endcase
            
    		  // 确定ALUOp的值
            case(opcode)
                sub: ALUOp = 3'b001;
                Or: ALUOp = 3'b101;
                And: ALUOp = 3'b110;
                ori: ALUOp = 3'b101;
                slt: ALUOp = 3'b010;
                sll: ALUOp = 3'b100;
                beq: ALUOp = 3'b001;
                default: ALUOp = 3'b000;
            endcase
            
    		  // 防止在IF阶段写数据
            if (state == sif) begin
                RegWre = 0;
                DataMemRW = 0;
            end
        end
    	
    	 
    endmodule

    二、         算术运算单元(ALU)

    模块ALU接收寄存器的数据和控制信号作为输入,将结果输出,具体设计如下:

    `timescale 1ns / 1ps
    module ALU(input [31:0] ReadData1, ReadData2, inExt,
               input ALUSrcB,
    			  input [2:0] ALUOp,
    			  output wire zero,
    			  output reg [31:0] result);
    	 
    	 initial begin
            result = 0;
        end
    
        wire [31:0] B;
    	 assign B = ALUSrcB? inExt : ReadData2;
    	 assign zero = (result? 0 : 1);
    	 
    	 always @(ReadData1 or ReadData2 or B or ALUOp) begin
            case(ALUOp)
                3'b000: result = ReadData1 + B;  // A + B
                3'b001: result = ReadData1 - B;  // A - B
                3'b010: result = (ReadData1 < B ? 1 : 0);  // 比较A与B
                3'b011: result = ReadData1 >> B; // A右移B位
                3'b100: result = ReadData1 << B; // A左移B位
                3'b101: result = ReadData1 | B; // 或
                3'b110: result = ReadData1 & B; // 与
                3'b111: result = (~ReadData1 & B) | (ReadData1 & ~B); // 异或
            default: result = 0;
        endcase
      end
    	 
    endmodule
    


    三、         PC模块(PC)

    相比单周期的PC单元,这里的PC模块中多了一个四选一的的地址数据选择器,目的在于根据控制信号正确匹配pc地址,同样输出当前PC地址,具体设计如下:

    `timescale 1ns / 1ps
    module PC(input clk, Reset, PCWre,
              input [1:0] PCSrc,
              input wire [31:0] imm, addr, RDout1,
              output reg [31:0] Address);
    			 
    	 always @(PCWre or negedge Reset) begin // 这里和单周期不太一样,存在延迟的问题,只有当pcWre改变的时候或者Reset改变的时候再检测
            if (Reset == 0) begin
                Address = 0;
            end else if (PCWre) begin
                if (PCSrc == 2'b00) begin
    				    Address = Address+4;
    				end else if (PCSrc == 2'b01) begin
    				    Address = imm*4+Address+4;
    				end else if (PCSrc == 2'b10) begin
    				    Address = RDout1;
    				end else if (PCSrc == 2'b11) begin
    				    Address = addr;
    				end
            end
        end
    
    endmodule
    

    四、         PCAddr模块(补充address)

    用于跳转指令的地址补充,输出32位的地址,模块实现如下:

    `timescale 1ns / 1ps
    
    module PCAddr(input [25:0] in_addr,
                  input [31:0] PC0,
    				  output reg [31:0] addr);
        wire [27:0] mid;
    	 assign mid = in_addr << 2;
        always @(in_addr) begin
            addr <= {PC0[31:28], mid[27:0]};
        end
    
    endmodule

    五、         扩展单元(Extend)

    相比单周期的扩展,此处的扩展内容多了一些,包括sa扩展、立即数扩展等,扩展选择由控制信号ExtSel控制,最后输出完整32位数据。

    `timescale 1ns / 1ps
    
    module Extend(input [15:0] in_num,
                  input [1:0] ExtSel,
    				  output reg [31:0] out);
    				  
        always @(in_num or ExtSel) begin
            case(ExtSel)
                2'b00: out <= {{27{0}}, in_num[10:6]}; // 扩充 sa
                2'b01: out <= {{16{0}}, in_num[15:0]}; // 扩充立即数, 如 ori指令
                2'b10: out <= {{16{in_num[15]}}, in_num[15:0]}; // 符号扩充立即数,如addi、lw、sw、beq指令
                default: out <= {{16{in_num[15]}}, in_num[15:0]}; // 默认符号扩展
            endcase
        end
    
    endmodule

    六、         数据存储单元(DataMemory)

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

    `timescale 1ns / 1ps
    
    module DataMemory(input [31:0] addr, Data2,
                      input DataMemRW,
    		  output reg [31:0] DataOut);
    					  
    	 reg [7:0] memory [0:63];
    	 integer i;
    	 initial begin
    	     for (i = 0; i < 64; i = i+1) memory[i] <= 0;
    	 end
    	 
        always @(addr or Data2 or DataMemRW) begin
          if (DataMemRW) begin // write data
              memory[addr] = Data2[31:24];
              memory[addr+1] = Data2[23:16];
              memory[addr+2] = Data2[15:8];
              memory[addr+3] = Data2[7:0];
          end else begin // read data
              DataOut[31:24] = memory[addr];
              DataOut[23:16] = memory[addr+1];
              DataOut[15:8] = memory[addr+2];
              DataOut[7:0] = memory[addr+3];
          end
        end
    
    endmodule
    

    七、         指令存储单元(InsMemory)

    将指令集以二进制的文件(my_store.txt)存入当前目录,然后通过读取文件的方式将指令存储到内存中,最后实现指令的读取。其中,

    内部指令实现:

    将需要测试的汇编指令程序转化为指令代码,如下:

    地址

    汇编程序

    指令代码

    op(6)

    rs(5)

    rt(5)

    rd(5)/immediate (16)

    16进制数代码

    0x00000000

    j 0x00000008

    111000

    00 00000000 00000000 00000010

     

    0x00000004

    jr  $31

    111001

    11111

    00000

    0x00000008

    addi  $1,$0,8

    000010

    00000

    00001

    0000 0000 0000 1000

    =

    08010008

    0x0000000C

    ori  $2,$0,2

    010010

    00000

    00010

    0000 0000 0000 0010

    =

    48020002

    0x00000010

    add  $3,$1,$2

    000000

    00001

    00010

    00011 00000000000

    =

    00221800

    0x00000014

    sub  $4,$1,$2

    000001

    00001

    00010

    00100 00000000000

    =

    04222000

    0x00000018

    and  $5,$3,$2

    010001

    00011

    00010

    00101 00000000000

    =

    44622800

    0x0000001C

    or  $6,$1,$2

    010000

    00001

    00010

    00110 00000000000

    =

    40223000

    0x00000020

    move  $11,$1

    100000

    00001

    00000

    01011 00000000000

    =

    80205800

    0x00000024

    slt  $7,$1,$2

    100111

    00001

    00010

    00111 00000000000

    =

    9C223800

    0x00000028

    slt  $8,$2,$1

    100111

    00010

    00001

    01000 00000000000

    =

    9C414000

    0x0000002C

    sll  $2,$2,2

    011000

    00010

    00000

    00010 00010 000000

    =

    60401080

    0x00000030

    beq  $1,$2,-2 转02C

    110100

    00001

    00010

    1111 1111 1111 1110

    =

    D022FFFE

    0x00000034

    sw  $9,0($3)

    110000

    00011

    01001

    0000 0000 0000 0000

    =

    C0690000

    0x00000038

    jal  0x00000004

    111010

    00 00000000 00000000 0000001

    =

     

    0x0000003C

    lw  $10,2($1)

    110001

    00001

    01010

    0000 0000 0000 0010

    =

    C42A0002

    0x00000040

    halt

    111111

    00000

    00000

    0000000000000000

    =

    FC000000

     

    根据上表,可以创建my_store.txt的二进制指令文件,从而进行存取,二进制文件如下:


    最终模块设计如下:

    `timescale 1ns / 1ps
    
    module InsMemory(input [31:0] addr,
                     input InsMemRW, IRWre, clk,
    					  output reg [31:0] ins);
    					  
        reg [31:0] ins_out;
    	 reg [7:0] mem [0:127];
    	 
    	 initial begin
    	     $readmemb("my_store.txt", mem);
    		  //ins_out = 0;
    	 end
    
        always @( addr or InsMemRW) begin
            if (InsMemRW) begin
              ins_out[31:24] = mem[addr];
              ins_out[23:16] = mem[addr+1];
              ins_out[15:8] = mem[addr+2];
              ins_out[7:0] = mem[addr+3];
            end
    	 end
    	 
    	 always @(posedge clk) begin
    	     if (IRWre) ins <= ins_out;
    	 end
    
    endmodule

    八、         寄存器单元(RegFile)

    寄存器文件单元的功能是接收instructionMemory中的rs,rt,rd作为输入,输出对应寄存器的数据,从而达到取寄存器里的数据的目的,具体设计如下:

    需要注意的是,在其内部实现的过程中,为了防止0号寄存器写入数据需要在writeReg的时候多加入一个判断条件,即writeReg不等于0时写入数据。

    `timescale 1ns / 1ps
    
    module RegFile(input [4:0] rs, rt, rd,
                   input clk, RegWre, WrRegData,
    		      	input [1:0] RegOut,
    			  	   input [31:0] PC4, memData,
    			 	   output reg [31:0] data1, data2);
    
        reg [31:0] i_data;
    	 
    	 
    	 reg [4:0] temp;
    	 
    	 reg [31:0] register [0:31];
    	 integer i;
        initial begin
            for (i = 0 ; i < 32; i = i+1) 
    		      register[i] = 0;
        end
        
        always @(negedge clk) begin
    	     case(RegOut)
    	         2'b00: temp = 5'b11111;
    		      2'b01: temp = rt;
    		      2'b10: temp = rd;
    				default temp = 0;
            endcase
    		  assign i_data = WrRegData? memData : PC4;
    	     assign data1 = register[rs];
            assign data2 = register[rt];
            if ((temp != 0) && (RegWre == 1)) begin // temp != 0 确保零号寄存器不会改变
                register[temp] <= i_data;
            end
        end
    
    endmodule
    

    九、         二选一数据模块(DataSelect_2)

    简单的数据二选一,用于数据存储单元后面的数据选择,可见数据通路图,实现如下:

    `timescale 1ns / 1ps
    
    module DataSelect_2(input [31:0] A, B,
                        input Sign,
    						  output wire [31:0] Get);
    
        assign Get = Sign ? B : A;
    
    endmodule
    

    十、         数据延迟模板模块(DataLate)

    用于数据延迟,目的是使得数据正常输入输出,从数据通路图中可知此模板可在四处地方有用,已分析,所以具体模板实现如下:

    `timescale 1ns / 1ps
    
    module DataLate(input [31:0] i_data,
                    input clk,
                    output reg [31:0] o_data);
    
        always @(negedge clk) begin
            o_data = i_data;
        end
    
    endmodule

    十一、顶层模块(Top)

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

    `include "ALU.v"
    `include "DataLate.v"
    `include "DataMemory.v"
    `include "DataSelect_2.v"
    `include "Extend.v"
    `include "InsMemory.v"
    `include "PC.v"
    `include "PCAddr.v"
    `include "RegFile.v"
    `include "ControlUnit.v"
    `timescale 1ns / 1ps
    
    module Top(input clk, reset,
               output wire [2:0] state_out,
               output wire [5:0] opcode,
    			  output wire [4:0] rs, rt, rd,
               // output ins[31:26], ins[25:21], ins[20:16], ins[15:11],
               output wire [31:0] ins, ReadData1, ReadData2, pc0, result);
    			  
    	 assign opcode = ins[31:26];
    	 assign rs = ins[25:21];
    	 assign rt = ins[20:16];
    	 assign rd = ins[15:11];
    
        // 数据通路
        wire [31:0] j_addr, out1, out2, result1, i_IR, extendData, LateOut1, LateOut2, DataOut;
        wire zero;
    	 
        // 控制信号
        wire [2:0] ALUOp;
        wire [1:0] ExtSel, RegOut, PCSrc;
        wire PCWre, IRWre, InsMemRW, WrRegData, RegWre, ALUSrcB, DataMemRW, ALUM2Reg;
    
    	 PC pc(clk, reset, PCWre, PCSrc, extendData, j_addr, ReadData1, pc0);
    
    	 InsMemory insmemory(pc0, InsMemRW, IRWre, clk, ins);
    	
    	 PCAddr pcaddr(ins[25:0], pc0, j_addr);
    	 
    	 RegFile regfile(ins[25:21], ins[20:16], ins[15:11], clk, RegWre, WrRegData, RegOut, (pc0+4), LateOut2, ReadData1, ReadData2);
    	
             DataLate ADR(ReadData1, clk, out1);
    	 DataLate BDR(ReadData2, clk, out2);
    	
    	 Extend extend(ins[15:0], ExtSel, extendData);
    	 
             ALU alu(out1, out2, extendData, ALUSrcB, ALUOp, zero, result);
    	 
    	 DataLate ALUout(result, clk, result1);
    	 
    	 DataMemory datamemory(result1, out2, DataMemRW, DataOut);
    	 
    	 DataSelect_2 dataselect_2(result, DataOut, ALUM2Reg, LateOut1);
    	
    	 DataLate ALUM2DR(LateOut1, clk, LateOut2);
    	 
    	controlUnit control(ins[31:26], zero, clk, reset,PCWre, InsMemRW, IRWre, WrRegData, RegWre, ALUSrcB, ALUM2Reg, DataMemRW, ExtSel, RegOut, PCSrc, ALUOp, state_out);
    	
    
    endmodule
    


    十二、测试程序(test)

    从顶层模块中可以看出整个CPU的输入只有时钟信号clk和重置信号Reset,所以测试程序代码比较简单。(参照单周期CPU)

                 clk = 0;

                  reset= 0;

                  clk= ~clk;

     

                  //Wait 100 ns for global reset to finish

                  #100;

                 reset = 1;

                  forever #100 clk = ~clk;

     

    最后,套路贴结果(部分):



    至此,完工!!!

    展开全文
  • 单周期cpu带仿真,课设资料,保质保量!直接可以用的,单周期cpu verilog设计及仿真!
  • 单周期CPU项目

    2012-12-04 21:34:36
    自己搞的一个16位单周期CPU,希望能对别人有所帮助。
  • 包含华科计组实验 8条指令单周期cpu周期微程序地址转移,在同一个.circ文件中 包含24条指令cpu的.circ和excel的控制信号表,还有周期微程序地址转移excel表。 还有相关的.jar等相关文件 以及包含24条指令cpu的...
  • logisim单周期CPU

    2017-12-17 21:57:42
    使用Logisim软件描述的单周期CPU,支持MIPS指令,可扩展性较好
  • 单周期MIPS CPU设计

    2020-12-09 15:45:32
    单周期MIPS CPU设计,利用运算器实验,存储系统实验中构建的运算器、寄存器文件、存储系统等部件以及Logisim中其它功能部件构建一个32位MIPS CPU单周期处理器。
  • 单周期CPU8指令实现

    2020-12-07 12:46:03
    【压缩包中含有完整代码】 计组实验课的Verilog代码实现单周期8指令CPU Ps:使用前先阅读readme 文件
  • 16位单周期CPU设计

    2016-05-17 21:30:48
    使用Verilog实现16位单周期CPU的设计
  • 单周期CPU,single_cycle_cpu,南京大学计算机系计算机组成原理实验-Single-cycle CPU, single_cycle_cpu, Nanjing University Computer Science Department of Computer Composition Principle Experiment

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 195,351
精华内容 78,140
关键字:

多周期cpu比单周期快