在第一节的设计结构图中,我们可以看到,ctrl.v模块译码后,就可以得到我们实际运算所需要的数据,有两种:1.寄存器值,通过译码后得到的地址在寄存器堆中读出;2.立即数值,在译码后进行扩展得到的完整的立即数值。
首先来看寄存器堆,一共有32个寄存器:
接口名称表示每个寄存器的功能,本章暂不涉及。特别的,x0寄存器始终为0(只读存储器,这在之后的代码中可以看出)
1.reg_file.v(寄存器堆)
`include "rvseed_defines.v" //主要功能是定义了一个32*32的寄存器堆,实现了寄存器堆的读和写逻辑 module reg_file ( input clk, input rst_n, input reg_wen, // register write enable input [`REG_ADDR_WIDTH-1:0] reg_waddr, // register write address input [`CPU_WIDTH-1:0] reg_wdata, // register write data input [`REG_ADDR_WIDTH-1:0] reg1_raddr, // register 1 read address input [`REG_ADDR_WIDTH-1:0] reg2_raddr, // register 2 read address output reg [`CPU_WIDTH-1:0] reg1_rdata, // register 1 read data output reg [`CPU_WIDTH-1:0] reg2_rdata // register 2 read data ); reg [`CPU_WIDTH-1:0] reg_f [0:`REG_DATA_DEPTH-1]; //深度固定为32,因为一共有32个寄存器,每一个寄存器为32位宽 // register write,寄存器写逻辑 always @(posedge clk or negedge rst_n) begin if (rst_n && reg_wen && (reg_waddr != `REG_ADDR_WIDTH'b0)) // x0 read only,不能是复位状态,写使能有效,写地址不能为0(反向说明x0寄存器只读) reg_f[reg_waddr] <= reg_wdata; //将数据写入寄存器 end // register 1 read,寄存器读逻辑 always @(*) begin if(reg1_raddr == `REG_ADDR_WIDTH'b0) reg1_rdata = `CPU_WIDTH'b0; else reg1_rdata = reg_f[reg1_raddr]; //只要给一个读地址,那么就会把数据读出,没有“开关”控制 end // register 2 read,寄存器读逻辑 always @(*) begin if(reg2_raddr == `REG_ADDR_WIDTH'b0) reg2_rdata = `CPU_WIDTH'b0; else reg2_rdata = reg_f[reg2_raddr]; end endmodule
相应的解释说明以在注释中给出,这部分主要就是定义了一个寄存器堆,然后通过输入地址信号和控制信号可以实现寄存器值的读出,特别注意的是,输入的控制信号和地址信号均由上一章ctrl.v模块给出,也就是译码的结果。
立即数扩展模块主要实现立即数的补全(将立即数扩展为32位),这里我们直接上代码:
2.imm_gen.v(立即数扩展模块)
`include "rvseed_defines.v" //立即数扩展模块 module imm_gen ( input [`CPU_WIDTH-1:0] inst, // instruction input,输入为我们的指令(有一个报错是inst的最低7位没有用到,那个是opcode,和这个程序没有关系) input [`IMM_GEN_OP_WIDTH-1:0] imm_gen_op, // immediate extend opcode,输入立即数扩展操作码,决定立即数扩展为什么样子(由上一章的ctrl.v模块提供) output reg [`CPU_WIDTH-1:0] imm // immediate ); //扩展思路:将指令中的立即数摆在正确的位置上,拼接后进行高位符号位扩展,低位0扩展 always @(*) begin imm = `CPU_WIDTH'b0; case (imm_gen_op) `IMM_GEN_I: imm = {{20{inst[31]}},inst[31:20]}; `IMM_GEN_S: imm = {{20{inst[31]}},inst[31:25],inst[11:7]}; `IMM_GEN_B: imm = {{20{inst[31]}},inst[7],inst[30:25],inst[11:8], 1'b0}; `IMM_GEN_J: imm = {{12{inst[31]}},inst[19:12],inst[20],inst[30:21], 1'b0}; `IMM_GEN_U: imm = {inst[31:12],12'b0}; endcase end endmodule
我们以I型指令为例:I型指令的立即数在指令的31-20位,同时也是立即数的最低12位,因此在扩展中截取inst[31:20]作为立即数的低12位,高20位则将inst[31]复制20次,这也就是高位符号位扩展的意思。
至于低位0扩展,可以参考U型指令,U型指令的立即数在指令的高20位,同时也是立即数的高20位,所以低12位就全部补0即可。
P.S:本章的测试中up主定义了一个顶层模块rvseed.v,因为我没有做仿真所以我暂时不考虑了
标签:教程,20,imm,Rong,WIDTH,inst,寄存器,reg From: https://www.cnblogs.com/liwilling/p/17900609.html