diff --git a/.gitignore b/.gitignore index 6bae0e15f3..73e201a2e1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ autom4te.cache/ .gdb_history .#* *~ +.vscode + diff --git a/difftest/.gitignore b/difftest/.gitignore new file mode 100644 index 0000000000..378eac25d3 --- /dev/null +++ b/difftest/.gitignore @@ -0,0 +1 @@ +build diff --git a/difftest/Makefile b/difftest/Makefile new file mode 100644 index 0000000000..91b1939127 --- /dev/null +++ b/difftest/Makefile @@ -0,0 +1,84 @@ +CC = clang +CXX = clang++ + +GUEST_ISA = riscv64 +BUILD_DIR = $(abspath ./build) +TARGET = $(BUILD_DIR)/$(GUEST_ISA)-spike-so + +# Dependencies for building Spike +SPIKE_PATH = $(abspath ..) +SPIKE_TARGET = $(BUILD_DIR)/spike +SPIKE_MAKEFILE = $(BUILD_DIR)/Makefile + +SPIKE_CFLAGS = -O3 -DDIFFTEST +SPIKE_CXXFLAGS = -O3 -DDIFFTEST -Wno-c99-designator +SPIKE_LDFLAGS = + +ifneq ($(CPU),) +SPIKE_CFLAGS += -DCPU_$(CPU) +SPIKE_CXXFLAGS += -DCPU_$(CPU) +endif + +ifeq ($(DEBUG),) +SPIKE_CFLAGS += -g0 +SPIKE_CXXFLAGS += -g0 +endif + +ifeq ($(SANCOV),1) +SPIKE_CFLAGS += -fsanitize-coverage=trace-pc-guard -fsanitize-coverage=pc-table -g +SPIKE_CXXFLAGS += -fsanitize-coverage=trace-pc-guard -fsanitize-coverage=pc-table -g +SPIKE_LDFLAGS += -fsanitize-coverage=trace-pc-guard -fsanitize-coverage=pc-table -g +endif + +CONFIGURE_FLAGS = -q CC=$(CC) CXX=$(CXX) \ + --with-boost=no --with-boost-asio=no --with-boost-regex=no \ + CFLAGS="$(SPIKE_CFLAGS)" CXXFLAGS="$(SPIKE_CXXFLAGS)" \ + LDFLAGS="$(SPIKE_LDFLAGS)" + +SPIKE_INCS = fesvr riscv disasm customext fdt softfloat spike_main spike_dasm +INC_PATH = -I$(SPIKE_PATH) -I$(BUILD_DIR) $(addprefix -I$(SPIKE_PATH)/, $(SPIKE_INCS)) + +SPIKE_SRCS = $(shell find $(SPIKE_PATH) -maxdepth 2 -name '*.cc') \ + $(shell find $(SPIKE_PATH) -maxdepth 2 -name '*.h') +SPIKE_LIBS = libriscv.a libdisasm.a libsoftfloat.a libfesvr.a libfdt.a +INC_LIBS = $(addprefix $(BUILD_DIR)/, $(SPIKE_LIBS)) + +# We need some utilities from spike +CXX_UTILS = $(BUILD_DIR)/difftest_utils.cc + +$(CXX_UTILS): $(SPIKE_PATH)/spike_main/spike.cc + cp $< $(CXX_UTILS) + @sed -i 's/static //g' $@ + @sed -i 's/int main/static int spike_main/g' $@ + +# Dependencies for building the difftest dynamic library +DIFFTEST_SOURCES = $(shell find . -maxdepth 1 -name '*.cc') $(CXX_UTILS) +DIFFTEST_HEADERS = $(shell find . -maxdepth 1 -name '*.h') +CXXFLAGS = $(SPIKE_CXXFLAGS) --std=c++17 -shared -fPIC +ifneq ($(SANCOV),1) +CXXFLAGS += -flto +endif + +ifneq ($(DEBUG),) +CONFIGURE_FLAGS += --enable-commitlog +CXXFLAGS += -DDIFFTEST_LOG_FILE=\"$(NOOP_HOME)/build/spike.log\" +endif + +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +$(SPIKE_MAKEFILE): $(SPIKE_PATH)/configure | $(BUILD_DIR) + cd $(@D) && $(abspath $(SPIKE_PATH))/configure $(CONFIGURE_FLAGS) + +$(SPIKE_TARGET): $(SPIKE_MAKEFILE) $(SPIKE_SRCS) + $(MAKE) -s -C $(BUILD_DIR) + +$(TARGET): $(SPIKE_TARGET) $(DIFFTEST_SOURCES) $(DIFFTEST_HEADERS) | $(BUILD_DIR) + $(CXX) $(CXXFLAGS) $(INC_PATH) $(DIFFTEST_SOURCES) $(INC_LIBS) -o $@ + +clean: + rm -rf $(BUILD_DIR) + +.DEFAULT_GOAL = $(TARGET) + +.PHONY: clean diff --git a/difftest/README.md b/difftest/README.md new file mode 100644 index 0000000000..c2b67b54fb --- /dev/null +++ b/difftest/README.md @@ -0,0 +1,31 @@ +# Spike as a Reference Model + +The Spike RISC-V ISA simulator can be a golden reference model for CPU co-simulation as well. +This directory contains the wrapper files for using Spike as the REF used in DiffTest. + +## Supported CPUs + +RISC-V allows diverse implementation-specific behaviors as long as they don't violate the ISA. +Therefore, to co-simulate Spike, one of the RISC-V implementations, with another implementation +without errors, we have to align their behaviors on the undefined or design-specific regions. + +Currently we are supporting CPUs including: +- [XiangShan](https://github.com/OpenXiangShan/XiangShan) +- [NutShell](https://github.com/OSCPU/NutShell) +- [Rocket Chip](https://github.com/chipsalliance/rocket-chip) + +## How to Compile + +To compile the Spike into a dynamic library for co-simulation via DiffTest, run: + +``` +cd riscv-isa-sim/difftest +make CPU=XIANGSHAN -jN +``` + +Replace `XIANGSHAN` with `ROCKET` or `NUTSHELL` for co-simulation with Rocket or NutShell. + +## Coverage Instrumentation with LLVM + +Add `SANCOV=1` to the `make` command to instrument the source code with LLVM coverage. +This enables fuzzers to be guided with branch coverage feedback from a golden RISC-V model. diff --git a/difftest/difftest-def.h b/difftest/difftest-def.h new file mode 100644 index 0000000000..0846164b59 --- /dev/null +++ b/difftest/difftest-def.h @@ -0,0 +1,46 @@ +#ifndef __DIFFTEST_DEF_H +#define __DIFFTEST_DEF_H + +#if defined(CPU_NUTSHELL) +#elif defined(CPU_XIANGSHAN) +#elif defined(CPU_ROCKET_CHIP) +#else +// This is the default CPU +#define CPU_NUTSHELL +#endif + +#if defined(CPU_XIANGSHAN) || defined(CPU_ROCKET_CHIP) +#define CONFIG_DIFF_FPU +#endif + +#if defined(CPU_XIANGSHAN) +#define CONFIG_DIFF_DEBUG_MODE +// #define CONFIG_DIFF_RVV // Default off +#endif + +#if defined(CPU_NUTSHELL) +#define CONFIG_DIFF_ISA_STRING "rv64imaczicsr_zifencei" +#define CONFIG_MEMORY_SIZE (2 * 1024 * 1024 * 1024UL) +#define CONFIG_FLASH_BASE 0x40000000UL +#define CONFIG_FLASH_SIZE 0x1000UL +#define CONFIG_PMP_NUM 0 +#elif defined(CPU_XIANGSHAN) + #if defined(CONFIG_DIFF_RVV) + #define CONFIG_DIFF_ISA_STRING "RV64IMAFDCV_zba_zbb_zbc_zbs_zbkb_zbkc_zbkx_zknd_zkne_zknh_zksed_zksh_svinval" + #else + #define CONFIG_DIFF_ISA_STRING "RV64IMAFDC_zba_zbb_zbc_zbs_zbkb_zbkc_zbkx_zknd_zkne_zknh_zksed_zksh_svinval" + #endif // CONFIG_DIFF_RVV +#define CONFIG_MEMORY_SIZE (16 * 1024 * 1024 * 1024UL) +#define CONFIG_FLASH_BASE 0x10000000UL +#define CONFIG_FLASH_SIZE 0x100000UL +#define CONFIG_PMP_NUM 16 +#define CONFIG_PMP_GRAN 12 +#elif defined(CPU_ROCKET_CHIP) +#define CONFIG_DIFF_ISA_STRING "rv64imafdczicsr_zifencei_zihpm_zicntr" +#define CONFIG_MEMORY_SIZE (2 * 1024 * 1024 * 1024UL) +#define CONFIG_FLASH_BASE 0x10000000UL +#define CONFIG_FLASH_SIZE 0x10000UL +#define CONFIG_PMP_NUM 0 +#endif + +#endif diff --git a/difftest/difftest.cc b/difftest/difftest.cc new file mode 100644 index 0000000000..8dc3dccd7e --- /dev/null +++ b/difftest/difftest.cc @@ -0,0 +1,524 @@ +#include "decode_macros.h" +#include "difftest.h" +#include "disasm.h" +#include "softfloat.h" + +// Some static utilities and configurations +static debug_module_config_t difftest_dm_config = { + .progbufsize = 2, + .max_sba_data_width = 0, + .require_authentication = false, + .abstract_rti = 0, + .support_hasel = true, + .support_abstract_csr_access = true, + .support_abstract_fpr_access = true, + .support_haltgroups = true, + .support_impebreak = false +}; +extern std::vector> make_mems(const std::vector &layout); + +static DifftestRef *ref = nullptr; +static size_t overrided_mem_size = 0; +static size_t overrided_mhartid = 0; + +DifftestRef::DifftestRef() : + cfg(create_cfg()), + mems(make_mems(cfg->mem_layout())), + plugin_devices(create_devices()), + sim(create_sim(cfg)), + p(sim->get_core(0UL)), + state(p->get_state()) { +#if CONFIG_PMP_NUM > 0 + p->set_pmp_granularity(1 << CONFIG_PMP_GRAN); +#endif +} + +DifftestRef::~DifftestRef() { + delete cfg; + for (const auto& pair : mems) { + delete pair.second; + } + for (const auto& pair : plugin_devices) { + delete pair.second; + } + delete sim; +} + +void DifftestRef::step(uint64_t n) { + sim->step(n); +} + +void DifftestRef::skip_one(bool isRVC, bool wen, uint32_t wdest, uint64_t wdata) { + state->pc += isRVC ? 2 : 4; + // TODO: what if skip with fpwen? + if (wen) { + state->XPR.write(wdest, wdata); + } + // minstret decrements itself when written to match Spike's automated increment. + // Therefore, we need to add + 2 here. + state->minstret->write(state->minstret->read() + 2); +} + +void DifftestRef::get_regs(diff_context_t *ctx) { + for (int i = 0; i < NXPR; i++) { + ctx->gpr[i] = state->XPR[i]; + } +#ifdef CONFIG_DIFF_FPU + for (int i = 0; i < NFPR; i++) { + ctx->fpr[i] = unboxF64(state->FPR[i]); + } +#endif + ctx->pc = state->pc; + ctx->mstatus = state->mstatus->read(); + ctx->mcause = state->mcause->read(); + ctx->mepc = state->mepc->read(); + ctx->sstatus = state->sstatus->read(); + ctx->scause = state->scause->read(); + ctx->sepc = state->sepc->read(); + ctx->satp = state->satp->read(); + ctx->mip = state->mip->read(); + ctx->mie = state->mie->read(); + ctx->mscratch = state->csrmap[CSR_MSCRATCH]->read(); + ctx->sscratch = state->csrmap[CSR_SSCRATCH]->read(); + ctx->mideleg = state->mideleg->read(); + ctx->medeleg = state->medeleg->read(); + ctx->mtval = state->mtval->read(); + ctx->stval = state->stval->read(); + ctx->mtvec = state->mtvec->read(); + ctx->stvec = state->stvec->read(); + ctx->priv = state->prv; +#ifdef DIFF_DEBUG_MODE + ctx->debugMode = state->debug_mode; + ctx->dcsr = state->dcsr->read(); + ctx->dpc = state->dpc->read(); + ctx->dscratch0 = state->csrmap[CSR_DSCRATCH0]->read(); + ctx->dscratch1 = state->csrmap[CSR_DSCRATCH1]->read(); +#endif // DIFF_DEBUG_MODE + +#ifdef CONFIG_DIFF_RVV + auto& vstate = p->VU; + /*******************************ONLY FOR VLEN=128,ELEN=64*******************************************/ + for(int i = 0; i < NVPR; i++){ + auto vReg_Val0 = vstate.elt(i, 0,false); + auto vReg_Val1 = vstate.elt(i, 1,false); + ctx->vr[i]._64[0] = vReg_Val0; + ctx->vr[i]._64[1] = vReg_Val1; + } + /***************************************************************************************************/ + ctx->vstart = vstate.vstart->read(); + ctx->vxsat = vstate.vxsat->read(); + ctx->vxrm = vstate.vxrm->read(); + ctx->vcsr = state->csrmap[CSR_VCSR]->read(); + ctx->vl = vstate.vl->read(); + ctx->vtype = vstate.vtype->read(); + ctx->vlenb = vstate.vlenb; +#endif // CONFIG_DIFF_RVV +} + +void DifftestRef::set_regs(diff_context_t *ctx, bool on_demand) { + for (int i = 0; i < NXPR; i++) { + if (!on_demand || state->XPR[i] != ctx->gpr[i]) { + state->XPR.write(i, ctx->gpr[i]); + } + } +#ifdef CONFIG_DIFF_FPU + for (int i = 0; i < NFPR; i++) { + if (!on_demand || unboxF64(state->FPR[i]) != ctx->fpr[i]) { + state->FPR.write(i, freg(f64(ctx->fpr[i]))); + } + } +#endif + if (!on_demand || state->pc != ctx->pc) { + state->pc = ctx->pc; + } + if (!on_demand || state->mstatus->read() != ctx->mstatus) { + state->mstatus->write(ctx->mstatus); + } + if (!on_demand || state->mcause->read() != ctx->mcause) { + state->mcause->write(ctx->mcause); + } + if (!on_demand || state->mepc->read() != ctx->mepc) { + state->mepc->write(ctx->mepc); + } + if (!on_demand || state->sstatus->read() != ctx->sstatus) { + state->sstatus->write(ctx->sstatus); + } + if (!on_demand || state->scause->read() != ctx->scause) { + state->scause->write(ctx->scause); + } + if (!on_demand || state->sepc->read() != ctx->sepc) { + state->sepc->write(ctx->sepc); + } + if (!on_demand || state->satp->read() != ctx->satp) { + state->satp->write(ctx->satp); + } + if (!on_demand || state->mip->read() != ctx->mip) { + state->mip->write(ctx->mip); + } + if (!on_demand || state->mie->read() != ctx->mie) { + state->mie->write(ctx->mie); + } + if (!on_demand || state->csrmap[CSR_MSCRATCH]->read() != ctx->mscratch) { + state->csrmap[CSR_MSCRATCH]->write(ctx->mscratch); + } + if (!on_demand || state->csrmap[CSR_SSCRATCH]->read() != ctx->sscratch) { + state->csrmap[CSR_SSCRATCH]->write(ctx->sscratch); + } + if (!on_demand || state->mideleg->read() != ctx->mideleg) { + state->mideleg->write(ctx->mideleg); + } + if (!on_demand || state->medeleg->read() != ctx->medeleg) { + state->medeleg->write(ctx->medeleg); + } + if (!on_demand || state->mtval->read() != ctx->mtval) { + state->mtval->write(ctx->mtval); + } + if (!on_demand || state->stval->read() != ctx->stval) { + state->stval->write(ctx->stval); + } + if (!on_demand || state->mtvec->read() != ctx->mtvec) { + state->mtvec->write(ctx->mtvec); + } + if (!on_demand || state->stvec->read() != ctx->stvec) { + state->stvec->write(ctx->stvec); + } + if (!on_demand || state->prv != ctx->priv) { + state->prv = ctx->priv; + } +#ifdef DIFF_DEBUG_MODE + if (!on_demand || state->debug_mode->read() != ctx->debugMode) { + state->debug_mode = ctx->debugMode; + } + if (!on_demand || state->dcsr->read() != ctx->dcsr) { + state->dcsr->write(ctx->dcsr); + } + if (!on_demand || state->dpc->read() != ctx->dpc) { + state->dpc->write(ctx->dpc); + } + if (!on_demand || state->csrmap[CSR_DSCRATCH0]->read() != ctx->dscratch0) { + state->csrmap[CSR_DSCRATCH0]->write(ctx->dscratch0); + } + if (!on_demand || state->csrmap[CSR_DSCRATCH1]->read() != ctx->dscratch1) { + state->csrmap[CSR_DSCRATCH1]->write(ctx->dscratch1); + } +#endif // DIFF_DEBUG_MODE + +#ifdef CONFIG_DIFF_RVV + auto& vstate = p->VU; + /**********************ONLY FOR VLEN=128,ELEN=64************************************/ + for (int i = 0; i < NVPR; i++) { + auto &vReg_Val0 = p->VU.elt(i, 0, true); + auto &vReg_Val1 = p->VU.elt(i, 1, true); + if (!on_demand || vReg_Val0 != ctx->vr[i]._64[0]) { + vReg_Val0 = ctx->vr[i]._64[0]; + } + if(!on_demand || vReg_Val1 != ctx->vr[i]._64[1]){ + vReg_Val1 = ctx->vr[i]._64[1]; + } + } + /***********************************************************************************/ + if (!on_demand || vstate.vstart->read() != ctx->vstart) { + vstate.vstart->write_raw(ctx->vstart); + } + /**********************NEED TO ADD WRITE*********************************************/ + if (!on_demand || vstate.vxsat->read() != ctx->vxsat) { + // vstate.vxsat->write(ctx->vxsat); + } + if (!on_demand || vstate.vxrm->read() != ctx->vxrm) { + vstate.vxrm->write_raw(ctx->vxrm); + } + /******************************Don't need write vcsr**********************************/ + // if (!on_demand || state->csrmap[CSR_VCSR]->read() !=ctx->vcsr) { + // csrmap[CSR_VCSR]->write(ctx->vcsr); + // } + if (!on_demand || vstate.vl->read() != ctx->vl) { + vstate.vl->write_raw(ctx->vl); + } + if (!on_demand || vstate.vtype->read() != ctx->vtype) { + vstate.vtype->write_raw(ctx->vtype); + } + if (!on_demand || vstate.vlenb != ctx->vlenb) { + vstate.vlenb = ctx->vlenb; + } +#endif // CONFIG_DIFF_RVV +} + +void DifftestRef::memcpy_from_dut(reg_t dest, void* src, size_t n) { + while (n) { + bool is_zero = true; + for (int i=0; i < (PGSIZE/sizeof(uint64_t)); i++) { + if (((uint64_t*)src)[i] != 0) { + is_zero = false; + break; + } + } + + size_t n_bytes = (n > PGSIZE) ? PGSIZE : n; + if (!is_zero) { + char *base = sim->addr_to_mem(dest); + memcpy(base, src, n_bytes); + } + dest += PGSIZE; + src = (char *)src + PGSIZE; + n -= n_bytes; + } +} + +void DifftestRef::debug_memcpy_from_dut(reg_t dest, void* src, size_t n) { +#ifdef CONFIG_DIFF_DEBUG_MODE + // addr is absolute physical addr + // now we are not using this right now because I don't know how spike does if from device + dummy_debug_t* dummy_debug = (dummy_debug_t *)plugin_devices.front().second; + if (!dummy_debug) { + return; + } + // start addr is virtual addr used by simulator + int offset = (dest - DM_BASE_ADDR) / sizeof(uint8_t); + uint8_t* start_addr = dummy_debug->dummy_debug_mem + offset; + memcpy(start_addr, src, n); // TODO copy to device not addr +#else +#endif +} + +int DifftestRef::store_commit(uint64_t *addr, uint64_t *data, uint8_t *mask) { + return sim->dut_store_commit(addr, data, mask); +} + +void DifftestRef::raise_intr(uint64_t no) { + // Debug Intr + if (no == 0xc) { + p->halt_request = p->HR_REGULAR; + step(0); + p->halt_request = p->HR_NONE; + } else { + uint64_t mip_bit = 0x1UL << (no & 0xf); + bool is_timer_interrupt = mip_bit & 0xa0UL; + bool is_external_interrupt = mip_bit & 0xb00UL; + bool from_outside = !(mip_bit & state->mip->read()); + bool external_set = (is_timer_interrupt || is_external_interrupt) && from_outside; + if (external_set) { + state->mip->backdoor_write_with_mask(mip_bit, mip_bit); + step(1); + state->mip->backdoor_write_with_mask(mip_bit, ~mip_bit); + } else { + step(1); + } + } +} + +void DifftestRef::display() { + int i; + for (i = 0; i < 32; i ++) { + printf("%4s: " FMT_WORD " ", xpr_name[i], state->XPR[i]); + if (i % 4 == 3) { + printf("\n"); + } + } + for (i = 0; i < 32; i ++) { + printf("%4s: " FMT_WORD " ", fpr_name[i], unboxF64(state->FPR[i])); + if (i % 4 == 3) { + printf("\n"); + } + } + printf("pc: " FMT_WORD " mstatus: " FMT_WORD " mcause: " FMT_WORD " mepc: " FMT_WORD "\n", + state->pc, state->mstatus->read(), state->mcause->read(), state->mepc->read()); + printf("%22s sstatus: " FMT_WORD " scause: " FMT_WORD " sepc: " FMT_WORD "\n", + "", state->sstatus->read(), state->scause->read(), state->sepc->read()); + printf("satp: " FMT_WORD "\n", state->satp->read()); + printf("mip: " FMT_WORD " mie: " FMT_WORD " mscratch: " FMT_WORD " sscratch: " FMT_WORD "\n", + state->mip->read(), state->mie->read(), state->csrmap[CSR_MSCRATCH]->read(), state->csrmap[CSR_MSCRATCH]->read()); + printf("mideleg: " FMT_WORD " medeleg: " FMT_WORD "\n", + state->mideleg->read(), state->medeleg->read()); + printf("mtval: " FMT_WORD " stval: " FMT_WORD " mtvec: " FMT_WORD " stvec: " FMT_WORD "\n", + state->mtval->read(), state->stval->read(), state->mtvec->read(), state->stvec->read()); + printf("privilege mode:%ld\n", state->prv); + for (int i = 0; i < CONFIG_PMP_NUM; i++) { + auto cfgidx = i / 4; + if (p->get_xlen() == 64) { + cfgidx -= cfgidx % 2; + } + unsigned pmpcfg = (state->csrmap[CSR_PMPCFG0 + cfgidx]->read() >> (i % (p->get_xlen() / 8)) * 8) & 0xffU; + printf("%2d: cfg:0x%02x addr:0x%016lx", i, pmpcfg, state->pmpaddr[i]->read()); + if (i % 2 == 1) printf("\n"); + else printf(" | "); + } + fflush(stdout); +} + +const cfg_t *DifftestRef::create_cfg() { + auto mem_size = overrided_mem_size ? overrided_mem_size : CONFIG_MEMORY_SIZE; + auto memory_layout = std::vector{ + mem_cfg_t{DRAM_BASE, mem_size}, + }; + auto const cfg = new cfg_t( + // std::pair default_initrd_bounds, + std::make_pair(0, 0), + // const char *default_bootargs, + nullptr, + // const char *default_isa, + CONFIG_DIFF_ISA_STRING, + // const char *default_priv + DEFAULT_PRIV, + // const char *default_varch, + DEFAULT_VARCH, + // const bool default_misaligned, + false, + // const endianness_t default_endianness, + endianness_little, + // const reg_t default_pmpregions, + CONFIG_PMP_NUM, + // const std::vector &default_mem_layout, + memory_layout, + // const std::vector default_hartids, + std::vector{overrided_mhartid}, + // bool default_real_time_clint, + false, + // const reg_t default_trigger_count + 0 + ); + return cfg; +} + +const std::vector> DifftestRef::create_devices() { +#if defined(CONFIG_FLASH_BASE) && defined(CONFIG_FLASH_SIZE) + // Initialize the flash with preset instructions + const uint32_t flash_init[] = { + 0x0010029bUL, // CONFIG_FLASH_SIZE + 0: addiw t0, zero, 1 + 0x01f29293UL, // CONFIG_FLASH_SIZE + 4: slli t0, t0, 0x1f + 0x00028067UL, // CONFIG_FLASH_SIZE + 8: jr t0 + }; + std::vector rom_data((char*)flash_init, (char*)flash_init + sizeof(flash_init)); + rom_data.resize(CONFIG_FLASH_SIZE, 0); +#endif + return std::vector>{ +#ifdef CONFIG_DIFF_DEBUG_MODE + std::make_pair(reg_t(DM_BASE_ADDR), new dummy_debug_t), +#endif +#if defined(CONFIG_FLASH_BASE) && defined(CONFIG_FLASH_SIZE) + std::make_pair(reg_t(CONFIG_FLASH_BASE), new rom_device_t(rom_data)), +#endif + }; +} + +sim_t *DifftestRef::create_sim(const cfg_t *cfg) { + sim_t *s = new sim_t( + // const cfg_t *cfg, + cfg, + // bool halted, + false, + // std::vector> mems + mems, + // std::vector> plugin_devices + plugin_devices, + // const std::vector& args + std::vector{}, + // const debug_module_config_t &dm_config + difftest_dm_config, + // const char *log_path +#ifndef LOG_PATH + nullptr, +#else + LOG_PATH, +#endif // LOG_PATH + //bool dtb_enabled, const char *dtb_file, bool socket_enabled, FILE *cmd_file + false, nullptr, false, nullptr + ); + + return s; +} + +// Following are the interfaces for co-simulation with other designs + +extern "C" { + +int difftest_disambiguation_state() { + return ref->disambiguation_state(); +} + +void difftest_memcpy(uint64_t addr, void *buf, size_t n, bool direction) { + if (direction == DIFFTEST_TO_REF) { + ref->memcpy_from_dut(addr, buf, n); + } else { + printf("difftest_memcpy with DIFFTEST_TO_DUT is not supported yet\n"); + fflush(stdout); + assert(0); + } +} + +void difftest_regcpy(diff_context_t* dut, bool direction, bool on_demand) { + if (direction == DIFFTEST_TO_REF) { + ref->set_regs(dut, on_demand); + } else { + ref->get_regs(dut); + } +} + +void difftest_csrcpy(void *dut, bool direction) { + +} + +void difftest_uarchstatus_sync(void *dut) { + ref->update_uarch_status(dut); +} + +void update_dynamic_config(void* config) { + ref->update_dynamic_config(config); +} + +void difftest_exec(uint64_t n) { + ref->step(n); +} + +void difftest_skip_one(bool isRVC, bool wen, uint32_t wdest, uint64_t wdata) { + ref->skip_one(isRVC, wen, wdest, wdata); +} + +void difftest_init(int port) { + ref = new DifftestRef; +#ifdef RISCV_ENABLE_COMMITLOG + setvbuf(p->get_log_file(), NULL, _IONBF, 0); +#endif +} + +void difftest_raise_intr(uint64_t NO) { + ref->raise_intr(NO); +} + +void isa_reg_display() { + ref->display(); +} + +void difftest_display() { + ref->display(); +} + +int difftest_store_commit(uint64_t *addr, uint64_t *data, uint8_t *mask) { + return ref->store_commit(addr, data, mask); +} + +uint64_t difftest_guided_exec(void *) { + ref->step(1); + return 0; +} + +void debug_mem_sync(reg_t addr, void* buf, size_t n) { + ref->debug_memcpy_from_dut(addr, buf, n); +} + +void difftest_load_flash(void *flash_bin, size_t size) { + +} + +void difftest_set_mhartid(int mhartid) { + overrided_mhartid = mhartid; +} + +void difftest_close() { + delete ref; +} + +void difftest_set_ramsize(size_t size) { + overrided_mem_size = size; +} + +} diff --git a/difftest/difftest.h b/difftest/difftest.h new file mode 100644 index 0000000000..87baac35e3 --- /dev/null +++ b/difftest/difftest.h @@ -0,0 +1,133 @@ +#ifndef __DIFFTEST_H +#define __DIFFTEST_H + +#include +#include +#include +#include + +#include "difftest-def.h" +#include "dummy_debug.h" +#include "sim.h" + +enum { DIFFTEST_TO_DUT, DIFFTEST_TO_REF }; +#define FMT_WORD "0x%016lx" + +# define DIFFTEST_REG_SIZE (sizeof(uint64_t) * 33) // GRPs + pc + +#ifndef DIFFTEST_LOG_FILE +#define DIFFTEST_LOG_FILE nullptr +#endif + +/***************DON'T CHANGE ORDER****************************/ +typedef struct { + uint64_t gpr[32]; +#ifdef CONFIG_DIFF_FPU + uint64_t fpr[32]; +#endif + uint64_t priv; + uint64_t mstatus; + uint64_t sstatus; + uint64_t mepc; + uint64_t sepc; + uint64_t mtval; + uint64_t stval; + uint64_t mtvec; + uint64_t stvec; + uint64_t mcause; + uint64_t scause; + uint64_t satp; + uint64_t mip; + uint64_t mie; + uint64_t mscratch; + uint64_t sscratch; + uint64_t mideleg; + uint64_t medeleg; + uint64_t pc; +#ifdef CONFIG_DIFF_RVV + #define VLEN 128 + #define VENUM64 (VLEN/64) + #define VENUM32 (VLEN/32) + #define VENUM16 (VLEN/16) + #define VENUM8 (VLEN/8) + + union { + uint64_t _64[VENUM64]; + uint32_t _32[VENUM32]; + uint16_t _16[VENUM16]; + uint8_t _8[VENUM8]; + } vr[32]; + + uint64_t vstart; + uint64_t vxsat; + uint64_t vxrm; + uint64_t vcsr; + uint64_t vl; + uint64_t vtype; + uint64_t vlenb; +#endif // CONFIG_DIFF_RVV + +#ifdef CONFIG_DIFF_DEBUG_MODE + uint64_t debugMode; + uint64_t dcsr; + uint64_t dpc; + uint64_t dscratch0; + uint64_t dscratch1; +#endif // CONFIG_DIFF_DEBUG_MODE +} diff_context_t; + +class DifftestRefConfig { +public: + bool ignore_illegal_mem_access = false; + bool debug_difftest = false; +}; + +class DifftestUarchStatus { +public: + uint64_t sc_failed = 0; +}; + +class DifftestRef { +public: + DifftestRef(); + ~DifftestRef(); + void step(uint64_t n); + void skip_one(bool isRVC, bool wen, uint32_t wdest, uint64_t wdata); + void get_regs(diff_context_t *ctx); + void set_regs(diff_context_t *ctx, bool on_demand); + void memcpy_from_dut(reg_t dest, void* src, size_t n); + void debug_memcpy_from_dut(reg_t dest, void* src, size_t n); + int store_commit(uint64_t *addr, uint64_t *data, uint8_t *mask); + void raise_intr(uint64_t no); + void display(); + void update_dynamic_config(void* config) { +#ifdef RISCV_ENABLE_COMMITLOG + p->enable_log_commits(); +#endif + auto c = (DifftestRefConfig *)config; + sim->enable_difftest_logs = c->debug_difftest; + } + void update_uarch_status(void *status) { + auto s = (DifftestUarchStatus *)status; + if (s->sc_failed) { + sim->sc_failed = true; + } + } + inline int disambiguation_state() { + return sim->in_ambiguation_state(); + } + +private: + const cfg_t *cfg; + const std::vector> mems; + const std::vector> plugin_devices; + sim_t * const sim; + processor_t * const p; + state_t * const state; + + const cfg_t *create_cfg(); + const std::vector> create_devices(); + sim_t *create_sim(const cfg_t *cfg); +}; + +#endif diff --git a/difftest/difftrace.h b/difftest/difftrace.h new file mode 100644 index 0000000000..5b55f817a2 --- /dev/null +++ b/difftest/difftrace.h @@ -0,0 +1,198 @@ +#ifndef __DIFFTRACE_H +#define __DIFFTRACE_H + +#include +#include +#include +#include "difftest-def.h" + +class store_trace_t { +public: + uint64_t paddr; + uint64_t data; + uint8_t mask; + + store_trace_t(uint64_t paddr, uint64_t data, uint8_t mask) : paddr(paddr), data(data), mask(mask) { + do_align(); + } + store_trace_t(uint64_t paddr, uint64_t data, int len) : paddr(paddr), data(data), mask((1U << len) - 1) { + if (len != 8) { + this->data &= (1UL << (len * 8)) - 1UL; + } + do_align(); + }; + +private: + void do_align() { + uint64_t offset = paddr % 8UL; + if (offset) { + int len = std::log2((long)mask + 1); + paddr = paddr - offset; + data &= (1UL << (len * 8)) - 1UL; + data <<= offset << 3; + mask <<= offset; + } + } +}; + +class store_tracker_t { +public: + store_tracker_t() : dirty_accessed(false) {} + + inline bool state() { return dirty_accessed; } + inline void state_reset() { dirty_accessed = false; } + inline void reset() { + dirty.clear(); + state_reset(); + } + inline void on_store(uint64_t address) { + dirty.insert(hash_key(address)); + } + inline void on_read(uint64_t address) { + auto key = hash_key(address); + if (dirty.count(key) > 0 || (check_next(address) && dirty.count(key + 1) > 0)) { + dirty_accessed = true; + } + } + +private: + bool dirty_accessed; + std::unordered_set dirty; + +protected: + inline virtual uint64_t hash_key(uint64_t address) { + return address / sizeof(uint64_t); + } + inline virtual bool check_next(uint64_t address) { + return address % sizeof(uint64_t); + } +}; + + +class diff_trace_t +{ +private: + std::queue store_trace; + // self-modified code. fence.i is the barrier + store_tracker_t smc_tracker; + // pte access. sfence.vma is the barrier + store_tracker_t pte_tracker; + // Writing satp does not imply any ordering constraints between + // page-table updates and subsequent address translations. + // Note: satp_written is too coarse-grained and must be optimized in the future. + bool satp_written = false; + + enum class MemAccessType { INSTRUCTION, LOAD, STORE, PTW }; + static const char *accessTypeString(MemAccessType value) { + switch (value) { + case MemAccessType::INSTRUCTION: return "instr"; + case MemAccessType::LOAD: return "load"; + case MemAccessType::STORE: return "store"; + case MemAccessType::PTW: return "ptw"; + default: return "unknown"; + } + } + + void difftest_log_mem(MemAccessType t, uint64_t paddr, uint64_t data, int len) { + difftest_log("mem_%-5s addr: 0x%lx, data: 0x%016lx, len: %d", accessTypeString(t), paddr, data, len); + if (t == MemAccessType::STORE) { + smc_tracker.on_store(paddr); + pte_tracker.on_store(paddr); + bool do_trace = !is_amo; +#ifdef CONFIG_DIFF_AMO_STORE + do_trace = true; +#endif + if (do_trace) { + store_trace_t trace{paddr, data, len}; + store_trace.push(trace); + } + is_amo = false; + } + else if (t == MemAccessType::INSTRUCTION) { + smc_tracker.on_read(paddr); + } + else if (t == MemAccessType::PTW) { + pte_tracker.on_read(paddr); + } + } + + void difftest_log_mem(MemAccessType t, uint64_t paddr, void *data, int len) { + difftest_log_mem(t, paddr, *(const uint64_t *)data, len); + } + +public: + bool enable_difftest_logs = false; + bool has_touched_vm = false; + bool is_amo = false; + bool sc_failed = false; + + void difftest_log(const char *__restrict __fmt, ...) { + if (unlikely(enable_difftest_logs)) { + va_list args; + va_start(args, __fmt); + fprintf(stderr, "[Spike] "); + vfprintf(stderr, __fmt, args); + fprintf(stderr, "\n"); + fflush(stderr); + va_end(args); + } + } + +#define __DIFFTEST_LOG_INTERFACE(name, type) \ + void inline difftest_log_mem_##name(uint64_t paddr, void *data, int len) { \ + difftest_log_mem(MemAccessType::type, paddr, *(const uint64_t *)data, len); \ + } + + __DIFFTEST_LOG_INTERFACE(instr, INSTRUCTION) + __DIFFTEST_LOG_INTERFACE(load, LOAD) + __DIFFTEST_LOG_INTERFACE(store, STORE) + __DIFFTEST_LOG_INTERFACE(ptw, PTW) + + int dut_store_commit(uint64_t *addr, uint64_t *data, uint8_t *mask) { + if (store_trace.empty()) { + printf("Store commit error: the store trace is empty.\n"); + return -1; + } + + store_trace_t ref = store_trace.front(); + store_trace_t dut{*addr, *data, *mask}; + if (ref.paddr != dut.paddr || ref.data != dut.data || ref.mask != dut.mask) { + *addr = ref.paddr; + *data = ref.data; + *mask = ref.mask; + return -1; + } + + store_trace.pop(); + return 0; + } + + void on_fence_i() { + smc_tracker.reset(); + } + + void on_sfence_vma() { + pte_tracker.reset(); + satp_written = false; + } + + void on_satp_update(bool is_safe) { + if (!is_safe && has_touched_vm) { + satp_written = true; + } + } + + void clear_ambiguation_state() { + smc_tracker.state_reset(); + pte_tracker.state_reset(); + satp_written = false; + } + + bool in_ambiguation_state() { + bool s = smc_tracker.state() || pte_tracker.state() || satp_written; + clear_ambiguation_state(); + return s; + } +}; + +#endif diff --git a/difftest/dummy_debug.cc b/difftest/dummy_debug.cc new file mode 100644 index 0000000000..90db8b33cd --- /dev/null +++ b/difftest/dummy_debug.cc @@ -0,0 +1,40 @@ +#include "dummy_debug.h" +#include "sim.h" +#include "mmu.h" + +dummy_debug_t::~dummy_debug_t() +{ + +} + +bool dummy_debug_t::load(reg_t addr, size_t len, uint8_t* bytes) +{ + // addr is internal addr! + // assert(addr > DM_BASE_ADDR); + // assert(addr + len <= DM_END_ADDR); + if (addr < DM_BASE_ADDR || addr + len > DM_END_ADDR) + return false; + + int offset = addr / sizeof(uint8_t); + memcpy(bytes, &dummy_debug_mem[offset], len); + + return true; +} + +bool dummy_debug_t::store(reg_t addr, size_t len, const uint8_t* bytes) +{ + // nothing is actually stored + // because currently spike dm does not need to be working + // assert(addr < DM_BASE_ADDR); + // assert(addr + len <= DM_END_ADDR); + if (addr < DM_BASE_ADDR || addr + len > DM_END_ADDR) + return false; + return true; +} + +// bool dummy_debug_t::update_dummy_mem(reg_t addr, size_t len, const uint8_t* bytes) +// { +// assert(addr < DM_BASE_ADDR); +// assert(addr + len <= DM_END_ADDR); +// return memcpy((void *) (addr - DM_BASE_ADDR + dummy_debug_mem), bytes, len); +// } \ No newline at end of file diff --git a/difftest/dummy_debug.h b/difftest/dummy_debug.h new file mode 100644 index 0000000000..7d26bdf5cb --- /dev/null +++ b/difftest/dummy_debug.h @@ -0,0 +1,23 @@ +#ifndef __DUMMY_DEBUG_H +#define __DUMMY_DEBUG_H + +#include "sim.h" +#include "abstract_device.h" +#include "mmu.h" + +#define DM_BASE_ADDR 0x38020000L +#define DM_END_ADDR 0x38021000L +#define DUMMY_MEM_SIZE_BYTE (DM_END_ADDR - DM_BASE_ADDR) / sizeof(uint8_t) + +class dummy_debug_t : public abstract_device_t +{ + public: + bool load(reg_t addr, size_t len, uint8_t* bytes); + bool store(reg_t addr, size_t len, const uint8_t* bytes); + ~dummy_debug_t(); + // we create a mem Region that is updated on every dut if/load/store + uint8_t dummy_debug_mem[DUMMY_MEM_SIZE_BYTE]; + bool update_dummy_mem(reg_t addr, size_t len, const uint8_t* bytes); +}; + +#endif diff --git a/fesvr/htif.cc b/fesvr/htif.cc index 3f93f7b507..05b75bc99a 100644 --- a/fesvr/htif.cc +++ b/fesvr/htif.cc @@ -49,30 +49,39 @@ htif_t::htif_t() tohost_addr(0), fromhost_addr(0), exitcode(0), stopped(false), syscall_proxy(this) { +#if !defined(SPIKE_FUZZ) && !defined(DIFFTEST) signal(SIGINT, &handle_signal); signal(SIGTERM, &handle_signal); signal(SIGABRT, &handle_signal); // we still want to call static destructors +#endif } htif_t::htif_t(int argc, char** argv) : htif_t() { //Set line size as 16 by default. line_size = 16; +#ifndef DIFFTEST parse_arguments(argc, argv); +#endif register_devices(); } htif_t::htif_t(const std::vector& args) : htif_t() { + // Set line size as 16 by default. + line_size = 16; +#ifndef DIFFTEST int argc = args.size() + 1; char * argv[argc]; argv[0] = (char *) "htif"; for (unsigned int i = 0; i < args.size(); i++) { argv[i+1] = (char *) args[i].c_str(); } - //Set line size as 16 by default. - line_size = 16; parse_arguments(argc, argv); +#else + // No need for parse_arguments because the arguments are empty. + // Calling it will cause errors because of its usage of getopt. +#endif register_devices(); } @@ -116,7 +125,7 @@ std::map htif_t::load_payload(const std::string& payload, else throw std::runtime_error( "could not open " + payload + "; searched paths:\n" + - "\t. (current directory)\n" + + "\t. (current directory)\n" + "\t" + PREFIX TARGET_DIR + " (based on configured --prefix and --with-target)" ); } diff --git a/riscv/csrs.cc b/riscv/csrs.cc index 7ea07d104c..d205230d7c 100644 --- a/riscv/csrs.cc +++ b/riscv/csrs.cc @@ -355,11 +355,21 @@ epc_csr_t::epc_csr_t(processor_t* const proc, const reg_t addr): } reg_t epc_csr_t::read() const noexcept { +#ifdef CPU_ROCKET_CHIP + uint64_t r = this->val & proc->pc_alignment_mask(); + uint64_t hi = ((r >> 39) & 0x1) ? -1UL : 0; + uint64_t mask = (1UL << 40) - 1; + return (r & mask) | (hi & (~mask)); +#else return val & proc->pc_alignment_mask(); +#endif } bool epc_csr_t::unlogged_write(const reg_t val) noexcept { this->val = val & ~(reg_t)1; +#ifdef CPU_ROCKET_CHIP + this->val &= (1UL << 40) - 1; +#endif return true; } @@ -374,7 +384,11 @@ reg_t tvec_csr_t::read() const noexcept { } bool tvec_csr_t::unlogged_write(const reg_t val) noexcept { +#if defined(CPU_NUTSHELL) + this->val = val & ~(reg_t)3; +#else this->val = val & ~(reg_t)2; +#endif return true; } @@ -501,7 +515,18 @@ bool mstatus_csr_t::unlogged_write(const reg_t val) noexcept { const reg_t requested_mpp = proc->legalize_privilege(get_field(val, MSTATUS_MPP)); const reg_t adjusted_val = set_field(val, MSTATUS_MPP, requested_mpp); +#ifdef CPU_ROCKET_CHIP + reg_t new_mstatus = (read() & ~mask) | (adjusted_val & mask); + unsigned fs = (new_mstatus >> 13) & 0x3; + if (fs == 0x1 || fs == 0x2) { + new_mstatus |= 0x3 << 13; + } +#elif defined(CPU_NUTSHELL) + reg_t new_mstatus = (read() & ~mask) | (adjusted_val & mask); + new_mstatus ^= new_mstatus & (0x3 << 13); // FS is always zero +#else const reg_t new_mstatus = (read() & ~mask) | (adjusted_val & mask); +#endif maybe_flush_tlb(new_mstatus); this->val = adjust_sd(new_mstatus); return true; @@ -622,6 +647,18 @@ bool sstatus_csr_t::enabled(const reg_t which) { misa_csr_t::misa_csr_t(processor_t* const proc, const reg_t addr, const reg_t max_isa): basic_csr_t(proc, addr, max_isa), max_isa(max_isa), +#if defined(CPU_ROCKET_CHIP) + write_mask(max_isa & (0 // allow MAFDQCHV bits in MISA to be modified + | (1L << ('M' - 'A')) + | (1L << ('A' - 'A')) + | (1L << ('F' - 'A')) + | (1L << ('D' - 'A')) + | (1L << ('C' - 'A')) + | (1L << ('V' - 'A')) + ) +#elif defined(CPU_NUTSHELL) || defined (CPU_XIANGSHAN) + write_mask(0 // not allowed +#else write_mask(max_isa & (0 // allow MAFDQCHV bits in MISA to be modified | (1L << ('M' - 'A')) | (1L << ('A' - 'A')) @@ -632,6 +669,7 @@ misa_csr_t::misa_csr_t(processor_t* const proc, const reg_t addr, const reg_t ma | (1L << ('H' - 'A')) | (1L << ('V' - 'A')) ) +#endif ) { } @@ -874,15 +912,23 @@ void medeleg_csr_t::verify_permissions(insn_t insn, bool write) const { bool medeleg_csr_t::unlogged_write(const reg_t val) noexcept { const reg_t mask = 0 | (1 << CAUSE_MISALIGNED_FETCH) +#if !defined(CPU_ROCKET_CHIP) | (1 << CAUSE_FETCH_ACCESS) +#endif | (1 << CAUSE_ILLEGAL_INSTRUCTION) | (1 << CAUSE_BREAKPOINT) | (1 << CAUSE_MISALIGNED_LOAD) +#if !defined(CPU_ROCKET_CHIP) | (1 << CAUSE_LOAD_ACCESS) - | (1 << CAUSE_MISALIGNED_STORE) +#endif + | (1 << CAUSE_MISALIGNED_STORE) +#if !defined(CPU_ROCKET_CHIP) | (1 << CAUSE_STORE_ACCESS) +#endif | (1 << CAUSE_USER_ECALL) +#if !defined(CPU_ROCKET_CHIP) | (1 << CAUSE_SUPERVISOR_ECALL) +#endif | (1 << CAUSE_FETCH_PAGE_FAULT) | (1 << CAUSE_LOAD_PAGE_FAULT) | (1 << CAUSE_STORE_PAGE_FAULT) @@ -914,8 +960,11 @@ base_atp_csr_t::base_atp_csr_t(processor_t* const proc, const reg_t addr): bool base_atp_csr_t::unlogged_write(const reg_t val) noexcept { const reg_t newval = proc->supports_impl(IMPL_MMU) ? compute_new_satp(val) : 0; - if (newval != read()) - proc->get_mmu()->flush_tlb(); + if (newval != read()) { + // It should be safe to change from Bare mode (no translation) + bool is_safe = get_field(read(), SATP64_MODE) == SATP_MODE_OFF; + proc->get_mmu()->flush_tlb_on_satp_update(is_safe); + } return basic_csr_t::unlogged_write(newval); } @@ -1155,16 +1204,57 @@ bool hgatp_csr_t::unlogged_write(const reg_t val) noexcept { return basic_csr_t::unlogged_write((read() & ~mask) | (val & mask)); } +dtrig_csr_t::dtrig_csr_t(processor_t* const proc, const reg_t addr): + dtrig_csr_t(proc, addr, 0) { +} + +dtrig_csr_t::dtrig_csr_t(processor_t* const proc, const reg_t addr, reg_t val): + basic_csr_t(proc, addr, val) { +} + +void dtrig_csr_t::verify_permissions(insn_t insn, bool write) const { + basic_csr_t::verify_permissions(insn, write); + if (!proc->extension_enabled(EXT_SDTRIG)) + throw trap_illegal_instruction(insn.bits()); +} + +const_dtrig_csr_t::const_dtrig_csr_t(processor_t* const proc, const reg_t addr, reg_t val): + const_csr_t(proc, addr, val), dtrig(proc, addr) { +} + +void const_dtrig_csr_t::verify_permissions(insn_t insn, bool write) const { + const_csr_t::verify_permissions(insn, write); + dtrig.verify_permissions(insn, write); +} + +masked_dtrig_csr_t::masked_dtrig_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init): + masked_csr_t(proc, addr, mask, init), dtrig(proc, addr) { +} + +void masked_dtrig_csr_t::verify_permissions(insn_t insn, bool write) const { + masked_csr_t::verify_permissions(insn, write); + dtrig.verify_permissions(insn, write); +} + +proxy_dtrig_csr_t::proxy_dtrig_csr_t(processor_t* const proc, const reg_t addr, csr_t_p delegate): + proxy_csr_t(proc, addr, delegate), dtrig(proc, addr) { +} + +void proxy_dtrig_csr_t::verify_permissions(insn_t insn, bool write) const { + proxy_csr_t::verify_permissions(insn, write); + dtrig.verify_permissions(insn, write); +} + tselect_csr_t::tselect_csr_t(processor_t* const proc, const reg_t addr): - basic_csr_t(proc, addr, 0) { + dtrig_csr_t(proc, addr, 0) { } bool tselect_csr_t::unlogged_write(const reg_t val) noexcept { - return basic_csr_t::unlogged_write((val < proc->TM.count()) ? val : read()); + return dtrig_csr_t::unlogged_write((val < proc->TM.count()) ? val : read()); } tdata1_csr_t::tdata1_csr_t(processor_t* const proc, const reg_t addr): - csr_t(proc, addr) { + dtrig_csr_t(proc, addr) { } reg_t tdata1_csr_t::read() const noexcept { @@ -1176,7 +1266,7 @@ bool tdata1_csr_t::unlogged_write(const reg_t val) noexcept { } tdata2_csr_t::tdata2_csr_t(processor_t* const proc, const reg_t addr): - csr_t(proc, addr) { + dtrig_csr_t(proc, addr) { } reg_t tdata2_csr_t::read() const noexcept { @@ -1188,7 +1278,7 @@ bool tdata2_csr_t::unlogged_write(const reg_t val) noexcept { } tdata3_csr_t::tdata3_csr_t(processor_t* const proc, const reg_t addr): - csr_t(proc, addr) { + dtrig_csr_t(proc, addr) { } reg_t tdata3_csr_t::read() const noexcept { @@ -1200,7 +1290,7 @@ bool tdata3_csr_t::unlogged_write(const reg_t val) noexcept { } tinfo_csr_t::tinfo_csr_t(processor_t* const proc, const reg_t addr) : - csr_t(proc, addr) { + dtrig_csr_t(proc, addr) { } reg_t tinfo_csr_t::read() const noexcept { diff --git a/riscv/csrs.h b/riscv/csrs.h index 07d6d82ac5..0e4633eaa3 100644 --- a/riscv/csrs.h +++ b/riscv/csrs.h @@ -599,14 +599,46 @@ class hgatp_csr_t: public basic_csr_t { virtual bool unlogged_write(const reg_t val) noexcept override; }; -class tselect_csr_t: public basic_csr_t { +// For CSRs that only exist if Sdtrig is enabled +class dtrig_csr_t: public basic_csr_t { + public: + dtrig_csr_t(processor_t* const proc, const reg_t addr); + dtrig_csr_t(processor_t* const proc, const reg_t addr, reg_t val); + virtual void verify_permissions(insn_t insn, bool write) const override; +}; + +class const_dtrig_csr_t: public const_csr_t { + public: + const_dtrig_csr_t(processor_t* const proc, const reg_t addr, reg_t val); + virtual void verify_permissions(insn_t insn, bool write) const override; + protected: + dtrig_csr_t dtrig; +}; + +class masked_dtrig_csr_t: public masked_csr_t { + public: + masked_dtrig_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init); + virtual void verify_permissions(insn_t insn, bool write) const override; + protected: + dtrig_csr_t dtrig; +}; + +class proxy_dtrig_csr_t: public proxy_csr_t { + public: + proxy_dtrig_csr_t(processor_t* const proc, const reg_t addr, csr_t_p delegate); + virtual void verify_permissions(insn_t insn, bool write) const override; + protected: + dtrig_csr_t dtrig; +}; + +class tselect_csr_t: public dtrig_csr_t { public: tselect_csr_t(processor_t* const proc, const reg_t addr); protected: virtual bool unlogged_write(const reg_t val) noexcept override; }; -class tdata1_csr_t: public csr_t { +class tdata1_csr_t: public dtrig_csr_t { public: tdata1_csr_t(processor_t* const proc, const reg_t addr); virtual reg_t read() const noexcept override; @@ -614,7 +646,7 @@ class tdata1_csr_t: public csr_t { virtual bool unlogged_write(const reg_t val) noexcept override; }; -class tdata2_csr_t: public csr_t { +class tdata2_csr_t: public dtrig_csr_t { public: tdata2_csr_t(processor_t* const proc, const reg_t addr); virtual reg_t read() const noexcept override; @@ -622,7 +654,7 @@ class tdata2_csr_t: public csr_t { virtual bool unlogged_write(const reg_t val) noexcept override; }; -class tdata3_csr_t: public csr_t { +class tdata3_csr_t: public dtrig_csr_t { public: tdata3_csr_t(processor_t* const proc, const reg_t addr); virtual reg_t read() const noexcept override; @@ -630,7 +662,7 @@ class tdata3_csr_t: public csr_t { virtual bool unlogged_write(const reg_t val) noexcept override; }; -class tinfo_csr_t: public csr_t { +class tinfo_csr_t: public dtrig_csr_t { public: tinfo_csr_t(processor_t* const proc, const reg_t addr); virtual reg_t read() const noexcept override; diff --git a/riscv/decode.h b/riscv/decode.h index dad32a1e31..953f8f5478 100644 --- a/riscv/decode.h +++ b/riscv/decode.h @@ -60,12 +60,18 @@ const int NCSR = 4096; #define FSR_NXA (FPEXC_NX << FSR_AEXC_SHIFT) #define FSR_AEXC (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA) -#define insn_length(x) \ +#define insn_length_all(x) \ (((x) & 0x03) < 0x03 ? 2 : \ ((x) & 0x1f) < 0x1f ? 4 : \ ((x) & 0x3f) < 0x3f ? 6 : \ 8) +#define insn_length(x) \ + (insn_length_all(x) > MAX_INSN_LENGTH ? MAX_INSN_LENGTH : insn_length_all(x)) +#ifdef DIFFTEST +#define MAX_INSN_LENGTH 4 +#else #define MAX_INSN_LENGTH 8 +#endif #define PC_ALIGN 2 #define Sn(n) ((n) < 2 ? X_S0 + (n) : X_Sn + (n)) diff --git a/riscv/encoding.h b/riscv/encoding.h index db7b0215b6..57f8ab571d 100644 --- a/riscv/encoding.h +++ b/riscv/encoding.h @@ -245,7 +245,11 @@ #define SATP32_ASID 0x7FC00000 #define SATP32_PPN 0x003FFFFF #define SATP64_MODE 0xF000000000000000 +#if defined(CPU_ROCKET_CHIP) +#define SATP64_ASID 0x0000000000000000 +#else #define SATP64_ASID 0x0FFFF00000000000 +#endif #define SATP64_PPN 0x00000FFFFFFFFFFF #define SATP_MODE_OFF 0 diff --git a/riscv/execute.cc b/riscv/execute.cc index 295879d4db..2e1d9ffd22 100644 --- a/riscv/execute.cc +++ b/riscv/execute.cc @@ -280,6 +280,7 @@ void processor_t::step(size_t n) insn_fetch_t fetch = mmu->load_insn(pc); if (debug && !state.serialized) disasm(fetch.insn); + sim->difftest_log("pc = 0x%lx inst 0x%x", pc, fetch.insn); pc = execute_insn_logged(this, pc, fetch); advance_pc(); } @@ -289,6 +290,7 @@ void processor_t::step(size_t n) // Main simulation loop, fast path. for (auto ic_entry = _mmu->access_icache(pc); ; ) { auto fetch = ic_entry->data; + sim->difftest_log("pc = 0x%lx inst 0x%x", pc, fetch.insn); pc = execute_insn_fast(this, pc, fetch); ic_entry = ic_entry->next; if (unlikely(ic_entry->tag != pc)) diff --git a/riscv/insns/fence_i.h b/riscv/insns/fence_i.h index 38dcaf3fce..719ef53e43 100644 --- a/riscv/insns/fence_i.h +++ b/riscv/insns/fence_i.h @@ -1 +1 @@ -MMU.flush_icache(); +MMU.flush_icache_on_fence_i(); diff --git a/riscv/insns/sfence_vma.h b/riscv/insns/sfence_vma.h index 7d6c01a8be..516ae7a7ed 100644 --- a/riscv/insns/sfence_vma.h +++ b/riscv/insns/sfence_vma.h @@ -6,4 +6,4 @@ if (STATE.v) { } else { require_privilege(get_field(STATE.mstatus->read(), MSTATUS_TVM) ? PRV_M : PRV_S); } -MMU.flush_tlb(); +MMU.flush_tlb_on_sfence_vma(); diff --git a/riscv/isa_parser.cc b/riscv/isa_parser.cc index 1c4300c958..9423efc28d 100644 --- a/riscv/isa_parser.cc +++ b/riscv/isa_parser.cc @@ -207,6 +207,8 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv) } else if (ext_str == "zkr") { extension_table[EXT_ZKR] = true; } else if (ext_str == "zkt") { + } else if (ext_str == "sdtrig") { + extension_table[EXT_SDTRIG] = true; } else if (ext_str == "smepmp") { extension_table[EXT_SMEPMP] = true; } else if (ext_str == "smstateen") { diff --git a/riscv/isa_parser.h b/riscv/isa_parser.h index 3cbee7dea0..4e7a334b6e 100644 --- a/riscv/isa_parser.h +++ b/riscv/isa_parser.h @@ -39,6 +39,7 @@ typedef enum { EXT_ZPSFOPERAND, EXT_ZVFH, EXT_ZVFHMIN, + EXT_SDTRIG, EXT_SMEPMP, EXT_SMSTATEEN, EXT_SMRNMI, diff --git a/riscv/mmu.cc b/riscv/mmu.cc index 3f90060e82..740c456f35 100644 --- a/riscv/mmu.cc +++ b/riscv/mmu.cc @@ -33,6 +33,12 @@ void mmu_t::flush_icache() icache[i].tag = -1; } +void mmu_t::flush_icache_on_fence_i() +{ + sim->on_fence_i(); + flush_icache(); +} + void mmu_t::flush_tlb() { memset(tlb_insn_tag, -1, sizeof(tlb_insn_tag)); @@ -42,6 +48,18 @@ void mmu_t::flush_tlb() flush_icache(); } +void mmu_t::flush_tlb_on_sfence_vma() +{ + sim->on_sfence_vma(); + flush_tlb(); +} + +void mmu_t::flush_tlb_on_satp_update(bool is_safe) +{ + sim->on_satp_update(is_safe); + flush_tlb(); +} + void throw_access_exception(bool virt, reg_t addr, access_type type) { switch (type) { @@ -196,6 +214,8 @@ void mmu_t::load_slow_path_intrapage(reg_t len, uint8_t* bytes, mem_access_info_ if (!access_info.flags.is_special_access() && vpn == (tlb_load_tag[vpn % TLB_ENTRIES] & ~TLB_CHECK_TRIGGERS)) { auto host_addr = tlb_data[vpn % TLB_ENTRIES].host_offset + addr; memcpy(bytes, host_addr, len); + auto paddr = tlb_data[vpn % TLB_ENTRIES].target_offset + addr; + sim->difftest_log_mem_load(paddr, host_addr, len); return; } @@ -207,6 +227,7 @@ void mmu_t::load_slow_path_intrapage(reg_t len, uint8_t* bytes, mem_access_info_ if (auto host_addr = sim->addr_to_mem(paddr)) { memcpy(bytes, host_addr, len); + sim->difftest_log_mem_load(paddr, host_addr, len); if (tracer.interested_in_range(paddr, paddr + PGSIZE, LOAD)) tracer.trace(paddr, len, LOAD); else if (!access_info.flags.is_special_access()) @@ -258,6 +279,8 @@ void mmu_t::store_slow_path_intrapage(reg_t len, const uint8_t* bytes, mem_acces if (actually_store) { auto host_addr = tlb_data[vpn % TLB_ENTRIES].host_offset + addr; memcpy(host_addr, bytes, len); + auto paddr = tlb_data[vpn % TLB_ENTRIES].target_offset + addr; + sim->difftest_log_mem_store(paddr, host_addr, len); } return; } @@ -267,6 +290,7 @@ void mmu_t::store_slow_path_intrapage(reg_t len, const uint8_t* bytes, mem_acces if (actually_store) { if (auto host_addr = sim->addr_to_mem(paddr)) { memcpy(host_addr, bytes, len); + sim->difftest_log_mem_store(paddr, host_addr, len); if (tracer.interested_in_range(paddr, paddr + PGSIZE, STORE)) tracer.trace(paddr, len, STORE); else if (!access_info.flags.is_special_access()) @@ -486,6 +510,8 @@ reg_t mmu_t::walk(mem_access_info_t access_info) if (vm.levels == 0) return s2xlate(addr, addr & ((reg_t(2) << (proc->xlen-1))-1), type, type, virt, hlvx) & ~page_mask; // zero-extend from xlen + sim->has_touched_vm = true; + bool s_mode = mode == PRV_S; bool sum = proc->state.sstatus->readvirt(virt) & MSTATUS_SUM; bool mxr = (proc->state.sstatus->readvirt(false) | proc->state.sstatus->readvirt(virt)) & MSTATUS_MXR; @@ -505,6 +531,7 @@ reg_t mmu_t::walk(mem_access_info_t access_info) // check that physical address of PTE is legal auto pte_paddr = s2xlate(addr, base + idx * vm.ptesize, LOAD, type, virt, false); reg_t pte = pte_load(pte_paddr, addr, virt, type, vm.ptesize); + sim->difftest_log("ptw: level %d, vaddr 0x%lx, pg_base 0x%lx, p_pte 0x%lx, pte.val 0x%lx", i, addr, base, pte_paddr, pte); reg_t ppn = (pte & ~reg_t(PTE_ATTR)) >> PTE_PPN_SHIFT; bool pbmte = virt ? (proc->get_state()->henvcfg->read() & HENVCFG_PBMTE) : (proc->get_state()->menvcfg->read() & MENVCFG_PBMTE); bool hade = virt ? (proc->get_state()->henvcfg->read() & HENVCFG_HADE) : (proc->get_state()->menvcfg->read() & MENVCFG_HADE); diff --git a/riscv/mmu.h b/riscv/mmu.h index 46c54ce88a..fb567272cb 100644 --- a/riscv/mmu.h +++ b/riscv/mmu.h @@ -19,7 +19,13 @@ #define PGSHIFT 12 const reg_t PGSIZE = 1 << PGSHIFT; const reg_t PGMASK = ~(PGSIZE-1); +#if defined(CPU_ROCKET_CHIP) || defined(CPU_NUTSHELL) +#define MAX_PADDR_BITS 32 +#elif defined(CPU_XIANGSHAN) +#define MAX_PADDR_BITS 36 +#else #define MAX_PADDR_BITS 56 // imposed by Sv39 / Sv48 +#endif struct insn_fetch_t { @@ -102,6 +108,8 @@ class mmu_t if (likely(!xlate_flags.is_special_access() && aligned && tlb_hit)) { res = *(target_endian*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr); + auto paddr = tlb_data[vpn % TLB_ENTRIES].target_offset + addr; + sim->difftest_log_mem_load(paddr, &res, sizeof(T)); } else { load_slow_path(addr, sizeof(T), (uint8_t*)&res, xlate_flags); } @@ -144,6 +152,9 @@ class mmu_t if (!xlate_flags.is_special_access() && likely(aligned && tlb_hit)) { *(target_endian*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr) = to_target(val); + auto paddr = tlb_data[vpn % TLB_ENTRIES].target_offset + addr; + auto v = to_target(val); + sim->difftest_log_mem_store(paddr, &v, sizeof(T)); } else { target_endian target_val = to_target(val); store_slow_path(addr, sizeof(T), (const uint8_t*)&target_val, xlate_flags, true, false); @@ -182,6 +193,7 @@ class mmu_t convert_load_traps_to_store_traps({ store_slow_path(addr, sizeof(T), nullptr, {false, false, false}, false, true); auto lhs = load(addr); + sim->is_amo = true; store(addr, f(lhs)); return lhs; }) @@ -192,8 +204,10 @@ class mmu_t convert_load_traps_to_store_traps({ store_slow_path(addr, sizeof(T), nullptr, {false, false, false}, false, true); auto lhs = load(addr); - if (lhs == comp) + if (lhs == comp) { + sim->is_amo = true; store(addr, swap); + } return lhs; }) } @@ -248,8 +262,15 @@ class mmu_t } reg_t paddr = translate(generate_access_info(vaddr, STORE, {false, false, false}), 1); - if (sim->reservable(paddr)) + if (sim->reservable(paddr)) { +#ifdef DIFFTEST + // We assume practical hardware designs would have 64-byte (1 << 6 bytes) reservation sets. + auto index = [](reg_t addr) { return addr >> 6; }; + return index(load_reservation_address) == index(paddr); +#else return load_reservation_address == paddr; +#endif + } else throw trap_store_access_fault((proc) ? proc->state.v : false, vaddr, 0, 0); } @@ -258,9 +279,16 @@ class mmu_t bool store_conditional(reg_t addr, T val) { bool have_reservation = check_load_reservation(addr, sizeof(T)); + if (have_reservation && sim->sc_failed) { + sim->difftest_log("The REF is forced to have an SC failure according to the DUT."); + have_reservation = false; + } + sim->sc_failed = false; - if (have_reservation) + if (have_reservation) { + sim->is_amo = true; store(addr, val); + } yield_load_reservation(); @@ -294,13 +322,13 @@ class mmu_t } else if (length == 2) { // entire instruction already fetched } else if (length == 6) { - insn |= (insn_bits_t)from_le(*(const uint16_t*)translate_insn_addr_to_host(addr + 4)) << 32; insn |= (insn_bits_t)from_le(*(const uint16_t*)translate_insn_addr_to_host(addr + 2)) << 16; + insn |= (insn_bits_t)from_le(*(const uint16_t*)translate_insn_addr_to_host(addr + 4)) << 32; } else { static_assert(sizeof(insn_bits_t) == 8, "insn_bits_t must be uint64_t"); - insn |= (insn_bits_t)from_le(*(const uint16_t*)translate_insn_addr_to_host(addr + 6)) << 48; - insn |= (insn_bits_t)from_le(*(const uint16_t*)translate_insn_addr_to_host(addr + 4)) << 32; insn |= (insn_bits_t)from_le(*(const uint16_t*)translate_insn_addr_to_host(addr + 2)) << 16; + insn |= (insn_bits_t)from_le(*(const uint16_t*)translate_insn_addr_to_host(addr + 4)) << 32; + insn |= (insn_bits_t)from_le(*(const uint16_t*)translate_insn_addr_to_host(addr + 6)) << 48; } insn_fetch_t fetch = {proc->decode_insn(insn), insn}; @@ -309,6 +337,7 @@ class mmu_t entry->data = fetch; reg_t paddr = tlb_entry.target_offset + addr;; + sim->difftest_log_mem_instr(paddr, &insn, sizeof(insn)); if (tracer.interested_in_range(paddr, paddr + 1, FETCH)) { entry->tag = -1; tracer.trace(paddr, length, FETCH); @@ -331,7 +360,10 @@ class mmu_t } void flush_tlb(); + void flush_tlb_on_sfence_vma(); + void flush_tlb_on_satp_update(bool is_safe); void flush_icache(); + void flush_icache_on_fence_i(); void register_memtracer(memtracer_t*); @@ -430,6 +462,7 @@ class mmu_t target_endian target_pte; if (host_pte_addr) { memcpy(&target_pte, host_pte_addr, ptesize); + sim->difftest_log_mem_ptw(pte_paddr, host_pte_addr, ptesize); } else if (!mmio_load(pte_paddr, ptesize, (uint8_t*)&target_pte)) { throw_access_exception(virt, addr, trap_type); } diff --git a/riscv/processor.cc b/riscv/processor.cc index 1d5675a51a..f5ce520c33 100644 --- a/riscv/processor.cc +++ b/riscv/processor.cc @@ -69,7 +69,11 @@ processor_t::processor_t(const isa_parser_t *isa, const cfg_t *cfg, if (isa->get_max_xlen() == 32) set_mmu_capability(IMPL_MMU_SV32); else if (isa->get_max_xlen() == 64) +#if defined(CPU_ROCKET_CHIP) || defined(CPU_NUTSHELL) || defined(CPU_XIANGSHAN) + set_mmu_capability(IMPL_MMU_SV39); +#else set_mmu_capability(IMPL_MMU_SV57); +#endif set_impl(IMPL_MMU_ASID, true); set_impl(IMPL_MMU_VMID, true); @@ -223,7 +227,9 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) if (proc->extension_enabled_const(EXT_ZICNTR)) { csrmap[CSR_INSTRET] = std::make_shared(proc, CSR_INSTRET, minstret); csrmap[CSR_CYCLE] = std::make_shared(proc, CSR_CYCLE, mcycle); +#ifndef DIFFTEST csrmap[CSR_TIME] = time_proxy = std::make_shared(proc, CSR_TIME, time); +#endif } if (xlen == 32) { csr_t_p minstreth, mcycleh; @@ -235,7 +241,9 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) auto timeh = std::make_shared(proc, CSR_TIMEH, time); csrmap[CSR_INSTRETH] = std::make_shared(proc, CSR_INSTRETH, minstreth); csrmap[CSR_CYCLEH] = std::make_shared(proc, CSR_CYCLEH, mcycleh); +#ifndef DIFFTEST csrmap[CSR_TIMEH] = std::make_shared(proc, CSR_TIMEH, timeh); +#endif } } else { csrmap[CSR_MINSTRET] = minstret; @@ -328,7 +336,11 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) csrmap[CSR_MEDELEG] = medeleg = std::make_shared(proc, CSR_MEDELEG); csrmap[CSR_MIDELEG] = mideleg = std::make_shared(proc, CSR_MIDELEG); +#ifdef CPU_ROCKET_CHIP + const reg_t counteren_mask = (proc->extension_enabled_const(EXT_ZICNTR) ? 0x7UL : 0x0); +#else const reg_t counteren_mask = (proc->extension_enabled_const(EXT_ZICNTR) ? 0x7UL : 0x0) | (proc->extension_enabled_const(EXT_ZIHPM) ? 0xfffffff8ULL : 0x0); +#endif mcounteren = std::make_shared(proc, CSR_MCOUNTEREN, counteren_mask, 0); if (proc->extension_enabled_const('U')) csrmap[CSR_MCOUNTEREN] = mcounteren; csrmap[CSR_SCOUNTEREN] = scounteren = std::make_shared(proc, CSR_SCOUNTEREN, counteren_mask, 0); @@ -403,16 +415,16 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) csrmap[CSR_TDATA3] = std::make_shared(proc, CSR_TDATA3); csrmap[CSR_TINFO] = std::make_shared(proc, CSR_TINFO); } else { - csrmap[CSR_TDATA1] = std::make_shared(proc, CSR_TDATA1, 0); - csrmap[CSR_TDATA2] = tdata2 = std::make_shared(proc, CSR_TDATA2, 0); - csrmap[CSR_TDATA3] = std::make_shared(proc, CSR_TDATA3, 0); - csrmap[CSR_TINFO] = std::make_shared(proc, CSR_TINFO, 0); + csrmap[CSR_TDATA1] = std::make_shared(proc, CSR_TDATA1, 0); + csrmap[CSR_TDATA2] = tdata2 = std::make_shared(proc, CSR_TDATA2, 0); + csrmap[CSR_TDATA3] = std::make_shared(proc, CSR_TDATA3, 0); + csrmap[CSR_TINFO] = std::make_shared(proc, CSR_TINFO, 0); } unsigned scontext_length = (xlen == 32 ? 16 : 34); // debug spec suggests 16-bit for RV32 and 34-bit for RV64 - csrmap[CSR_SCONTEXT] = scontext = std::make_shared(proc, CSR_SCONTEXT, (reg_t(1) << scontext_length) - 1, 0); + csrmap[CSR_SCONTEXT] = scontext = std::make_shared(proc, CSR_SCONTEXT, (reg_t(1) << scontext_length) - 1, 0); unsigned hcontext_length = (xlen == 32 ? 6 : 13) + (proc->extension_enabled('H') ? 1 : 0); // debug spec suggest 7-bit (6-bit) for RV32 and 14-bit (13-bit) for RV64 with (without) H extension - csrmap[CSR_HCONTEXT] = std::make_shared(proc, CSR_HCONTEXT, (reg_t(1) << hcontext_length) - 1, 0); - csrmap[CSR_MCONTEXT] = mcontext = std::make_shared(proc, CSR_MCONTEXT, csrmap[CSR_HCONTEXT]); + csrmap[CSR_HCONTEXT] = std::make_shared(proc, CSR_HCONTEXT, (reg_t(1) << hcontext_length) - 1, 0); + csrmap[CSR_MCONTEXT] = mcontext = std::make_shared(proc, CSR_MCONTEXT, csrmap[CSR_HCONTEXT]); debug_mode = false; single_step = STEP_NONE; @@ -433,8 +445,19 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) csrmap[CSR_SEED] = std::make_shared(proc, CSR_SEED); +#ifdef CPU_ROCKET_CHIP + csrmap[CSR_MARCHID] = std::make_shared(proc, CSR_MARCHID, 1); + csrmap[CSR_MIMPID] = std::make_shared(proc, CSR_MIMPID, 0x20181004); +#elif defined(CPU_NUTSHELL) + csrmap[CSR_MARCHID] = std::make_shared(proc, CSR_MARCHID, 0); + csrmap[CSR_MIMPID] = std::make_shared(proc, CSR_MIMPID, 0); +#elif defined(CPU_XIANGSHAN) + csrmap[CSR_MARCHID] = std::make_shared(proc, CSR_MARCHID, 25); + csrmap[CSR_MIMPID] = std::make_shared(proc, CSR_MIMPID, 0); +#else csrmap[CSR_MARCHID] = std::make_shared(proc, CSR_MARCHID, 5); csrmap[CSR_MIMPID] = std::make_shared(proc, CSR_MIMPID, 0); +#endif csrmap[CSR_MVENDORID] = std::make_shared(proc, CSR_MVENDORID, 0); csrmap[CSR_MHARTID] = std::make_shared(proc, CSR_MHARTID, proc->get_id()); csrmap[CSR_MCONFIGPTR] = std::make_shared(proc, CSR_MCONFIGPTR, 0); @@ -561,12 +584,14 @@ void processor_t::reset() VU.reset(); in_wfi = false; +#ifndef DIFFTEST if (n_pmp > 0) { // For backwards compatibility with software that is unaware of PMP, // initialize PMP to permit unprivileged access to all of memory. put_csr(CSR_PMPADDR0, ~reg_t(0)); put_csr(CSR_PMPCFG0, PMP_R | PMP_W | PMP_X | PMP_NAPOT); } +#endif for (auto e : custom_extensions) // reset any extensions e.second->reset(); @@ -768,6 +793,16 @@ void processor_t::debug_output_log(std::stringstream *s) } } +static uint64_t encode_vaddr(uint64_t vaddr) { + int64_t hi = (int64_t)vaddr >> 39; + if (hi == 0 || hi == -1) { + return vaddr; + } + hi = ((vaddr >> 38) & 0x1) ? 0 : -1; + uint64_t mask = (1UL << 39) - 1; + return (vaddr & mask) | (hi & (~mask)); +} + void processor_t::take_trap(trap_t& t, reg_t epc) { unsigned max_xlen = isa->get_max_xlen(); @@ -811,7 +846,11 @@ void processor_t::take_trap(trap_t& t, reg_t epc) reg_t vector = (state.vstvec->read() & 1) && interrupt ? 4 * bit : 0; state.pc = (state.vstvec->read() & ~(reg_t)1) + vector; state.vscause->write((interrupt) ? (t.cause() - 1) : t.cause()); +#ifdef CPU_ROCKET_CHIP + state.vsepc->write(encode_vaddr(epc)); +#else state.vsepc->write(epc); +#endif state.vstval->write(t.get_tval()); reg_t s = state.sstatus->read(); @@ -825,7 +864,11 @@ void processor_t::take_trap(trap_t& t, reg_t epc) reg_t vector = (state.nonvirtual_stvec->read() & 1) && interrupt ? 4 * bit : 0; state.pc = (state.nonvirtual_stvec->read() & ~(reg_t)1) + vector; state.nonvirtual_scause->write(t.cause()); +#ifdef CPU_ROCKET_CHIP + state.nonvirtual_sepc->write(encode_vaddr(epc)); +#else state.nonvirtual_sepc->write(epc); +#endif state.nonvirtual_stval->write(t.get_tval()); state.htval->write(t.get_tval2()); state.htinst->write(t.get_tinst()); @@ -853,7 +896,11 @@ void processor_t::take_trap(trap_t& t, reg_t epc) const reg_t rnmi_trap_handler_address = 0; const bool nmie = !(state.mnstatus && !get_field(state.mnstatus->read(), MNSTATUS_NMIE)); state.pc = !nmie ? rnmi_trap_handler_address : trap_handler_address; +#ifdef CPU_ROCKET_CHIP + state.mepc->write(encode_vaddr(epc)); +#else state.mepc->write(epc); +#endif state.mcause->write(t.cause()); state.mtval->write(t.get_tval()); state.mtval2->write(t.get_tval2()); diff --git a/riscv/processor.h b/riscv/processor.h index 1b00808977..437dd42dac 100644 --- a/riscv/processor.h +++ b/riscv/processor.h @@ -261,12 +261,20 @@ class processor_t : public abstract_device_t return impl_table[impl]; } reg_t pc_alignment_mask() { +#ifdef CPU_ROCKET_CHIP + const int ialign = extension_enabled('C') ? 16 : 32; +#else const int ialign = extension_enabled(EXT_ZCA) ? 16 : 32; +#endif return ~(reg_t)(ialign == 16 ? 0 : 2); } void check_pc_alignment(reg_t pc) { if (unlikely(pc & ~pc_alignment_mask())) +#ifdef CPU_ROCKET_CHIP + throw trap_instruction_address_misaligned(state.v, 0, 0, 0); +#else throw trap_instruction_address_misaligned(state.v, pc, 0, 0); +#endif } reg_t legalize_privilege(reg_t); void set_privilege(reg_t, bool); diff --git a/riscv/sim.cc b/riscv/sim.cc index dcbd469d32..1340c3a8b6 100644 --- a/riscv/sim.cc +++ b/riscv/sim.cc @@ -59,9 +59,13 @@ sim_t::sim_t(const cfg_t *cfg, bool halted, remote_bitbang(NULL), debug_module(this, dm_config) { +#if !defined(SPIKE_FUZZ) && !defined(DIFFTEST) signal(SIGINT, &handle_signal); +#endif +#ifndef DIFFTEST sout_.rdbuf(std::cerr.rdbuf()); // debug output goes to stderr by default +#endif // DIFFTEST for (auto& x : mems) bus.add_device(x.first, x.second); diff --git a/riscv/sim.h b/riscv/sim.h index 3109173f19..881d6ebdb3 100644 --- a/riscv/sim.h +++ b/riscv/sim.h @@ -83,8 +83,16 @@ class sim_t : public htif_t, public simif_t std::ostream sout_; // used for socket and terminal interface processor_t* get_core(const std::string& i); +#if defined(SPIKE_FUZZ) || defined(DIFFTEST) +public: +#endif void step(size_t n); // step through simulation +#if defined(SPIKE_FUZZ) || defined(DIFFTEST) +private: + static const size_t INTERLEAVE = -1ULL; // disable the timer +#else static const size_t INTERLEAVE = 5000; +#endif static const size_t INSNS_PER_RTC_TICK = 100; // 10 MHz clock for 1 BIPS core static const size_t CPU_HZ = 1000000000; // 1GHz CPU size_t current_step; @@ -96,7 +104,13 @@ class sim_t : public htif_t, public simif_t std::optional> next_interactive_action; // memory-mapped I/O routines +#if defined(DIFFTEST) +public: +#endif virtual char* addr_to_mem(reg_t paddr) override; +#if defined(DIFFTEST) +private: +#endif virtual bool mmio_load(reg_t paddr, size_t len, uint8_t* bytes) override; virtual bool mmio_store(reg_t paddr, size_t len, const uint8_t* bytes) override; void make_dtb(const char* dtb_file); diff --git a/riscv/simif.h b/riscv/simif.h index aeab5dba77..417e331e7a 100644 --- a/riscv/simif.h +++ b/riscv/simif.h @@ -6,12 +6,13 @@ #include #include "decode.h" #include "cfg.h" +#include "difftest/difftrace.h" class processor_t; class mmu_t; // this is the interface to the simulator used by the processors and memory -class simif_t +class simif_t : public diff_trace_t { public: // should return NULL for MMIO addresses diff --git a/riscv/trap.h b/riscv/trap.h index 54948fdde1..cf4699615b 100644 --- a/riscv/trap.h +++ b/riscv/trap.h @@ -47,7 +47,13 @@ class insn_trap_t : public trap_t : trap_t(which), gva(gva), tval(tval) {} bool has_gva() override { return gva; } bool has_tval() override { return true; } - reg_t get_tval() override { return tval; } + reg_t get_tval() override { +#ifdef CPU_ROCKET_CHIP + return tval; +#else + return 0; +#endif + } private: bool gva; reg_t tval; diff --git a/spike_fuzz/Makefile b/spike_fuzz/Makefile new file mode 100644 index 0000000000..05dbff8380 --- /dev/null +++ b/spike_fuzz/Makefile @@ -0,0 +1,67 @@ +BUILD_DIR = $(abspath ./build) + +CC = clang +CXX = clang++ + +AFL_CC ?= $(CC) +AFL_CXX ?= $(CXX) + +# build spike +SPIKE_PATH = $(abspath ..) +SPIKE_CONFIGURE = $(SPIKE_PATH)/configure + +SPIKE_CFLAGS = -O3 -g0 -DSPIKE_FUZZ +SPIKE_CXXFLAGS = -O3 -Wno-c99-designator -g0 -DSPIKE_FUZZ +SPIKE_LDFLAGS = + +SPIKE_CONFIGURE_FLAGS = -q CC=$(CC) CXX=$(CXX) CFLAGS="$(SPIKE_CFLAGS)" \ + CXXFLAGS="$(SPIKE_CXXFLAGS)" LDFLAGS="$(SPIKE_LDFLAGS)" \ + --with-boost=no --with-boost-asio=no --with-boost-regex=no +SPIKE_MAKEFILE = $(BUILD_DIR)/Makefile + +# spike_fuzz sources +FUZZ_SRCS = $(shell find . -maxdepth 1 -name '*.cc') +FUZZ_OBJS = $(addprefix $(BUILD_DIR)/, $(patsubst %.cc, %.o, $(FUZZ_SRCS))) +CXX_FLAGS = -O3 --std=c++20 -fPIC -DSPIKE_FUZZ + +ifneq ($(SPIKE_DEBUG),) +CXX_FLAGS += -DLOG_PATH=\"$(BUILD_DIR)/spike.log\" +endif + +SPIKE_INC = fesvr riscv disasm customext fdt softfloat spike_main spike_dasm +CXX_FLAGS += -I$(SPIKE_PATH) $(addprefix -I$(SPIKE_PATH)/, $(SPIKE_INC)) +CXX_FLAGS += -I$(BUILD_DIR) + +# spike_fuzz elf dependencies +SPIKE_LIB = libriscv.a libdisasm.a libsoftfloat.a libfesvr.a libfdt.a +SPIKE_LINK = $(addprefix $(BUILD_DIR)/, $(SPIKE_LIB)) + + +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +$(SPIKE_MAKEFILE): | $(BUILD_DIR) + cd $(BUILD_DIR) && $(SPIKE_CONFIGURE) $(SPIKE_CONFIGURE_FLAGS) + +spike: $(SPIKE_MAKEFILE) + $(MAKE) -s -C $(BUILD_DIR) CC=$(AFL_CC) CXX=$(AFL_CXX) + +$(BUILD_DIR)/%.o: %.cc spike + $(AFL_CXX) $(CXX_FLAGS) $(INC_PATH) $< -c -o $@ + +libfuzz: spike $(FUZZ_OBJS) + +fuzz: libfuzz + $(AFL_CXX) $(FUZZ_OBJS) $(SPIKE_LINK) -o $(BUILD_DIR)/spike_fuzz + +dummy: libfuzz spike_fuzz.c + $(CC) -c spike_fuzz.c -o $(BUILD_DIR)/spike_fuzz.o + $(CXX) $(BUILD_DIR)/spike_fuzz.o $(FUZZ_OBJS) $(SPIKE_LINK) \ + -o $(BUILD_DIR)/spike_fuzz -ldl -lpthread + +clean: + rm -rf $(BUILD_DIR) + +.DEFAULT_GOAL = dummy + +.PHONY: clean spike fuzz dummy libfuzz diff --git a/spike_fuzz/harness.cc b/spike_fuzz/harness.cc new file mode 100644 index 0000000000..bb3ded5cfa --- /dev/null +++ b/spike_fuzz/harness.cc @@ -0,0 +1,107 @@ +#include +#include +#include +#include "sim.h" +#include "mmu.h" + + +extern "C" const size_t CONFIG_MSIZE = 2 * 1024 * 1024 * 1024UL; + +std::vector parse_mem_layout(const char *arg); +std::vector> make_mems(const std::vector &layout); + +static sim_t *spike_init(const uint8_t *data, size_t size) { + std::vector difftest_htif_args; + difftest_htif_args.push_back(""); + char mem_layout_str[100]; + sprintf(mem_layout_str, "0x%x:0x%lx", DRAM_BASE, CONFIG_MSIZE); + auto memory_layout = parse_mem_layout(mem_layout_str); + std::vector> difftest_plugin_devices{}; + auto const cfg = new cfg_t( + // std::pair default_initrd_bounds, + std::make_pair(0, 0), + // const char *default_bootargs, + nullptr, + // const char *default_isa, + "RV64IMAFDC_zba_zbb_zbc_zbs_zbkb_zbkc_zbkx_zknd_zkne_zknh_zksed_zksh_svinval", + // const char *default_priv + DEFAULT_PRIV, + // const char *default_varch, + DEFAULT_VARCH, + // const bool default_misaligned, + false, + // const endianness_t default_endianness, + endianness_little, + // const reg_t default_pmpregions, + 0, + // const std::vector &default_mem_layout, + memory_layout, + // const std::vector default_hartids, + std::vector{0}, + // bool default_real_time_clint, + false, + // const reg_t default_trigger_count + 0 + ); + + const debug_module_config_t difftest_dm_config = { + .progbufsize = 2, + .max_sba_data_width = 0, + .require_authentication = false, + .abstract_rti = 0, + .support_hasel = true, + .support_abstract_csr_access = true, + .support_abstract_fpr_access = true, + .support_haltgroups = false, + .support_impebreak = false + }; + sim_t *s = new sim_t( + // const cfg_t *cfg, + cfg, + // bool halted, + false, + // std::vector> mems + make_mems(memory_layout), + // std::vector> plugin_devices + difftest_plugin_devices, + // const std::vector& args + difftest_htif_args, + // const debug_module_config_t &dm_config + difftest_dm_config, + // const char *log_path +#ifndef LOG_PATH + nullptr, +#else + LOG_PATH, +#endif // LOG_PATH + //bool dtb_enabled, const char *dtb_file, bool socket_enabled, FILE *cmd_file + false, nullptr, false, nullptr + ); + + // enable commit log +#ifdef LOG_PATH + printf("commit log is enabled\n"); + s->configure_log(false, true); +#endif // LOG_PATH + + // set initialized states + auto p = s->get_core(0UL); + // set pc to 0x8000_0000 + p->get_state()->pc = 0x80000000; + // initialize the memory with specified data + auto mmu = p->get_mmu(); + for (size_t i = 0; i < size; i++) { + mmu->store(DRAM_BASE + i, data[i]); + } + + return s; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + auto s = spike_init(data, size); + for (int i = 0; i < 1000; i++) { + s->step(1); + } + delete s; + return 0; +} diff --git a/spike_fuzz/spike_fuzz.c b/spike_fuzz/spike_fuzz.c new file mode 100644 index 0000000000..7710650ea2 --- /dev/null +++ b/spike_fuzz/spike_fuzz.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +extern size_t CONFIG_MSIZE; +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +size_t load_from_file(char *filename, uint8_t *data) { + FILE *fp = fopen(filename, "rb"); + if (fp == NULL) { + printf("Can not open '%s'\n", filename); + return 0; + } + + fseek(fp, 0, SEEK_END); + size_t img_size = ftell(fp); + if (img_size > CONFIG_MSIZE) { + img_size = CONFIG_MSIZE; + } + + fseek(fp, 0, SEEK_SET); + fread(data, img_size, 1, fp); + + fclose(fp); + + return img_size; +} + +int main(int argc, char *argv[]) { + uint8_t *data = malloc(CONFIG_MSIZE); + for (int i = 1; i < argc; i++) { + printf("Running %s\n", argv[i]); + size_t size = load_from_file(argv[i], data); + LLVMFuzzerTestOneInput(data, size); + } + free(data); + return 0; +} diff --git a/spike_fuzz/utils.cc b/spike_fuzz/utils.cc new file mode 100644 index 0000000000..c2be837da0 --- /dev/null +++ b/spike_fuzz/utils.cc @@ -0,0 +1,166 @@ +// #include +// #include +// #include +#include "arith.h" +#include "sim.h" +#include "mmu.h" +#include "disasm.h" + + +bool sort_mem_region(const mem_cfg_t &a, const mem_cfg_t &b) +{ + if (a.get_base() == b.get_base()) + return (a.get_size() < b.get_size()); + else + return (a.get_base() < b.get_base()); +} + +static bool check_mem_overlap(const mem_cfg_t& L, const mem_cfg_t& R) +{ + return std::max(L.get_base(), R.get_base()) <= std::min(L.get_inclusive_end(), R.get_inclusive_end()); +} + +static bool check_if_merge_covers_64bit_space(const mem_cfg_t& L, + const mem_cfg_t& R) +{ + if (!check_mem_overlap(L, R)) + return false; + + auto start = std::min(L.get_base(), R.get_base()); + auto end = std::max(L.get_inclusive_end(), R.get_inclusive_end()); + + return (start == 0ull) && (end == std::numeric_limits::max()); +} + +static mem_cfg_t merge_mem_regions(const mem_cfg_t& L, const mem_cfg_t& R) +{ + // one can merge only intersecting regions + assert(check_mem_overlap(L, R)); + + const auto merged_base = std::min(L.get_base(), R.get_base()); + const auto merged_end_incl = std::max(L.get_inclusive_end(), R.get_inclusive_end()); + const auto merged_size = merged_end_incl - merged_base + 1; + + return mem_cfg_t(merged_base, merged_size); +} + + +// check the user specified memory regions and merge the overlapping or +// eliminate the containing parts +static std::vector +merge_overlapping_memory_regions(std::vector mems) +{ + if (mems.empty()) + return {}; + + std::sort(mems.begin(), mems.end(), sort_mem_region); + + std::vector merged_mem; + merged_mem.push_back(mems.front()); + + for (auto mem_it = std::next(mems.begin()); mem_it != mems.end(); ++mem_it) { + const auto& mem_int = *mem_it; + if (!check_mem_overlap(merged_mem.back(), mem_int)) { + merged_mem.push_back(mem_int); + continue; + } + // there is a weird corner case preventing two memory regions from being + // merged: if the resulting size of a region is 2^64 bytes - currently, + // such regions are not representable by mem_cfg_t class (because the + // actual size field is effectively a 64 bit value) + // so we create two smaller memory regions that total for 2^64 bytes as + // a workaround + if (check_if_merge_covers_64bit_space(merged_mem.back(), mem_int)) { + merged_mem.clear(); + merged_mem.push_back(mem_cfg_t(0ull, 0ull - PGSIZE)); + merged_mem.push_back(mem_cfg_t(0ull - PGSIZE, PGSIZE)); + break; + } + merged_mem.back() = merge_mem_regions(merged_mem.back(), mem_int); + } + + return merged_mem; +} + + +std::vector parse_mem_layout(const char* arg) +{ + std::vector res; + + // handle legacy mem argument + char* p; + auto mb = strtoull(arg, &p, 0); + if (*p == 0) { + reg_t size = reg_t(mb) << 20; + if (size != (size_t)size) + throw std::runtime_error("Size would overflow size_t"); + res.push_back(mem_cfg_t(reg_t(DRAM_BASE), size)); + return res; + } + + // handle base/size tuples + while (true) { + auto base = strtoull(arg, &p, 0); + // if (!*p || *p != ':') + // help(); + auto size = strtoull(p + 1, &p, 0); + + // page-align base and size + auto base0 = base, size0 = size; + size += base0 % PGSIZE; + base -= base0 % PGSIZE; + if (size % PGSIZE != 0) + size += PGSIZE - size % PGSIZE; + + if (size != size0) { + fprintf(stderr, "Warning: the memory at [0x%llX, 0x%llX] has been realigned\n" + "to the %ld KiB page size: [0x%llX, 0x%llX]\n", + base0, base0 + size0 - 1, long(PGSIZE / 1024), base, base + size - 1); + } + + if (!mem_cfg_t::check_if_supported(base, size)) { + fprintf(stderr, "Unsupported memory region " + "{base = 0x%llX, size = 0x%llX} specified\n", + base, size); + exit(EXIT_FAILURE); + } + + const unsigned long long max_allowed_pa = (1ull << MAX_PADDR_BITS) - 1ull; + assert(max_allowed_pa <= std::numeric_limits::max()); + mem_cfg_t mem_region(base, size); + if (mem_region.get_inclusive_end() > max_allowed_pa) { + int bits_required = 64 - clz(mem_region.get_inclusive_end()); + fprintf(stderr, "Unsupported memory region " + "{base = 0x%" PRIX64 ", size = 0x%" PRIX64 "} specified," + " which requires %d bits of physical address\n" + " The largest accessible physical address " + "is 0x%llX (defined by MAX_PADDR_BITS constant, which is %d)\n", + mem_region.get_base(), mem_region.get_size(), bits_required, + max_allowed_pa, MAX_PADDR_BITS); + exit(EXIT_FAILURE); + } + + res.push_back(mem_region); + + if (!*p) + break; + // if (*p != ',') + // help(); + arg = p + 1; + } + + auto merged_mem = merge_overlapping_memory_regions(res); + + assert(!merged_mem.empty()); + return merged_mem; +} + +std::vector> make_mems(const std::vector &layout) +{ + std::vector> mems; + mems.reserve(layout.size()); + for (const auto &cfg : layout) { + mems.push_back(std::make_pair(cfg.get_base(), new mem_t(cfg.get_size()))); + } + return mems; +}