精华内容
下载资源
问答
  • MIPS-单周期CPU设计

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

    MIPS-单周期CPU设计

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

    这里写图片描述


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

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

    这里写图片描述
    这里写图片描述

    实验器材
    电脑一台、Xilinx ISE 软件一套。

    实验分析与设计
    1、 根据数据通路图,将整个cpu设计分为六个模块。
    这里写图片描述
    分别为指令读取模块,控制单元,指令寄存器,扩展模块,运算模块和数据存储器。用一个顶层代码将cpucode.v将所有的模块统一管理,还有必要的wire线声明。
    写完这几行代码后ctrl+s就会自动生成子模块,add source即可,记得命名一致

    //操作码给ControlUnit,zero用来bne和beq指令的判断跳转
    ControlUnit controlunit (operation, zero, PCWre, ALUSrcB, ALUM2Reg, RegWre, InsMemRW, DataMemRW, ExtSel, PCSrc, RegOut,ALUOp);
    
    RegisterFile registerfile (rs, rt, rd, write_data, RegWre, RegOut,clk,readData1,readData2);
    
    Extend extend(immediate_16, ExtSel, immediate_32);
    
    ALU alu(readData1, readData2, immediate_32, ALUSrcB, ALUOp, zero, result);
    
    DataSaver datasaver(result, readData2, DataMemRW, ALUM2Reg, write_data);

    除此之外,我将pc模块直接写在了主模块中,因为pc的操作涉及到初始化,所以分模块对于变量的修改会比较麻烦一些。

    //执行下一条指令
    always@(posedge clk) begin
       if  ( PCWre == 1)
        PC <= (PCSrc == 0)? PC + 4 : PC + 4 + immediate_32 * 4;
        else 
        PC <= PC;
    end
    
    initial begin
        PC = 0;
        clk = 0;
    end
       always #500
            clk = ~clk;

    2、 对于instructionSave模块,只需要申请一块mem空间,将指令读入并存储起来即可。并在PC改变的时候将新的指令赋值给指令线。
    注意:由于mem是直接拿到一条指令的,所以PC加4不对,为了解决这个问题,将PC**右移两位**即可。

    module InstructionSave(
        input [31:0] PC,
        output reg [31:0] instruction
        );
        reg [31:0] mem [0:64];
        initial begin
            $readmemb("my_test_rom.txt", mem);
       end
    
        always@(PC) begin
                instruction <= mem[PC >> 2];
                //$display("the instruction now is %d", instruction);
            end
    endmodule

    3、 对于controlUnit模块,要负责产生信号。对各个不同的操作数要产生不同的信号值。
    首先我们先列出各个信号各个值的作用。
    这里写图片描述
    这里写图片描述
    接着列出各个指令下(操作码)应该有的值。
    这里写图片描述

    最初分析肯定是传入参数直接赋值。后来经过同学指点,发现有更简单的方法:如下

        always@(operation) begin
                //初始化这些变量
                i_add = 0;
                i_addi = 0;
                i_sub = 0;
                i_ori = 0;
                i_and = 0;
                i_or = 0;
                i_move = 0;
                i_sw = 0;
                i_lw = 0;
                i_beq = 0;
                i_halt = 0;
                //如果是对应操作码则将对应变量名置为1
                case(operation)
                    ADD: i_add = 1;
                    ADDI: i_addi = 1;
                    SUB: i_sub = 1;
                    ORI: i_ori = 1;
                    AND: i_and = 1;
                    OR: i_or = 1;
                    MOVE: i_move = 1;
                    SW: i_sw = 1;
                    LW: i_lw = 1;
                    BEQ: i_beq = 1;
                    HALT: i_halt = 1;
                endcase
            end
            //有点类似数字电路中学到的真值表。将各个信号直接用操作码的变量表示。
            assign PCWre = !i_halt;
            assign ALUSrcB = i_addi || i_ori || i_sw || i_lw;
            assign ALUM2Reg = i_lw;
            assign RegWre = !(i_sw || i_beq);
            assign InsMemRW = 0;
            assign DataMemRW = i_sw;
            assign ExtSel = !i_ori;   //除了ori是0扩展,其他的也可以为符号扩展
            assign PCSrc = (i_beq && zero); //beq且相减之后值为0
            assign RegOut = !(i_addi || i_ori || i_lw);
            assign ALUOp = {i_and, i_ori || i_or, i_sub || i_ori || i_or || i_beq};

    即用或操作进行管理,减少了许多重复的代码量。

    4、 对于registerFile模块,根据老师讲课给的寄存器行为代码稍加修改即可。
    实现取数,存数的功能

    module RegisterFile(
       input    [4:0]   rs,rt,rd,
        input   [31:0] write_data,
        input       RegWre,RegOut,clk,  
        output [31:0] readData1,readData2
        );
    //  integer i;
        wire [4:0] rin;
        assign rin = (RegOut == 0) ? rt : rd;
    
        //声明31个寄存器,不需要0号寄存器因为后面会判断
        reg     [31:0]  register [1:31]; // r1 - r31
        integer i;
        initial begin
          for (i = 0; i < 32; i = i + 1)
            register[i] = 0;
        end
        //0号寄存器值固定为0
        assign readData1 = (rs == 0)? 0 : register[rs]; // 取数
        assign readData2 = (rt == 0)? 0 : register[rt]; // 取数
    
        //当时钟上升沿到来,将计算得到的值或者拿到的值存入指定寄存器
        always @(posedge clk) begin
                if ((rin != 0) && (RegWre == 1)) begin // RegWre==1时写入
                register[rin] <= write_data;
            end
        end
    endmodule

    5、 对于extend模块,较为简单,甚至可以一句话说清。三种情况,ExtSel == 0 零扩展, == 1,符号扩展

    assign immediate_32 = (ExtSel)? {{16{immediate_16[15]}}, immediate_16[15:0]} : {{16{1'b0}}, immediate_16[15:0]};
    

    6、 对于alu模块
    要根据输入的ALUop进行计算,初次之外,如果计算结果为0,要将zero变量置1,因为要考虑beq的影响。Beq是相当于减法,如果zero等于1且操作码为beq时会进行跳转。
    这里写图片描述

        wire [31:0] alub;
        //ALUSrcB == 1 要计算立即数
        assign alub = (ALUSrcB == 0) ? readData2 : immediate_32;
    
        always@( readData1 or alub or ALUOp) begin
           $display("here");
            case (ALUOp)
                3'b000: result <= readData1 + alub;
                3'b001: result <= readData1 - alub;
                3'b010: result <= alub - readData1;
                3'b011: result <= readData1 | alub;
                3'b100: result <= readData1 & alub;
                3'b101: result <= ~readData1 & alub;
                3'b110: result <= (~readData1 & alub) | (readData1 & ~alub);
                3'b111: result <= (readData1 & alub) | (~readData1 & ~alub);
            endcase
    
            end
            assign zero = (result == 0) ? 1 : 0;

    7、对于DataSave数据存储模块,有存储数据,获得数据,以及选择是运算输出还是lw输出几个功能。

    always@(result or DataMemRW) begin
            if (DataMemRW == 0) //lw
                DataOut = DataMem[result];
            else   //sw
                DataMem[result] = readData2;
    end
         // == 0 是alu运算输出, == 1是数据存储输出  数据选择器
            assign write_data = (ALUM2Reg == 0) ? result : DataOut;

    至此,模块分析完毕,下面是数据测试。
    编写简单的测试程序段:
    这里写图片描述

    my_rom_test.txt 如下:

    0000000000000000000000000000000
    00000100000000010000000000000100
    00000100000000100000000000001000
    10011000010000100000000000000000
    00000000010000010001100000000000
    00001000011000010001100000000000
    11000000010000111111111111111110
    01000000001000010000000000000001
    01001000010000010001100000000000
    10000000010000000001100000000000
    01000100011000100000100000000000
    10011100010001000000000000000000
    11111100000000000000000000000000

    下面对每条指令进行分析:选中cpucode.v,右键仿真运行,Memory可查看寄存器的值
    这里写图片描述
    这里写图片描述
    这里写图片描述
    这里写图片描述
    这里写图片描述
    这里写图片描述

    这里写图片描述
    这里写图片描述
    这里写图片描述

    实验心得
    问题一、不理解wire reg 这些的具体用法,经常出现语法错误:
    解决:wire是线,在某一模块定义后,可传入其子模块。当变量改变时能够互相传递。wire在该定义模块不能作为表达式的左值。reg 则可以作为左值。在需要修改变量的时候要用reg声明。如将wire传到子模块后,需要修改值可以用output reg …
    问题二、<= 和 = 的区别?
    解决:<= 是非阻塞式赋值, = 是阻塞式赋值。当 = 这个赋值语句执行的时候是不允许有其它语句执行的,这就是阻塞的原因。而非阻塞赋值,例如a<=b;当这个赋值语句执行的时候是不阻碍其它语句执行的。
    问题三、主模块将immediate_32这个变量声明拼写错误后,传入拼写正确,但是不报错,能运行。
    解决:这绝对是我debug最久的一个问题。第一个addi语句一直没法成功执行。Immediate_32在主模块的值是zzzzzzzz高阻态。而子模块extend却能够得出正确的结果。Alu拿不到值,后面的运算没法进行,整个程序都动不了。找了很久才发现变量声明错误。我只想知道为什么verilog不支持报这种语法错误。

    心得:
    不管怎么说花了好长时间写出第一个cpu还是相当兴奋的,虽然简单,但是通过整个过程能够理解到cpu工作的一些基本原理,再和理论课的知识结合起来,印象就十分深刻了。测试过程中一步一步地改善自己的代码,一步一步调试自己的错误。希望自己以后还是要多细心点吧,不要再犯低级的拼写错误了。verilog和之前学的语言不一样,整个并发式执行开始的时候有点难理解,整个思维都要跟着变化,现在一个简单的mips cpu下来,感觉也挺好玩的吧。受益良多。

    完整代码

    cpucode.v

    `timescale 1ns / 1ps
    
    `timescale 1ns / 1ps
    
    module CPU_CPU_sch_tb();
    
    //此模块中需要赋值的变量
        wire [5:0] operation;
        wire [4:0] rs;
        wire [4:0] rt;
        wire [4:0] rd;
        wire [15:0] immediate_16;
        reg clk;
        wire [31:0] result;
        wire [31:0] write_data;
    
    //controlunit产生的控制信号线
        wire PCWre;
        wire ALUSrcB;
        wire ALUM2Reg;
        wire RegWre;
        wire InsMemRW;
        wire DataMemRW;
        wire ExtSel;
        wire PCSrc;
        wire RegOut;
        wire [2:0] ALUOp;
    
    //其他的模块相互传递的线
        wire [31:0] instruction;
        reg [31:0] PC;
    
        wire [31:0] immediate_32;
        wire [31:0] readData1;
        wire [31:0] readData2;
        wire zero;
    
    
    initial begin
        PC = 0;
        clk = 0;
    end
       always #500
            clk = ~clk;
    
    
    //输入PC地址,需要InsMemRW的值来确定是读指令还是写指令,得到的值存在instruction
    InstructionSave instructionsave(PC, instruction);
    
    //分解拿到的指令instruction各个组块
    assign operation[5:0] = instruction[31:26];
    assign rs = instruction[25:21];
    assign rt = instruction[20:16];
    assign rd = instruction[15:11];
    assign immediate_16 = instruction[15:0];
    
    //操作码给ControlUnit,zero用来bne和beq指令的判断跳转
    ControlUnit controlunit (operation, zero, PCWre, ALUSrcB, ALUM2Reg, RegWre, InsMemRW, DataMemRW, ExtSel, PCSrc, RegOut,ALUOp);
    
    RegisterFile registerfile (rs, rt, rd, write_data, RegWre, RegOut,clk,readData1,readData2);
    
    Extend extend(immediate_16, ExtSel, immediate_32);
    
    ALU alu(readData1, readData2, immediate_32, ALUSrcB, ALUOp, zero, result);
    
    DataSaver datasaver(result, readData2, DataMemRW, ALUM2Reg, write_data);
    
    
    
    //执行下一条指令
    always@(posedge clk) begin
       if  ( PCWre == 1)
        PC <= (PCSrc == 0)? PC + 4 : PC + 4 + immediate_32 * 4;
        else 
        PC <= PC;
    end
    
    endmodule
    

    instructionSave.v

    `timescale 1ns / 1ps
    
    module InstructionSave(
        input [31:0] PC,
        output reg [31:0] instruction
        );
        reg [31:0] mem [0:64];
        initial begin
            $readmemb("my_test_rom.txt", mem);
       end
    
        always@(PC) begin
                instruction <= mem[PC >> 2];
                //$display("the instruction now is %d", instruction);
            end
    endmodule
    

    controlUnit.v

    `timescale 1ns / 1ps
    
    module ControlUnit(
        input [5:0] operation,
        input zero,
        output  PCWre,
        output  ALUSrcB,
        output  ALUM2Reg,
        output  RegWre,
        output  InsMemRW,
        output  DataMemRW,
        output  ExtSel,
        output  PCSrc,
        output  RegOut,
        output  [2:0] ALUOp
        );
    
        parameter ADD = 6'b000000, ADDI = 6'b000001, SUB = 6'b000010, ORI = 6'b010000,
                     AND = 6'b010001, OR = 6'b010010, MOVE = 6'b100000, SW = 6'b100110,
                     LW = 6'b100111, BEQ = 6'b110000, HALT = 6'b111111;
    
       reg i_add, i_addi, i_sub, i_ori, i_and, i_or, i_move, i_sw, i_lw, i_beq, i_halt;
    
        always@(operation) begin
                //初始化这些变量
                i_add = 0;
                i_addi = 0;
                i_sub = 0;
                i_ori = 0;
                i_and = 0;
                i_or = 0;
                i_move = 0;
                i_sw = 0;
                i_lw = 0;
                i_beq = 0;
                i_halt = 0;
                //如果是对应操作码则将对应变量名置为1
                case(operation)
                    ADD: i_add = 1;
                    ADDI: i_addi = 1;
                    SUB: i_sub = 1;
                    ORI: i_ori = 1;
                    AND: i_and = 1;
                    OR: i_or = 1;
                    MOVE: i_move = 1;
                    SW: i_sw = 1;
                    LW: i_lw = 1;
                    BEQ: i_beq = 1;
                    HALT: i_halt = 1;
                endcase
            end
            //有点类似数字电路中学到的真值表。将各个信号直接用操作码的变量表示。
            assign PCWre = !i_halt;
            assign ALUSrcB = i_addi || i_ori || i_sw || i_lw;
            assign ALUM2Reg = i_lw;
            assign RegWre = !(i_sw || i_beq);
            assign InsMemRW = 0;
            assign DataMemRW = i_sw;
            assign ExtSel = !i_ori;   //除了ori是0扩展,其他的也可以为符号扩展
            assign PCSrc = (i_beq && zero); //beq且相减之后值为0
            assign RegOut = !(i_addi || i_ori || i_lw);
            assign ALUOp = {i_and, i_ori || i_or, i_sub || i_ori || i_or || i_beq};
    
    endmodule
    

    registerFile.v

    `timescale 1ns / 1ps
    
    module RegisterFile(
       input    [4:0]   rs,rt,rd,
        input   [31:0] write_data,
        input       RegWre,RegOut,clk,  
        output [31:0] readData1,readData2
        );
    //  integer i;
        wire [4:0] rin;
        assign rin = (RegOut == 0) ? rt : rd;
    
        //声明31个寄存器,不需要0号寄存器因为后面会判断
        reg     [31:0]  register [1:31]; // r1 - r31
        integer i;
        initial begin
          for (i = 0; i < 32; i = i + 1)
            register[i] = 0;
        end
        //0号寄存器值固定为0
        assign readData1 = (rs == 0)? 0 : register[rs]; // 取数
        assign readData2 = (rt == 0)? 0 : register[rt]; // 取数
    
        //当时钟上升沿到来,将计算得到的值或者拿到的值存入指定寄存器
        always @(posedge clk) begin
                if ((rin != 0) && (RegWre == 1)) begin // RegWre==1时写入
                register[rin] <= write_data;
            end
        end
    endmodule
    
    

    extend.v

    `timescale 1ns / 1ps
    
    module Extend(
        input [15:0] immediate_16,
        input ExtSel,
        output [31:0]immediate_32 
        );
    
        assign immediate_32 = (ExtSel)? {{16{immediate_16[15]}}, immediate_16[15:0]} : {{16{1'b0}}, immediate_16[15:0]};
    
    endmodule
    
    
    

    alu.v

    module ALU( 
        input [31:0] readData1,
        input [31:0] readData2,
        input [31:0] immediate_32,
        input ALUSrcB,
        input [2:0] ALUOp,
        output wire zero,
        output reg [31:0] result
        );
    
    
        wire [31:0] alub;
        //ALUSrcB == 1 要计算立即数
        assign alub = (ALUSrcB == 0) ? readData2 : immediate_32;
    
        always@( readData1 or alub or ALUOp) begin
           $display("here");
            case (ALUOp)
                3'b000: result <= readData1 + alub;
                3'b001: result <= readData1 - alub;
                3'b010: result <= alub - readData1;
                3'b011: result <= readData1 | alub;
                3'b100: result <= readData1 & alub;
                3'b101: result <= ~readData1 & alub;
                3'b110: result <= (~readData1 & alub) | (readData1 & ~alub);
                3'b111: result <= (readData1 & alub) | (~readData1 & ~alub);
            endcase
    
            end
            assign zero = (result == 0) ? 1 : 0;
    
    
    
    endmodule

    datasaver.v

    module DataSaver(
        input [31:0] result,
        input [31:0] readData2,
        input DataMemRW,
        input ALUM2Reg,
        output [31:0] write_data
        );
    
        reg [31:0] DataMem [0:63];
        reg [31:0] DataOut;
        //初始化
        integer i;
        initial begin
            for (i = 0; i < 64; i = i + 1)
                DataMem[i] = 0;
        end
    
        //通过判断DataMemRW的值来判断要进行读操作还是写操作
    
        always@(result or DataMemRW) begin
            if (DataMemRW == 0) //lw
                DataOut = DataMem[result];
            else   //sw
                DataMem[result] = readData2;
        end
         // == 0 是alu运算输出, == 1是数据存储输出  数据选择器
            assign write_data = (ALUM2Reg == 0) ? result : DataOut;
    endmodule
    展开全文
  • 此为中山大学17级计算机组成与设计实验课题。为不影响老师的教学,...掌握单周期CPU数据通路的构成、原理及其设计方法; 掌握单周期CPU的实现方法,代码实现方法; 认识和掌握指令与CPU的关系; 掌握测试单周期C...

    此为中山大学17级计算机组成与设计实验课题。为不影响老师的教学,本文已经删去所有实现代码,完整含代码版本将在本学期结束时发布在我的个人博客https://wu-kan.cn/_posts/2018-11-23-单周期CPU设计/

    实验目的

    1. 掌握单周期CPU数据通路图的构成、原理及其设计方法;
    2. 掌握单周期CPU的实现方法,代码实现方法;
    3. 认识和掌握指令与CPU的关系;
    4. 掌握测试单周期CPU的方法。

    实验内容

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

    算术运算指令

    add rd rs rt

    000000rs(5位)rt(5位)rd(5位)reserved

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

    sub rd rs rt

    000001rs(5位)rt(5位)rd(5位)reserved

    功能:rd←rs - rt。

    addiu rt rs immediate

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

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

    逻辑运算指令

    andi rt rs immediate

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

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

    and rd rs rt

    010001rs(5位)rt(5位)rd(5位)reserved

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

    ori rt rs immediate

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

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

    or rd rs rt

    010011rs(5位)rt(5位)rd(5位)reserved

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

    移位指令

    sll rd rt sa

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

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

    比较指令

    slti rt rs immediate

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

    功能:if (rs< (sign-extend)immediate) rt =1 else rt=0, 带符号比较,详见ALU运算功能表。

    存储器读/写指令

    sw rt immediate(rs)

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

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

    lw rt immediate(rs)

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

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

    分支指令

    beq rs rt immediate

    110000rs(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位的,也就是以上说的“指令之间指令条数”。

    bne rs rt immediate

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

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

    bltz rs immediate

    110010rs(5位)00000immediate(16位)

    功能:if(rs<$zero) pc←pc + 4 + (sign-extend)immediate <<2 else pc ←pc + 4。

    跳转指令

    j addr

    111000addr[27:2]

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

    停机指令

    halt

    11111100000000000000000000000000(26位)

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

    实验原理

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

    CPU在处理指令的几个步骤

    取指令IF
    指令译码ID
    指令执行EXE
    存储器访问MEM
    结果写回WB

    图1 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类型)用来指定指令的功能与操作码配合使用
    immediate16位立即数,用作无符号的逻辑操作数、有符号的算术操作数、数据加载(Laod)/数据保存(Store)指令的数据地址字节偏移量和分支指令中相对程序计数器(PC)的有符号偏移量;
    address地址

    R类型

    31-2625-2120-1615-1110-65-0
    oprsrtrdsafunc
    6位5位5位5位5位6位

    I类型

    31-2625-2120-1615-0
    oprsrtimmediate
    6位5位5位16位

    J类型

    31-2625-0
    opaddress
    6位26位

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

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

    上图是一个简单的基本上能够在单周期CPU上完成所要求设计的指令功能的数据通路和必要的控制线路图。

    指令执行的结果总是在时钟下降沿保存到寄存器和存储器中,PC的改变是在时钟上升沿进行的,这样稳定性较好。另外,值得注意的问题,设计时,用模块化的思想方法设计,关于ALU设计、存储器设计、寄存器组设计等等,也是必须认真考虑的问题。

    其中指令和数据各存储在不同存储器中,即有指令存储器和数据存储器。访问存储器时,先给出内存地址,然后由读或写信号控制操作。对于寄存器组,先给出寄存器地址,读操作时不需要时钟信号,输出端就直接输出相应数据;而在写操作时,在 WE使能信号为1时,在时钟边沿触发将数据写入寄存器。

    控制信号的作用表

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

    控制信号名状态“0”状态“1”
    Reset初始化PC为0PC接收新地址
    PCWrePC不更改,相关指令:haltPC更改,相关指令:除指令halt外
    ALUSrcA来自寄存器堆data1输出,相关指令:add、sub、addiu、or、and、andi、ori、slti、beq、bne、bltz、sw、lw来自移位数sa,同时,进行(zero-extend)sa,即,相关指令:sll
    ALUSrcB来自寄存器堆data2输出,相关指令:add、sub、or、and、beq、bne、bltz来自sign或zero扩展的立即数,相关指令:addiu、andi、ori、slti、sw、lw
    DBDataSrc来自ALU运算结果的输出,相关指令:add、addiu、sub、ori、or、and、andi、slti、sll来自数据存储器(Data MEM)的输出,相关指令:lw
    RegWre无写寄存器组寄存器,相关指令:beq、bne、bltz、sw、halt寄存器组写使能,相关指令:add、addiu、sub、ori、or、and、andi、slti、sll、lw
    InsMemRW写指令存储器读指令存储器(Ins. Data)
    mRD输出高阻态读数据存储器,相关指令:lw
    mWR无操作写数据存储器,相关指令:sw
    RegDst写寄存器组寄存器的地址,来自rt字段,相关指令:addiu、andi、ori、slti、lw写寄存器组寄存器的地址,来自rd字段,相关指令:add、sub、and、or、sll
    ExtSel(zero-extend)immediate(0扩展),相关指令:andi、ori(sign-extend)immediate(符号扩展),相关指令:addiu、slti、sw、lw、beq、bne、bltz

    ALUOp的功能表

    ALUOp[2…0]功能描述相关指令
    000Y=A+Badd、addiu、sw、lw、j、halt
    001Y=A–Bsub、beq、bne、bltz
    010Y=B<<AB左移A位sll
    011Y=A∨Bori、or
    100Y=A∧Bandi、and
    101Y=A<B不带符号比较A<B
    110Y=A[31]!=B[31]?A[31]>B[31]:A<B带符号比较A<Bslti
    111Y=A^B异或

    PCSrc的功能表

    PCSrc[1…0]功能相关指令
    00pc<-pc+4add、addiu、sub、or、ori、and、andi、slti、sll、sw、lw、beq(zero=0)、bne(zero=1)、bltz(sign=0)
    01pc<-pc+4+(sign-extend)immediate<<2beq(zero=1)、bne(zero=0)、bltz(sign=1)
    10pc<-{(pc+4)[31:28],addr[27:2],2’b00}j
    11未用

    相关部件及引脚说明:

    Instruction Memory

    指令存储器。

    Iaddr指令存储器地址输入端口
    IDataIn指令存储器数据输入端口(指令代码输入端口)
    IDataOut指令存储器数据输出端口(指令代码输出端口)
    RW指令存储器读写控制信号,为0写,为1读

    Data Memory

    数据存储器。

    Daddr数据存储器地址输入端口
    DataIn数据存储器数据输入端口
    DataOut数据存储器数据输出端口
    RD数据存储器读控制信号,为0读
    WR数据存储器写控制信号,为0写

    Register File:

    寄存器组。

    Read Reg1rs寄存器地址输入端口
    Read Reg2rt寄存器地址输入端口
    Write Reg将数据写入的寄存器端口,其地址来源rt或rd字段
    Write Data写入寄存器的数据输入端口
    Read Data1rs寄存器数据输出端口
    Read Data2rt寄存器数据输出端口
    WE写使能信号,为1时,在时钟边沿触发写入

    ALU: 算术逻辑单元

    resultALU运算结果
    zero运算结果标志,结果为0,则zero=1;否则zero=0
    sign运算结果标志,结果最高位为0,则sign=0,正数;否则,sign=1,负数

    实验器材

    电脑一台,Xilinx Vivado 2017.4 软件一套,Basys3实验板一块。

    实验过程与结果

    代码实现

    SingleCPU.v

    单周期CPU的顶层连接文件,主要是调用下层模块并将它们输入输出连在一起,并计算下一个指令的地址(正常+4或跳转)。

    
    

    ControlUnit.v

    控制信号模块,通过解析op得到该指令的各种控制信号。定义了很多用到的常量,可读性还是比较高的。
    控制单元通过输入的zero零标志位与当前指令中对应的指令部分来确定当前整个CPU程序中各模块的工作和协作情况,根据CPU运行逻辑,事先对整个CPU中控制信号的控制,以此来达到指挥各个模块协同工作的目的。

    
    

    ALU.v

    该部分为算术逻辑单元,用于逻辑指令计算和跳转指令比较。ALUOp用于控制算数的类型,A、B为输入数,result为运算结果,zero、sign主要用于beq、bne、bltz等指令的判断。
    ALU算术逻辑单元的功能是根据控制信号从输入的数据中选取对应的操作数,根据操作码进行运算并输出结果与零标志位。

    
    

    DataMemory.v

    该部分控制内存存储,用于内存存储、读写。用255大小的8位寄存器数组模拟内存,采用小端模式。DataMenRW控制内存读写。由于指令为真实地址,所以不需要<<2

    
    

    InstructionMemory.v

    该部分为指令寄存器,通过一个256大小的8位寄存器数组来保存从文件输入的全部指令。然后通过输入的地址,找到相应的指令,输出到IDataOut。
    指令存储器的功能是存储读入的所有32-bit位宽的指令,根据程序计数器PC中的指令地址进行取指令操作并对指令类型进行分析,通过指令类型对所取指令的各字段进行区分识别,最后将对应部分传递给其他模块进行后续处理。
    指令存储器中每个单元的位宽为8-bit,也就是存储每条32-bit位宽的指令都需要占据4个单元,所以第n(n大于或等于0)条指令所对应的起始地址为4n,且占据第4n,4n+1,4n+2,4n+3这四个单元。取出指令就是将这四个单元分别取出,因为指令的存储服从高位指令存储在低位地址的规则,所以4n单元中的字段是该条指令的最高8位,后面以此类推,并通过左移操作将指令的四个单元部分移动到相对应的位置,以此来得到所存指令。

    
    

    Multiplexer32.v

    三十二线双路选择器。

    
    

    Multiplexer5.v

    五线双路选择器。

    
    

    PC.v

    CLK上升沿触发,更改指令地址。由于指令地址存储在寄存器里,一开始需要赋currentAddress为0。Reset是重置信号,当为1时,指令寄存器地址重置。PCWre的作用为保留现场,如果PCWre为0,指令地址不变。
    PC程序计数器用于存放当前指令的地址,当PC的值发生改变的时候,CPU会根据程序计数器PC中新得到的指令地址,从指令存储器中取出对应地址的指令。在单周期CPU的运行周期中,PC值的变化是最先的,而且是根据PCSrc控制信号的值选择指令地址是要进行PC+4或者跳转等操作。若PC程序计数器检测到Reset输入信号为0时,则对程序计数器存储的当前指令地址进行清零处理。

    
    

    RegisterFile.v

    该部分为寄存器读写单元,储存寄存器组,并根据地址对寄存器组进行读写。WE的作用是控制寄存器是否写入。同上,通过一个32大小的32位寄存器数组来模拟寄存器,开始时全部置0。通过访问寄存器的地址,来获取寄存器里面的值,并进行操作。(由于$0恒为0,所以写入寄存器的地址不能为0)
    寄存器组中的每个寄存器位宽32-bit,是存放ALU计算所需要的临时数据的,与数据存储器不同,可能会在程序执行的过程中被多次覆盖,而数据存储器内的数据一般只有sw指令才能进行修改覆盖。寄存器组会根据操作码opCode与rs,rt字段相应的地址读取数据,同时将rs,rt寄存器的地址和其中的数据输出,在CLK的下降沿到来时将数据存放到rd或者rt字段的相应地址的寄存器内。

    
    

    SignZeroExtend.v

    比较简单的一个模块,用于立即数的扩展。ExtSel为控制补位信号。判断后,将extendImmediate的前16位全补1或0即可。

    
    

    仿真检验

    将指令转换成二进制代码,如下表:

    地址汇编程序op(6)rs(5)rt(5)rd(5)/immediate(16)16进制数代码
    0x00000000addiu $1,$0,8000010000000000100000000 0000100008010008
    0x00000004ori $2,$0,2010010000000001000000000 0000001048020002
    0x00000008add $3,$2,$1000000000100000100011000 0000000000411800
    0x0000000Csub $5,$3,$2000001000110001000101000 0000000004622800
    0x00000010and $4,$5,$2010001001010001000100000 0000000044a22000
    0x00000014or $8,$4,$2010011001000001001000000 000000004c824000
    0x00000018sll $8,$8,1011000000000100001000000 0100000060084040
    0x0000001Cbne $8,$1,-2110001010000000111111111 11111110c501fffe
    0x00000020slti $6,$2,4011100000100011000000000 0000010070460004
    0x00000024slti $7,$6,0011100001100011100000000 0000000070c70000
    0x00000028addiu $7,$7,8000010001110011100000000 0000100008e70008
    0x0000002Cbeq $7,$1,-2110000001110000111111111 11111110c0e1fffe
    0x00000030sw $2,4($1)100110000010001000000000 0000010098220004
    0x00000034lw $9,4($1)100111000010100100000000 000001009c290004
    0x00000038addiu $10,$0,-2000010000000101011111111 11111110080afffe
    0x0000003Caddiu $10,$10,1000010010100101000000000 00000001094a0001
    0x00000040bltz $10,-2110010010100000011111111 11111110c940fffe
    0x00000044andi $11,$2,2010000000100101100000000 00000010404b0002
    0x00000048j 0x00000050111000000000000000000000 00010100e0000014
    0x0000004Cor $8,$4,$2010011001000001001000000 000000004c824000
    0x00000050halt111111000000000000000000 00000000fc000000

    input.txt

    00001000 00000001 00000000 00001000
    01001000 00000010 00000000 00000010
    00000000 01000001 00011000 00000000
    00000100 01100010 00101000 00000000
    01000100 10100010 00100000 00000000
    01001100 10000010 01000000 00000000
    01100000 00001000 01000000 01000000
    11000101 00000001 11111111 11111110
    01110000 01000110 00000000 00000100
    01110000 11000111 00000000 00000000
    00001000 11100111 00000000 00001000
    11000000 11100001 11111111 11111110
    10011000 00100010 00000000 00000100
    10011100 00101001 00000000 00000100
    00001000 00001010 11111111 11111110
    00001001 01001010 00000000 00000001
    11001001 01000000 11111111 11111110
    01000000 01001011 00000000 00000010
    11100000 00000000 00000000 00010100
    01001100 10000010 01000000 00000000
    11111100 00000000 00000000 00000000
    

    Sim.v

    仿真模块。

    
    

    仿真波形

    波形比较长,分成三部分逐一分析。
    在这里插入图片描述
    在这里插入图片描述
    前100ps为初始化,各寄存器的值被初始化为0。
    第800ps时执行了bne $8,$1,-2,此时寄存器$8的值是4,寄存器$1的值是8,两者不等,发生了一步跳转,于是第900ps的地址跳转到00000018
    在第900ps前寄存器1~11的值依次为(16进制)8,2,a,0,8,0,0,4,0,0,0
    在这里插入图片描述
    在这里插入图片描述
    第1000ps时再次执行了bne $8,$1,-2,此时寄存器$8的值是8,寄存器$1的值是8,两者相等,pc+4执行下一条指令。
    第1400ps时执行了beq $7,$1,-2,此时寄存器$7的值是8,寄存器$1的值是8,两者相等,发生了一步跳转,于是第1500ps的地址跳转到00000028
    第1600ps时再次执行了beq $7,$1,-2,此时寄存器$7的值是10,寄存器$1的值是8,两者不等,pc+4执行下一条指令。
    在第1800ps前寄存器1~11的值依次为(16进制)8,2,a,0,8,1,10,8,0,0,0
    在这里插入图片描述
    在这里插入图片描述
    第2100ps时执行了bltz $10,-2,此时寄存器$10的值是ffffffff,小于0,发生了一步跳转,于是第2200ps的地址跳转到0000003c
    第2300ps时再次执行了bltz $10,-2,此时寄存器$10的值是0,不小于0,pc+4执行下一条指令。
    第2500ps时执行了j 0x00000050,于是第2600ps的地址跳转到00000050
    第2600ps时执行了halt,程序终止,pc不再跳转。
    在第2700ps前寄存器1~11的值依次为(16进制)8,2,a,0,8,1,10,8,2,0,2

    烧写到Basys3实验板

    Basys3.v

    顶层模块。

    
    

    Debounce.v

    按键消抖模块。Basys3板采用的是机械按键,在按下按键时按键会出现人眼无法观测但是系统会检测到的抖动变化,这可能会使短时间内电平频繁变化,导致程序接收到许多错误的触发信号而出现许多不可知的错误。消抖操作是每当检测到CLK上升沿到来时检测一次当前电平信号并记录,同计数器开始计数,若在计数器达到5000之前电平发生变化,则将计数器清零,若达到5000,则将该记录电平取反输出。
    因为程序开始时已经运行第一条指令,为避免跳过第一条指令计算值的写入,我们的输入需要从下降沿开始,因此我们给按键信号取反后再输入。

    
    

    SegLED.v

    数码管译码模块。译码模块将CPU运算的结果转换成7段数码管中各个数码管显示所需的高低电平信号,该单元的输入为4-bit位宽的二进制数。其中,七段数码管的八个电平控制输出中最低位是小数点的显示信号,但小数点在CPU运行时没有用到,恰好用于标记Reset状态。

    
    

    运行结果

    端口映射

    在这里插入图片描述

    初始化

    在这里插入图片描述
    所有寄存器被初始化为0。

    第1条指令addiu $1,$0,8

    在这里插入图片描述
    当前地址00,下一地址04。
    在这里插入图片描述
    0号寄存器,值为0。
    在这里插入图片描述
    1号寄存器,值为0。
    在这里插入图片描述
    ALU结果为8。

    第2条指令ori $2,$0,2

    在这里插入图片描述
    当前地址04,下一地址08。
    在这里插入图片描述
    0号寄存器,值为0。
    在这里插入图片描述
    2号寄存器,值为0。
    在这里插入图片描述
    ALU结果为2。

    第3条指令add $3,$2,$1

    在这里插入图片描述
    当前地址08,下一地址0c。
    在这里插入图片描述
    2号寄存器,值为2。
    在这里插入图片描述
    1号寄存器,3号寄存器。
    在这里插入图片描述
    输出结果为0a。

    第4条指令sub $5,$3,$2

    在这里插入图片描述
    当前地址0c,下一地址10。
    在这里插入图片描述
    3号寄存器,值为0a。
    在这里插入图片描述
    2号寄存器,值为2。
    在这里插入图片描述
    ALU结果为8。

    第5条指令and $4,$5,$2

    在这里插入图片描述
    当前地址10,下一地址14。
    在这里插入图片描述
    5号寄存器,值为3。
    在这里插入图片描述
    2号寄存器,值为2。
    在这里插入图片描述
    ALU结果为0。

    实验心得

    对于第一次使用verilog语言的我来说,设计单周期CPU是一个不小的挑战。总的来说,从开始构思到真正写板完成大约用了三整天的时间,期间遇到了很多有困难的地方,也翻了很多网上的博客。不得不说,网上能找到的很多博客给出的代码都是有些问题的,确实在一定程度上误导了自己。但是这样一个比较复杂的系统又确实很难什么都不去参考而直接写出来。

    因此,还是希望老师能够多做一些有关verilog语言的讲学吧,遇到的很多问题其实都是关于语法方面的,因为vivado并不会对语法错误进行提示(我用的2017.4版会在可能错误的地方画一道波浪线但是并不会提示错在哪里),而我遇到的问题很多都是语法方面的(例如,在readmemb的时候地址的斜杠和操作系统的是反的,这花费了我很多时间检查才发现)。

    展开全文
  • 单周期CPU设计实验

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

    小火龙每年都会改一下实验内容,以下内容仅17年的实验。
    代码下载
    一. 实验目的
    (1) 掌握单周期CPU数据通路图的构成、原理及其设计方法;
    (2) 掌握单周期CPU的实现方法,代码实现方法;
    (3) 认识和掌握指令与CPU的关系;
    (4) 掌握测试单周期CPU的方法;
    (5) 掌握单周期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)sll rd, rt,sa
    011000 未用 rt(5位) rd(5位) sa reserved
    功能:rd<-rt<<(zero-extend)sa,左移sa位 ,(zero-extend)sa

    ==>比较指令
    (8) slt rd, rs, rt 带符号数
    011100 rs(5位) rt(5位) rd(5位) reserved
    功能:if (rs < rt) rd =1 else rd=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
    功能:if(rs!=rt) pc←pc + 4 + (sign-extend)immediate <<2 else pc ←pc + 4
    特别说明:与beq不同点是,不等时转移,相等时顺序执行。

    (13)bgtz rs,immediate
    110010 rs(5位) 00000 immediate
    功能:if(rs>0) pc←pc + 4 + (sign-extend)immediate <<2 else pc ←pc + 4

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

    ==> 停机指令
    (15)halt
    111111 00000000000000000000000000(26位)
    功能:停机;不改变PC的值,PC保持不变。

    三. 实验原理
    单周期CPU指的是一条指令的执行在一个时钟周期内完成,然后开始下一条指令的执行,即一条指令用一个时钟周期完成。电平从低到高变化的瞬间称为时钟上升沿,两个相邻时钟上升沿之间的时间间隔称为一个时钟周期。时钟周期一般也称振荡周期。
    CPU在处理指令时,一般需要经过以下几个步骤:
    (1) 取指令(IF):根据程序计数器PC中的指令地址,从存储器中取出一条指令,同时,PC根据指令字长度自动递增产生下一条指令所需要的指令地址,但遇到“地址转移”指令时,则控制器把“转移地址”送入PC,当然得到的“地址”需要做些变换才送入PC。
    (2) 指令译码(ID):对取指令操作中得到的指令进行分析并译码,确定这条指令需要完成的操作,从而产生相应的操作控制信号,用于驱动执行状态中的各种操作。
    (3) 指令执行(EXE):根据指令译码得到的操作控制信号,具体地执行指令动作,然后转移到结果写回状态。
    (4) 存储器访问(MEM):所有需要访问存储器的操作都将在这个步骤中执行,该步骤给出存储器的数据地址,把数据写入到存储器中数据地址所指定的存储单元或者从存储器中得到数据地址单元中的数据。
    (5) 结果写回(WB):指令执行的结果或者访问存储器中得到的数据写回相应的目的寄存器中。
    单周期CPU,是在一个时钟周期内完成这五个阶段的处理。
    这里写图片描述
    图1 单周期CPU指令处理过程
    MIPS指令的三种格式:
    这里写图片描述
    其中,
    op:为操作码;
    rs:只读。为第1个源操作数寄存器,寄存器地址(编号)是0000011111,001F;
    rt:可读可写。为第2个源操作数寄存器,或目的操作数寄存器,寄存器地址(同上);
    rd:只写。为目的操作数寄存器,寄存器地址(同上);
    sa:为位移量(shift amt),移位指令用于指定移多少位;
    funct:为功能码,在寄存器类型指令中(R类型)用来指定指令的功能与操作码配合使用;
    immediate:为16位立即数,用作无符号的逻辑操作数、有符号的算术操作数、数据加载(Load)/数据保存(Store)指令的数据地址字节偏移量和分支指令中相对程序计数器(PC)的有符号偏移量;
    address:为地址。
    这里写图片描述
    图2 单周期CPU数据通路和控制线路图

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

    相关部件及引脚说明:
    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
    sign,运算结果标志,结果最高位为0,则sign=0,正数;否则,sign=1,负数

    表2 ALU运算功能表
    这里写图片描述
    表3 控制信号与指令的关系表
    这里写图片描述
    这里写图片描述
    附:z为zero,s为sign

    表4 测试程序代码
    这里写图片描述

    表5 寄存器组值的变化
    这里写图片描述
    这里写图片描述
    表6 Basy3板显示内容

    附:一个格子有两个数据,上面的为按下按键时钟上升沿后的显示,下面的为松开按键时钟下降沿的显示,上升沿和下降沿数据的改变说明寄存器的写入是下降沿触发的,而读数不需要时钟,所以到下降沿后数据会改变。

    四. 实验器材
    电脑一台,Xilinx Vivado 软件一套,Basys3板一块。

    五. 实验过程与结果
    1.实验过程
    单周期CPU的思想就是一个周期之内完成一条指令,在时钟上升沿完成PC的改变,下降沿完成寄存器组和RAM的数据保存。每条指令有它自己的数据通路。根据数据通路图的情况,可以对每一个控制端口列出对应的情况,于是便有上面的表3。
    这里写图片描述
    addi、ori的数据通路图
    这里写图片描述
    add、sub、and、or、slt的数据通路图
    这里写图片描述
    sw、lw数据通路图
    这里写图片描述
    sll的数据通路图
    这里写图片描述
    j的数据通路图
    这里写图片描述
    beq、bne、bgtz的数据通路图
    这里写图片描述
    上图是我对单周期CPU写的模块,对应数据通路图每个部分都有实现。二选择器,三选择器没有并入ALU和PC,也是分别模块化。
    这里写图片描述
    数据通路图的作用,可以在写ControlUnit时体现出来,使用case会写出200多行,既不美观,也不容易找出当中的错误,通过控制信号表可以节省代码量,更容易找出设计当中的错误。
    这里写图片描述
    上图是消抖模块,设置SAMPLE_TIME为20ns,每一纳秒记录一次电平,只有超过20ns不变,才认为这是一次完整的输入。
    这里写图片描述
    这是指令分割模块,方便后续的操作,不用每次都要手动分割。在数据通路上sa是5位数,然而传进去是32位,因此我在数据分割的时候,顺便把sa的位数扩展了,方便sll的操作。

    这里写图片描述
    这是位数扩展模块,若ExtSel为0,则对立即数进行补0扩展,否则对立即数进行符号扩展。
    这里写图片描述
    这是寄存器组模块,这是最重要的模块之一,读取数据不需要时钟,写数据需要时钟下降沿。但老师给的代码有个问题,就是reset后,PC为0时没有时钟下降沿保存$1,再按按键PC就是0x04了,因此reset后需要自己补充写寄存器,不然$1没保存,后面全错。
    这里写图片描述
    这个是ROM代码的一部分,老师给的代码是0的时候读,但表1是1的时候读,这部分要把读信号初始化为1。在这个实验中,PC,ROM是两个初始化的部分,没有初始化,PC不会改变,而且ROM也没有读取读取数据,造成后面数据出现X和Z的现象。

    展开全文
  • 单周期CPU设计

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

    欢迎访问我的个人博客:[Talk is cheap. Show me the code!]。我相信会有所收获的。


    学习前人以造福后人...

    网上关于单周期CPU设计这个实验的博客很多,质量也不错,但关于Verilog代码的编写好像也没全部给出,造成“全抄”后依旧无法解决该实验,所以,我决定真正“造福”后人,把代码全部给出,以供后人学习,如有错漏希望大家能谅解。

    一.     实验目的 

    (1) 掌握单周期CPU数据通路图的构成、原理及其设计方法;

    (2) 掌握单周期CPU的实现方法,代码实现方法;

    (3) 认识和掌握指令与CPU的关系;

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

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

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

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

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

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

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

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

    图1  单周期CPU指令处理过程

    MIPS32的指令的三种格式:

    R类型:

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

    op

    rs

    rt

    rd

    sa

    func

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

     

    I类型:

    31        26 25         21 20        16 15                       0

    op

    rs

    rt

    immediate

    6位         5位          5位                16位

     

    J类型:

    31        26 25                                                0

    op

    address

    6位                            26位

    其中,

    op:为操作码;

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

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

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

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

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

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

    address:为地址。

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

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

    表1 控制信号的作用

    控制信号名

    状态“0”

    状态“1”

    PCWre

    PC不更改,相关指令:halt

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

    ALUSrcB

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

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

    ALUM2Reg

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

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

    RegWre

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

    sw、halt

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

    InsMemRW

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

    写指令存储器

    DataMemRW

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

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

    ExtSel

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

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

    (sign-extend)immediate(符号扩展

    PCSrc

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

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

    RegOut

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

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

    ALUOp[2..0]

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

    相关部件及引脚说明:

    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可以看出各控制信号与相应指令之间的相互关系,根据这种关系就可以得出控制信号与指令之间的关系表(留给学生完成),再根据关系表可以写出各控制信号的逻辑表达式,这样控制单元部分就可实现了。

    指令执行的结果总是在下个时钟到来前开始保存到寄存器、或存储器中,PC的改变也是在这个时候进行。另外,值得注意的问题,设计时,用模块化的思想方法设计关于ALU设计、存储器设计、寄存器组设计等等,也是必须认真考虑的问题。可以参考其他资料文档,里面有相应的设计方法介绍。(资料文档:MIPS汇编与单周期CPU.ppt)。

     

    四.     实验器材

    电脑一台、Xilinx Vivado 软件一套。

    五.     实验分析与设计

    分析:主要对图2  单周期CPU数据通路和控制线路图进行分析,我们不妨把CPU分成13个模块:pc、pcAddFour、pcAddImmediate、InstructionsMemory、ControlUnit、RegisterFile、ALU、DataMemory、signExtend、Mux_5、Mux_32_ALUSrcB、Mux_32_ALUM2Reg、Mux_32_PCSrc,下面对各个模块进行简单分析。

    1.pc即程序计数器program counter,如下图,其功能是获取当前指令的32位地址,传给指令存储器,并根据pc+4或者pc+4+immediate*4得到下一条指令的地址。由时钟信号CLK上升沿触发,并接收Reset重置信号,当Reset=1,pc的指令地址重置为0x00000000。

    2.pcAddFourpc = pc + 4,当前指令传入指令存储器后,计算下一条指令地址,如下图:

    3.pcAddImmediatepc = pc + 4 + (immediate_32*4),当需要进行跳转的时候,计算出跳转之后的指令的地址,注意immediate_32需要左移两位。

    4.InstructionsMemory指令存储器,通过指令存储器地址输入端口IAddr接收来自pc的指令地址,来获取该地址上的指令并通过数据输出端口IDataOut将指令传给寄存器堆RegisterFile。

    5.ControlUnit控制单元,主要用于发送控制各个模块的信号,实现控制各个模块的功能,主要的控制信号有10个,分别为:PCWre,ALUSrcB, ALUM2Reg, RegWre, InsMemRW, DataMemRW, ExtSel, PCSrc, RegOut, ALUOp,有关各个信号的功能放到设计中详述。另外接收来自指令的操作码opcode和来自ALU运算结果标志zero的值。

    下面是各个控制信号和指令的关系表:

     

    控制信号

    指令

    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

    X

    X

    1

    1

    X

    001

    halt

    x

    0

    x

    X

    x

    0

    X

    X

    0

    X

    xxx

     

    6.RegisterFile寄存器堆,用来处理指令存储器传过来的指令,具体分为R型指令和I型指令,

    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位

    然后将寄存器rs、rt有选择地送进ALU进行算术逻辑运算,然后将运算结果送回rt或者rd寄存器中。

    7.ALU算术逻辑单元,根据3位ALUOp控制信号来实现不同的运算,zero为运算结果标志,若结果为0输出为1,反之为0.

    8.DataMemory数据存储器,来自ALU的运算结果result作为地址输入端口DAddr,来自ControlUnit的控制信号DataMemRW控制数据存储器的读写操作,DataIn接收来自寄存器堆的第二个数据输入,对应sw指令。

    9.signExtend用于扩展16位立即数,将其转化为32位立即数,有选择地输入到ALU进行运算(I型指令)。

    10.Mux_5目的寄存器选择器:通过控制信号RegOut选择目的寄存器rt或者rd。若是R型指令,则目的寄存器为rd,若是I型指令则为rt寄存器。

    11.Mux_32_ALUSrcB扩展立即数选择器:通过控制信号ALUSrcB来判断输入ALU的第二个数据是rt寄存器中的数据还是32位立即数。若为1,则是32位立即数。

    12.Mux_32_ALUM2Reg数据写回寄存器选择器:通过控制信号ALUM2Reg来判断写回目的寄存器的数据是来自ALU的运算结果还是来自数据存储器DataMemory的数据。若为1,则是来自DataMemory的数据,且该指令为lw。

    13.Mux_32_PCSrcPC地址跳转选择器:通过控制信号PCSrc来判断是否需要跳转,若为0,即不跳转,下一条指令地址为pc+4;若为1,即跳转,下一条指令地址为pc+4+immediate_32*4.

    设计接下来对每一个模块进行设计,为了更好地说明,对各个模块涉及的参数详述。

    1.pc

    参数

    功能

    PCWre

    PC的写使能信号,若PCWre =0,即停机,则PC不更改,若PCWre = 1,PC可更改;

    CLK

    时钟信号,上升沿触发

    Reset

    重置信号,若Reset = 1重置

    [31:0]out_pc

    从pc输出的32位指令地址

    [31:0]in_pc

    输入pc的32位指令地址,可为pc+4或者pc+4+immediate_32*4

     








    2.pcAddFour

    参数

    功能

    [31:0]in_pc

    输入的指令地址

    [31:0]out_pc

    输出指令地址=输入指令地址+4=in_pc+4

     

    3.pcAddImmediate

    参数

    功能

    [31:0]in_pc

    输入的指令地址,此时已为pc+4

    [31:0]offset

    32位立即数

    [31:0]out_pc

    输出的32位指令地址 = 输入的指令地址+立即数*4=pc+4+offset*4

     

    4.InstructionsMemory

    参数

    功能

    InsMemRW

    读写控制信号,0:读,1:写

    [31:0]IAddr

    输入的当前指令地址

    [31:0]instruction

    输出的32位指令

     

    5.ControlUnit

    参数

    功能

    [5:0]opcode

    6位的指令操作码

    zero

    ALU的计算结果标志信号。运算结果为0时,zero = 1,不为0时,zero = 0

    PCWre

    PC是否需要更改的控制信号,0停机不更改

    ALUSrcB

    判断是否选择扩展立即数,0:非立即数(R型指令),1:立即数(I型指令)

    ALUM2Reg

    判断写回目的寄存器的数据来源,0:来自ALU的运算结果,1:来自DataMemory中的数据(lw指令)

    RegWre

    寄存器堆的写使能信号

    InsMemRW

    InstructionsMemory的读写信号,0:读,1:写

    DataMemRW

    DataMemory的读写信号,0:读,1:写

    ExtSel

    是否扩展立即数的控制信号,1扩展

    PCSrc

    判断PC的地址是否需要跳转,1跳转

    RegOut

    用于判断R型和I型指令中rt、rd寄存器谁为目的寄存器,R:rd;I:rt。

    [2:0]ALUOp

    ALU的控制信号,决定ALU的算术逻辑运算

     

    6.RegisterFile

    参数

    功能

    RegWre

    使能信号,当为1时,in_data写回rd寄存器,否则写回rt寄存器

    CLK

    时钟信号,上升沿触发

    [4:0]rs

    rs寄存器编号

    [4:0]rt

    rt寄存器编号

    [4:0]rd

    rd寄存器编号

    [31:0]in_data

    写回目的寄存器的32位结果

    [31:0]ReadData1

    输出rs寄存器的数据

    [31:0]ReadData2

    输出rt寄存器的数据

     

    7.ALU

    参数

    功能

    [2:0]ALUOp

    控制信号,决定ALU进行的算术逻辑运算

    zero

    运算结果为0,zero为1,否则为0

    [31:0]A

    第一个操作数A

    [31:0]B

    第二个操作数B

    [31:0]result

    32位的运算结果

     

    8.DataMemory

    参数

    功能

    CLK

    时钟信号,下降沿触发

    DataMemRW

    控制信号,0:读(lw),1:写(sw)

    [31:0]DAddr

    将ALU运算结果作为地址输入端口

    [31:0]DataIn

    来自寄存器堆的第二个数据输入

    [31:0]DataOut

    32位数据有选择地写回目的寄存器中

     

    9.signExtend

    参数

    功能

    ExtSel

    控制信号,0:不扩展,1:扩展

    [15:0]immediate_16

    从指令存储器接收到的32位指令中的16位立即数

    [31:0]immediate_32

    输出扩展为32位的立即数

     

    10.Mux_5

    参数

    功能

    RegOut

    控制信号RegOut选择目的寄存器,0:选择rt,1:选择rd

    [4:0]A

    寄存器rt

    [4:0]B

    寄存器rd

    selected

    输出选择结果A or B

     

    11.Mux_32

    参数

    功能

    signal

    控制信号(ALUSrcB、ALUM2Reg、PCSrc),0:选择A,1:选择B

    [31:0]A

    输入数据来源A

    [31:0]B

    输入数据来源B

    selected

    输出选择结果A or B

     

    编写用来测试的汇编程序及相应的机器代码,如下表:

    地址(十六进制)

    汇编程序

    指令代码

    op(6)

    rs(5)

    rt(5)

    rd(5)/immediate (16)

    0x00000000

    addi  $1,$0,4

    000001

    00000

    00001

    0000 0000 0000 0100

    0x00000004

    addi  $2,$0,4     

    000001

    00000

    00010

    0000 0000 0000 0100

    0x00000008

    move  $3,$1,$0

    100000

    00001

    00000

    00011

    00000

    000000

    0x0000000C

    beq  $1,$2,1(到14)

    110000

    00001

    00010

    0000 0000 0000 0001

    0x00000010

    addi  $4,$0,4

    000001

    00000

    00100

    0000 0000 0000 0100

    0x00000014

    sub  $4,$2,$1

    000010

    00010

    00001

    00100

    00000

    000000

    0x00000018

    beq  $3,$4,1(不转)

    110000

    00011

    00100

    0000 0000 0000 0001

    0x0000001C

    sw   $2,0($2)

    100110

    00010

    00010

    0000 0000 0000 0000

    0x00000020

    lw   $4,0($2)

    100111

    00010

    00100

    0000 0000 0000 0000

    0x00000024

    ori   $4,$3,1

    010000

    00011

    00100

    0000 0000 0000 0001

    0x00000028

    or   $5,$4,$1

    010010

    00100

    00001

    00101

    00000

    000000

    0x0000002C

    and  $5,$4,$1 

    010001

    00100

    00001

    00101

    00000

    000000

    0x00000030

    add  $5,$1,$2  

    000000

    00001

    00010

    00101

    00000

    000000

    0x00000034

    halt

    111111

    00000

    00000

    0000 0000 0000 0000

     

     

     

     

     

     

     

     

     














    这里很多截图没放上,但关键部分也已经描述清楚,同时也欢迎下载整个项目代码及我的实验报告。

    下载链接:

    单周期CPU设计实验报告及项目代码

     

    展开全文
  • 实验二 : 单周期CPU设计与实现 一.实验目的 二.实验内容 三.实验原理 1.相关部件及引脚说明(根据个人设计有所修改): 2.FPGA板的端口与功能设计: 四.实验器材 五.仿真代码实现 1.单周期CPU模块代码 ...
  • 掌握单周期CPU数据通路的构成、原理及其设计方法; 掌握单周期CPU的实现方法,代码实现方法; 认识和掌握指令与CPU的关系; 掌握测试单周期CPU的方法; 掌握单周期CPU的实现方法。   ...
  • 单周期CPU——verilog语言实现

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

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

    千次阅读 2018-09-25 22:08:06
    设计一个单周期CPU,该CPU至少能实现以下指令功能操作。 必须写一段测试用的汇编程序,而且必须包含所要求的所有指令,slti指令必须检查两种情况:“小于”和“大于等于”;beq、bne:“不等”和“等”。这段汇编...
  • 此为中山大学17级计算机组成与设计实验课题。...认识和掌握多周期数据通路的构成、原理及其设计方法; 掌握多周期CPU的实现方法,代码实现方法; 编写一个编译器,将MIPS汇编程序编译为二进制机...
  • 大量使用比较器造成硬件开销巨大,测试一下发现这尼玛真是个老爷车还没单周期跑的快,自己电脑cpu占百分之八九十只能跑到20多赫兹,一个走马灯跑了半天,可优化空间太大了,写了那么多天造成这个破样,有点悲剧。...
  • 一、实现功能 实现了部分RV32I指令集中的部分指令类型,如下...实现了单周期CPU和五级流水线。其中,五级流水线为顺序读取、顺序放回;跳转指令可以冲刷流水线;没有预测跳转功能;占用资源较多,微架构面积较大。 ...
  • 周期CPU设计与实现实验

    千次阅读 2018-07-24 19:44:03
    (1) 认识和掌握多周期数据通路原理及其设计方法; (2) 掌握多周期CPU的实现方法,代码实现方法; (3) 编写一个编译器,将MIPS汇编程序编译为二进制机器码; (4) 掌握多周期CPU的测试方法; (5) 掌握多周期CPU...
  • 一、是的,作为设计师的你,需要关心硬件。 设计师小伙伴们,不知道你们有没有...用高昂的价格买来在设计中用不到的功能,或买到根本不适合自己所从事设计工作的应用需求的电脑硬件,岂不是事倍功半了。 所以,为什么
  • 一、是的,作为设计师的你,需要关心硬件。 设计师小伙伴们,不知道你们有没有...用高昂的价格买来在设计中用不到的功能,或买到根本不适合自己所从事设计工作的应用需求的电脑硬件,岂不是事倍功半了。 所以,为什么
  • Android Activity/Fragment 生命周期

    千次阅读 2014-04-28 14:12:38
    本篇文章的目的,是帮助Fragment的...大屏幕如平板,小屏幕如手机,平板电脑设计使得其有更多的空间来放更多的UI组件,而多出来的空间存放UI使其会产生更多的交互,从而诞生了Fragments 。 所以说无论是手机还是...
  • 本系列文章将介绍我的MIPS I CPU的实现,我们先规定我们的路线: MIPS I中的R型、I型、J型指令,暂不支持syscall和break指令 流水线 寄存器重编号 有一级指令缓存以及一级数据缓存 支持数据联络(通过UART与电脑...
  • 如下为Vivado工具的报告位置: 对于这些跨时钟域的情况,一般我们要在逻辑设计的时候就解决,当然之后也要对其进行约束,一般可以设置为false path等,即让综合工具不要机关算尽般去布局布线让时序满足要求(这会...
  • 文章目录回路控制系统整定参数整定要求常用整定方法理论计算方法理论计算整定法工程整定方法经验法(试凑法)临界比例带法(边界稳定法)衰减曲线法响应曲线法(动态特性参数法) 回路控制系统整定 参数整定要求 通过...
  • 本文针对手指静脉识别中的图像采集,优化图像质量等难点问题,自主设计、研发出一套低成本的手指静脉采集终端。 首先,基于对课题的需求的仔细分析和探讨,提出了手指静脉识别系统的总体设计方案,并确定了设计方案...
  • 《计算机组成原理实验》 多周期CPU

    千次阅读 2019-08-27 11:35:22
    《计算机组成原理实验》 多周期CPU 前言 这是中山大学2018年计算机组成原理实验中多周期CPU的实验报告,仿真...认识和掌握多周期数据通路的构成、原理及其设计方法; 掌握多周期CPU的实现方法,代码实现方法;...
  • 设计模式大全

    万次阅读 热门讨论 2006-12-22 21:07:00
    Longronglin之设计模式:Christopher Alexander 说过:“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动”。模式描述为:在...
  • 图像算法的工程优化技术

    千次阅读 2016-02-29 21:35:52
    图形处理器(英语:Graphics Processing Unit,缩写:GPU),又称显示核心、视觉处理器、显示芯片,是一种专门在个人电脑、工作站、游戏机和移动设备(如平板电脑、智能手机等)上图像运算工作的微处理器。...
  • 电脑硬件入门基础知识——看完就会选电脑

    万次阅读 多人点赞 2018-06-22 15:40:09
    再也不想当电脑小白了,搞深度学习还是要懂些电脑硬件标准的呀一台电脑需要知道哪些基本部件(1) 机箱,一般电脑的主要零件都放在这里(2) 显示器,用来看电脑的工作过程,要不然,你都不知道电脑究竟在做什么 (3) 键盘和...
  • 软件项目开发周期中数据库设计 1.需求分析阶段:分析客户的业务和数据处理需求 2.概要设计阶段:设计数据库的E-R模型,确认需求信息的正确和完整 3.详细设计阶段:应用三大范式审核数据库结构 4.代码编写阶段...
  • 硬件电路的设计 (附文件) 系统的功能分析及体系结构设计 3.1.1系统功能分析 本设计由STC89C52单片机电路+DHT11温湿度传感器店里了+蓝牙电路+电源电路组成。 1、通过单片机采集DHT11检测温湿度值,并且通过蓝牙将...
  • 首先得需要去网上下载一个Junit4的一个jar包,保存到自己本地电脑里面打开myEclipse新建一个Java项目,通过右击项目-->Build Path --->Configure Build Path -->Add External JARs--->找到自己刚保存的jar路径,点击...
  • 像以前的Dos就是道程序设计系统。 多道程序设计 在计算机内存中同时存放几道相互独立的程序,它们在管理程序控制之下,相互穿插的运行。多道程序设计必须有硬件基础作为保证。 时钟中断 即为多道程序...
  • 计算机组成原理 — GPU 图形处理器

    万次阅读 多人点赞 2019-08-12 19:32:44
    因此,对电脑处理器的要求,就是需要大量的并行的重复计算,GPU 正好有这个专长,时势造英雄,因此,GPU 就出山担当重任了。 训练 :我们可以把深度学习的训练看成学习过程。人工神经网络是分层的、是在层与层...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 31,636
精华内容 12,654
关键字:

单周期电脑设计图