Menu Close

RISC-V Register File Implementation and Decoder Module(4)Instrcution Decoder

In the design of RISC-V FPGA, the core core module of RISC-V includes the instruction decoding module. The main function of this module is to parse the assembly machine code input by ITCM according to the current PC. Including getting the corresponding assembly language command, the required rs1, rs2 registers, the rd register that needs to be stored, etc.

Related reference articles:

RISC-V teaching plan

 

The structure of the cpu core design framework in RISC-V:

fii_riscv_decorder

In the vivado project file:

The input of the instr_dec module is machine code in assembly language, and the output is:

RS1_IDX: general register rs1 ID

RS2_IDX: general register rs2 ID

RD_IDX: general register RD ID

instr_group: Include: OP_IMM, OP, LUI, AUIPC, JAL, JALR, BRANCH, LOAD, STORE, CSR, etc.

opimm_instr: Include: SRAI, SRLI, SLLI, ANDI, ORI, XORI, SLTIU, SLTI, ADDI

op_instr: includes: SRA, SUB, SRL, SLL, XOR, OR, AND, SLTU, SLT, ADD

branch_instr: includes: BGTU, BGT, BLTU, BLT, BNE, BEQ

shamt: data displacement width I_imm: I-type immediate data

S_imm: S-type immediate

J_imm: J-type immediate

U_imm: U-type immediate

B_imm: B-type immediate

load_instr: Include: LBU, LB, LHU, LH, LW

store_instr: includes: SB, SH, SW

csr_instr: includes: SCRRCI, CSRRSI, CSRRWI, SCRRC, CSRRS, CSRRW

csr_addr: select one of the 4095 csr registers CSR_imm: csr immediate  

 

decoder module code:

module instr_dec 
( 
input sys_clk,                  // system clock 
input [ 31: 0 ] i_instr,        // assembly language machine code read from ITCM 

output RV32I,                   // decoded output in RV32I format. Currently, we do not support RV32C, RV64 etc. 

output [ 4: 0 ] o_rs1_idx,      // one of the 32 general-purpose registers specified by rs1 in the assembly instruction 
output [ 4: 0 ] o_rs2_idx,      // one of the 32 general-purpose registers specified by rs2 in the assembly instruction 
output [ 4: 0 ] o_rd_idx,       // one of the 32 general-purpose registers specified by rd in the assembly instruction 

output [ 15: 0 ] o_instr_group, // instruction group, LOAD, STORE, CSR, BRANCH, OP, OP_imm, etc. Instruction grouping 

output [ 8: 0 ] o_opimm_instr,  // each instruction in the opimm instruction, including: SRAI, SRLI, SLLI, ANDI, ORI, XORI, SLTIU, SLTI, ADDI 
output [ 9: 0 ] o_op_instr,     // op Each of the instructions, including: SRA, SUB, SRL, SLL, XOR, OR, AND, SLTU, SLT, ADD
output [ 5: 0 ] o_branch_instr, // each instruction in the branch instruction, including: BGTU, BGT, BLTU, BLT, BNE, BEQ 
output [ 4: 0 ] o_load_instr,   // each instruction in the load instruction, including : LBU, LB, LHU, LH, LW 
output [ 2: 0 ] o_store_instr,  // each instruction in the store instruction, including: SB, SH, SW 
output [ 1: 0 ] o_fence_instr,  // each instruction in the fence instruction One instruction, including: fence, fence.i 

output [ 5: 0 ] o_csr_instr,    // Each instruction in CSR instructions, including: SCRRCI, CSRRSI, CSRRWI, SCRRC, CSRRS, CSRRW 
output [11: 0 ] o_csr_addr,     // Select one of the 4095 csr registers CSR_imm: csr immediate 

output [ 4: 0 ] o_shamt,        // data shift width I_imm: I-type immediate 
output [ 31: 0 ] o_I_imm,       // I-type immediate 
output [ 31: 0 ] o_S_imm,       // R-type immediate 
output [ 31: 0 ] o_B_imm,       // S-type immediate output
output [ 31: 0 ] o_J_imm,       // J-type immediate 
output [ 31: 0 ] o_U_imm,       // U-type immediate 
output [ 31: 0 ] o_csr_imm,     // CSR immediate 
//output [31:0] o_pc 
output o_mret,    // interrupt return 
output o_ecall,   // request execution environment by raising an environment call exception 
output o_ebreak,  // debug interrupt breakpoint 
output o_dret,    // debug interrupt return 
output o_wfi      // wait for interrupt 

); 

