verilog单条指令RISC-V处理器(ori的实现)

risc-v有六种基本指令格式

  1. R-type
    寄存机-寄存器操作
  2. I-type
    短立即数和访存load操作
  3. S-type
    访存store操作
  4. B-type
    条件跳转操作
  5. U-type
    长立即数操作
  6. J-type
    无条件跳转
  7. opcode(操作码):指令的基本操作,这个缩写是它惯用名称。
  8. rd: 目的操作寄存器,用来存放操作结果。
  9. funct3: 一个另外的操作码字段。
  10. rs1: 第一个源操作数寄存器。
  11. rs2: 第二个源操作数寄存器。
  12. funct7: 一个另外的操作码字段。
  13. imm:立即数
  14. watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2tvbmdoaGhoaA==,size_16,color_FFFFFF,t_70

 

本文实现ori(立即数或运算)的指令,其属于I型指令:

(1)取指阶段寄存器(无论几条指令该阶段均相同)

module pc_reg(//取指阶段模块
    input              clk,//时钟脉冲
    input              rst_n,//复位信号
    output reg [31:0]  pc,//指令指针
    output reg         ce  //使能端
    );

//posedge表示上升沿有效,negedge表示下降沿有效
//当复位信号有效时,使能端ce=0,pc=0;
//否则使能ce=1,pc每个时钟周期加4
always @ (posedge clk or negedge rst_n) begin
    if(~rst_n)              
        ce <= 1'b0;        //将宽度为1的二进制零赋值给ce,即rst=0->ce=0,否则ce=1
    else
        ce <= 1'b1;      
end

always @ (posedge clk) begin  //有时钟脉冲来且没有复位信号(ce=1)时pc=pc+4,否则pc=0
    if(~ce)
        pc <= 32'b0;
    else
        pc <= pc + 4'h4;
end
endmodule

取指和译码之间的流水寄存器(无论几条指令该阶段均相同)\

他将取指阶段得到的指令暂存起来等到下一个时钟周期将取指阶段的结果送入译码阶段



module if_id(//取指和译码之间的寄存器模块
    input              clk,
    input              rst_n,
    input [31:0]       if_pc,       //取指阶段的pc
    input [31:0]       if_inst,       //
    output reg [31:0]  id_pc,      //译码阶段的pc
    output reg [31:0]  id_inst
    );
    
always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin               //复位信号有效时将取指阶段结果传给译码阶段           
        id_pc <= 32'b0;
        id_inst <= 32'b0;
    end
    else begin 
        id_pc <= if_pc;
        id_inst <= if_inst;
    end
end
endmodule

(2)译码阶段的包含regfile.v文件,id.v文件,id_ex.v文件

创建一个有32个32位的寄存器组,包含一个写端口,两个读端口

regfile.v文件:



module regfile(
    input              rst_n,
    input              clk,
 //写端口   
    input [4:0]        waddr,
    input [31:0]       wdata,
    input              we,
 //读端口1   
    input [4:0]        raddr1,
    input              re1,
    output reg [31:0]  rdata1,
  //读端口2  
    input [4:0]        raddr2,
    input              re2,
    output reg [31:0]  rdata2
    );
    
reg [31:0] mem_r [0:31];

