2. 压缩指令解码器设计

任务目标

设计一个RISC-V压缩指令解码器,接收16位压缩指令(RVC),输出等价的32位标准指令展开形式。本案例覆盖RVC中最常用的四类指令格式:CR(寄存器操作)、CI(立即数操作)、CL(访存-load)和CS(访存-store),共映射8条具体指令。

核心映射规则

解码器依据RISC-V压缩指令规范(版本1.9+)实现以下关键映射:

  • 寄存器扩展:压缩指令中的rd'/rs1'/rs2'(3位)通过查表映射到完整寄存器号——x8~x15对应压缩编码0~7,x0对应编码0(仅用于c.jr/c.jalr),x2(sp)对应编码0(仅用于c.addi16sp)。
  • 立即数重组:压缩指令将32位指令的立即数拆分到多个不连续字段,解码时需按规范重新拼接并做符号扩展。
  • 指令类型判定:通过funct3与opcode低2位([1:0]=11)组合识别指令类别。

完整实现

// rvc_decoder.v
module rvc_decoder (
    input  wire [15:0] rvc_instr,
    output reg  [31:0] rv32_instr,
    output reg         is_valid
);

    // 压缩寄存器编号 → 完整寄存器号映射
    function [4:0] map_reg;
        input [2:0] rvc_reg;
        begin
            case (rvc_reg)
                3'd0: map_reg = 5'd8;   // x8  (s0)
                3'd1: map_reg = 5'd9;   // x9  (s1)
                3'd2: map_reg = 5'd10;  // x10 (a0)
                3'd3: map_reg = 5'd11;  // x11 (a1)
                3'd4: map_reg = 5'd12;  // x12 (a2)
                3'd5: map_reg = 5'd13;  // x13 (a3)
                3'd6: map_reg = 5'd14;  // x14 (a4)
                3'd7: map_reg = 5'd15;  // x15 (a5)
                default: map_reg = 5'd0;
            endcase
        end
    endfunction

    // 符号扩展函数
    function [31:0] sign_extend;
        input [15:0] imm;
        input [3:0]  width;
        begin
            case (width)
                4'd6:  sign_extend = {{26{imm[5]}}, imm[5:0]};
                4'd9:  sign_extend = {{23{imm[8]}}, imm[8:0]};
                4'd12: sign_extend = {{20{imm[11]}}, imm[11:0]};
                default: sign_extend = 32'd0;
            endcase
        end
    endfunction

    always @(*) begin
        // 默认输出
        rv32_instr = 32'd0;
        is_valid   = 1'b0;

        // 仅处理压缩指令(低2位为2'b00)
        if (rvc_instr[1:0] != 2'b00) begin
            is_valid = 1'b0;
            return;
        end

        case (rvc_instr[15:13])  // funct3
            3'b100: begin
                // CR格式:c.jr / c.jalr / c.mv / c.add
                if (rvc_instr[12] == 1'b0) begin
                    // c.jr 或 c.mv
                    if (rvc_instr[11:7] == 5'd0) begin
                        // c.jr rs1 → jalr x0, 0(rs1)
                        rv32_instr = {12'd0, map_reg(rvc_instr[6:4]), 3'b000, 5'd0, 7'b1100111};
                        is_valid = 1'b1;
                    end else begin
                        // c.mv rd, rs2 → addi rd, x0, rs2
                        rv32_instr = {12'd0, 5'd0, 3'b000, map_reg(rvc_instr[11:7]), 7'b0010011};
                        is_valid = 1'b1;
                    end
                end else begin
                    // c.jalr 或 c.add
                    if (rvc_instr[11:7] == 5'd0) begin
                        // c.jalr rs1 → jalr x1, 0(rs1)
                        rv32_instr = {12'd0, map_reg(rvc_instr[6:4]), 3'b000, 5'd1, 7'b1100111};
                        is_valid = 1'b1;
                    end else begin
                        // c.add rd, rs2 → add rd, rd, rs2
                        rv32_instr = {7'd0, map_reg(rvc_instr[6:4]), map_reg(rvc_instr[11:7]), 3'b000,
                                      map_reg(rvc_instr[11:7]), 7'b0110011};
                        is_valid = 1'b1;
                    end
                end
            end

            3'b010: begin
                // CI格式:c.li / c.addi / c.addi16sp
                if (rvc_instr[12] == 1'b0) begin
                    // c.li rd, imm → addi rd, x0, imm
                    rv32_instr = {sign_extend({rvc_instr[12], rvc_instr[6:2]}, 6'd6),
                                 5'd0, 3'b000, map_reg(rvc_instr[11:7]), 7'b0010011};
                    is_valid = 1'b1;
                end else begin
                    if (rvc_instr[11:7] == 5'd2) begin
                        // c.addi16sp imm → addi x2, x2, imm
                        rv32_instr = {sign_extend({rvc_instr[12], rvc_instr[4:3], rvc_instr[5],
                                                   rvc_instr[2], rvc_instr[6]}, 9'd9),
                                     5'd2, 3'b000, 5'd2, 7'b0010011};
                        is_valid = 1'b1;
                    end else begin
                        // c.addi rd, imm → addi rd, rd, imm
                        rv32_instr = {sign_extend({rvc_instr[12], rvc_instr[6:2]}, 6'd6),
                                     map_reg(rvc_instr[11:7]), 3'b000,
                                     map_reg(rvc_instr[11:7]), 7'b0010011};
                        is_valid = 1'b1;
                    end
                end
            end

            3'b000: begin
                // CL格式:c.lw
                if (rvc_instr[11:10] == 2'b00) begin
                    // c.lw rd, offset(rs1) → lw rd, offset(rs1)
                    rv32_instr = {sign_extend({rvc_instr[5], rvc_instr[12:10], rvc_instr[6],
                                               rvc_instr[2]}, 6'd6),
                                 map_reg(rvc_instr[9:7]), 3'b010,
                                 map_reg(rvc_instr[4:2]), 7'b0000011};
                    is_valid = 1'b1;
                end
            end

            3'b110: begin
                // CS格式:c.sw
                if (rvc_instr[11:10] == 2'b00) begin
                    // c.sw rs2, offset(rs1) → sw rs2, offset(rs1)
                    rv32_instr = {sign_extend({rvc_instr[5], rvc_instr[12:10], rvc_instr[6],
                                               rvc_instr[2]}, 6'd6),
                                 map_reg(rvc_instr[9:7]), 3'b010,
                                 map_reg(rvc_instr[4:2]), 7'b0100011};
                    is_valid = 1'b1;
                end
            end

            default: is_valid = 1'b0;
        endcase
    end

endmodule

测试平台与验证

// rvc_decoder_tb.v
`timescale 1ns/1ps
module rvc_decoder_tb;
    reg  [15:0] rvc_instr;
    wire [31:0] rv32_instr;
    wire        is_valid;

    rvc_decoder uut (.rvc_instr(rvc_instr), .rv32_instr(rv32_instr), .is_valid(is_valid));

    initial begin
        $display("=== RVC Decoder Test ===");

        // 测试1: c.mv x10, x11 (0x8A6A) → addi x10, x0, x11
        rvc_instr = 16'h8A6A; #10;
        $display("c.mv  a0,a1  -> %08h (expect 000005B3)", rv32_instr);

        // 测试2: c.add x10, x11 (0x8D6A) → add x10, x10, x11
        rvc_instr = 16'h8D6A; #10;
        $display("c.add a0,a1  -> %08h (expect 00B50533)", rv32_instr);

        // 测试3: c.li x10, 5 (0x452A) → addi x10, x0, 5
        rvc_instr = 16'h452A; #10;
        $display("c.li  a0,5   -> %08h (expect 00500513)", rv32_instr);

        // 测试4: c.lw x10, 4(x11) (0x4A58) → lw x10, 4(x11)
        rvc_instr = 16'h4A58; #10;
        $display("c.lw  a0,4(a1) -> %08h (expect 0045A503)", rv32_instr);

        // 测试5: c.sw x10, 4(x11) (0xAA58) → sw x10, 4(x11)
        rvc_instr = 16'hAA58; #10;
        $display("c.sw  a0,4(a1) -> %08h (expect 00A5A423)", rv32_instr);

        // 测试6: c.jr x10 (0x8502) → jalr x0, 0(x10)
        rvc_instr = 16'h8502; #10;
        $display("c.jr  a0    -> %08h (expect 00050067)", rv32_instr);

        // 测试7: c.jalr x10 (0x9502) → jalr x1, 0(x10)
        rvc_instr = 16'h9502; #10;
        $display("c.jalr a0   -> %08h (expect 000500E7)", rv32_instr);

        // 测试8: c.addi16sp -16 (0x7121) → addi x2, x2, -16
        rvc_instr = 16'h7121; #10;
        $display("c.addi16sp -16 -> %08h (expect FF010113)", rv32_instr);

        $finish;
    end
endmodule

运行与验证

# 编译并运行仿真(Icarus Verilog)
iverilog -o rvc_tb rvc_decoder.v rvc_decoder_tb.v
vvp rvc_tb

通过条件:仿真输出的每条指令展开值与注释中的expect值完全一致,且is_valid信号为高。若所有8条测试均匹配,则解码器实现正确。

关键设计说明

  • 压缩指令的opcode低2位固定为2'b00,解码器以此识别RVC指令;非压缩指令直接置is_valid=0。
  • 立即数重组严格遵循RVC规范:c.lw/c.sw的6位立即数由{imm[5], imm[3:2], imm[6], imm[1:0]}拼接;c.addi16sp的9位立即数由{imm[8], imm[4:3], imm[5], imm[2], imm[6]}拼接。
  • 寄存器映射表覆盖x8~x15,与RVC规范中rd'/rs1'/rs2'的编码一致。