4. 压缩加载存储指令

任务目标

本案例要求实现RISC-V压缩指令中c.lw与c.sw的译码与偏移量计算逻辑。输入为16位压缩指令编码,输出为对应的加载/存储操作类型、目标寄存器、基址寄存器以及经过扩展的12位立即数偏移量。

指令格式与偏移规则

c.lw与c.sw共享同一编码格式(仅opcode位不同),其字段布局如下:

  • funct3(bit[15:13]):010表示c.lw,110表示c.sw
  • imm[5:3](bit[12:10])与imm[2](bit[6])与imm[7:6](bit[5:4]):共同构成8位无符号立即数
  • rs1(bit[9:7]):基址寄存器编号(0-7,映射到x8-x15)
  • rd/rs2(bit[4:2]):加载目标寄存器或存储源寄存器编号(0-7,映射到x8-x15)

偏移量计算规则:将分散的立即数位拼接为8位值uimm[7:0],其中uimm[7:6]来自指令bit[5:4],uimm[5:3]来自bit[12:10],uimm[2]来自bit[6],uimm[1:0]恒为0。最终偏移量为该8位值乘以4(即左移2位),得到无符号12位字节偏移。

完整实现

// c_lw_sw_decoder.c
#include <stdio.h>
#include <stdint.h>
#include <assert.h>

typedef struct {
    int is_load;          // 1=c.lw, 0=c.sw
    int rs1;              // 基址寄存器编号 (0-7, 对应x8-x15)
    int rd_rs2;           // 加载目标/存储源寄存器编号 (0-7)
    uint16_t offset;      // 计算后的字节偏移量 (0-1020, 4的倍数)
} c_lw_sw_info;

// 解码c.lw/c.sw指令,成功返回1,非法编码返回0
int decode_c_lw_sw(uint16_t inst, c_lw_sw_info *info) {
    uint16_t funct3 = (inst >> 13) & 0x7;
    if (funct3 != 0b010 && funct3 != 0b110) return 0;  // 非c.lw/c.sw
    
    // 提取字段
    int rs1 = (inst >> 7) & 0x7;
    int rd_rs2 = (inst >> 2) & 0x7;
    
    // 拼接立即数: uimm[7:6]=inst[5:4], uimm[5:3]=inst[12:10], uimm[2]=inst[6]
    uint16_t uimm = 0;
    uimm |= ((inst >> 4) & 0x3) << 6;    // bit[5:4] -> uimm[7:6]
    uimm |= ((inst >> 10) & 0x7) << 3;   // bit[12:10] -> uimm[5:3]
    uimm |= ((inst >> 6) & 0x1) << 2;    // bit[6] -> uimm[2]
    // uimm[1:0] 恒为0
    
    info->is_load = (funct3 == 0b010);
    info->rs1 = rs1;
    info->rd_rs2 = rd_rs2;
    info->offset = uimm << 2;  // 乘以4得到字节偏移
    
    return 1;
}

// 测试用例
void test_decode() {
    c_lw_sw_info info;
    
    // c.lw x9, 8(x10)  => 编码: funct3=010, rs1=2(x10), rd=1(x9), uimm=8
    // uimm[7:6]=00, uimm[5:3]=001, uimm[2]=0, 拼接后uimm=0b00001000=8
    uint16_t inst1 = 0b0100000100100001;  // 手动构造
    assert(decode_c_lw_sw(inst1, &info) == 1);
    assert(info.is_load == 1);
    assert(info.rs1 == 2);
    assert(info.rd_rs2 == 1);
    assert(info.offset == 32);  // 8*4=32
    
    // c.sw x15, 4(x8)  => funct3=110, rs1=0(x8), rs2=7(x15), uimm=4
    uint16_t inst2 = 0b1100000000011100;
    assert(decode_c_lw_sw(inst2, &info) == 1);
    assert(info.is_load == 0);
    assert(info.rs1 == 0);
    assert(info.rd_rs2 == 7);
    assert(info.offset == 16);  // 4*4=16
    
    // 非法指令: funct3=000 (c.addi4spn)
    assert(decode_c_lw_sw(0x0000, &info) == 0);
    
    printf("所有测试通过\n");
}

int main() {
    test_decode();
    return 0;
}

编译与运行

gcc -o c_lw_sw_decoder c_lw_sw_decoder.c -Wall
./c_lw_sw_decoder

验证方法

程序内置了3组测试用例:

  1. c.lw x9, 8(x10):验证加载类型识别、寄存器编号提取和偏移量计算(8×4=32字节)
  2. c.sw x15, 4(x8):验证存储类型识别和偏移量计算(4×4=16字节)
  3. 非法编码:确认非c.lw/c.sw指令被正确拒绝

运行后若输出所有测试通过且程序退出码为0,则说明解码逻辑正确。可通过修改inst1/inst2的立即数位(bit[12:10]、bit[6]、bit[5:4])来验证不同偏移量的拼接结果,例如将inst1的bit[12:10]改为011(uimm[5:3]=3),则偏移量变为(3<<3+0)<<2=96字节。

关键说明

  • 压缩指令中的寄存器编号0-7对应标准RISC-V整数寄存器x8-x15,本实现直接输出编号,由上层负责映射
  • 偏移量始终为4的倍数(因为uimm[1:0]=0),最大为0xFF<<2=1020字节
  • 本实现仅覆盖c.lw/c.sw两种压缩存储访问指令,其他压缩指令(如c.ld/c.sd)需单独处理