diff-test.c 4.0 KB
Newer Older
1 2
#include <dlfcn.h>

Z
Zihao Yu 已提交
3 4 5
#include "nemu.h"
#include "monitor/monitor.h"

Z
Zihao Yu 已提交
6 7 8 9
void (*ref_difftest_memcpy_from_dut)(paddr_t dest, void *src, size_t n) = NULL;
void (*ref_difftest_getregs)(void *c) = NULL;
void (*ref_difftest_setregs)(const void *c) = NULL;
void (*ref_difftest_exec)(uint64_t n) = NULL;
Z
Zihao Yu 已提交
10

11 12 13
static bool is_skip_ref = false;
static int skip_dut_nr_instr = 0;
static bool is_detach = false;
Z
Zihao Yu 已提交
14

15 16 17
// this is used to let ref skip instructions which
// can not produce consistent behavior with NEMU
void difftest_skip_ref() {
18
  if (is_detach) return;
19
  is_skip_ref = true;
20 21 22 23 24 25 26 27
  // 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_instr = 0;
28 29 30 31 32
}

// 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.
33 34 35 36
// 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) {
37
  if (is_detach) return;
38 39 40 41 42
  skip_dut_nr_instr += nr_dut;

  while (nr_ref -- > 0) {
    ref_difftest_exec(1);
  }
43
}
Z
Zihao Yu 已提交
44

Z
Zihao Yu 已提交
45
bool isa_difftest_checkregs(CPU_state *ref_r, vaddr_t pc);
Z
Zihao Yu 已提交
46
void isa_difftest_attach(void);
Z
Zihao Yu 已提交
47

48 49 50 51
void init_difftest(char *ref_so_file, long img_size) {
#ifndef DIFF_TEST
  return;
#endif
Z
Zihao Yu 已提交
52

53
  assert(ref_so_file != NULL);
Z
Zihao Yu 已提交
54

55 56 57 58 59 60 61 62 63
  void *handle;
  handle = dlopen(ref_so_file, RTLD_LAZY | RTLD_DEEPBIND);
  assert(handle);

  ref_difftest_memcpy_from_dut = dlsym(handle, "difftest_memcpy_from_dut");
  assert(ref_difftest_memcpy_from_dut);

  ref_difftest_getregs = dlsym(handle, "difftest_getregs");
  assert(ref_difftest_getregs);
Z
Zihao Yu 已提交
64

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
  ref_difftest_setregs = dlsym(handle, "difftest_setregs");
  assert(ref_difftest_setregs);

  ref_difftest_exec = dlsym(handle, "difftest_exec");
  assert(ref_difftest_exec);

  void (*ref_difftest_init)(void) = dlsym(handle, "difftest_init");
  assert(ref_difftest_init);

  Log("Differential testing: \33[1;32m%s\33[0m", "ON");
  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 include/common.h.", ref_so_file);

  ref_difftest_init();
Z
Zihao Yu 已提交
80
  ref_difftest_memcpy_from_dut(PC_START, guest_to_host(IMAGE_START), img_size);
81 82
  char *mainargs = guest_to_host(0);
  ref_difftest_memcpy_from_dut(PC_START - IMAGE_START, mainargs, strlen(mainargs) + 1);
Z
Zihao Yu 已提交
83
  ref_difftest_setregs(&cpu);
Z
Zihao Yu 已提交
84 85
}

86 87 88 89 90 91 92 93 94 95 96
static void checkregs(CPU_state *ref, vaddr_t pc) {
  // TODO: Check the registers state with QEMU.
  if (!isa_difftest_checkregs(ref, pc)) {
    extern void isa_reg_display(void);
    isa_reg_display();
    nemu_state.state = NEMU_ABORT;
    nemu_state.halt_pc = pc;
  }
}

void difftest_step(vaddr_t ori_pc, vaddr_t next_pc) {
97
  CPU_state ref_r;
Z
Zihao Yu 已提交
98

99 100
  if (is_detach) return;

101
  if (skip_dut_nr_instr > 0) {
102 103 104
    ref_difftest_getregs(&ref_r);
    if (ref_r.pc == next_pc) {
      checkregs(&ref_r, next_pc);
105 106
      skip_dut_nr_instr = 0;
      return;
107
    }
108 109
    skip_dut_nr_instr --;
    if (skip_dut_nr_instr == 0)
Z
Zihao Yu 已提交
110
      panic("can not catch up with ref.pc = " FMT_WORD " at pc = " FMT_WORD, ref_r.pc, ori_pc);
Z
Zihao Yu 已提交
111 112 113
    return;
  }

114 115
  if (is_skip_ref) {
    // to skip the checking of an instruction, just copy the reg state to reference design
Z
Zihao Yu 已提交
116
    ref_difftest_setregs(&cpu);
117
    is_skip_ref = false;
Z
Zihao Yu 已提交
118 119 120
    return;
  }

121 122
  ref_difftest_exec(1);
  ref_difftest_getregs(&ref_r);
Z
Zihao Yu 已提交
123

124
  checkregs(&ref_r, ori_pc);
Z
Zihao Yu 已提交
125
}
126 127 128 129 130 131 132 133 134 135 136 137

void difftest_detach() {
  is_detach = true;
}

void difftest_attach() {
#ifndef DIFF_TEST
  return;
#endif

  is_detach = false;
  is_skip_ref = false;
138
  skip_dut_nr_instr = 0;
139

Z
Zihao Yu 已提交
140
  isa_difftest_attach();
141
}