Closed x402 closed 2 years ago
指令中给出的有关操作数的信息有:目的寄存器、源寄存器(2个)、立即数,其中目的寄存器和源寄存器以索引的方式给出,即从指令中可以取到这些寄存器的索引(rd、rs1、rs2)。
在”执行“阶段之前,需要取得参与运算的数,这些数的来源有:源寄存器、立即数、PC,其中来自源寄存器的数需要通过索引访问寄存器组取得,立即数从指令中根据指令类型取出相应位进行拼接得到,PC则可以直接取。
所以decode_operand函数要完成以下几件事:
decode_operand
从指令的指定字段取得寄存器索引(rd、rs1、rs2)
根据指令类型从寄存器组取出数据(src1、src2)
根据指令类型从指令中取出立即数(imm)
// dest表示目的寄存器索引, src1、src2分别表示从rs1、rs2取出的数据, imm表示从指令中取得的立即数 static void decode_operand(Decode *s, int *dest, word_t *src1, word_t *src2, word_t *imm, int type) { uint32_t i = s->isa.inst.val; int rd = BITS(i, 11, 7); int rs1 = BITS(i, 19, 15); int rs2 = BITS(i, 24, 20); *dest = rd; switch (type) { // 根据指令类型决定是否从rs1、rs2取数,是否要取立即数(按哪种方式取) case TYPE_I: src1R(); immI(); break; case TYPE_U: immU(); break; case TYPE_S: src1R(); src2R(); immS(); break; } }
接下来在“执行”阶段,就可以用前面取到的操作数(src1、src2、imm、pc等)进行相应的操作,然后将结果写回寄存器或写入内存。这样很容易根据手册中定义的指令行为来编写相应的C语言代码。写回寄存器时根据译码阶段取得的索引(dest)访问寄存器组,用R(dest) = 结果表示。读写内存使用Mr、Mw函数,且riscv中访存地址一定为src1和imm相加的结果,所以访存函数就写成Mr(src1 + imm, LEN)和Mw(src1 + imm, LEN, DATA)的形式。
R(dest) = 结果
Mr(src1 + imm, LEN)
Mw(src1 + imm, LEN, DATA)
// auipc // 将pc与立即数相加,写入rd中 R(dest) = s->pc + imm
// ld // 从内存(addr=src1+imm)中取出8个字节写入rd R(dest) = Mr(src1 + imm, 8)
// sd // 将从rs2中取出的数写入内存(addr=src1+imm) Mw(src1 + imm, 8, src2)
另外,pc+4可以用s->snpc表示,将结果写回pc用s->dnpc = 结果表示。
pc+4
s->snpc
s->dnpc = 结果
根据上述思路,可以很容易地完成所有指令的INSTPAT。更进一步,可以在INSTPAT的区域定义更多的宏,写起来更自然(可以参考riscv-card)。(思路源自@LeoJhonSong)
#define rd R(dest) #define rs1 src1 #define rs2 src2 #define pc s->pc #define snpc s->snpc #define dnpc s->dnpc #define addr (src1+imm) ... INSTPAT(..., auipc, U, rd = pc + imm); INSTPAT(..., ld, I, rd = Mr(addr, 8)); INSTPAT(..., sd, S, Mw(addr, 8, rs2)); ... INSTPAT(..., jal, J, rd = snpc; dnpc = pc + imm); ... #undef rd #undef rs1 #undef rs2 #undef pc #undef snpc #undef dnpc #undef addr
NEMU-riscv64译码修改
指令中给出的有关操作数的信息有:目的寄存器、源寄存器(2个)、立即数,其中目的寄存器和源寄存器以索引的方式给出,即从指令中可以取到这些寄存器的索引(rd、rs1、rs2)。
在”执行“阶段之前,需要取得参与运算的数,这些数的来源有:源寄存器、立即数、PC,其中来自源寄存器的数需要通过索引访问寄存器组取得,立即数从指令中根据指令类型取出相应位进行拼接得到,PC则可以直接取。
所以
decode_operand
函数要完成以下几件事:从指令的指定字段取得寄存器索引(rd、rs1、rs2)
根据指令类型从寄存器组取出数据(src1、src2)
根据指令类型从指令中取出立即数(imm)
接下来在“执行”阶段,就可以用前面取到的操作数(src1、src2、imm、pc等)进行相应的操作,然后将结果写回寄存器或写入内存。这样很容易根据手册中定义的指令行为来编写相应的C语言代码。写回寄存器时根据译码阶段取得的索引(dest)访问寄存器组,用
R(dest) = 结果
表示。读写内存使用Mr、Mw函数,且riscv中访存地址一定为src1和imm相加的结果,所以访存函数就写成Mr(src1 + imm, LEN)
和Mw(src1 + imm, LEN, DATA)
的形式。另外,
pc+4
可以用s->snpc
表示,将结果写回pc用s->dnpc = 结果
表示。根据上述思路,可以很容易地完成所有指令的INSTPAT。更进一步,可以在INSTPAT的区域定义更多的宏,写起来更自然(可以参考riscv-card)。(思路源自@LeoJhonSong)