RISC-V FPGA 设计中, RISC-V 的核心核心模块中包括指令译码模块。 这个模块的主要功能是:根据当前的PC,对ITCM 输入的汇编机器码进行解析。包括得到相应的汇编语言命令,需要的rs1,rs2寄存器,需要存储的rd 寄存器等
相关参考文章:
RISC-V 教学教案
RISC-V 中cpu 核心设计框架组成结构:

在vivado 工程文件中:
instr_dec 模块 输入为汇编语言的机器码, 输出为:
RS1_IDX: 通用寄存器rs1 ID
RS2_IDX: 通用寄存器rs2 ID
RD_IDX: 通用寄存器RD ID
instr_group: 包括:OP_IMM, OP, LUI, AUIPC, JAL, JALR, BRANCH, LOAD, STORE, CSR, 等等
opimm_instr: 包括:SRAI, SRLI, SLLI, ANDI, ORI, XORI, SLTIU, SLTI, ADDI
op_instr: 包括:SRA, SUB, SRL, SLL, XOR, OR, AND, SLTU, SLT, ADD
branch_instr: 包括:BGTU, BGT, BLTU, BLT, BNE, BEQ
shamt: 数据位移宽度 I_imm: I-type 立即数
S_imm: S-type 立即数
J_imm: J-type 立即数
U_imm: U-type 立即数
B_imm: B-type 立即数
load_instr: 包括:LBU, LB, LHU, LH, LW
store_instr: 包括:SB, SH, SW
csr_instr: 包括:SCRRCI, CSRRSI, CSRRWI, SCRRC, CSRRS, CSRRW
csr_addr: 选择4095 个csr 寄存器中的一个 CSR_imm: csr 立即数
decoder 模块代码:
module instr_dec
(
input sys_clk, // 系统时钟
input [ 31: 0 ] i_instr, // 从ITCM 中读取的汇编语言机器码
output RV32I, // 译码输出的RV32I 格式. 目前,我们不支持RV32C, RV64等
output [ 4: 0 ] o_rs1_idx, // 汇编指令中rs1 所指定的32个通用寄存器中的一个
output [ 4: 0 ] o_rs2_idx, // 汇编指令中rs2 所指定的32个通用寄存器中的一个
output [ 4: 0 ] o_rd_idx, // 汇编指令中rd 所指定的32个通用寄存器中的一个
output [ 15: 0 ] o_instr_group, // 指令组,将LOAD,STORE,CSR,BRANCH,OP,OP_imm 等等 指令分组
output [ 8: 0 ] o_opimm_instr, // opimm 指令中的每一条指令,包括:SRAI, SRLI, SLLI, ANDI, ORI, XORI, SLTIU, SLTI, ADDI
output [ 9: 0 ] o_op_instr, // op 指令中的每一条指令,包括:SRA, SUB, SRL, SLL, XOR, OR, AND, SLTU, SLT, ADD
output [ 5: 0 ] o_branch_instr, // branch 指令中的每一条指令,包括:BGTU, BGT, BLTU, BLT, BNE, BEQ
output [ 4: 0 ] o_load_instr, // load 指令中的每一条指令,包括:LBU, LB, LHU, LH, LW
output [ 2: 0 ] o_store_instr, // store 指令中的每一条指令,包括:SB, SH, SW
output [ 1: 0 ] o_fence_instr, // fence 指令中的每一条指令,包括:fence, fence.i
output [ 5: 0 ] o_csr_instr, // CSR 指令中的每一条指令,包括:SCRRCI, CSRRSI, CSRRWI, SCRRC, CSRRS, CSRRW
output [11: 0 ] o_csr_addr, // 选择4095 个csr 寄存器中的一个 CSR_imm: csr 立即数
output [ 4: 0 ] o_shamt, // 数据位移宽度 I_imm: I-type 立即数
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 [ 31: 0 ] o_J_imm, // J-type immediate
output [ 31: 0 ] o_U_imm, // U-type immediate
output [ 31: 0 ] o_csr_imm, // CSR 立即数
//output [31:0] o_pc
output o_mret, // 中断返回
output o_ecall, // 通过引发环境调用异常来请求执行环境
output o_ebreak, // 调试中断断点
output o_dret, // 调试中断 返回
output o_wfi // 等待中断
);
//===============================================================================
// 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, rv32i_sh, rv32i_sw };
//===============================================================================
//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)查找汇编指令机器码最低7位,得到opcode:
wire [ 6: 0 ] opcode = i_instr[ 6: 0 ];
assign RV32I = ( opcode[ 1: 0 ] == 2'b11 ) && ( opcode[ 4: 2 ] != 3'b111 );
如果opcode 最低2位为 11, 并且最低5位 不能是 111111 , 那么当前指令位RV32i 指令。
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
如果是RV32I 指令, 先译码出是哪一个类指令。
2)将funct3,funct7 译码出来, funct3 主要用来指定当前类中具体那一条汇编指令; funct7 是当funct3 不够用时的一种补充,比如:某一类指令中由9条指令,但funct3 最多只能指定8条指令。
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) 拼接 不同指令中的立即数, 根据汇编指令, cpu 这里做的是相对应的解码
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)确定最终指令: 根据opcode 译码出来的大类, funct3,funct7 译码出来的具体指令, 最终确定当前的机器码是指令集当中的那一条指令。
