精华内容
下载资源
问答
  • Risc-V单周期CPU设计思考
    千次阅读
    2021-05-25 12:12:24

    Risc-V单周期CPU设计思考

    今年学校课程改革,计算机组成课开始教学Risc-V,写单周期CPU的时候发现网上好像大多都是MIPS的CPU设计,所以就把自己关于设计Verilog的一些思路整理出来。看完同学的设计之后再一次确定了我非常非常菜,本文就仅供参考。(为防影响以后的教学作业,大多数源代码已经隐去、并未将宏等解释性语言加入博客)。

    基础指令集的实现

    基础指令集(包括ALU计算、数据存储载入、跳转)的实现可以按照Risc-V书本上的数据通路实现。在这里插入图片描述

    Control模块

    Control模块实质上是一个opcode的译码模块,即通过输入的7位opcode(实际上是5位,因为01位始终为1)的实现可以通过一个always段和多个case语句进行译码。但是这样的弊端是所综合出的电路比较消耗资源。我采用的方式利用之前机逻课上学的简化真值表,写出每一个译码信号与原信号的逻辑关系,大致如下:

    wire is_branch, mem_ren, ALUop1, ALUop0, is_imm, reg_wen;
    wire DMemWrite, MemToReg;
    assign is_branch = inst[6] & ~inst[4];
    assign MemToReg = ~inst[6] & ~inst[5] & ~inst[4];
    assign ALUop1 = inst[4];
    assign ALUop0 = inst[6] & ~inst[2];
    assign DMemWrite = ~inst[6] & inst[5] & ~inst[4];
    assign is_imm = ~(inst[5] & inst[4]);
    assign reg_wen = ~inst[5] | inst[4] | inst[2];
    

    由于Risc-V并没有将32种opcode情况分配给每一种指令类型,无关项使得每一条指令都不必完全利用5位opcode,使得译码阶段更加节省资源。但是弊端是当增加一条新指令时,会对之前的译码模块产生很大的改变,而且在处理illegal instruction的时候也会非常麻烦。

    ImmGen模块

    立即数生成模块即可依照Risc-V的的指令集生成。

    always@(*)begin
            case( opcode )
                `OPCODE_LOAD: r = {{20{inst[31]}}, inst[31:20]};
                `OPCODE_STORE: r = {{20{inst[31]}}, inst[31:25], inst[11:7]};
                `OPCODE_BRANCH: r = {{19{inst[31]}}, inst[7], inst[30:25], inst[11:8], 1'b0};
                `OPCODE_OP_IMM: r = {{20{inst[31]}}, inst[31:20]};
                `OPCODE_JAL: r = {{11{inst[31]}}, inst[19:12], inst[20], inst[30:21], 1'b0};
                `OPCODE_LUI: r = {inst[31:12], 12'b0};
                `OPCODE_JALR: r = {{20{inst[31]}}, inst[31:20]};
                `OPCODE_AUIPC: r = {inst[31:12], 12'b0};
            endcase
    end
    

    ALU Control、ALU(与Comparator)模块

    ALU Control元件存在的意义是,在加载存储、跳转指令中,有些时候无法像寄存器(与立即数)之间运算由Fun3一样确定运算符号,但是这些符号却是确定的:

    1. 寄存器(与立即数)运算:由Fun3决定
    2. 加载存储:加法
    3. 有条件跳转指令:减法
      因此需要一个2位的ALUOP信号,但是在CPU设计的时候完全可以将ALU与Comparator分离,所以可以将ALUOP简化为1位信号。而ALU与Comparator的设计则和ImmGen基本相同,故不再赘述。

    Instruction Memory与Data Memory模块

    Memory的设计依托于verilog中读取内存的函数语句,值得一提的是由于PC在自增是总是增加4的倍数,故如果将Instruction Mem的指令寄存器设置为32位的,则需要除以4读入:

    (* ram_style = "block" *) reg [31:0] mem[0:4095];
    initial $readmemh("imem_data.mem", mem);
    assign imem_o_data = mem[imem_addr >> 2];
    

    (Data Memory部分代码略去)

    RegFile模块

    (略)

    PC相关模块

    PC的变化有以下几种情况:

    1. 有条件跳转并满足条件:跳转到目的地址
    2. 无条件跳转:跳转到目的地址
    3. 发生了中断异常:跳转到中断异常处理程序地址
    4. 无事发生:加4
    always@(posedge clk)begin
           if(mret_exce | ecall_exce | exce_happen | int_happen)begin
               pc <= pc_out;
           end
           else if(is_jalr)begin
               pc <= {alu_res[31:1], 1'b0};
           end
           else begin
               pc <= (is_jal | do_branch) ? pc_branch : (pc + 4);
           end
    end
    

    如何增加一条指令

    增加一条指令的大致步骤即为:

    1. 调整Control的真值表
    2. 是否需要新增元件(绝大多数情况是一个多路选择器)
    3. 是否需要对已有元件进行调整

    以Jal为例:

    调整Control的真值表

    (略)

    是否需要新增元件

    Jal属于无条件跳转指令,因此在PC增加的模块里需要引入,如果把之前有条件跳转的指令信号称为is_branch,把Comparator得到的结果为com_res,是否跳转为do_branch, 那么就会有:
    d o _ b r a n c h = i s _ b r a n c h & c o m _ r e s do\_branch = is\_branch \quad \& \quad com\_res do_branch=is_branch&com_res
    在新增了无条件跳转uncon_jump后,就会有:
    d o _ b r a n c h = ( i s _ b r a n c h & c o m _ r e s ) ∣ u n c o n _ j u m p do\_branch = (is\_branch \quad \& \quad com\_res) \quad | \quad uncon\_jump do_branch=(is_branch&com_res)uncon_jump

    是否需要对已有元件进行调整

    对于之前没有加入无条件跳转的SCPU,加入Jal、Jalr等语句后,需要对PC的模块进行相应调整。

    中断异常的增加

    中断异常的增加伴随着CSR寄存器与相关操作的增加,可以在正版Risc-V的ISA中看到相关资料,其他的一些资料往往伴随着一些错误,对CPU设计有着误导作用。

    异常发生的可能有

    1. 发生非法指令: opcode译码错误
    2. PC非对其异常:下一条将要执行的指令地址不是4的倍数
    3. L / S地址非对其异常:目标的地址不是4的倍数
    4. ECALL调用异常

    异常相关的处理

    捕捉到异常

    将异常信号、异常的相应信息传给CSRRegFile元件进行对应的改变

    并没有异常,对CSR相关寄存器的操作

    与正常的操作一样进行CSR相关寄存器的改变

    CSR寄存器的作用

    mtvec

    31 … 21 0
    BASE[31:2]MODE

    中断 / 异常发生时:

    mode = 0: pc <- BASE

    mode = 1: 向量模式,较为复杂

    mcause

    3130……0
    InterruptException Code

    Interrupt会记录是中断还是异常
    Exception Code记录的是中断或者异常的编码

    mstatus

    31……876……432……0
    MPIEMIE

    中断 / 异常发生时: mstatus[7] <- mstatus[3]
    MRET发生时: mstatus[3] <- mstatus[7]
    MIE是全局的中断使能

    mepc

    记录发生异常 / 中断时的PC的地址
    中断 / 异常发生时: mepc <- pc
    注意当异常是由跳转指令引起时,记录需要跳转指令的目标地址
    MRET时: pc <- mepc

    mtval

    当发生非法指令异常时:记录指令的值
    当发生PC非对齐异常时:记录PC的值
    当发生L / S地址非对其异常时: 记录L / S的地址

    mie

    31……876……432……10
    MEIEMTIE

    mie标记各种中断使能,关闭某个中断的使能后,则无法发生该种中断

    mip

    31…876…432…10
    MEIPMTIP

    记录某中断是否已经发生(只读,不支持指令写入)

    心得体会

    1. 时钟信号的通入较为讲究,一般而言对于读操作皆为常通,而写操作则在时钟上升或下降沿执行。
    2. 关于阻塞赋值和非阻塞赋值,只要是没有顺序关系的语句,都更加倾向于使用非阻塞赋值。

    Acknowledge

    感谢潘助教和姜老师的指导。

    更多相关内容
  • 单周期CPU设计全过程: 组成原理实验课,包含十六条指令实现,完整的代码以及详细的实验报告,是本人实验课的作业。用verilog HDL语言或者VHDL语言来编写,实现单周期CPU的设计。 用Verilog HDL语言编写MIPS32单...
  • 内含CPU所有Verilog源码、论文详细解析,作业成绩为优秀 所有代码和论文皆为原创,严禁二次转载!
  • 单周期CPU设计

    2017-05-05 15:29:17
    中山大学计组实验单周期CPU设计实验报告及项目代码
  • 单周期CPU设计vivado

    2018-12-03 17:10:27
    单周期CPU设计的文件,语言verilog,打开之后,可以用vivado直接跑,思路是正确的,具体的指令需要根据要求做小幅度调整
  • ZJU计算机组成课程作业,内含各部件代码,支持18条指令,包括slt,lui,slr,sll,jr,jal等指令。
  • 使用logisim布线完成的MIPS单周期CPU,可支持28条指令。跑马灯的代码已经装入了寄存器,可以直接开启时钟运行。
  • 单周期的整个项目,在电脑上安装vivado即可添加项目,我个人使用的是15版的。另外需要看波形图的,点击仿真,调节相关参数即可
  • 单周期CPU设计verilog

    2017-05-10 22:42:35
    单周期CPU设计verilog,课程设计
  • 1.设计CPU能够执行20条整数指令,每条指令的编码长度均为32位; 2.指令类型有:计算类型、访问存储器类型、条件转移类型和无条件转移类型等; 3.实现CPU的封装处理。 具体设计思路参见:...
  • 单周期MIPS CPU设计

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

    2018-02-05 15:56:35
    在理解MIPS指令集编码以及指令格式基础上,实现一个简化版的具有11条基本指令的单周期MIPS,利用Vivado在Xilinx N4板实现
  • 24条指令MIPS单周期CPU课程设计报告完整版
  • logisim平台下实现的单周期处理器,能够完美实现基本指令操作,内含7段数码管设计,操作方便直观。 1.处理器应支持的指令集MIPS-Lite:addu,subu,ori,lw,sw,beq,lui,j。 a)addu,subu可以不支持实现溢出。 2....
  • 自己写的,基于MIPS架构的单周期CPU。。
  • 设计一个单周期CPU,该CP能实现基本的r型,j型的指令功能操作。
  • 8指令单周期MIPS CPU设计 1、单周期硬布线控制器 2、单周期 MIPS(硬布线)
  • 1.设计CPU能够执行20条整数指令,每条指令的编码长度均为32位; 2.指令类型有:计算类型、访问存储器类型、条件转移类型和无条件转移类型等; 3.实现CPU的封装处理。 具体设计思路参见:...
  • logisim平台下实现的单周期处理器,能够完美实现基本指令操作,内含7段数码管设计,操作方便直观。 1.处理器应支持的指令集MIPS-Lite:addu,subu,ori,lw,sw,beq,lui,j。 a)addu,subu可以不支持实现溢出。 2....
  • Verilog projects cpu
  • 计组单周期CPU设计.zip

    2021-08-02 14:07:12
    根据袁春风老师编写的《计算机组成与系统结构第二版》的课程材料,完成的计组单周期CPU设计,已经完成简单测试。包括五条R型指令(add,sub,subu,slt,sltu),五条I型指令(addu,beq,ori,lw,sw),和一条J型指令(jump)...
  • 单周期cpu代码.zip

    2020-06-22 19:13:04
    单周期cpu代码.zip
  • 24条指令MIPS单周期CPU课设设计有这个压缩包足够
  • 这是计算机组成原理课设,基于verilog实现的单周期CPU代码,实现了add, addu, addi, addiu, sub, slt, and, or, xor, beq, j, sw, lw, lui,andi,addu,ori,xori,共18条指令,能够运行简单的冒泡排序。内含单周期...
  • 计算机组成原理大作业,VHDL 编写,FPGA测试实现15条指令
  • 16位单周期CPU设计_1

    2018-06-01 16:00:39
    使用Verilog实现16位单周期CPU,并且进行PCPU的软件仿真 之前上传的那个是32位的,传错了不好意思
  • (Verilog)单周期CPU设计 单周期CPU设计与实现原理分析

    (Verilog)单周期CPU设计

    单周期CPU设计与实现原理分析

    设计和实现一个支持加法指令的单周期 CPU。要求该加法指令(表示为 add r1,r2,
    r3)格式约定如下:
    采用寄存器寻址,r1,r2,r3 为寄存器编号,r1 和 r2 存放两个源操作数,r3
    为目标寄存器,其功能为[r1] + [r2] -> r3;
    指令字长 16 位,操作码和地址码字段分配如下所示:

    OpCoder1r2r3

    PC模块

    module PC(clk,rst,y);
    input clk,rst;
    output y;
    reg[7:0] y;
    initial y = 0;
    always@(posedge clk or negedge rst)begin
        if(!rst)
            y = 0;
        else
            y = y + 1;
    end
    endmodule
    

    InsMemory

    module InsMemory(Addr,Ins);
    input[7:0] Addr;
    output Ins;
    reg[15:0] Ins;
    
    reg [15:0]unit[8'd255:0];//25616位存储单元
    integer i,j;
    initial begin
        for(i=0;i<256;i=i+1)begin
            j = i % 5;
            unit[i][2:0] = j+2;
            unit[i][5:3] = j+1;
            unit[i][8:6] = j;
            unit[i][15:9] = i%3;
        end
    end
    always@(*)begin
         Ins = unit[Addr];
     end 
    endmodule
    

    RegFile

    module RegFile(
        clk,    //  时钟信号
        RegRW,  //读写信号,为1时写入(上升沿有效),为0时,读数据,
        read_reg1,//要读出数据1的地址
        read_reg2,//要读出数据2的地址
        write_reg,//要写入数据的地址
        write_data,//要写入的数据
        reg1,//要读出的数据1
        reg2//要读出的数据2
        );
        input clk,RegRW;
        input[2:0] read_reg1,read_reg2,write_reg;
        input[15:0] write_data;
        output[15:0] reg1,reg2;
        
        integer i;
        reg[15:0] regfile[7:0];//816位的寄存器
        initial begin
            for(i=0;i<8;i=i+1)
                regfile[i] = 2*i+1;
        end
        always@(posedge clk)begin
            if(RegRW == 1)
                regfile[write_reg] = write_data;
        end
        
        assign reg1 = regfile[read_reg1];
        assign reg2 = regfile[read_reg2];
    endmodule
    

    DataMemory

    module DataMemory(
        DataRW,//读写信号,当为1时,写入;0时,读出
        DAddr, //读或写的地址
        DataIn,//要写入的数据
        DataOut,//要读出的数据
    
        );
        input DataRW;
        input[5:0] DAddr;
        input[15:0] DataIn;
        output reg[15:0] DataOut;
       
        reg [15:0] memory[6'd63:0];//6416位存储单元
        integer i;
        initial begin
            for(i=0;i<64;i=i+1)
                memory[i] = i*i;
        end
        always@(*)begin
            if(DataRW)
                memory[DAddr] = DataIn;
             else
                DataOut = memory[DAddr];
        end
    
    endmodule
    

    ALU

    module ALU(
        alu_op, //ALU操作控制
        A,      //操作数A
        B,      //操作数B
        result  //运算结果
        );
        input[2:0] alu_op;
        input[15:0] A,B;
        output reg[15:0]result;
        
        always@(*)begin
            case(alu_op)
                3'b000 : result = A+B;
                3'b001 : result = A-B;
                3'b010 : result = A * B;
                3'b011 : result = A / B;
                3'b100 : result = A & B;
                3'b101 : result = A | B;
                3'b110 : result = A ^ B;
                3'b111 : result = A << B;
                default:result = 0;
            endcase
        end
    endmodule
    
    

    ControlUnit

    module ControlUnit(
        input[5:0] op, //op操作符
        output reg RegOut,//二路选择器,为1时,输出r1,否则输出r3
        output reg M2Reg,//二路选择器,为1时,输出Mem[addr],否则输出[r1]+[r2]
        output reg RegRW,//寄存器堆读写信号,为1时写入,为0时,读出
        output reg DataMemRW,//数据存储器读写信号,为1时写入,为0时,读出
        output reg[2:0] alu_op//ALU的操作控制信号
        );
        initial begin 
            RegOut = 0;
            M2Reg = 0;
            RegRW = 0;
            DataMemRW = 0;
            alu_op = 0;
        end
        always@(*)begin
            case(op)
                //LDA   取数
                6'b000000:begin
                    RegOut = 1;
                    M2Reg  = 1;
                    RegRW  = 1;
                    DataMemRW = 0;
                    alu_op = 0;
                end
                
                //ADD   加法
                6'b000001:begin
                    RegOut = 0;
                    M2Reg  = 0;
                    RegRW  = 1;
                    DataMemRW = 0;
                    alu_op = 0;
                end
                
                //STA   存数
                6'b000010:begin
                    RegOut = 0;
                    M2Reg  = 0;
                    RegRW  = 0;
                    DataMemRW = 1;
                    alu_op = 0;
                end
                
                
            endcase
        end
    endmodule
    

    二路选择器

    由于不受时钟信号控制,始终会将r1,r2寄存器中的数据作为ALU的操作数输入,将r1寄存器的数据作为DataMemory的要写入的数据,将DataMemory中地址为addr中的数据读出。当要将数据写入寄存器堆时,要有个3线二路选择器,决定是将从数据存储器中读出的数据写入r1,还是将[r1]+[r2]的结果送入r3。由于要写入的数据有可能是从数据寄存器中读出的数据,也有可能是[r1]+[r2]的结果,所以要有个16线二路选择器决定要写入的数据

    module Multiplexer3(
        control,//选择信号,当为1时,输出in1,当为0时,输出in0
        in1,
        in0,
        out
        );
        input control;
        input[2:0] in1,in0;
        output[2:0]out;
        assign out = control == 1 ? in1 : in0;
    endmodule
    
    module Multiplexer16(
        control, //选择信号,当为1时,输出in1,当为0时,输出in0
        in1,
        in0,
        out
        );
        input control;
        input[15:0] in1,in0;
        output[15:0] out;
        
        assign out = control ? in1:in0;
    endmodule
    
    

    顶层模块

    module SignleCPU(
        input clk,
        input rst,
        output[5:0]op,
        output[2:0] r1,
        output[2:0] r2,
        output[2:0] r3,
        output[5:0]addr,
        output[15:0]ReadData1,
        output[15:0]ReadData2,
        output[15:0]WriteData
        );
        wire[15:0] Ins,DataOut,result;
        wire[7:0]InsAddress;
        wire[2:0] WriteReg,alu_op;
        wire RegOut,M2Reg,RegRW,DataMemRW;
        
        PC pc(clk,rst,InsAddress);
        
        InsMemory insmemory(InsAddress,Ins);
        assign op = Ins[15:9];
        assign r1 = Ins[8:6];
        assign r2 = Ins[5:3];
        assign r3 = Ins[2:0];
        assign addr = Ins[5:0];
        
        RegFile regfile(clk,RegRW,r1,r2,WriteReg,WriteData,ReadData1,ReadData2);
        
        ControlUnit cu(op,RegOut,M2Reg,RegRW,DataMemRW,alu_op);
        
        DataMemory datamem(DataMemRW,addr,ReadData1,DataOut);
        
        ALU alu(alu_op,ReadData1,ReadData2,result);
        
        Multiplexer3 mul3(RegOut,r1,r3,WriteReg);
        Multiplexer16 mul16(M2Reg,DataOut,result,WriteData);
    endmodule
    

    Test模块

    module Test();
        reg clk,rst;
        wire[2:0] r1,r2,r3;
        wire[5:0]addr;
        wire[15:0] ReadData1,ReadData2,WriteData;
        wire[5:0] op;
        SignleCPU f(clk,rst,op,r1,r2,r3,addr,ReadData1,ReadData2,WriteData);
        initial begin
            clk = 0;rst = 1;
            #40 $stop;
        end
        always #5 clk = ~clk;
    endmodule
    
    

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    代码
    Github
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 单周期CPU设计【Verilog】

    千次阅读 2021-02-23 21:20:13
    第一章 单周期CPU设计原理 为实现单周期CPU,本文首先研究了单周期CPU的相关理论和工作原理。本章首先简要介绍单周期CPU的基本概念,然后对CPU的工作原理进行简要分析,以便为单周期CPU设计和开发提供理论基础...

    第一章 单周期CPU的设计原理

    1.1 单周期CPU概述

    1.2 CPU工作原理

    第二章 单周期CPU的设计内容

    2.1 指令系统的设计

    2.1.1 概述

    2.1.2 运算类指令的设计

    2.1.3 传送类指令的设计

    2.1.4 存储类指令的设计

    2.1.5 控制类指令的设计

    2.2 整体框架的设计

    2.3 数据通路的设计

    2.4 控制信号的设计

    第三章 单周期CPU的具体实现

    3.1 底层模块的实现

    3.1.1 程序计数器PC

    3.1.2 指令存储器InstructionMemory

    3.1.3 寄存器组RegisterFile

    3.1.4 算术逻辑单元ALU

    3.1.5 数据存储器DataMemory

    3.1.6 控制单元ControlUnit

    3.2 顶层模块的实现

    第四章 单周期CPU的仿真验证

    4.1 测试程序

    4.2 波形仿真


    第一章 单周期CPU的设计原理

    为实现单周期CPU,本文首先研究了单周期CPU的相关理论和工作原理。本章首先简要介绍单周期CPU的基本概念,然后对CPU的工作原理进行简要分析,以便为单周期CPU的设计和开发提供理论基础。

    1.1 单周期CPU概述

    中央处理器,即CPU,作为计算机系统的运算和控制核心,是信息处理、程序运行的最终执行单元。在CPU内部,电平从低到高变化的瞬间称为时钟上升沿,两个相邻时钟上升沿之间的时间间隔称为一个时钟周期。单周期CPU指的是一条指令的执行在一个时钟周期内完成,然后开始下一条指令的执行,即一条指令用一个时钟周期完成。

    1.2 CPU工作原理

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

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

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

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

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

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

    CPU的指令处理过程如下:

     

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

    第二章 单周期CPU的设计内容

    2.1 指令系统的设计

    2.1.1 概述

    本文所设计的单周期CPU的指令系统采用类似MIPS的设计风格,包括以下四类指令:

    (1) 运算类指令;

    (2) 传送类指令;

    (3) 存储类指令;

    (4) 控制类指令;

    其中,所有指令的操作码部分用4位二进制表示,寄存器编号用3位二进制表示。在下述的具体设计表示中,以助记符表示的是汇编指令;以代码表示的则是二进制机器指令。

    2.1.2 运算类指令的设计

    运算类指令共有8条,具体的指令功能与指令格式如下:

    (1) add rd,rs,rt

    0000

    rs(3位)

    rt(3位)

    rd(3位)

    reserved(未用)

    功能:将寄存器rs和rt中的值相加并将结果写回寄存器rd。reserved为预留部分,即未用,一般填“0”。

    (2) addi rt,rs,immediate

    0001

    rs(3位)

    rt(3位)

    immediate(6位)

    功能:将立即数immediate进行符号扩展后再和寄存器rs中的值相加并将结果写回寄存器rt。

    (3) sub rd,rs,rt

    0010

    rs(3位)

    rt(3位)

    rd(3位)

    reserved(未用)

    功能:用寄存器rs中的值减去rt中的值并将结果写回寄存器rd。reserved为预留部分,即未用,一般填“0”。

    (4) or rd,rs,rt

    0011

    rs(3位)

    rt(3位)

    rd(3位)

    reserved(未用)

    功能:将寄存器rs和rt中的值进行按位或运算并将结果写回寄存器rd。reserved为预留部分,即未用,一般填“0”。

    (5) ori rt,rs,immediate

    0100

    rs(3位)

    rt(3位)

    immediate(6位)

    功能:将立即数immediate进行“0”扩展后再和寄存器rs中的值进行按位或运算并将结果写回寄存器rt。

    (6) and rd,rs,rt

    0101

    rs(3位)

    rt(3位)

    rd(3位)

    reserved(未用)

    功能:将寄存器rs和rt中的值进行按位与运算并将结果写回寄存器rd。reserved为预留部分,即未用,一般填“0”。

    (7) sll rd,rt,sa

    0110

    未用(3位)

    rt(3位)

    rd(3位)

    sa(3位)

    功能:先将sa表示的立即数进行“0”拓展,然后将寄存器rt中的值左移sa位并将结果写回寄存器rd。

    (8) slt rd,rs,rt

    0111

    rs(3位)

    rt(3位)

    rd(3位)

    reserved(未用)

    功能:将寄存器rs和rt中的值作为带符号数进行比较,若rs中的值更小,则将寄存器rd中的值置1,否则置0。

    2.1.3 传送类指令的设计

    传送类指令共有2条,具体的指令功能与指令格式如下:

    (1) mov rd,rt

    1000

    未用(3位)

    rt(3位)

    rd(3位)

    reserved(未用)

    功能:将寄存器rt中的值传送到寄存器rd。reserved为预留部分,即未用,一般填“0”。

    (2) movi rt,immediate

    1001

    未用(3位)

    rt(3位)

    immediate(6位)

    功能:将立即数immediate进行符号扩展后传送到寄存器rt。

    2.1.4 存储类指令的设计

    存储类指令共有2条,具体的指令功能与指令格式如下:

    (1) sw  rt,immediate(rs)

    1010

    rs(3位)

    rt(3位)

    immediate(6位)

    功能:将寄存器rt中的值存储到内存中,存储单元的地址为寄存器rs中的值加上经符号拓展的立即数immediate。

    (2) lw  rt,immediate(rs)

    1011

    rs(3位)

    rt(3位)

    immediate(6位)

    功能:将内存中的值加载到寄存器rt中,存储单元的地址为寄存器rs中的值加上经符号拓展的立即数immediate。

    2.1.5 控制类指令的设计

    控制类指令共有4条,具体的指令功能与指令格式如下:

    (1) beq  rs,rt,immediate

    1100

    rs(3位)

    rt(3位)

    immediate(6位)

    功能:比较寄存器rs和rt中的值,若相等则跳转到目标处执行,否则继续执行下一条指令。跳转的目标地址与当前指令的下一条指令的相对位移由立即数immediate给出。

    (2) bgtz  rs,immediate

    1101

    rs(3位)

    000

    immediate(6位)

    功能:判断寄存器rs中的值是否大于0,若大于0则跳转到目标处执行,否则继续执行下一条指令。跳转的目标地址与当前指令的下一条指令的相对位移由立即数immediate给出。

    (3) j  addr

    1110

    addr(12位)

    功能:执行无条件跳转,跳转的目的地址组成如下:高3位为PC中值的高3位,第1-12位由addr给出,最低位为0。

    (4) halt

    1111

    000000000000(12位)

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

    2.2 整体框架的设计

    本文所设计的单周期CPU的整体框架主要包括七部分:程序计数器、指令寄存器、寄存器组、算术逻辑单元、数据存储器、控制单元和顶层模块。具体框架如下:

    2.3 数据通路的设计

    本文所设计的单周期CPU的数据通路如下:

    其中Ins.MEM为指令存储器,Data.MEM为数据存储器。访问存储器时,先给出内存地址,然后由读或写信号控制操作。对于寄存器组Register File,先给出寄存器编号,读操作时,输出端就直接输出相应数据;而在写操作时,当写使能信号为1时,在时钟边沿触发将数据写入寄存器。

    主要模块接口说明如下:

    (1) Instruction Memory:指令存储器。

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

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

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

    (2) Data Memory:数据存储器。

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

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

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

    RD,数据存储器读控制信号,为0读

    WR,数据存储器写控制信号,为0写

    (3) Register File:寄存器组。

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

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

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

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

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

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

    WE,写使能信号,为1时,在时钟边沿触发写入

    (4) ALU:算术逻辑单元。

    A,操作数A输入端口

    B,操作数B输入端口

    result,ALU运算结果

    zero,运算结果标志,结果为0,则zero=1;否则zero=0

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

    2.4 控制信号的设计

    本文所设计的单周期CPU的各控制信号如下:

     各控制信号的功能如下:

    控制信号名

    状态“0”

    状态“1”

    Reset

    初始化PC为0

    PC接收新地址

    PCWre

    PC不更改,相关指令:halt

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

    ALUSrcA

    来自寄存器堆data1输出,相关指令:add、addi、sub、or、ori、and、slt、mov、movi、beq、bgtz、sw、lw

    来自经过“0”拓展的移位数sa,即 {{13{0}},sa},相关指令:sll

    ALUSrcB

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

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

    DBDataSrc

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

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

    RegWre

    寄存器组写不使能,相关指令:sw、beq、bgtz、j、halt

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

    InsMemRW

    写指令存储器

    读指令存储器

    RD

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

    无操作

    WR

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

    无操作

    RegDst

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

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

    ExtSel

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

    (sign-extend)immediate符号扩展,相关指令:addi、movi、sw、lw、beq、bgtz

    PCSrc[1..0]

    00:pc<-pc+2,相关指令:add、addi、sub、or、ori、and、sll、slt、sw、lw、beq(zero=0)、bgtz(sign=1,或zero=1);

    01:pc<-pc+2+(sign-extend)immediate,相关指令:beq(zero=1)、bgtz(sign=0,zero=0);

    10:pc<-{(pc+2)[15..13],addr[12..1],0},相关指令:j;

    11:未用

    ALUOp[2..0]

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

    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

    if (A<B && (A[15] == B[15] ) )

        Y = 1;

    else if ( A[15] && !B[15)  Y = 1;

    else Y = 0; 

    比较A与B

    带符号

    111

    Y = B

    直送

    在具体进行控制时,PC的改变是在时钟上升沿进行的,指令执行的结果总是在时钟下降沿保存到寄存器和存储器中,这样稳定性较好。

    第三章 单周期CPU的具体实现

    3.1 底层模块的实现

    单周期CPU每个时钟周期可被划分为五个阶段,对应的有五个最关键的底层模块:IF阶段对应PC,ID阶段对应InstructionMemory和RegisterFile,EXE阶段对应ALU,MEM阶段对应DataMemory,WB阶段也对应RegisterFile。除此之外,还需要生成控制信号的控制单元(ControlUnit)等。下面具体介绍上述各主要模块的设计与实现。

    3.1.1 程序计数器PC

    PC为时序逻辑,在时钟上升沿到来时,若PCWre为1,则输出下一条待处理的指令地址,否则不输出新的指令地址。若Reset为0,则设置输出的指令地址为0。具体代码如下:

    module PC(    
        input clk,    
        input [15:0] PCin,    
        input PCWre,    
        input Reset,    
        output reg [15:0] PCout    
    );    
          
        initial begin    
            PCout <= 0;   
        end  
            
        always@(posedge clk) begin    
            if(Reset == 0) begin    
                PCout <= 0;    
            end    
            else if(PCWre == 0) begin    
                PCout <= PCout;    
            end    
            else begin    
                PCout <= PCin;    
            end    
        end  
            
    endmodule    
    

    3.1.2 指令存储器InstructionMemory

    InstructionMemory为组合逻辑,内含128个字节的mem。mem中存放要执行的测试程序段的机器码,使用initial语句中的$readmemb伪指令从Instructions.txt文件中读取出来。当输入一个16位长的指令地址,输出对应地址中的16位机器指令。具体代码如下:

    module InsMemory(    
        input InsMemRW,    
        input [15:0] address,    
        output reg [15:0] DataOut    
    );    
    
        reg [7:0] mem [0:127];   
    
        initial begin    
            DataOut = 16'b1111000000000000;  
            $readmemb("Instructions.txt", mem);      
        end   
          
        always@(*) begin   
            DataOut[15:8] <= mem[address];    
            DataOut[7:0] <= mem[address+1];  
        end   
           
    endmodule    
    

    3.1.3 寄存器组RegisterFile

    RegisterFile中定义了一个含有8个寄存器的寄存器组,设计逻辑包括两部分:一方面,读寄存器组为组合逻辑,当输入一个3位长的寄存器编号时,输出对应寄存器中的数值。另一方面,写寄存器组是时序逻辑,在时钟下降沿到来时触发写入。具体代码如下:

    module RegFile(    
        input CLK,    
        input RST,    
        input RegWre,    
        input [2:0] ReadReg1,    
        input [2:0] ReadReg2,    
        input [2:0] WriteReg,    
        input [15:0] WriteData,    
        output [15:0] ReadData1,    
        output [15:0] ReadData2    
    );    
      
        reg [15:0] regFile[0:7];  
           
        integer i;    
          
        assign ReadData1 = regFile[ReadReg1];   
        assign ReadData2 = regFile[ReadReg2];  
            
        always @ (negedge CLK) begin    
            if (RST == 0) begin    
                for(i=1;i<8;i=i+1)    
                    regFile[i] <= 0;    
            end    
            else if(RegWre == 1 && WriteReg != 0) begin    
                regFile[WriteReg] <= WriteData;    
            end    
        end    
                
    endmodule    
    

    3.1.4 算术逻辑单元ALU

    ALU为组合逻辑,根据控制信号对两个操作数进行相应的运算,并输出结果。具体代码如下:

    module ALU(    
        input [2:0] ALUopcode,    
        input [15:0] rega,    
        input [15:0] regb,    
        output reg [15:0] result,    
        output zero,    
        output sign    
    );    
      
        assign zero = (result==0)?1:0;    
        assign sign = result[15];    
          
        always @( ALUopcode or rega or regb ) begin    
            case (ALUopcode)    
                3'b000 : result = rega + regb;    
                3'b001 : result = rega - regb;    
                3'b010 : result = regb << rega;    
                3'b011 : result = rega | regb;    
                3'b100 : result = rega & regb;    
                3'b101 : result = (rega < regb)?1:0;     
                3'b110 : begin    
                    if (rega<regb &&(( rega[15] == 0 && regb[15]==0) ||    
                    (rega[15] == 1 && regb[15]==1))) result = 1;    
                    else if (rega[15] == 0 && regb[15]==1) result = 0;    
                    else if ( rega[15] == 1 && regb[15]==0) result = 1;    
                    else result = 0;    
                end    
                3'b111 : result = regb;    
            endcase    
        end   
           
    endmodule    
    

    3.1.5 数据存储器DataMemory

    DataMemory中包含128个字节的ram,设计逻辑包括两部分:一方面,读存储器为组合逻辑,当输入一个16位长的数据地址时,输出对应地址中的16位数据。另一方面,写存储器是时序逻辑,在时钟下降沿到来时触发写入。具体代码如下:

    module DataMemory(    
        input clk,    
        input [15:0] address,    
        input RD,    
        input WR,    
        input [15:0] DataIn,    
        output [15:0] DataOut    
    );    
        
        reg [7:0] ram[0:127];  
            
        integer i;    
          
        initial begin;    
            for(i=0;i<128;i=i+1)    
                ram[i]<=0;    
        end    
           
        assign DataOut[7:0] = (RD == 0)? ram[address+1]:8'bz;    
        assign DataOut[15:8] = (RD == 0)? ram[address]:8'bz;    
       
        always@(negedge clk) begin    
            if(WR == 0) begin    
                if(address>=0 && address<128) begin    
                    ram[address] <= DataIn[15:8];    
                    ram[address+1] <= DataIn[7:0];     
                end    
            end    
        end  
            
    endmodule    
    

    3.1.6 控制单元ControlUnit

    ControlUnit为组合逻辑,将机器码中的操作码(opcode)转换为各个控制信号,从而控制不同的指令在不同的数据通路中传输。具体代码如下:

    module ControlUnit(    
        input [3:0] opcode,    
        input zero,    
        input sign,    
        output reg PCWre,    
        output reg ALUSrcA,    
        output reg ALUSrcB,    
        output reg DBDataSrc,    
        output reg RegWre,    
        output reg InsMemRW,    
        output reg RD,    
        output reg WR,    
        output reg RegDst,    
        output reg ExtSel,    
        output reg [1:0] PCSrc,    
        output reg [2:0] ALUOp    
    );    
      
        initial begin    
            RD = 1;    
            WR = 1;    
            RegWre = 0;   
            PCWre = 0;   
            InsMemRW = 1;    
        end   
           
        always@ (opcode) begin    
            case(opcode)     
                4'b0000:begin // add    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 0;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 1;    
                    ALUOp = 3'b000;    
                end    
                4'b0001:begin //addi    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 1;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 0;    
                    ExtSel = 1;    
                    ALUOp = 3'b000;    
                end    
                4'b0010:begin //sub    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 0;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 1;    
                    ALUOp = 3'b001;    
                end  
                4'b0011:begin // or    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 0;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 1;    
                    ALUOp = 3'b011;    
                end     
                4'b0100:begin // ori    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 1;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 0;    
                    ExtSel = 0;    
                    ALUOp = 3'b011;    
                end    
                4'b0101:begin //and    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 0;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 1;    
                    ALUOp = 3'b100;    
                end    
                4'b0110:begin //sll    
                    PCWre = 1;    
                    ALUSrcA = 1;    
                    ALUSrcB = 0;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 1;    
                    ALUOp = 3'b010;    
                end    
                4'b0111:begin //slt    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 0;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 1;    
                    ALUOp = 3'b110;    
                end  
                4'b1000:begin //mov    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 0;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 1;    
                    ExtSel = 1;    
                    ALUOp = 3'b111;    
                end  
                4'b1001:begin //movi    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 1;    
                    DBDataSrc = 0;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    RegDst = 0;    
                    ExtSel = 1;    
                    ALUOp = 3'b111;    
                end      
                4'b1010:begin //sw    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 1;    
                    RegWre = 0;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 0;    
                    ExtSel =1;    
                    ALUOp = 3'b000;    
                end    
                4'b1011:begin //lw    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 1;    
                    DBDataSrc = 1;    
                    RegWre = 1;    
                    InsMemRW = 1;    
                    RD = 0;    
                    WR = 1;    
                    RegDst = 0;    
                    ExtSel = 1;    
                    ALUOp = 3'b000;    
                end   
                4'b1100:begin //beq    
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 0;    
                    RegWre = 0;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    ExtSel = 1;    
                    ALUOp = 3'b001;    
                end    
                4'b1101:begin  //bgtz  
                    PCWre = 1;    
                    ALUSrcA = 0;    
                    ALUSrcB = 0;    
                    RegWre = 0;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    ExtSel = 1;    
                    ALUOp = 3'b001;    
                end    
                4'b1110:begin //j    
                    PCWre = 1;    
                    RegWre = 0;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                    ALUOp = 3'b010;    
                end    
                4'b1111:begin //halt    
                    PCWre = 0;    
                    RegWre = 0;    
                    InsMemRW = 1;    
                    RD = 1;    
                    WR = 1;    
                end    
                default:begin    
                    RD = 1;    
                    WR = 1;    
                    RegWre = 0;    
                    InsMemRW = 0;    
                end    
            endcase    
        end   
           
        always@(opcode or zero or sign) begin    
            if(opcode == 4'b1110) // j    
                PCSrc = 2'b10;    
            else if(opcode == 4'b1100) begin  
                if(zero == 1)  
                    PCSrc = 2'b01;  
                else  
                    PCSrc = 2'b00;  
            end  
            else if(opcode == 4'b1101) begin  
                if(zero == 0 && sign == 0)    
                    PCSrc = 2'b01;    
                else    
                    PCSrc = 2'b00;  
            end    
            else begin    
                PCSrc = 2'b00;    
            end    
        end   
           
    endmodule    
    

    3.2 顶层模块的实现

    在顶层模块中,通过实例化各个底层模块,并使用导线将它们按照数据通路图连接起来,构成单周期CPU的完整结构,具体原理图如下:

    第四章 单周期CPU的仿真验证

    根据上述实现的单周期CPU,利用指令系统中所有的指令设计一个测试程序,对该CPU进行如下仿真验证。

    4.1 测试程序

    使用该指令系统中所有指令,设计出如下一段具有实际意义的测试程序。该程序计算数字1到5的和以及按位或的和,并将两者中较大者存储到内存中地址为14的存储单元中,最后读出该存储单元中的值。具体指令序列如下:

    地址

    汇编程序

    机器指令代码(二进制)

    0x0000

    movi     $0,0

    10010000 00000000

    0x0002

    mov      $1,$0

    10000000 00001000

    0x0004

    and      $3,$1,$0

    01010010 00011000

    0x0006

    addi      $2,$0,6

    00010000 10000110

    0x0008

    ori      $3,$0,1

    01000000 11000001

    0x000A

    add      $5,$0,$3

    00000000 11101000

    0x000C

    or       $6,$1,$3

    00110010 11110000

    0x000E

    mov      $0,$5

    10000001 01000000

    0x0010

    mov      $1,$6

    10000001 10001000

    0x0012

    addi      $4,$3,1

    00010111 00000001

    0x0014

    mov      $3,$4

    10000001 00011000

    0x0016

    beq      $3,$2,1

    11000110 10000001

    0x0018

    j         5

    11100000 00000101

    0x001A

    slt      $5,$0,$1

    01110000 01101000

    0x001C

    movi     $4,4

    10010001 00000100

    0x001E

    sub     $6,$2,$4

    00100101 00110000

    0x0020

    sll       $7,$6,1

    01100001 10111001

    0x0022

    bgtz      $5,2

    11011010 00000010

    0x0024

    sw      $0,10($7)

    10101110 00001010

    0x0026

    j         21

    11100000 00010101

    0x0028

    sw      $1,10($7)

    10101110 01001010

    0x002A

    lw       $6,10($7)

    10111111 10001010

    0x002C

    halt

    11110000 00000000

    4.2 波形仿真

    在仿真过程中,设置如下输入和输出,并显示如下寄存器中的内容:

    输入

    clk

    CPU时钟信号,周期为10ns

    reset

    CPU复位信号,0为复位

    输出

    nowIns

    当前指令

    nextPC

    下一条指令地址

    nowPC

    当前PC地址

    ALUSrcA

    ALU操作数A

    ALUSrcB

    ALU操作数

    ALUResult

    ALU运算结果

    finalData

    写回的结果

    寄存器

    RegFile[0]-RegFile[7]

    寄存器组

    DataMemory[10]-DataMemory[15]

    数据存储器

    仿真结果如下:

    (1) movi $0,0至ori $3,$0,1

    (2) add $5,$0,$3至j 5 (第一轮循环)

    (3) add $5,$0,$3至j 5 (第二轮循环)

    (4) add $5,$0,$3至j 5 (第三轮循环)

    (5) add $5,$0,$3至j 5 (第四轮循环)

    (6) add $5,$0,$3至beq $3,$2,1 (第五轮循环)

    (7) slt $5,$0,$1至sll $7,$6,1

    (8) bgtz $5,2至halt

     

    展开全文
  • 基于FPGA的单周期CPU设计与实现.pdf

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 132,313
精华内容 52,925
关键字:

单周期cpu设计