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'的编码一致。