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组测试用例:
- c.lw x9, 8(x10):验证加载类型识别、寄存器编号提取和偏移量计算(8×4=32字节)
- c.sw x15, 4(x8):验证存储类型识别和偏移量计算(4×4=16字节)
- 非法编码:确认非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)需单独处理