// $(NEMU_HOME)/src/cpu/cpu-exec.c voidcpu_exec(uint64_t n) { g_print_step = (n < MAX_INST_TO_PRINT); switch (nemu_state.state) { case NEMU_END: case NEMU_ABORT: printf("Program execution has ended. To restart the program, exit NEMU and run again.\n"); return; default: nemu_state.state = NEMU_RUNNING; }
switch (nemu_state.state) { case NEMU_RUNNING: nemu_state.state = NEMU_STOP; break;
case NEMU_END: case NEMU_ABORT: Log("nemu: %s at pc = " FMT_WORD, (nemu_state.state == NEMU_ABORT ? ANSI_FMT("ABORT", ANSI_FG_RED) : (nemu_state.halt_ret == 0 ? ANSI_FMT("HIT GOOD TRAP", ANSI_FG_GREEN) : ANSI_FMT("HIT BAD TRAP", ANSI_FG_RED))), nemu_state.halt_pc); // fall through case NEMU_QUIT: statistic(); } }
static void execute(uint64_t n)
这个函数也是个壳子,真正的CPU还要看exec_once()函数,如果调用它,CPU会执行一个周期
1 2 3 4 5 6 7 8 9 10 11
// $(NEMU_HOME)/src/cpu/cpu-exec.c staticvoidexecute(uint64_t n) { Decode s; for (;n > 0; n --) { exec_once(&s, cpu.pc); g_nr_guest_inst ++; trace_and_difftest(&s, cpu.pc); if (nemu_state.state != NEMU_RUNNING) break; IFDEF(CONFIG_DEVICE, device_update()); } }
这里声明了一个译码结构体变量struct Decode,趁机解释一波:
1 2 3 4 5 6 7
typedefstructDecode { vaddr_t pc; vaddr_t snpc; // static next pc vaddr_t dnpc; // dynamic next pc ISADecodeInfo isa; IFDEF(CONFIG_ITRACE, char logbuf[128]); } Decode;
staticvoiddecode_operand(Decode *s, int *rd, word_t *src1, word_t *src2, word_t *imm, int type) { uint32_t i = s->isa.inst.val; int rs1 = BITS(i, 19, 15); int rs2 = BITS(i, 24, 20); *rd = BITS(i, 11, 7); switch (type) { case TYPE_I: src1R(); immI(); break; case TYPE_U: immU(); break; case TYPE_S: src1R(); src2R(); immS(); break; case TYPE_R: src1R(); src2R(); break; case TYPE_B: src1R(); src2R(); immB(); break; case TYPE_J: immJ(); break; } }
Step 3 执行指令语义
将指令行为copy到代码中即。最后通过goto语句跳转出来
实现指令
在实现这些指令的行为时,需要严格遵循riscv32的指令规范,并且要注意类型转换和符号扩展
首先介绍三个常用的宏
1 2 3
#define BITMASK(bits) ((1ull << (bits)) - 1) // make bitmask like 1111...111 len of mask is `bits` #define BITS(x, hi, lo) (((x) >> (lo)) & BITMASK((hi) - (lo) + 1)) // similar to x[hi:lo] in verilog #define SEXT(x, len) ({ struct { int64_t n : len; } __x = { .n = x }; (uint64_t)__x.n; }) // signed extended from x[len-1:0] to x[63:0]
宏BITMASK(bits)返回bits位unsigne long long类型的掩码
宏BITS(x, hi, lo)对x进行从高位hi到低位lo的切片,类似verilog中的x[hi:lo],返回值的类型同样时unsigne long long
宏SEXT(x, len)将输入x的最低len位进行符号扩展至uint64_t
下面解释一下SEXT()宏:
1 2 3 4 5 6 7 8
#define SEXT(x, len) ({ struct { int64_t n : len; } __x = { .n = x }; (uint64_t)__x.n; })
int64_t n : len 这是结构中的位域,表示只取n的后len位,对应于verilog的[len-1:0]位,然后会将len位的数据进行符号位扩展至int64_t,符号位为n[len-1]的数据。 最后强制类型转换为uint64_t
# Building add-run [riscv32-nemu] # Building am-archive [riscv32-nemu] + CC src/platform/nemu/trm.c In file included from /home/lyq/Desktop/ysyx-workbench/abstract-machine/am/include/am.h:4, from /home/lyq/Desktop/ysyx-workbench/abstract-machine/am/src/platform/nemu/trm.c:1: /opt/riscv64-linux/lib/gcc/riscv64-unknown-linux-gnu/13.2.0/include/stdint.h:9:16: fatal error: stdint.h: No such file or directory 9 | # include_next <stdint.h> | ^~~~~~~~~~ compilation terminated. make[2]: *** [/home/lyq/Desktop/ysyx-workbench/abstract-machine/Makefile:110: /home/lyq/Desktop/ysyx-workbench/abstract-machine/am/build/riscv32-nemu/src/platform/nemu/trm.o] Error 1 make[1]: *** [/home/lyq/Desktop/ysyx-workbench/abstract-machine/Makefile:129: am] Error 2 test list [1 item(s)]: add [ add] ***FAIL***
这啥?找不到stdint.h头文件?遂STFW,在Stack Exchange中发现了解决方案:
One way to fix this error is: restrict gcc in using stdint-gcc.h
This can be done by adding c compiler flag
-ffreestanding
to gcc
For more info on what is freestanding visit here and implied -fno-builtin visit here