Rong晔大佬教程学习(2):取指

发布时间 2023-12-13 16:22:33作者: 郭若晨是我的rbq

1.rvseed_defines.v(定义了一些参数,没有实际意义)

  该文件定义了一些基本参数,在后续的代码中都会调用该文件

// simulation clock period
`define SIM_PERIOD 20 // 20ns -> 50MHz 

// processor 
`define CPU_WIDTH 32 // 32位的CPU

// instruction memory
`define INST_MEM_ADDR_DEPTH 1024
`define INST_MEM_ADDR_WIDTH 10 // 2^10 = 1024

  因为设计的是32位的CPU,所以CPU的深度CPU_WIDTH为32。

 

2.inst_mem.v(指令存储器)

`include "rvseed_defines.v"
module inst_mem (
    input      [`CPU_WIDTH-1:0] curr_pc, // current pc addr,当前的pc值
    output reg [`CPU_WIDTH-1:0] inst     // instruction,输出的指令
);

reg [`CPU_WIDTH-1:0] inst_mem_f [0:`INST_MEM_ADDR_DEPTH-1]; //存储器的位宽和处理器的位宽一致,深度设置为1024,因此地址位宽应该为10

//根据当前的pc值取出存储器对应地址的数据
always @(*) begin
    inst = inst_mem_f[curr_pc[`INST_MEM_ADDR_WIDTH+2-1:2]]; 
end
endmodule

  模块的输入为curr_pc,即当前的pc值,输出为inst,是输出的指令,两个量都是32位的;

  定义了一个1024*32的一个存储器inst_mem,因为这里设计一个处理器,所以直接定义即可,这样会消耗逻辑资源,实际情况是使用现存的存储资源

  inst从inst_mem中取指令,关于这行代码,我的理解是:

/*
这里为什么是INST_MEM_ADDR_WIDTH+2-1这样写,是因为比如一开始curr_pc的值是32个0,取他的[11:2]位,
这样就是相当于inst = inst_mem_f[0],正好取到第一行的32位的指令(inst_mem_f是一个1024*32的列表),
因此,如果是要取到第二个指令,那么就应该让curr_pc+4,也就是变成0000....010,这样他的[11:2]就成了000..01
正好取到了inst_mem_f[1],也就是下一条指令,即第二个指令
*/

/*
+2是因为指令输出只需要11:2位,后两位是数据存储所用到的(这里up主没有说,我是看评论区的)
*/

 

3.pc_reg.v(程序计数器)

//程序计数器,告诉处理器现在执行哪条指令
`include "rvseed_defines.v"

module pc_reg (
    input                       clk,     // system clock
    input                       rst_n,   // active low reset
    output reg                  ena,     // system enable
    input      [`CPU_WIDTH-1:0] next_pc, // next pc addr
    output reg [`CPU_WIDTH-1:0] curr_pc  // current pc addr
);

//控制处理器的运行和暂停
always @ (posedge clk or negedge rst_n) begin
    if(~rst_n)
        ena <= 1'b0;
    else
        ena <= 1'b1;      
end

always @ (posedge clk or negedge rst_n) begin
    if(~rst_n)
        curr_pc <= `CPU_WIDTH'b0;   //复位,则回到第一条指令的位置
    else
        curr_pc <= next_pc;     //将当前的pc值更新为外部提供的next_pc,next_pc由mux模块提供
end    

endmodule

  ena是使能信号,决定处理器的运行和暂停(这在下一个mux_pc模块中体现)

  第二个模块是pc值的更新,复位则回到第一条指令,否则将当前的pc值更新为next_pc。

 

4.mux_pc.v(多路选择器)

`include "rvseed_defines.v"

module mux_pc (
    input                          ena,
    input                          branch,  // branch type 
    input                          zero,    // alu result is zero
    input                          jump,    // jump type 
    input      [`CPU_WIDTH-1:0]    imm,     // immediate  
    input      [`CPU_WIDTH-1:0]    curr_pc, // current pc addr
    output reg [`CPU_WIDTH-1:0]    next_pc  // next pc addr
    );

always @(*) begin
    if (~ena) //复位,使能为0无效,则pc值不做更新,相当于是在暂停
        next_pc = curr_pc;
    else if (branch && ~zero) // bne:分支跳转
        next_pc = curr_pc + imm;    //将当前的指令值和指令中的立即数相加,作为一个新的指令值的
    else if (jump) // jal :跳转和链接
        next_pc = curr_pc + imm;    //将当前的指令值和指令中的立即数相加,作为一个新的指令值的
    else    //标准情况:pc值不做跳转也不做保留,则将当前的pc值加4,作为下一个pc,相当于读取下一条指令,为什么加4看inst-mem中有解释的
        next_pc = curr_pc + `CPU_WIDTH'h4;   
end
endmodule

  因为下一条指令next_pc会受到不同因素的影响,因此由一个多路选择器进行裁决。