假设
-
指令集为RV64I
-
内存地址开始于
0x8000 0000
-
使用如下的代码:
Cstatic const uint32_t img [] = { 0x00000297, // auipc t0,0 0x00028823, // sb zero,16(t0) 0x0102c503, // lbu a0,16(t0) 0x00100073, // ebreak (used as nemu_trap) 0xdeadbeef, // some data };
-
Decode
的定义如下Ctypedef struct Decode { uint64_t pc; uint64_t snpc; // static next pc uint64_t dnpc; // dynamic next pc struct { uint32_t inst; } isa; } Decode;
-
cpu.pc
初始值为0x8000 0000
-
从调用
exec_once()
并开始执行auipc
开始考虑 -
忽略trace和difftest相关的操作
exec_once(&s, cpu.pc);
调用exec_once(&s, cpu.pc)
,当前cpu.pc
值为0x8000 0000
进入exec_once()
后,更新s
:
C
s->pc = 0x80000000;
s->snpc = 0x80000000;
调用isa_exec_once(s)
isa_exec_once(s)
利用inst_fetch(&s->snpc, 4)
更新s
。inst_fetch
返回snpc
对应的指令地址(uint32_t
类型),并且更新snpc
为下一条指令所在地址(s->snpc += 4
, 即0x8000 0004
)。
更新后的s
:
C
s->pc = 0x80000000;
s->snpc = 0x80000004;
s->isa.inst = 0x80000000;
之后执行decode_exec(s)
并将其结果返回.
decode_exec(s)
更新s->dnpc = s->snpc
, 此时s
:
C
s->pc = 0x80000000;
s->snpc = 0x80000004;
s->dnpc = 0x80000004;
s->isa.inst = 0x80000000;
定义标签地址, 用于之后跳转:
C
const void * __instpat_end = &&__instpat_end_;
指令匹配:
C
INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc , U, R(rd) = s->pc + imm);
INSTPAT("??????? ????? ????? 100 ????? 00000 11", lbu , I, R(rd) = Mr(src1 + imm, 1));
INSTPAT("??????? ????? ????? 000 ????? 01000 11", sb , S, Mw(src1 + imm, 1, src2));
INSTPAT("0000000 00001 00000 000 00000 11100 11", ebreak , N, NEMUTRAP(s->pc, R(10))); // R(10) is $a0
INSTPAT("??????? ????? ????? ??? ????? ????? ??", inv , N, INV(s->pc));
INSTPAT() 指令匹配宏
宏展开后, 匹配成功第一条规则:
C
do {
uint64_t key, mask, shift;
pattern_decode("??????? ????? ????? ??? ????? 00101 11",
(sizeof("??????? ????? ????? ??? ????? 00101 11") - 1), &key, &mask, &shift);
if ((((uint64_t)((s)->isa.inst) >> shift) & mask) == key) {
{
int rd = 0;
word_t src1 = 0, src2 = 0, imm = 0;
decode_operand(s, &rd, &src1, &src2, &imm, TYPE_U);
(cpu.gpr[check_reg_idx(rd)]) = s->pc + imm ;
};
goto *(__instpat_end);
}
} while (0)
其中(sizeof("??????? ????? ????? ??? ????? 00101 11") - 1) == 38
pattern_decode() 解码宏
进入pattern_decode()
宏后, 定义临时变量uint64_t __key = 0, __mask = 0, __shift = 0;
. 之后pattern_decode()
会利用辅助宏macro(i)
.
在macro(i)
中:
- 若
str[i]
是'1'
,__key
左移并且低位补1; 若不是,__key
左移并且低位补0; - 若
str[i]
是'?'
,__mask
左移并且低位补0;
若不是,__mask
左移并且低位补1 - 若
str[i]
是'?'
,__shift
加1, 否则立刻清0
通过:
C
#define macro2(i) macro(i); macro((i) + 1)
#define macro4(i) macro2(i); macro2((i) + 2)
#define macro8(i) macro4(i); macro4((i) + 4)
#define macro16(i) macro8(i); macro8((i) + 8)
#define macro32(i) macro16(i); macro16((i) + 16)
#define macro64(i) macro32(i); macro32((i) + 32)
macro64(0);
处理指令掩码中的38个字符.
本例中, __key
最终为0010111
, __mask
最终为000...00(共25个0)1111111
, __shift
最终为0
跳转到finish
标签后执行:
C
finish:
*key = __key >> __shift;
*mask = __mask >> __shift;
*shift = __shift;
然后退出pattern_decode()
宏, 进入INSTPAT
宏的:
C
if ((((uint64_t)INSTPAT_INST(s) >> shift) & mask) == key) { \
INSTPAT_MATCH(s, ##__VA_ARGS__); \
goto *(__instpat_end); \
} \
(uint64_t)INSTPAT_INST(s)
即执行的指令inst
. inst
被右移shift
位, 然后和mask
按位与, 判断是否和key
完全相等
如果相等, 则执行:
C
INSTPAT_MATCH(s, ##__VA_ARGS__); \
goto *(__instpat_end); \
ISNT_MATCH() 执行指令宏
在这个宏里, 会利用decode_operand()
函数来译码出rd
, src
, src2
, imm
. 然后执行__VA_ARGS__ ;
. __VA_ARGS__
就是在INSTPAT()
中传入的指令的具体操作. 对于auipc
这条指令, __VA_ARGS
就是R(rd) = s->pc + imm
.