//== ===================================================== ============================ 
// riscv instruction basic decode OP DECODE 
wire [ 6: 0 ] opcode = i_instr[ 6: 0 ]; 
wire [ 2: 0 ] funct3 = i_instr[ 14: 12 ]; 
wire [ 6: 0 ] funct7 = i_instr[ 31: 25 ];

assign RV32I = ( opcode[ 1: 0 ] == 2'b11 ) && ( opcode[ 4: 2 ] != 3'b111 ); 

//register index decode 
assign o_rd_idx = i_instr[ 11: 7 ]; 
assign o_rs1_idx = i_instr [ 19: 15 ]; 
assign o_rs2_idx = i_instr[ 24: 20 ]; 
//================================== ================================================== 
// decode opcode [6:0] 
wire LUI    = ( opcode[ 6: 0 ] == 7'b011_0111 ); // TYPE U lui 
wire AUIPC  = ( opcode[ 6: 0 ] == 7'b001_0111 ); // TYPE U auipc 

wire JAL    = ( opcode[ 6: 0 ] == 7'b110_1111 ); // TYPE J jal 
wire JALR   = ( opcode[ 6: 0 ] == 7'b110_0111 ); // TYPE I jalr 

wire BRANCH = ( opcode[ 6: 0 ] == 7'b110_0011 ); // TYPE B 

wire LOAD   = ( opcode[ 6: 0 ] == 7'b000_0011 ); // TYPE I load
wire STORE  = ( opcode[ 6: 0 ] == 7'b010_0011 ); // YTPE S store 

wire OP_IMM = ( opcode[ 6: 0 ] == 7'b001_0011 ); // TYPE I operator 
wire OP     = ( opcode[ 6: 0 ] == 7'b011_0011 ); // TYPE R operator 

wire FENCE  = ( opcode[ 6: 0 ] == 7'b000_1111 ); // TYPE I fence 
wire CSR    = ( opcode[ 6: 0 ] == 7'b111_0011 ); // TYPE I csr 




assign o_instr_group = { FENCE, CSR, STORE, LOAD, BRANCH, JALR, JAL, AUIPC, LUI, OP, OP_IMM }; 

//========== ===================================================== ================= 
// funct3 decode 
wire funct3_0 = ( funct3 == 3'b000 ); 
wire funct3_1 = ( funct3 == 3'b001 ); 
wire funct3_2 = ( funct3 == 3'b010 ); 
wire funct3_3 = ( funct3 == 3'b011 );
wire funct3_4 = ( funct3 == 3'b100 ); 
wire funct3_5 = ( funct3 == 3'b101 ); 
wire funct3_6 = ( funct3 == 3'b110 ); 
wire funct3_7 = ( funct3 == 3'b111 ); 
// ===================================================== ============================ 
//funct7 decode 
wire funct7_0 = ( funct7 == 7'b0 ); 
wire funct7_20 = ( funct7 == 7'b010_0000 ); 
//========================================== ======================================= 
//imm & shamt decode 

assign o_I_imm = { { 20 { i_instr[ 31 ] } } , i_instr[ 31: 20 ] }; //addi/slti/sltiu/andi/ori/xori, lw/lh/lhu/lb/lbu, JALR, SLLI/SRLi/SRAI 

assign o_S_imm = { { 20{ i_instr[ 31 ] } }, i_instr[ 31: 25 ], i_instr[ 11: 7 ] }; //s-type instruction, provide imm or store instruction

assign o_B_imm = { { 19{ i_instr[ 31 ] } }, i_instr[ 31 ], i_instr[ 7 ], i_instr[ 30: 25 ], i_instr[ 11: 8 ], 1'b0 }; 

assign o_U_imm = { i_instr[ 31 : 12 ], 12'b0 }; 

assign o_J_imm = { { 11{ i_instr[ 31 ] } }, i_instr[ 31 ], i_instr[ 19: 12 ], i_instr[ 20 ], i_instr[ 30: 21 ], 1'b0 }; //for JAL, 

assign o_shamt = i_instr[ 24: 20 ]; 

assign o_csr_imm = {27'b0,i_instr[19:15]}; 

//================ ===================================================== ============== 
// instruction decode for I-type operator 7'b001_0011 
wire rv32i_addi  = OP_IMM & funct3_0; 
wire rv32i_slti  = OP_IMM & funct3_2; 
wire rv32i_sltiu = OP_IMM & funct3_3; 
wire rv32i_xori  = OP_IMM & funct3_4; 
wire rv32i_ori   = OP_IMM & funct3_6;
wire rv32i_andi  = OP_IMM & funct3_7; 

wire rv32i_slli  = OP_IMM & funct3_1 ; 
wire rv32i_srli  = OP_IMM & funct3_5 & funct7_0; 
wire rv32i_srai  = OP_IMM & funct3_5 & funct7_20; 

assign o_opimm_instr = { rv32i_srai, rv32i_srli, rv32i_slli, 
rv32i_andi, rv32i_ori, rv32i_xori, 
rv32i_sltiu, rv32i_slti, rv32i_addi }; 

//============================================== ===================================== 
//instruction decode for R-type 

wire rv32i_add  = OP & funct3_0 & funct7_0; 
wire rv32i_sub  = OP & funct3_0 & funct7_20; 

wire rv32i_sll  = OP & funct3_1 & funct7_0; 

wire rv32i_slt  = OP & funct3_2 & funct7_0; 
wire rv32i_sltu = OP & funct3_3 & funct7_0;
wire rv32i_xor  = OP & funct3_4 & funct7_0; 

wire rv32i_srl  = OP & funct3_5 & funct7_0; 
wire rv32i_sra  = OP & funct3_5 & funct7_20; 

wire rv32i_or   = OP & funct3_6 & funct7_0; 
wire rv32i_and  = OP & funct3_7 & funct7_0; 

assign o_op_instr = { rv32i_sra, rv32i_sub, rv32i_srl,
rv32i_sll, rv32i_xor, rv32i_or,
rv32i_and, rv32i_sltu, rv32i_slt,
rv32i_add }; //============================ ===================================================== = //instruction for conditional branch wire rv32i_beq = BRANCH & funct3_0; wire rv32i_bne = BRANCH & funct3_1; wire rv32i_blt = BRANCH & funct3_4; wire rv32i_bltu = BRANCH & funct3_6; wire rv32i_bgt = BRANCH & funct3_5; wire rv32i_bgtu = BRANCH & funct3_7; assign o_branch_instr = { rv32i_bgtu, rv32i_bgt, rv32i_bltu, rv32i_blt, rv32i_bne, rv32i_beq }; //========================== ===================================================== ===== // memory operation load/store wire rv32i_lw = LOAD & funct3_2; wire rv32i_lh = LOAD & funct3_1; wire rv32i_lhu = LOAD & funct3_5; wire rv32i_lb = LOAD & funct3_0; wire rv32i_lbu = LOAD & funct3_4; wire rv32i_sw = STORE & funct3_2; wire rv32i_sh = STORE & funct3_1; wire rv32i_sb = STORE & funct3_0; assign o_load_instr = { rv32i_lbu, rv32i_lb, rv32i_lhu, rv32i_lh, rv32i_lw }; assign o_store_instr = { rv32i_sb, rv2 //==================================================== ============================== //instruction for fence wire rv32i_fence = FENCE & funct3_0; wire rv32i_fence_i = FENCE & funct3_1; assign o_fence_instr = {rv32i_fence_i, rv32i_fence }; //========================================== ========================================= //instruction for csr wire rv32i_csrrw = CSR & funct3_1; wire rv32i_csrrs = CSR & funct3_2; wire rv32i_csrrc = CSR & funct3_3; wire rv32i_csrrwi = CSR & funct3_5; wire rv32i_csrrsi = CSR & funct3_6; wire rv32i_csrrci = CSR & funct3_7; assign o_csr_instr = { rv32i_csrrci, rv32i_csrrsi, rv32i_csrrwi, rv32i_csrrc, rv32i_csrrs , rv32i_csrrw}; assign o_csr_addr = i_instr[ 31: 20 ]; //========================================== ========================================== // System Instructions assign o_ecall = CSR & funct3_0 & (i_instr[31:20] == 12'b0000_0000_0000); assign o_ebreak = CSR & funct3_0 & (i_instr[31:20] == 12'b0000_0000_0001); assign o_mret = CSR & funct3_0 & (i_instr[31:20] == 12'b0011_0000_0010); assign o_dret = CSR & funct3_0 & (i_instr[31:20] == 12'b0111_1011_0010); assign o_wfi = CSR & funct3_0 & (i_instr[31:20] == 12'b0001_0000_0101); endmodule

 

1) Find the lowest 7 bits of the assembly instruction machine code and
get the opcode :

wire[ 6: 0 ] opcode = i_instr[ 6: 0 ]; 
assign RV32I = ( opcode[ 1: 0 ] == 2'b11 ) && ( opcode[ 4: 2 ] != 3'b111 );
if opcode is lowest 2 bits are 11, and the lowest 5 bits cannot be 111111,
then the current instruction bit is the RV32i instruction.

wire LUI            = ( opcode[ 6: 0 ] == 7’b011_0111 ); // TYPE U lui
wire AUIPC      = ( opcode[ 6: 0 ] == 7’b001_0111 ); // TYPE U auipc

wire JAL            = ( opcode[ 6: 0 ] == 7’b110_1111 ); // TYPE J jal
wire JALR        = ( opcode[ 6: 0 ] == 7’b110_0111 ); // TYPE I jalr

wire BRANCH = ( opcode[ 6: 0 ] == 7’b110_0011 ); // TYPE B

wire LOAD        = ( opcode[ 6: 0 ] == 7’b000_0011 ); // TYPE I load
wire STORE      = ( opcode[ 6: 0 ] == 7’b010_0011 ); // YTPE S store

wire OP_IMM = ( opcode[ 6: 0 ] == 7’b001_0011 ); // TYPE I operator
wire OP              = ( opcode[ 6: 0 ] == 7’b011_0011 ); // TYPE R operator

wire FENCE      = ( opcode[ 6: 0 ] == 7’b000_1111 ); // TYPE I fence
wire CSR            = ( opcode[ 6: 0 ] == 7’b111_0011 ); // TYPE I csr

If it is an RV32I instruction, first decode which class of instruction it is.

2) Decode funct3 and funct7. funct3 is mainly used to specify the specific
assembly instruction in the current class; funct7 is a supplement when
funct3 is not enough. Only 8 instructions can be specified.

  
wire [ 2: 0 ] funct3 = i_instr[ 14: 12 ];
wire [ 6: 0 ] funct7 = i_instr[ 31: 25 ];

assign o_rd_idx = i_instr[ 11: 7 ];
assign o_rs1_idx = i_instr[ 19: 15 ];
assign o_rs2_idx = i_instr[ 24: 20 ];

3) Splicing the immediate data in different instructions,
according to the assembly instructions, what the cpu does here is the
corresponding decoding

assign o_I_imm = { { 20{ i_instr[ 31 ] } } , i_instr[ 31: 20 ] }; //addi/slti/sltiu/andi/ori/xori, lw/lh/lhu/lb/lbu, JALR, SLLI/ SRLi/SRAI

assign o_S_imm = { { 20{ i_instr[ 31 ] } }, i_instr[ 31: 25 ], i_instr[ 11: 7 ] }; //s-type instruction, provide imm or store instruction

assign o_B_imm = { { 19{ i_instr[ 31 ] } }, i_instr[ 31 ], i_instr[ 7 ], i_instr[ 30: 25 ], i_instr[ 11: 8 ], 1’b0 };

assign o_U_imm = { i_instr[ 31: 12 ], 12’b0 };

assign o_J_imm = { { 11{ i_instr[ 31 ] } }, i_instr[ 31 ], i_instr[ 19: 12 ], i_instr[ 20 ], i_instr[ 30: 21 ], 1’b0 }; //for JAL,

assign o_shamt = i_instr[ 24: 20 ];

assign o_csr_imm = {27’b0,i_instr[19:15]};

 

4) Determine the final instruction :

According to the categories decoded by opcode, the specific instructions decoded by funct3 and funct7, and finally determine that the current machine code is the instruction in the instruction set.

Posted in FPGA, RISC-V, RISC-V Textbook, Textbook and Training Project

Related Articles

Leave a Reply

Your email address will not be published.

Leave the field below empty!