In the RISC-V RV32I CPU core, 32 general purpose registers are required. These registers are a critical part of parsing riscv assembly instructions. Related reference articles:
RISC-V teaching plan
In each risc-v cpu of RV32I, there are 32 general-purpose registers. The X0 register is always ‘0’ when it is read. When writing to the x0 register, the cpu hardware can ignore it (do nothing). For the RV32E cpu, the 16 general-purpose registers x16-x31 can be omitted, and only the 16 registers x0-x15 are reserved. This design can reduce the size of the cpu, but it is not very general. Because even the cpu size of rv32I is not very big. XLEN can vary according to riscv design, for RV64, XLEN = 64; for RV32, XLEN = 32. Our current riscv cpu design is RV32I, so XLEN = 32.
module regfile_I( input sys_clk, // system clock input i_EXE_vld, // execute the enable signal input [ 4: 0 ] i_rs1_idx, // rs1 in assembly instructions input [ 4: 0 ] i_rs2_idx, // rs2 in assembly instructions output [ 31: 0 ] o_rs1_val, // rs1 in the assembly instruction, the value of the selected register among the corresponding 32 general-purpose registers output [ 31: 0 ] o_rs2_val, // rs2 in the assembly instruction, the value of the selected register among the corresponding 32 general-purpose registers output o_wb_rdy, // write-back ready, always equal to 1 input i_wb_wen, // write general register signal input [ 4: 0 ] i_wb_rd_idx, // rd in assembly instructions, input [ 31: 0 ] i_wb_val // rd in the assembly instruction, the value of the selected register among the corresponding 32 general-purpose registers ); wire[31:0] rf_r[31:1]; wire [ 31: 1 ] rf_wen; genvar i; generate for ( i = 1; i < 32; i = i + 1 ) begin : REG assign rf_wen[ i ] = i_EXE_vld & i_wb_wen & ( i_wb_rd_idx == i ) ; fii_dffl #( 32 ) rf_dffl ( rf_wen[ i ], i_wb_val, rf_r[ i ], sys_clk ); end endgenerate `ifdef sim assign o_rs1_val = ( i_rs1_idx == 0 ) ? 32'b0 : ( rf_r[ i_rs1_idx ] == 32'hxxxxxxxx ) ? 32'b0 : rf_r[ i_rs1_idx ]; assign o_rs2_val = ( i_rs2_idx == 0 ) ? 32'b0 : ( rf_r[ i_rs2_idx ] == 32'hxxxxxxxx ) ? 32'b0 : rf_r[ i_rs2_idx ]; `else assign o_rs1_val = ( i_rs1_idx == 0 ) ? 32'b0 : rf_r[ i_rs1_idx ]; assign o_rs2_val = ( i_rs2_idx == 0 ) ? 32'b0 : rf_r[ i_rs2_idx ]; `endif assign o_wb_rdy = 1'b1; endmodule
input [ 4: 0 ] i_rs1_idx , // rs1 in assembly instructions
input [ 4: 0 ] i_rs2_idx , // rs2 in assembly instructions
output [ 31: 0 ] o_rs1_val , // rs1 in the assembly instruction, the value of the selected register among the corresponding 32 general-purpose registers
output [ 31: 0 ] o_rs2_val , // rs2 in the assembly instruction, the value of the selected register among the corresponding 32 general-purpose registers
output o_wb_rdy, // write-back ready, always equal to 1
input i_wb_wen , // write general register signal
input [ 4: 0 ] i_wb_rd_idx ,// rd in assembly instructions,
input [ 31: 0 ] i_wb_val // rd in the assembly instruction, the value of the selected register among the corresponding 32 general-purpose registers
In the code snippet: write operation
genvar i;
generate
for ( i = 1; i < 32; i = i + 1 )
begin : REG
assign rf_wen[ i ] = i_EXE_vld & i_wb_wen & ( i_wb_rd_idx == i ) ;
fii_dffl #( 32 ) rf_dffl ( rf_wen[ i ], i_wb_val, rf_r[ i ], sys_clk );
end
endgenerate
fii_dffl file code:
module fii_dffl # (
parameter DW = 32
) (
input ld,
input [DW-1:0] din,
output reg [DW-1:0] q = 0,
input clk
);
always @(posedge clk )
if (ld)
q <= #1 din;
endmodule
In the code snippet: read operation
assign o_rs1_val = ( i_rs1_idx == 0 ) ? 32’b0 : rf_r[ i_rs1_idx ];
assign o_rs2_val = ( i_rs2_idx == 0 ) ? 32’b0 : rf_r[ i_rs2_idx ] ;
When reading x0 (whether rs1 or rs2 reads x0), it returns 0 directly; in other cases (1-31), it returns according to the value in the register.