精华内容
下载资源
问答
  • 单周期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语言,之前学的懵懵懂懂的,最重要的是学会模块化,将一项工作分成多个模块进行完成,先简化成小部分,然后再将其组合起来。

    展开全文
  • 多周期CPU设计

    万次阅读 多人点赞 2016-05-24 22:47:01
    和单周期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;

     

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



    至此,完工!!!

    展开全文
  • 单周期MIPS CPU设计&单周期硬布线控制器 数据通路如下,主要利用一个硬布线控制器,指令寄存器,PC计数器、寄存器堆(之前实验中的MIPS寄存器文件),ALU加法器(已经封装好的),数据存储器来实现。这里的连接...
    大家好,我是小黄鸭,又来更新了,这个实验了一天,也过了,文章下面有我的联系方式,有问题请私聊。
    

    这个实验线路比较复杂,尤其是在数据通路部分,再者,对于指令的运行方式和微程序的转换逻辑要求很高

    单周期MIPS CPU设计&单周期硬布线控制器

    1. 数据通路如下,主要利用一个硬布线控制器,指令寄存器,PC计数器、寄存器堆(之前实验中的MIPS寄存器文件),ALU加法器(已经封装好的),数据存储器来实现。这里的连接就按照给出的框图进行连接就可以了,注意细节不要出现小错误数据通路数据通路
      补充一点,这里的停机是靠位于左上方的计数器,计算周期数。在计数器中设置最大值为224,当周期达到224时即可停机。
    2. 单周期硬布线控制器的设计
      1. 首先是指令译码逻辑的设计,该实验只涉及8条核心的MIPS指令。而这8条MIPS指令的指令字段已经在附件中给出(关于MIPS指令字段可参考我另一篇关于单总线定长&变长的博客biubiu传送门),并且电路底部文字也给出了关于SYSCALL的提示,因此,这部分只需根据相应的OPFUNC字段进行简单地逻辑比较就可实现。指令译码逻辑
      2. ALU控制逻辑的设计,由于该MIPS CPU设计中有关的8条核心MIPS指令中,对于ALU运算逻辑单元中只涉及到加法和比较,因此这一部分可以大大简化。只有运行STL指令时,需要选择比较运算,其余都是加法运算。控制器逻辑
      3. 对于控制器输出信号的设计,则要根据硬布线控制器中所包含的9中控制信号进行分析,如下图。主要考虑每种控制信号的产生条件。这里更高的要求是掌握8条核心指令集在执行时全部周期中所设计的控制信号,已经使用相应控制信号的作用
        控制信号
        控制信号1
    3. 当你上面两部分设计完成后,再次打开该电路文件时,你的整个数据通路中就只存在绿线和黑线了。这时候就要进行最后一步测试,在数据通路的指令存储器中加载sort文件。最终,会在相应的周期数停机,并呈现一个升序排列。测试停机

    微程序地址转移逻辑设计

    1. 实验目的是了解微程序控制器中微程序分支的基本原理,这里的话建议去听一下中国大学有关多周期微程序CPU那一节的相关知识,能够帮助你更好的理解。而首先是要求设计微地址转移逻辑,也是最简单的一部分。
    2. 主要根据的下面这个指令状态变换图,这个状态图可谓是整个微程序CPU设计的核心,从开始的地址转移逻辑的设计到中间的微程序控制器设计以及最后的整个CPU数据通路的设计都有一定的帮助状态变化图
    3. 在相应的EXCEL表中进行填写,这个表格相对简单,主要是关于机器指令信号及其相关的微程序入口地址的填写。填写完毕后,利用Logisim的自生成功能,就可以实现电路要求的功能了。excel地址

    有问题可在下方评论或者私信q453682174.

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

    2020-08-27 19:59:42
    流水线CPU设计(不考虑相关冲突) 所谓流起水来,就是理想情况下,流水线每一级都有指令且每个时钟周期都能处理完一条指令。 2.1 添加流水级间缓存 电路流水线化的主要初衷是为了缩短时序器件之间组合逻辑关键路径的...
  • 实验四、单周期 CPU 设计与实现——单指令 CPU

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

    2013-10-21 11:42:03
    参考:1、实验一 简化的RISC CPU设计 2、有限状态机 3、EDA实验_简单微处理器设计 4、简单微处理器的设计
  • 31条指令单周期cpu设计(Verilog)-(二)总体设计

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

    千次阅读 多人点赞 2019-07-03 23:30:07
    通过设计并实现支持10条指令的CPU,进一步理解和掌握CPU设计的基本原理和过程。 二、实验内容: 设计和实现一个支持如下十条指令的单周期CPU。 Ø非访存指令 u清除累加器指令CLA u累加器取反指令COM u算术右移一位...
  • 简单CPU设计

    万次阅读 多人点赞 2018-01-03 13:30:50
    设计CPU的主体思路如下: 要设计一个简单的CPU,我们需要先设计一个简易版的指令系统,然后根据该指令系统搭建对应的数据通路,在数据通路的基础上实现控制逻辑,下一步是加上时钟信号,划分流水线,紧接着解决流水...
  • 实验一 单周期MIPS CPU设计实验二 微程序地址转移逻辑设计实验三 MIPS微程序CPU设计实验四 硬布线控制器状态机设计实验五 多周期MIPS硬布线控制器CPU设计(排序程序) 实验一 单周期MIPS CPU设计 实验二 微程序地址...
  • 为不影响老师的教学,本文已经删去所有实现代码,完整含代码版本将在本学期结束时发布在我的个人博客(https://wu-kan.github.io/posts/计算机组成原理/单周期CPU设计) 实验目的 掌握单周期CPU数据通路图的构成、...
  • Vivado Verilog语言 简易CPU设计

    千次阅读 多人点赞 2019-07-13 09:05:12
    大三下期末的计算机组成课程设计要求完成一个建议的CPU设计,为了这个课设,,,不得不自己下了一个Vivado(8G多,心疼流量),自己学了一波Verilog语言,这语言是真有东西,发誓再也不碰硬件的我学者语言就像是乡下人...
  • 多周期CPU设计与实现

    万次阅读 多人点赞 2018-06-29 12:04:34
    多周期CPU设计与实现 实验内容:学校资料 设计一个多周期CPU,该CPU至少能实现以下指令功能操作。指令与格式如下: ==&gt; 算术运算指令 1. add rd , rs, rt (说明:以助记符表示,是汇编指令;以代码...
  • (Verilog)单周期CPU设计

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

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

    千次阅读 2017-05-05 15:49:09
    网上关于单周期CPU设计这个实验的博客很多,质量也不错,但关于Verilog代码的编写好像也没全部给出,造成“全抄”后依旧无法解决该实验,所以,我决定真正“造福”后人,把代码全部给出,以供后人学习,如有错漏希望...
  • Verilog实现MIPS的5级流水线cpu设计(Modelsim仿真)

    千次阅读 多人点赞 2020-01-16 18:38:30
    Verilog实现IMPS的5级流水线cpu设计 本篇文章是在功能上实现cpu设计,而非结构上实现。结构上实现可以跳转到(此为个人推荐): Verilog流水线CPU设计(超详细) 此外有与本文章配套的资源,文章中不懂的地方可以...
  • CPU设计原理

    2018-12-10 14:51:40
    要说到设计的复杂程度,那还是CPU了!这个不用讨论,很简单的道理你看看显卡芯片的更新速度和CPU的更新速度就可见一斑了。还是简单说说他们的设计原理吧。 但是,现在我要问一句:“什么是CPU?”我相信大多数人并...
  • 华中科技大学计算机组成MIPS CPU设计(必须)1-3关

    千次阅读 多人点赞 2020-06-11 20:42:31
    MIPS CPU设计(必须) 单周期MIPS CPU设计 微程序地址转移逻辑设计 MIPS微程序CPU设计
  • CPU设计初探

    千次阅读 2010-12-17 03:19:00
    CPU设计初探 习惯了“实践-理论-实践”的学习模式,对硬件知之甚少却学完了《计算机组成原理》,我努力尝试让自己找到这门课的感觉。没有FPGA的试验器材,也没有多少真实芯片在身边,于是我拾起《数字电路》,准备在...
  • 单周期CPU设计(一)

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

    万次阅读 多人点赞 2016-04-22 22:16:04
    MIPS-单周期CPU设计设计一个单周期CPU,该CPU至少能实现以下指令功能操作。需设计的指令与格式如下: 实验原理 单周期CPU指的是一条指令的执行在一个时钟周期内完成,然后开始下一条指令的执行,即一条指令用...
  • 目录实验目的:第1关:单周期MIPS CPU设计二.微程序地址转移逻辑设计三.MIPS微程序CPU设计四.硬布线控制器状态机设计五.多周期MIPS硬布线控制器CPU设计(排序程序)测试结果: 实验目的: 1、理解计算机中的重要部件...
  • 为不影响老师的教学,本文已经删去所有实现代码,完整含代码版本将在本学期结束时发布在我的个人博客(https://wu-kan.github.io/posts/计算机组成原理/多周期CPU设计) 实验目的 认识和掌握多周期数据通路图的构成...
  • 浅谈CPU设计及其应用程序运行机制

    千次阅读 2021-04-27 19:36:56
    最近看了不少关于CPU设计的一些开源项目和文章,看了之后感觉作者真的太厉害了,反观自己实在太菜了!不过正经来说,这些项目看多了自然就会对CPU的设计和运行有一个更清晰的理解! 在平时的课程和竞赛中会经常接触...
  • 单周期CPU设计与实现原理分析

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

    千次阅读 2016-05-17 22:11:14
    Verilog之CPU设计——16位单周期
  • 湖南大学数字电路与逻辑设计大实验使用VHDL完成简易CPU设计 湖南大学数字电路与逻辑设计简易CPU设计大实验,使用VHDL实现。 本课程力图以“培养学生现代数字系统设计能力”为目标,贯彻以CPU设计为核心,以层次化、...
  • 基于状态机的简易RISC CPU设计

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 26,030
精华内容 10,412
关键字:

cpu设计