always @ (posedge clk or negedge rst_n) begin      //写入寄存器
    if(rst_n) begin
        if((waddr != 5'b0) && (we))
            mem_r[waddr] <= wdata;
    end
end

always @ (*) begin                                 //从端口1读出数据
    if(~rst_n)
        rdata1 <= 32'b0;
    else if(raddr1 == 5'b0)
        rdata1 <= 32'b0;
    else if((raddr1 == waddr) && re1 && we)        //如果写入的地址与要读出的地址相同
        rdata1 <= wdata;                           //则直接将写入数据读出
    else if(re1)
        rdata1 <= mem_r[raddr1];
    else
        rdata1 <= 32'b0;
end

always @ (*) begin                                 //从端口2读出数据
    if(~rst_n)
        rdata2 <= 32'b0;
    else if(raddr2 == 5'b0)
        rdata2 <= 32'b0;
    else if((raddr2 == waddr) && re2 && we)
        rdata2 <= wdata;
    else if(re2)
        rdata2 <= mem_r[raddr2];
    else
        rdata2 <= 32'b0;
end

endmodule

id.v文件:(不同指令类型有所区别)

//将指令中的操作类型

module id(//译码阶段中信息转发

        input             rst_n,
        input [31:0]      pc_i,
        input [31:0]      inst_i,
        //读取register 
        input [31:0]      reg1_data_i,
        input [31:0]      reg2_data_i,
        //输出到regfile
        output reg        reg1_read_o,
        output reg        reg2_read_o,
        output reg [4:0]  reg1_addr_o,
        output reg [4:0]  reg2_addr_o,
        //送出到执行模块
        output reg [6:0]  aluop_o,
        output reg [2:0]  alusel_o,
        output reg [31:0] reg1_o,
        output reg [31:0] reg2_o,
        output reg [4:0]  wd_o,
        output reg        wreg_o,
        
//解决流水线冲突
        input             ex_wreg_i,
        input [31:0]      ex_wdata_i,
        input [4:0]       ex_wd_i,

        input             mem_wreg_i,
        input [31:0]      mem_wdata_i,
        input [4:0]       mem_wd_i
    );

wire [6:0] op  = inst_i[6:0];            //运算类型
wire [2:0] op1 = inst_i[14:12];          //具体运算方式

reg [31:0] imm;

always @ (*) begin
    if(~rst_n) begin
        aluop_o <= 0;
        alusel_o <= 0;
        wd_o <= 0;
        wreg_o <= 0;
        reg1_read_o <= 0;
        reg2_read_o <= 0;
        reg1_addr_o <= 0;
        reg2_addr_o <= 0;
        imm <= 0;
    end
    else begin
        aluop_o <= 0;
        alusel_o <= 0;
        wd_o <= inst_i[11:7];                     //目的寄存器地址
        wreg_o <= 1'b1;                           //目的寄存器使能
        reg1_read_o <= 0;
        reg2_read_o <= 0;
        reg1_addr_o <= inst_i[19:15];
        reg2_addr_o <= inst_i[24:20];
        imm <= 0;
        
        case(op)                                    //op有七位,指明操作码
            7'b0010011: begin                       //立即数操作:(0010011)为指令的[6:0]位
                case (op1)                        //(110)为ori的[14:12]位

                    3'b110: begin                           //ori  这里不同指令条数写法不同
                        wreg_o <= 1'b1;                     //是否写目的寄存器
                        aluop_o <= op;                      //运算类型
                        alusel_o <= op1;                    //运算方式
                        reg1_read_o <= 1'b1;                //是否读操作数1
                        reg2_read_o <= 1'b0;                //是否读操作数2
                        imm <= {{20{inst_i[31]}} , inst_i[31:20]};  //立即数扩展
                    end
                    default: begin
                    end
                endcase
            end
            default: begin
            end
        endcase
    end
end

always @ (*) begin
    if(~rst_n)
        reg1_o <= 0;
    else if((reg1_read_o) && (ex_wreg_i) && (ex_wd_i == reg1_addr_o))     //如果执行阶段的数据为译码阶段所要
                                                                          //读取的数据则直接将其送回译码阶段
        reg1_o <= ex_wdata_i;
    else if((reg1_read_o) && (mem_wreg_i) && (mem_wd_i == reg1_addr_o))   //如果访存阶段的数据为译码阶段所要
                                                                          //读取的数据则直接将其送回译码阶段
        reg1_o <= mem_wdata_i;
        
    else if(reg1_read_o)
        reg1_o <= reg1_data_i;
    else if(~reg1_read_o)
        reg1_o <= imm;    
    else
        reg1_o <= 0;
end

always @ (*) begin
    if(~rst_n)
        reg2_o <= 0;
        
    else if((reg2_read_o) && (ex_wreg_i) && (ex_wd_i == reg2_addr_o))
        reg2_o <= ex_wdata_i;
    else if((reg2_read_o) && (mem_wreg_i) && (mem_wd_i == reg2_addr_o))
        reg2_o <= mem_wdata_i;
        
    else if(reg2_read_o)
        reg2_o <= reg2_data_i;
    else if(~reg2_read_o)
        reg2_o <= imm;
    else
        reg2_o <= 0;    
end

id_ex.v文件(相同)

module id_ex(
        input                clk,
        input                rst_n,
        
        input [6:0]          id_aluop,
        input [2:0]          id_alusel,
        input [31:0]         id_reg1,
        input [31:0]         id_reg2,
        input [4:0]          id_wd,
        input                id_wreg,               //目的寄存器使能
        
        output reg [6:0]     ex_aluop,
        output reg [2:0]     ex_alusel,
        output reg [31:0]    ex_reg1,
        output reg [31:0]    ex_reg2,
        output reg [4:0]     ex_wd,
        output reg           ex_wreg
    );
    
always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        ex_aluop <= 0;
        ex_alusel <= 0;
        ex_reg1 <= 0;
        ex_reg2 <= 0;
        ex_wd <= 0;
        ex_wreg <= 0;
    end
    else begin
        ex_aluop <= id_aluop;
        ex_alusel <= id_alusel;
        ex_reg1 <= id_reg1;
        ex_reg2 <= id_reg2;
        ex_wd <= id_wd;
        ex_wreg <= id_wreg;   
    end
end
endmodule

(3)执行阶段:包括ex.v和ex_mem

ex.v文件:(不同指令条数及类型的写法不同)

module ex(
    input                rst_n,
    input [6:0]          aluop_i,
    input [2:0]          alusel_i,
    input [31:0]         reg1_i,
    input [31:0]         reg2_i,
    input [4:0]          wd_i,
    input                wreg_i,
    
    output reg [4:0]     wd_o,
    output reg           wreg_o,
    output reg [31:0]    wdata_o
    );

reg [31:0] result;

always @ (*) begin         //通过译码阶段发送过来的信息确定具体的运算操作
    if(~rst_n)
        result <= 0;
    else begin
        case(aluop_i)
            7'b0010011: begin
                case(alusel_i)
                    3'b110: result <= reg1_i | reg2_i;   //执行相应的运算
                    default: begin
                        result <= 0;
                    end
                endcase
            end
        endcase
    end
end

always @ (*) begin           //将运算结果发送到下一阶段
    wd_o <= wd_i;
    wreg_o <= wreg_i;
    case(aluop_i)
        7'b0010011: begin
            case(alusel_i)
                3'b110: wdata_o <= result;
                default: begin
                end
            endcase
        end
        default: begin
            wdata_o <= 0;
        end
    endcase
end
endmodule

ex_mem文件:(相同)

module ex_mem(
        input                clk,
        input                rst_n,
        
        input [4:0]          ex_wd,
        input                ex_wreg,
        input [31:0]         ex_wdata,
        
        output reg [4:0]     mem_wd,
        output reg           mem_wreg,
        output reg [31:0]    mem_wdata
    );
    
always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        mem_wd <= 0;
        mem_wreg <= 0;
        mem_wdata <= 0;
    end
    else begin
        mem_wd <= ex_wd;
        mem_wreg <= ex_wreg;
        mem_wdata <= ex_wdata;
    end
end
endmodule

(4)访存模块的设计:

mem.v(相同)

module mem(
    input                 rst_n,
    
    input [4:0]           wd_i,
    input                 wreg_i,
    input [31:0]          wdata_i,
    
    output reg [4:0]      wd_o,
    output reg            wreg_o,
    output reg [31:0]     wdata_o
    );
    
always @ (*) begin
    if(~rst_n) begin
        wd_o <= 0;
        wreg_o <= 0;
        wdata_o <= 0;
    end
    else begin
        wd_o <= wd_i;
        wreg_o <= wreg_i;
        wdata_o <= wdata_i;
    end
end
endmodule

mem_wb.v(相同)

module mem_wb(
    input                  rst_n,
    input                  clk,
    
    input [4:0]            mem_wd,
    input                  mem_wreg,
    input [31:0]           mem_wdata,

    output reg [4:0]       wb_wd,
    output reg             wb_wreg,
    output reg [31:0]      wb_wdata
    );

always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        wb_wd <= 0;
        wb_wreg <= 0;
        wb_wdata <= 0;
    end
    else begin
        wb_wd <= mem_wd;
        wb_wreg <= mem_wreg;
        wb_wdata <= mem_wdata;
    end
end
endmodule

(5)回写模块,流水线最后 将上述指令的结果写回寄存器,该部分功能在发生在regfile模块

(6)顶层模块设计:(相同)

// Additional Comments:
// 
//


module riscv(
        input                 rst_n,
        input                 clk,
        
        input [31:0]          rom_data_i,
        output [31:0]         rom_addr_o,
        output                rom_ce_o
    );

wire [31:0]             pc;
wire [31:0]             id_pc_i;
wire [31:0]             id_inst_i;

wire [6:0]              id_aluop_o;
wire [2:0]              id_alusel_o;
wire [31:0]             id_reg1_o;
wire [31:0]             id_reg2_o;
wire                    id_wreg_o;
wire [4:0]              id_wd_o;

wire [6:0]              ex_aluop_i;
wire [2:0]              ex_alusel_i;
wire [31:0]             ex_reg1_i;
wire [31:0]             ex_reg2_i;
wire                    ex_wreg_i;
wire [4:0]              ex_wd_i;
      //......


wire                    ex_wreg_o;
wire [4:0]              ex_wd_o;
wire [31:0]             ex_wdata_o;

wire                    mem_wreg_i;
wire [4:0]              mem_wd_i;
wire [31:0]             mem_wdata_i;

wire                    mem_wreg_o;
wire [4:0]              mem_wd_o;
wire [31:0]             mem_wdata_o;

wire                    wb_wreg_i;
wire [4:0]              wb_wd_i;
wire [31:0]             wb_wdata_i;

wire                    reg1_read;
wire                    reg2_read;
wire [31:0]             reg1_data;
wire [31:0]             reg2_data;
wire [4:0]              reg1_addr;
wire [4:0]              reg2_addr;

                        pc_reg pc_reg0(
                            .clk(clk),
                            .rst_n(rst_n),
                            .pc(pc),
                            .ce(rom_ce_o)
                        );
                        assign rom_addr_o = pc;
                        
                        if_id if_id0(
                            .clk(clk),
                            .rst_n(rst_n),
                            .if_pc(pc),
                            .if_inst(rom_data_i),
                            .id_pc(id_pc_i),
                            .id_inst(id_inst_i)
                        );
                        regfile regfile0(
                            .clk(clk),
                            .rst_n(rst_n),
                            .we(wb_wreg_i),
                            .waddr(wb_wd_i),
                            .wdata(wb_wdata_i),
                            .re1(reg1_read),
                            .raddr1(reg1_addr),
                            .rdata1(reg1_data),
                            .re2(reg2_read),
                            .raddr2(reg2_addr),
                            .rdata2(reg2_data)
                        );
id id0(
    .rst_n(rst_n),
    .pc_i(id_pc_i),
    .inst_i(id_inst_i),
    
    .reg1_data_i(reg1_data),
    .reg2_data_i(reg2_data),
    
    .reg1_read_o(reg1_read),
    .reg2_read_o(reg2_read),
    .reg1_addr_o(reg1_addr),
    .reg2_addr_o(reg2_addr),
    
    .aluop_o(id_aluop_o),
    .alusel_o(id_alusel_o),
    .reg1_o(id_reg1_o),
    .reg2_o(id_reg2_o),
    .wd_o(id_wd_o),
    .wreg_o(id_wreg_o),
    
    .ex_wreg_i(ex_wreg_o),
    .ex_wdata_i(ex_wdata_o),
    .ex_wd_i(ex_wd_o),

    .mem_wreg_i(mem_wreg_o),
    .mem_wdata_i(mem_wdata_o),
    .mem_wd_i(mem_wd_o)
);



id_ex id_ex0(
    .clk(clk),
    .rst_n(rst_n),
    
    .id_aluop(id_aluop_o),
    .id_alusel(id_alusel_o),
    .id_reg1(id_reg1_o),
    .id_reg2(id_reg2_o),
    .id_wd(id_wd_o),
    .id_wreg(id_wreg_o),
    
    .ex_aluop(ex_aluop_i),
    .ex_alusel(ex_alusel_i),
    .ex_reg1(ex_reg1_i),
    .ex_reg2(ex_reg2_i),
    .ex_wd(ex_wd_i),
    .ex_wreg(ex_wreg_i) 
);

ex ex0(
    .rst_n(rst_n),
    
    .aluop_i(ex_aluop_i),
    .alusel_i(ex_alusel_i),
    .reg1_i(ex_reg1_i),
    .reg2_i(ex_reg2_i),
    .wd_i(ex_wd_i),
    .wreg_i(ex_wreg_i),
    
    .wd_o(ex_wd_o),
    .wreg_o(ex_wreg_o),
    .wdata_o(ex_wdata_o)
);

ex_mem ex_mem0(
    .clk(clk),
    .rst_n(rst_n),
    
    .ex_wd(ex_wd_o),
    .ex_wreg(ex_wreg_o),
    .ex_wdata(ex_wdata_o),
    
    .mem_wd(mem_wd_i),
    .mem_wreg(mem_wreg_i),
    .mem_wdata(mem_wdata_i)
);

mem mem0(
    .rst_n(rst_n),
    
    .wd_i(mem_wd_i),
    .wreg_i(mem_wreg_i),
    .wdata_i(mem_wdata_i),
    
    .wd_o(mem_wd_o),
    .wreg_o(mem_wreg_o),
    .wdata_o(mem_wdata_o)
);

mem_wb mem_wb0(
    .clk(clk),
    .rst_n(rst_n),
    
    .mem_wd(mem_wd_o),
    .mem_wreg(mem_wreg_o),
    .mem_wdata(mem_wdata_o),
    
    .wb_wd(wb_wd_i),
    .wb_wreg(wb_wreg_i),
    .wb_wdata(wb_wdata_i)
);
endmodule

(7)指令存储器rom的功能:本处理器采用哈佛结构(指令与数据单独存放)想让处理器进行验证功能必须添加对应的指令存储器,CPU从其中取出指令


module inst_rom(
    input              ce,
    input [31:0]       addr,
    output reg [31:0]  inst
    );
    
reg [31:0] inst_mem [0:255];

initial $readmemh ("inst_rom.data", inst_mem);

always @ (*) begin
    if(~ce)
        inst <= 0;
    else
        inst <= inst_mem[addr[9:2]];
end
endmodule

(8)最小系统的设计(相同)将指令存储器ROM与之前所设计的CPU进行连接构成一个CPU的最小系统

module RISCVtop(
        input clk,
        input rst_n
    );
wire [31:0] inst_addr;
wire [31:0] inst;
wire        rom_ce;

riscv riscv0(
    .clk(clk),
    .rst_n(rst_n),
    .rom_addr_o(inst_addr),
    .rom_data_i(inst),
    .rom_ce_o(rom_ce)
);

inst_rom inst_rom0(
    .ce(rom_ce),
    .addr(inst_addr),
    .inst(inst)
);
endmodule

 

(9)在记事本等软件中编写测试文件代码

在text.v文件(相同)中编写测试文件

module test(
    );
reg clk;
reg rst_n;

initial begin
    clk = 0;
    rst_n = 0;
    #100;
    rst_n = 1'b1;
end
always #20 clk = ~clk;

RISCVtop RISCVtop0(
    .clk(clk),
    .rst_n(rst_n)
);

endmodule

未添加指令 前的仿真结果:

03a0b1f6230f4f4a85e9b85d0cccf512.png

 添加data文件(指令):

 

5deb1bd5882a44718492e9ea005eb482.png6daaf1a3ea4b4d30b2d339907f3c1d39.png

 

仿真结果:

 

 a64fad5e9c2e43f387774b2cc8911f7c.png

 遇到仿真出错的问题可能是由于多次仿真导致文件冲突,删除目录下sim_以后内的说有文件即可

 

 


基础知识:

一:数字表达方式有以下三种

(1)<位宽><进制><数字>,这是比较全面的描述方式

(2)<进制数字>在这种描述方式中,数字的位宽采用歌认位觉

(3)<数字>在这种描述方式中,采用默认进制:十进制

对于<进制>的表示:二进制整数用b或 B 表示,十进制整数用d或D表示,十六进制整h或H表示,八进制整数用o或O表示。具体用法示例
8'b10101111//位宽为8的数的二进制表示,'b表示二进制

4'ba//位宽为4的数的十六进制,'h表示十六进制

(2)5级流水线由取指、译码、执行、访存、回写这 5 个部分组成。5 级流水中的各个阶段的
具体工作如下:

取指:读取存储器中的指令,同时确定下一条指令的地址。

(2)译码:对取出的指令进行译码,获得指令中要读取的寄存器值,操作类型或者立即数等信息,针对立即数进行有符号或者无符号扩展等操作。如果包含转移指令且满足转称件,此阶段会给出目标地址。

(3)执行:将译码阶段发送出来的数据根据操作类型进行运算并发送出运算结果。如果执行的指令为Load(读)或 Store(写)指令,执行阶段也会计算读写的地址

(4)访存:如果有 Load(读)或 Store(写)指令,将会在此阶段访问存储器,否则此阶段可以忽略,执行阶段的数据将会直接发送到回写阶段。此阶段同时可以判断是否有异常需要处理如果有异常则进行异常处理。
(5)回写:将不写人存储器的运算结果写回相应的目的寄存器

 

三:对于多条指令的实现不需修改大多数模块,只要修改译码和执行两个模块即可。

四:imm <= {{20{inst_i[31]}} , inst_i[31:20]};  //立即数扩展:

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半截詩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值