NJU-ProjectN/nemu ics2023 initialized
NJU-ProjectN/nemu eb63cf3568dbf4e0c3c6ef462e6ec685550fabbc Merge pull request #76 from rijuyuezhu/master
This commit is contained in:
parent
1efe03efb9
commit
2824efad33
141 changed files with 19573 additions and 0 deletions
128
nemu/src/cpu/cpu-exec.c
Normal file
128
nemu/src/cpu/cpu-exec.c
Normal file
|
@ -0,0 +1,128 @@
|
|||
/***************************************************************************************
|
||||
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
|
||||
*
|
||||
* NEMU is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
***************************************************************************************/
|
||||
|
||||
#include <cpu/cpu.h>
|
||||
#include <cpu/decode.h>
|
||||
#include <cpu/difftest.h>
|
||||
#include <locale.h>
|
||||
|
||||
/* The assembly code of instructions executed is only output to the screen
|
||||
* when the number of instructions executed is less than this value.
|
||||
* This is useful when you use the `si' command.
|
||||
* You can modify this value as you want.
|
||||
*/
|
||||
#define MAX_INST_TO_PRINT 10
|
||||
|
||||
CPU_state cpu = {};
|
||||
uint64_t g_nr_guest_inst = 0;
|
||||
static uint64_t g_timer = 0; // unit: us
|
||||
static bool g_print_step = false;
|
||||
|
||||
void device_update();
|
||||
|
||||
static void trace_and_difftest(Decode *_this, vaddr_t dnpc) {
|
||||
#ifdef CONFIG_ITRACE_COND
|
||||
if (ITRACE_COND) { log_write("%s\n", _this->logbuf); }
|
||||
#endif
|
||||
if (g_print_step) { IFDEF(CONFIG_ITRACE, puts(_this->logbuf)); }
|
||||
IFDEF(CONFIG_DIFFTEST, difftest_step(_this->pc, dnpc));
|
||||
}
|
||||
|
||||
static void exec_once(Decode *s, vaddr_t pc) {
|
||||
s->pc = pc;
|
||||
s->snpc = pc;
|
||||
isa_exec_once(s);
|
||||
cpu.pc = s->dnpc;
|
||||
#ifdef CONFIG_ITRACE
|
||||
char *p = s->logbuf;
|
||||
p += snprintf(p, sizeof(s->logbuf), FMT_WORD ":", s->pc);
|
||||
int ilen = s->snpc - s->pc;
|
||||
int i;
|
||||
uint8_t *inst = (uint8_t *)&s->isa.inst.val;
|
||||
for (i = ilen - 1; i >= 0; i --) {
|
||||
p += snprintf(p, 4, " %02x", inst[i]);
|
||||
}
|
||||
int ilen_max = MUXDEF(CONFIG_ISA_x86, 8, 4);
|
||||
int space_len = ilen_max - ilen;
|
||||
if (space_len < 0) space_len = 0;
|
||||
space_len = space_len * 3 + 1;
|
||||
memset(p, ' ', space_len);
|
||||
p += space_len;
|
||||
|
||||
#ifndef CONFIG_ISA_loongarch32r
|
||||
void disassemble(char *str, int size, uint64_t pc, uint8_t *code, int nbyte);
|
||||
disassemble(p, s->logbuf + sizeof(s->logbuf) - p,
|
||||
MUXDEF(CONFIG_ISA_x86, s->snpc, s->pc), (uint8_t *)&s->isa.inst.val, ilen);
|
||||
#else
|
||||
p[0] = '\0'; // the upstream llvm does not support loongarch32r
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static void execute(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());
|
||||
}
|
||||
}
|
||||
|
||||
static void statistic() {
|
||||
IFNDEF(CONFIG_TARGET_AM, setlocale(LC_NUMERIC, ""));
|
||||
#define NUMBERIC_FMT MUXDEF(CONFIG_TARGET_AM, "%", "%'") PRIu64
|
||||
Log("host time spent = " NUMBERIC_FMT " us", g_timer);
|
||||
Log("total guest instructions = " NUMBERIC_FMT, g_nr_guest_inst);
|
||||
if (g_timer > 0) Log("simulation frequency = " NUMBERIC_FMT " inst/s", g_nr_guest_inst * 1000000 / g_timer);
|
||||
else Log("Finish running in less than 1 us and can not calculate the simulation frequency");
|
||||
}
|
||||
|
||||
void assert_fail_msg() {
|
||||
isa_reg_display();
|
||||
statistic();
|
||||
}
|
||||
|
||||
/* Simulate how the CPU works. */
|
||||
void cpu_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;
|
||||
}
|
||||
|
||||
uint64_t timer_start = get_time();
|
||||
|
||||
execute(n);
|
||||
|
||||
uint64_t timer_end = get_time();
|
||||
g_timer += timer_end - timer_start;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
132
nemu/src/cpu/difftest/dut.c
Normal file
132
nemu/src/cpu/difftest/dut.c
Normal file
|
@ -0,0 +1,132 @@
|
|||
/***************************************************************************************
|
||||
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
|
||||
*
|
||||
* NEMU is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
***************************************************************************************/
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <isa.h>
|
||||
#include <cpu/cpu.h>
|
||||
#include <memory/paddr.h>
|
||||
#include <utils.h>
|
||||
#include <difftest-def.h>
|
||||
|
||||
void (*ref_difftest_memcpy)(paddr_t addr, void *buf, size_t n, bool direction) = NULL;
|
||||
void (*ref_difftest_regcpy)(void *dut, bool direction) = NULL;
|
||||
void (*ref_difftest_exec)(uint64_t n) = NULL;
|
||||
void (*ref_difftest_raise_intr)(uint64_t NO) = NULL;
|
||||
|
||||
#ifdef CONFIG_DIFFTEST
|
||||
|
||||
static bool is_skip_ref = false;
|
||||
static int skip_dut_nr_inst = 0;
|
||||
|
||||
// this is used to let ref skip instructions which
|
||||
// can not produce consistent behavior with NEMU
|
||||
void difftest_skip_ref() {
|
||||
is_skip_ref = true;
|
||||
// If such an instruction is one of the instruction packing in QEMU
|
||||
// (see below), we end the process of catching up with QEMU's pc to
|
||||
// keep the consistent behavior in our best.
|
||||
// Note that this is still not perfect: if the packed instructions
|
||||
// already write some memory, and the incoming instruction in NEMU
|
||||
// will load that memory, we will encounter false negative. But such
|
||||
// situation is infrequent.
|
||||
skip_dut_nr_inst = 0;
|
||||
}
|
||||
|
||||
// this is used to deal with instruction packing in QEMU.
|
||||
// Sometimes letting QEMU step once will execute multiple instructions.
|
||||
// We should skip checking until NEMU's pc catches up with QEMU's pc.
|
||||
// The semantic is
|
||||
// Let REF run `nr_ref` instructions first.
|
||||
// We expect that DUT will catch up with REF within `nr_dut` instructions.
|
||||
void difftest_skip_dut(int nr_ref, int nr_dut) {
|
||||
skip_dut_nr_inst += nr_dut;
|
||||
|
||||
while (nr_ref -- > 0) {
|
||||
ref_difftest_exec(1);
|
||||
}
|
||||
}
|
||||
|
||||
void init_difftest(char *ref_so_file, long img_size, int port) {
|
||||
assert(ref_so_file != NULL);
|
||||
|
||||
void *handle;
|
||||
handle = dlopen(ref_so_file, RTLD_LAZY);
|
||||
assert(handle);
|
||||
|
||||
ref_difftest_memcpy = dlsym(handle, "difftest_memcpy");
|
||||
assert(ref_difftest_memcpy);
|
||||
|
||||
ref_difftest_regcpy = dlsym(handle, "difftest_regcpy");
|
||||
assert(ref_difftest_regcpy);
|
||||
|
||||
ref_difftest_exec = dlsym(handle, "difftest_exec");
|
||||
assert(ref_difftest_exec);
|
||||
|
||||
ref_difftest_raise_intr = dlsym(handle, "difftest_raise_intr");
|
||||
assert(ref_difftest_raise_intr);
|
||||
|
||||
void (*ref_difftest_init)(int) = dlsym(handle, "difftest_init");
|
||||
assert(ref_difftest_init);
|
||||
|
||||
Log("Differential testing: %s", ANSI_FMT("ON", ANSI_FG_GREEN));
|
||||
Log("The result of every instruction will be compared with %s. "
|
||||
"This will help you a lot for debugging, but also significantly reduce the performance. "
|
||||
"If it is not necessary, you can turn it off in menuconfig.", ref_so_file);
|
||||
|
||||
ref_difftest_init(port);
|
||||
ref_difftest_memcpy(RESET_VECTOR, guest_to_host(RESET_VECTOR), img_size, DIFFTEST_TO_REF);
|
||||
ref_difftest_regcpy(&cpu, DIFFTEST_TO_REF);
|
||||
}
|
||||
|
||||
static void checkregs(CPU_state *ref, vaddr_t pc) {
|
||||
if (!isa_difftest_checkregs(ref, pc)) {
|
||||
nemu_state.state = NEMU_ABORT;
|
||||
nemu_state.halt_pc = pc;
|
||||
isa_reg_display();
|
||||
}
|
||||
}
|
||||
|
||||
void difftest_step(vaddr_t pc, vaddr_t npc) {
|
||||
CPU_state ref_r;
|
||||
|
||||
if (skip_dut_nr_inst > 0) {
|
||||
ref_difftest_regcpy(&ref_r, DIFFTEST_TO_DUT);
|
||||
if (ref_r.pc == npc) {
|
||||
skip_dut_nr_inst = 0;
|
||||
checkregs(&ref_r, npc);
|
||||
return;
|
||||
}
|
||||
skip_dut_nr_inst --;
|
||||
if (skip_dut_nr_inst == 0)
|
||||
panic("can not catch up with ref.pc = " FMT_WORD " at pc = " FMT_WORD, ref_r.pc, pc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_skip_ref) {
|
||||
// to skip the checking of an instruction, just copy the reg state to reference design
|
||||
ref_difftest_regcpy(&cpu, DIFFTEST_TO_REF);
|
||||
is_skip_ref = false;
|
||||
return;
|
||||
}
|
||||
|
||||
ref_difftest_exec(1);
|
||||
ref_difftest_regcpy(&ref_r, DIFFTEST_TO_DUT);
|
||||
|
||||
checkregs(&ref_r, pc);
|
||||
}
|
||||
#else
|
||||
void init_difftest(char *ref_so_file, long img_size, int port) { }
|
||||
#endif
|
42
nemu/src/cpu/difftest/ref.c
Normal file
42
nemu/src/cpu/difftest/ref.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
/***************************************************************************************
|
||||
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
|
||||
*
|
||||
* NEMU is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
***************************************************************************************/
|
||||
|
||||
#include <isa.h>
|
||||
#include <cpu/cpu.h>
|
||||
#include <difftest-def.h>
|
||||
#include <memory/paddr.h>
|
||||
|
||||
__EXPORT void difftest_memcpy(paddr_t addr, void *buf, size_t n, bool direction) {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
__EXPORT void difftest_regcpy(void *dut, bool direction) {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
__EXPORT void difftest_exec(uint64_t n) {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
__EXPORT void difftest_raise_intr(word_t NO) {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
__EXPORT void difftest_init(int port) {
|
||||
void init_mem();
|
||||
init_mem();
|
||||
/* Perform ISA dependent initialization. */
|
||||
init_isa();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue