diff options
author | Palmer Dabbelt <palmer@dabbelt.com> | 2017-05-17 13:07:47 -0700 |
---|---|---|
committer | Palmer Dabbelt <palmer@dabbelt.com> | 2017-05-17 13:07:47 -0700 |
commit | b3caeee9858b3417d4110b921305c441d3c87653 (patch) | |
tree | 5d6d9b6aa540b2c910aee2d56f0541fe2008fe11 | |
parent | 68f1a893ec84ca734d83e8626c3abd2c8de78cda (diff) | |
parent | a1f754b2f0ec5fe72c86d6916d7c603e7727e68e (diff) | |
download | spike-b3caeee9858b3417d4110b921305c441d3c87653.zip spike-b3caeee9858b3417d4110b921305c441d3c87653.tar.gz spike-b3caeee9858b3417d4110b921305c441d3c87653.tar.bz2 |
Merge remote-tracking branch 'origin/priv-1.10'
103 files changed, 3830 insertions, 3179 deletions
@@ -1,5 +1,5 @@ -Copyright (c) 2013, The Regents of the University of California (Regents). -All Rights Reserved. +Copyright (c) 2010-2017, The Regents of the University of California +(Regents). All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -19,6 +19,7 @@ Build Steps We assume that the RISCV environment variable is set to the RISC-V tools install path, and that the riscv-fesvr package is installed there. + $ apt-get install device-tree-compiler $ mkdir build $ cd build $ ../configure --prefix=$RISCV --with-fesvr=$RISCV diff --git a/config.h.in b/config.h.in index a4070ff..137f195 100644 --- a/config.h.in +++ b/config.h.in @@ -6,6 +6,9 @@ /* Default value for --isa switch */ #undef DEFAULT_ISA +/* Path to the device-tree-compiler */ +#undef DTC + /* Define if subproject MCPPBS_SPROJ_NORM is enabled */ #undef DUMMY_ROCC_ENABLED @@ -66,9 +69,15 @@ /* Enable commit log generation */ #undef RISCV_ENABLE_COMMITLOG +/* Enable hardware management of PTE accessed and dirty bits */ +#undef RISCV_ENABLE_DIRTY + /* Enable PC histogram generation */ #undef RISCV_ENABLE_HISTOGRAM +/* Enable hardware support for misaligned loads and stores */ +#undef RISCV_ENABLE_MISALIGNED + /* Define if subproject MCPPBS_SPROJ_NORM is enabled */ #undef SOFTFLOAT_ENABLED @@ -636,6 +636,7 @@ enable_stow EGREP GREP CXXCPP +DTC RANLIB AR ac_ct_CXX @@ -675,6 +676,7 @@ infodir docdir oldincludedir includedir +runstatedir localstatedir sharedstatedir sysconfdir @@ -703,6 +705,8 @@ with_isa with_fesvr enable_commitlog enable_histogram +enable_dirty +enable_misaligned ' ac_precious_vars='build_alias host_alias @@ -756,6 +760,7 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' @@ -1008,6 +1013,15 @@ do | -silent | --silent | --silen | --sile | --sil) silent=yes ;; + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -1145,7 +1159,7 @@ fi for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir + libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -1298,6 +1312,7 @@ Fine tuning of the installation directories: --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] @@ -1336,6 +1351,10 @@ Optional Features: Enable all optional subprojects --enable-commitlog Enable commit log generation --enable-histogram Enable PC histogram generation + --enable-dirty Enable hardware management of PTE accessed and dirty + bits + --enable-misaligned Enable hardware support for misaligned loads and + stores Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -3363,6 +3382,51 @@ else RANLIB="$ac_cv_prog_RANLIB" fi +# Extract the first word of "dtc", so it can be a program name with args. +set dummy dtc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_DTC+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $DTC in + [\\/]* | ?:[\\/]*) + ac_cv_path_DTC="$DTC" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_DTC="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +DTC=$ac_cv_path_DTC +if test -n "$DTC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DTC" >&5 +$as_echo "$DTC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + +cat >>confdefs.h <<_ACEOF +#define DTC "$DTC" +_ACEOF + ac_ext=cpp @@ -4607,6 +4671,32 @@ $as_echo "#define RISCV_ENABLE_HISTOGRAM /**/" >>confdefs.h fi +# Check whether --enable-dirty was given. +if test "${enable_dirty+set}" = set; then : + enableval=$enable_dirty; +fi + +if test "x$enable_dirty" = "xyes"; then : + + +$as_echo "#define RISCV_ENABLE_DIRTY /**/" >>confdefs.h + + +fi + +# Check whether --enable-misaligned was given. +if test "${enable_misaligned+set}" = set; then : + enableval=$enable_misaligned; +fi + +if test "x$enable_misaligned" = "xyes"; then : + + +$as_echo "#define RISCV_ENABLE_MISALIGNED /**/" >>confdefs.h + + +fi + diff --git a/configure.ac b/configure.ac index dbf50c9..ea64de7 100644 --- a/configure.ac +++ b/configure.ac @@ -51,6 +51,8 @@ AC_PROG_CC AC_PROG_CXX AC_CHECK_TOOL([AR],[ar]) AC_CHECK_TOOL([RANLIB],[ranlib]) +AC_PATH_PROG([DTC],[dtc]) +AC_DEFINE_UNQUOTED(DTC, ["$DTC"], [Path to the device-tree-compiler]) AC_C_BIGENDIAN(AC_MSG_ERROR([Spike requires a little-endian host])) diff --git a/debug_rom/Makefile b/debug_rom/Makefile index b72f37d..4e9093c 100644 --- a/debug_rom/Makefile +++ b/debug_rom/Makefile @@ -6,8 +6,8 @@ OBJCOPY = $(RISCV)/bin/riscv64-unknown-elf-objcopy COMPILE = $(CC) -nostdlib -nostartfiles -I.. -Tlink.ld -ELFS = debug_rom debug_rom32 debug_rom64 -DEPS = debug_rom.S link.ld +ELFS = debug_rom +DEPS = debug_rom.S link.ld debug_rom_defines.h all: $(patsubst %,%.h,$(ELFS)) @@ -18,13 +18,7 @@ all: $(patsubst %,%.h,$(ELFS)) $(OBJCOPY) -O binary --only-section .text $^ $@ debug_rom: $(DEPS) - $(COMPILE) -DRV32 -DRV64 -o $@ $^ - -debug_rom32: $(DEPS) - $(COMPILE) -DRV32 -DDEBUG_RAM_SIZE=28 -o $@ $^ - -debug_rom64: $(DEPS) - $(COMPILE) -DRV64 -o $@ $^ + $(COMPILE) -o $@ $^ clean: rm -f $(ELFS) debug_rom*.raw debug_rom*.h diff --git a/debug_rom/debug_rom.S b/debug_rom/debug_rom.S index 74a934b..2ee7a31 100755 --- a/debug_rom/debug_rom.S +++ b/debug_rom/debug_rom.S @@ -1,152 +1,72 @@ -# This code should be functional. Doesn't have to be optimal. -# I'm writing it to prove that it can be done. - -#include "riscv/encoding.h" - -# TODO: Update these constants once they're finalized in the doc. - -#define DEBUG_RAM 0x400 -#ifndef DEBUG_RAM_SIZE -# define DEBUG_RAM_SIZE 64 -#endif - -#define CLEARDEBINT 0x100 -#define SETHALTNOT 0x10c - -#if (defined(RV32) + defined(RV64) + defined(RV128)) > 1 -# define MULTI_XLEN -#elif (defined(RV32) + defined(RV64) + defined(RV128)) == 0 -# error define one or more of RV32, RV64, RV128 -#endif +// See LICENSE.SiFive for license details. +#include "spike/encoding.h" +#include "debug_rom_defines.h" + + .option norvc .global entry - .global resume .global exception - # Automatically called when Debug Mode is first entered. -entry: j _entry - # Should be called by Debug RAM code that has finished execution and - # wants to return to Debug Mode. + // Entry location on ebreak, Halt, or Breakpoint + // It is the same for all harts. They branch when + // their GO or RESUME bit is set. + +entry: + jal zero, _entry resume: - j _resume + jal zero, _resume exception: - # Set the last word of Debug RAM to all ones, to indicate that we hit - # an exception. - li s0, ~0 - j _resume2 + jal zero, _exception -_resume: - li s0, 0 -_resume2: +_entry: + // This fence is required because the execution may have written something + // into the Abstract Data or Program Buffer registers. fence + csrw CSR_DSCRATCH, s0 // Save s0 to allow signaling MHARTID + + // We continue to let the hart know that we are halted in order that + // a DM which was reset is still made aware that a hart is halted. + // We keep checking both whether there is something the debugger wants + // us to do, or whether we should resume. +entry_loop: + csrr s0, CSR_MHARTID + sw s0, DEBUG_ROM_HALTED(zero) + lbu s0, DEBUG_ROM_FLAGS(s0) // 1 byte flag per hart. Only one hart advances here. + andi s0, s0, (1 << DEBUG_ROM_FLAG_GO) + bnez s0, going + csrr s0, CSR_MHARTID + lbu s0, DEBUG_ROM_FLAGS(s0) // multiple harts can resume here + andi s0, s0, (1 << DEBUG_ROM_FLAG_RESUME) + bnez s0, resume + jal zero, entry_loop + +_exception: + sw zero, DEBUG_ROM_EXCEPTION(zero) // Let debug module know you got an exception. + ebreak + +going: + csrr s0, CSR_DSCRATCH // Restore s0 here + sw zero, DEBUG_ROM_GOING(zero) // When debug module sees this write, the GO flag is reset. + fence + fence.i + jalr zero, zero, %lo(whereto) // Debug module will put different instructions and data in the RAM, + // so we use fence and fence.i for safety. (rocket-chip doesn't have this + // because jalr is special there) - # Restore s1. -#ifdef MULTI_XLEN - csrr s1, CSR_MISA -#endif - -#ifdef RV32 -# ifdef MULTI_XLEN - bltz s1, restore_not_32 -# endif - -restore_32: - lw s1, (DEBUG_RAM + DEBUG_RAM_SIZE - 4)(zero) -# if defined(RV64) || defined(RV128) - j finish_restore -# endif -#endif - -restore_not_32: -#if defined(RV64) && defined(RV128) - slli s1, s1, 1 - bltz s1, restore_128 -#endif - -#ifdef RV64 -restore_64: - ld s1, (DEBUG_RAM + DEBUG_RAM_SIZE - 8)(zero) -#endif -#if defined(RV64) && defined(RV128) - j finish_restore -#endif -#ifdef RV128 -restore_128: - lq s1, (DEBUG_RAM + DEBUG_RAM_SIZE - 16)(zero) -#endif - -finish_restore: - # s0 contains ~0 if we got here through an exception, and 0 otherwise. - # Store this to the last word in Debug RAM so the debugger can tell if - # an exception occurred. - sw s0, (DEBUG_RAM + DEBUG_RAM_SIZE - 4)(zero) - - # Clear debug interrupt. - csrr s0, CSR_MHARTID - sw s0, CLEARDEBINT(zero) - -check_halt: - csrr s0, CSR_DCSR - andi s0, s0, DCSR_HALT - bnez s0, wait_for_interrupt - -exit: - # Restore s0. - csrr s0, CSR_DSCRATCH +_resume: + csrr s0, CSR_MHARTID + sw s0, DEBUG_ROM_RESUMING(zero) // When Debug Module sees this write, the RESUME flag is reset. + csrr s0, CSR_DSCRATCH // Restore s0 dret -_entry: - # Save s0 in DSCRATCH - csrw CSR_DSCRATCH, s0 - - # Check why we're here - csrr s0, CSR_DCSR - # cause is in bits 8:6 of dcsr - andi s0, s0, DCSR_CAUSE - addi s0, s0, -(DCSR_CAUSE_DEBUGINT<<6) - bnez s0, spontaneous_halt - -jdebugram: - # Save s1 so that the debug program can use two registers. -#ifdef MULTI_XLEN - csrr s0, CSR_MISA -#endif - -#ifdef RV32 -# ifdef MULTI_XLEN - bltz s0, save_not_32 -# endif -save_32: - sw s1, (DEBUG_RAM + DEBUG_RAM_SIZE - 4)(zero) - jr zero, DEBUG_RAM -#endif - -save_not_32: -#if defined(RV64) && defined(RV128) - slli s0, s0, 1 - bltz s0, save_128 -#endif - -#ifdef RV64 -save_64: - sd s1, (DEBUG_RAM + DEBUG_RAM_SIZE - 8)(zero) - jr zero, DEBUG_RAM -#endif - -#ifdef RV128 -save_128: - sq s1, (DEBUG_RAM + DEBUG_RAM_SIZE - 16)(zero) - jr zero, DEBUG_RAM -#endif - -spontaneous_halt: - csrr s0, CSR_MHARTID - sw s0, SETHALTNOT(zero) - csrsi CSR_DCSR, DCSR_HALT - -wait_for_interrupt: - csrr s0, CSR_DCSR - andi s0, s0, DCSR_DEBUGINT - beqz s0, wait_for_interrupt - - j jdebugram + // END OF ACTUAL "ROM" CONTENTS. BELOW IS JUST FOR LINKER SCRIPT. + +.section .whereto +whereto: + nop + // Variable "ROM" This is : jal x0 abstract, jal x0 program_buffer, + // or jal x0 resume, as desired. + // Debug Module state machine tracks what is 'desired'. + // We don't need/want to use jalr here because all of the + // Variable ROM contents are set by + // Debug Module before setting the OK_GO byte. diff --git a/debug_rom/debug_rom.h b/debug_rom/debug_rom.h index b1e3bea..d21e166 100644 --- a/debug_rom/debug_rom.h +++ b/debug_rom/debug_rom.h @@ -1,16 +1,12 @@ static const unsigned char debug_rom_raw[] = { - 0x6f, 0x00, 0xc0, 0x04, 0x6f, 0x00, 0xc0, 0x00, 0x13, 0x04, 0xf0, 0xff, - 0x6f, 0x00, 0x80, 0x00, 0x13, 0x04, 0x00, 0x00, 0x0f, 0x00, 0xf0, 0x0f, - 0xf3, 0x24, 0x10, 0x30, 0x63, 0xc6, 0x04, 0x00, 0x83, 0x24, 0xc0, 0x43, - 0x6f, 0x00, 0x80, 0x00, 0x83, 0x34, 0x80, 0x43, 0x23, 0x2e, 0x80, 0x42, - 0x73, 0x24, 0x40, 0xf1, 0x23, 0x20, 0x80, 0x10, 0x73, 0x24, 0x00, 0x7b, - 0x13, 0x74, 0x84, 0x00, 0x63, 0x12, 0x04, 0x04, 0x73, 0x24, 0x20, 0x7b, - 0x73, 0x00, 0x20, 0x7b, 0x73, 0x10, 0x24, 0x7b, 0x73, 0x24, 0x00, 0x7b, - 0x13, 0x74, 0x04, 0x1c, 0x13, 0x04, 0x04, 0xf4, 0x63, 0x1e, 0x04, 0x00, - 0x73, 0x24, 0x10, 0x30, 0x63, 0x46, 0x04, 0x00, 0x23, 0x2e, 0x90, 0x42, - 0x67, 0x00, 0x00, 0x40, 0x23, 0x3c, 0x90, 0x42, 0x67, 0x00, 0x00, 0x40, - 0x73, 0x24, 0x40, 0xf1, 0x23, 0x26, 0x80, 0x10, 0x73, 0x60, 0x04, 0x7b, - 0x73, 0x24, 0x00, 0x7b, 0x13, 0x74, 0x04, 0x02, 0xe3, 0x0c, 0x04, 0xfe, - 0x6f, 0xf0, 0x1f, 0xfd + 0x6f, 0x00, 0xc0, 0x00, 0x6f, 0x00, 0x40, 0x05, 0x6f, 0x00, 0x40, 0x03, + 0x0f, 0x00, 0xf0, 0x0f, 0x73, 0x10, 0x24, 0x7b, 0x73, 0x24, 0x40, 0xf1, + 0x23, 0x20, 0x80, 0x10, 0x03, 0x44, 0x04, 0x40, 0x13, 0x74, 0x14, 0x00, + 0x63, 0x10, 0x04, 0x02, 0x73, 0x24, 0x40, 0xf1, 0x03, 0x44, 0x04, 0x40, + 0x13, 0x74, 0x24, 0x00, 0xe3, 0x18, 0x04, 0xfc, 0x6f, 0xf0, 0xdf, 0xfd, + 0x23, 0x26, 0x00, 0x10, 0x73, 0x00, 0x10, 0x00, 0x73, 0x24, 0x20, 0x7b, + 0x23, 0x22, 0x00, 0x10, 0x0f, 0x00, 0xf0, 0x0f, 0x0f, 0x10, 0x00, 0x00, + 0x67, 0x00, 0x00, 0x30, 0x73, 0x24, 0x40, 0xf1, 0x23, 0x24, 0x80, 0x10, + 0x73, 0x24, 0x20, 0x7b, 0x73, 0x00, 0x20, 0x7b }; -static const unsigned int debug_rom_raw_len = 148; +static const unsigned int debug_rom_raw_len = 104; diff --git a/debug_rom/debug_rom_defines.h b/debug_rom/debug_rom_defines.h new file mode 100644 index 0000000..616cf59 --- /dev/null +++ b/debug_rom/debug_rom_defines.h @@ -0,0 +1,23 @@ +// See LICENSE file for license details. + +#ifndef DEBUG_ROM_DEFINES_H +#define DEBUG_ROM_DEFINES_H + +// These are implementation-specific addresses in the Debug Module +#define DEBUG_ROM_HALTED 0x100 +#define DEBUG_ROM_GOING 0x104 +#define DEBUG_ROM_RESUMING 0x108 +#define DEBUG_ROM_EXCEPTION 0x10C + +// Region of memory where each hart has 1 +// byte to read. +#define DEBUG_ROM_FLAGS 0x400 +#define DEBUG_ROM_FLAG_GO 0 +#define DEBUG_ROM_FLAG_RESUME 1 + +// These needs to match the link.ld +#define DEBUG_ROM_WHERETO 0x300 +#define DEBUG_ROM_ENTRY 0x800 +#define DEBUG_ROM_TVEC 0x808 + +#endif diff --git a/debug_rom/link.ld b/debug_rom/link.ld index aba6ae8..897c42d 100644 --- a/debug_rom/link.ld +++ b/debug_rom/link.ld @@ -2,6 +2,10 @@ OUTPUT_ARCH( "riscv" ) ENTRY( entry ) SECTIONS { + .whereto 0x300 : + { + *(.whereto) + } . = 0x800; .text : { diff --git a/riscv/clint.cc b/riscv/clint.cc new file mode 100644 index 0000000..08508b4 --- /dev/null +++ b/riscv/clint.cc @@ -0,0 +1,72 @@ +#include "devices.h" +#include "processor.h" + +clint_t::clint_t(std::vector<processor_t*>& procs) + : procs(procs), mtimecmp(procs.size()) +{ +} + +/* 0000 msip hart 0 + * 0004 msip hart 1 + * 4000 mtimecmp hart 0 lo + * 4004 mtimecmp hart 0 hi + * 4008 mtimecmp hart 1 lo + * 400c mtimecmp hart 1 hi + * bff8 mtime lo + * bffc mtime hi + */ + +#define MSIP_BASE 0x0 +#define MTIMECMP_BASE 0x4000 +#define MTIME_BASE 0xbff8 + +bool clint_t::load(reg_t addr, size_t len, uint8_t* bytes) +{ + if (addr >= MSIP_BASE && addr + len <= MSIP_BASE + procs.size()*sizeof(msip_t)) { + std::vector<msip_t> msip(procs.size()); + for (size_t i = 0; i < procs.size(); ++i) + msip[i] = !!(procs[i]->state.mip & MIP_MSIP); + memcpy(bytes, (uint8_t*)&msip[0] + addr - MSIP_BASE, len); + } else if (addr >= MTIMECMP_BASE && addr + len <= MTIMECMP_BASE + procs.size()*sizeof(mtimecmp_t)) { + memcpy(bytes, (uint8_t*)&mtimecmp[0] + addr - MTIMECMP_BASE, len); + } else if (addr >= MTIME_BASE && addr + len <= MTIME_BASE + sizeof(mtime_t)) { + memcpy(bytes, (uint8_t*)&mtime + addr - MTIME_BASE, len); + } else { + return false; + } + return true; +} + +bool clint_t::store(reg_t addr, size_t len, const uint8_t* bytes) +{ + if (addr >= MSIP_BASE && addr + len <= MSIP_BASE + procs.size()*sizeof(msip_t)) { + std::vector<msip_t> msip(procs.size()); + std::vector<msip_t> mask(procs.size(), 0); + memcpy((uint8_t*)&msip[0] + addr - MSIP_BASE, bytes, len); + memset((uint8_t*)&mask[0] + addr - MSIP_BASE, 0xff, len); + for (size_t i = 0; i < procs.size(); ++i) { + if (!(mask[i] & 0xFF)) continue; + procs[i]->state.mip &= ~MIP_MSIP; + if (!!(msip[i] & 1)) + procs[i]->state.mip |= MIP_MSIP; + } + } else if (addr >= MTIMECMP_BASE && addr + len <= MTIMECMP_BASE + procs.size()*sizeof(mtimecmp_t)) { + memcpy((uint8_t*)&mtimecmp[0] + addr - MTIMECMP_BASE, bytes, len); + } else if (addr >= MTIME_BASE && addr + len <= MTIME_BASE + sizeof(mtime_t)) { + memcpy((uint8_t*)&mtime + addr - MTIME_BASE, bytes, len); + } else { + return false; + } + increment(0); + return true; +} + +void clint_t::increment(reg_t inc) +{ + mtime += inc; + for (size_t i = 0; i < procs.size(); i++) { + procs[i]->state.mip &= ~MIP_MTIP; + if (mtime >= mtimecmp[i]) + procs[i]->state.mip |= MIP_MTIP; + } +} diff --git a/riscv/debug_defines.h b/riscv/debug_defines.h new file mode 100644 index 0000000..cb07ec2 --- /dev/null +++ b/riscv/debug_defines.h @@ -0,0 +1,1413 @@ +#define DTM_IDCODE 0x01 +/* +* Identifies the release version of this part. + */ +#define DTM_IDCODE_VERSION_OFFSET 28 +#define DTM_IDCODE_VERSION_LENGTH 4 +#define DTM_IDCODE_VERSION (0xf << DTM_IDCODE_VERSION_OFFSET) +/* +* Identifies the designer's part number of this part. + */ +#define DTM_IDCODE_PARTNUMBER_OFFSET 12 +#define DTM_IDCODE_PARTNUMBER_LENGTH 16 +#define DTM_IDCODE_PARTNUMBER (0xffff << DTM_IDCODE_PARTNUMBER_OFFSET) +/* +* Identifies the designer/manufacturer of this part. Bits 6:0 must be +* bits 6:0 of the designer/manufacturer's Identification Code as +* assigned by JEDEC Standard JEP106. Bits 10:7 contain the modulo-16 +* count of the number of continuation characters (0x7f) in that same +* Identification Code. + */ +#define DTM_IDCODE_MANUFID_OFFSET 1 +#define DTM_IDCODE_MANUFID_LENGTH 11 +#define DTM_IDCODE_MANUFID (0x7ff << DTM_IDCODE_MANUFID_OFFSET) +#define DTM_IDCODE_1_OFFSET 0 +#define DTM_IDCODE_1_LENGTH 1 +#define DTM_IDCODE_1 (0x1 << DTM_IDCODE_1_OFFSET) +#define DTM_DTMCS 0x10 +/* +* Writing 1 to this bit does a hard reset of the DTM, +* causing the DTM to forget about any outstanding DMI transactions. +* In general this should only be used when the Debugger has +* reason to expect that the outstanding DMI transaction will never +* complete (e.g. a reset condition caused an inflight DMI transaction to +* be cancelled). + */ +#define DTM_DTMCS_DMIHARDRESET_OFFSET 17 +#define DTM_DTMCS_DMIHARDRESET_LENGTH 1 +#define DTM_DTMCS_DMIHARDRESET (0x1 << DTM_DTMCS_DMIHARDRESET_OFFSET) +/* +* Writing 1 to this bit clears the sticky error state +* and allows the DTM to retry or complete the previous +* transaction. + */ +#define DTM_DTMCS_DMIRESET_OFFSET 16 +#define DTM_DTMCS_DMIRESET_LENGTH 1 +#define DTM_DTMCS_DMIRESET (0x1 << DTM_DTMCS_DMIRESET_OFFSET) +/* +* This is a hint to the debugger of the minimum number of +* cycles a debugger should spend in +* Run-Test/Idle after every DMI scan to avoid a `busy' +* return code (\Fdmistat of 3). A debugger must still +* check \Fdmistat when necessary. +* +* 0: It is not necessary to enter Run-Test/Idle at all. +* +* 1: Enter Run-Test/Idle and leave it immediately. +* +* 2: Enter Run-Test/Idle and stay there for 1 cycle before leaving. +* +* And so on. + */ +#define DTM_DTMCS_IDLE_OFFSET 12 +#define DTM_DTMCS_IDLE_LENGTH 3 +#define DTM_DTMCS_IDLE (0x7 << DTM_DTMCS_IDLE_OFFSET) +/* +* 0: No error. +* +* 1: Reserved. Interpret the same as 2. +* +* 2: An operation failed (resulted in \Fop of 2). +* +* 3: An operation was attempted while a DMI access was still in +* progress (resulted in \Fop of 3). + */ +#define DTM_DTMCS_DMISTAT_OFFSET 10 +#define DTM_DTMCS_DMISTAT_LENGTH 2 +#define DTM_DTMCS_DMISTAT (0x3 << DTM_DTMCS_DMISTAT_OFFSET) +/* +* The size of \Faddress in \Rdmi. + */ +#define DTM_DTMCS_ABITS_OFFSET 4 +#define DTM_DTMCS_ABITS_LENGTH 6 +#define DTM_DTMCS_ABITS (0x3f << DTM_DTMCS_ABITS_OFFSET) +/* +* 0: Version described in spec version 0.11. +* +* 1: Version described in spec version 0.12 (and later?), which +* reduces the DMI data width to 32 bits. +* +* Other values are reserved for future use. + */ +#define DTM_DTMCS_VERSION_OFFSET 0 +#define DTM_DTMCS_VERSION_LENGTH 4 +#define DTM_DTMCS_VERSION (0xf << DTM_DTMCS_VERSION_OFFSET) +#define DTM_DMI 0x11 +/* +* Address used for DMI access. In Update-DR this value is used +* to access the DM over the DMI. + */ +#define DTM_DMI_ADDRESS_OFFSET 34 +#define DTM_DMI_ADDRESS_LENGTH abits +#define DTM_DMI_ADDRESS (((1L<<abits)-1) << DTM_DMI_ADDRESS_OFFSET) +/* +* The data to send to the DM over the DMI during Update-DR, and +* the data returned from the DM as a result of the previous operation. + */ +#define DTM_DMI_DATA_OFFSET 2 +#define DTM_DMI_DATA_LENGTH 32 +#define DTM_DMI_DATA (0xffffffffL << DTM_DMI_DATA_OFFSET) +/* +* When the debugger writes this field, it has the following meaning: +* +* 0: Ignore \Fdata. (nop) +* +* 1: Read from \Faddress. (read) +* +* 2: Write \Fdata to \Faddress. (write) +* +* 3: Reserved. +* +* When the debugger reads this field, it means the following: +* +* 0: The previous operation completed successfully. +* +* 1: Reserved. +* +* 2: A previous operation failed. The data scanned into \Rdmi in +* this access will be ignored. This status is sticky and can be +* cleared by writing \Fdmireset in \Rdtmcs. +* +* This indicates that the DM itself responded with an error, e.g. +* in the System Bus and Serial Port overflow/underflow cases. +* +* 3: An operation was attempted while a DMI request is still in +* progress. The data scanned into \Rdmi in this access will be +* ignored. This status is sticky and can be cleared by writing +* \Fdmireset in \Rdtmcs. If a debugger sees this status, it +* needs to give the target more TCK edges between Update-DR and +* Capture-DR. The simplest way to do that is to add extra transitions +* in Run-Test/Idle. +* +* (The DTM, DM, and/or component may be in different clock domains, +* so synchronization may be required. Some relatively fixed number of +* TCK ticks may be needed for the request to reach the DM, complete, +* and for the response to be synchronized back into the TCK domain.) + */ +#define DTM_DMI_OP_OFFSET 0 +#define DTM_DMI_OP_LENGTH 2 +#define DTM_DMI_OP (0x3L << DTM_DMI_OP_OFFSET) +#define CSR_DCSR 0x7b0 +/* +* 0: There is no external debug support. +* +* 1: External debug support exists as it is described in this document. +* +* Other values are reserved for future standards. + */ +#define CSR_DCSR_XDEBUGVER_OFFSET 30 +#define CSR_DCSR_XDEBUGVER_LENGTH 2 +#define CSR_DCSR_XDEBUGVER (0x3 << CSR_DCSR_XDEBUGVER_OFFSET) +/* +* When 1, {\tt ebreak} instructions in Machine Mode enter Debug Mode. + */ +#define CSR_DCSR_EBREAKM_OFFSET 15 +#define CSR_DCSR_EBREAKM_LENGTH 1 +#define CSR_DCSR_EBREAKM (0x1 << CSR_DCSR_EBREAKM_OFFSET) +/* +* When 1, {\tt ebreak} instructions in Hypervisor Mode enter Debug Mode. + */ +#define CSR_DCSR_EBREAKH_OFFSET 14 +#define CSR_DCSR_EBREAKH_LENGTH 1 +#define CSR_DCSR_EBREAKH (0x1 << CSR_DCSR_EBREAKH_OFFSET) +/* +* When 1, {\tt ebreak} instructions in Supervisor Mode enter Debug Mode. + */ +#define CSR_DCSR_EBREAKS_OFFSET 13 +#define CSR_DCSR_EBREAKS_LENGTH 1 +#define CSR_DCSR_EBREAKS (0x1 << CSR_DCSR_EBREAKS_OFFSET) +/* +* When 1, {\tt ebreak} instructions in User/Application Mode enter +* Debug Mode. + */ +#define CSR_DCSR_EBREAKU_OFFSET 12 +#define CSR_DCSR_EBREAKU_LENGTH 1 +#define CSR_DCSR_EBREAKU (0x1 << CSR_DCSR_EBREAKU_OFFSET) +/* +* 0: Increment counters as usual. +* +* 1: Don't increment any counters while in Debug Mode. This includes +* the {\tt cycle} and {\tt instret} CSRs. This is preferred for most +* debugging scenarios. +* +* An implementation may choose not to support writing to this bit. +* The debugger must read back the value it writes to check whether +* the feature is supported. + */ +#define CSR_DCSR_STOPCOUNT_OFFSET 10 +#define CSR_DCSR_STOPCOUNT_LENGTH 1 +#define CSR_DCSR_STOPCOUNT (0x1 << CSR_DCSR_STOPCOUNT_OFFSET) +/* +* 0: Increment timers as usual. +* +* 1: Don't increment any hart-local timers while in Debug Mode. +* +* An implementation may choose not to support writing to this bit. +* The debugger must read back the value it writes to check whether +* the feature is supported. + */ +#define CSR_DCSR_STOPTIME_OFFSET 9 +#define CSR_DCSR_STOPTIME_LENGTH 1 +#define CSR_DCSR_STOPTIME (0x1 << CSR_DCSR_STOPTIME_OFFSET) +/* +* Explains why Debug Mode was entered. +* +* When there are multiple reasons to enter Debug Mode in a single +* cycle, the cause with the highest priority is the one written. +* +* 1: An {\tt ebreak} instruction was executed. (priority 3) +* +* 2: The Trigger Module caused a halt. (priority 4) +* +* 3: \Fhaltreq was set. (priority 2) +* +* 4: The hart single stepped because \Fstep was set. (priority 1) +* +* Other values are reserved for future use. + */ +#define CSR_DCSR_CAUSE_OFFSET 6 +#define CSR_DCSR_CAUSE_LENGTH 3 +#define CSR_DCSR_CAUSE (0x7 << CSR_DCSR_CAUSE_OFFSET) +/* +* When set and not in Debug Mode, the hart will only execute a single +* instruction and then enter Debug Mode. +* Interrupts are disabled when this bit is set. +* If the instruction does not complete due to an exception, +* the hart will immediately enter Debug Mode before executing +* the trap handler, with appropriate exception registers set. + */ +#define CSR_DCSR_STEP_OFFSET 2 +#define CSR_DCSR_STEP_LENGTH 1 +#define CSR_DCSR_STEP (0x1 << CSR_DCSR_STEP_OFFSET) +/* +* Contains the privilege level the hart was operating in when Debug +* Mode was entered. The encoding is described in Table +* \ref{tab:privlevel}. A debugger can change this value to change +* the hart's privilege level when exiting Debug Mode. +* +* Not all privilege levels are supported on all harts. If the +* encoding written is not supported or the debugger is not allowed to +* change to it, the hart may change to any supported privilege level. + */ +#define CSR_DCSR_PRV_OFFSET 0 +#define CSR_DCSR_PRV_LENGTH 2 +#define CSR_DCSR_PRV (0x3 << CSR_DCSR_PRV_OFFSET) +#define CSR_DPC 0x7b1 +#define CSR_DPC_DPC_OFFSET 0 +#define CSR_DPC_DPC_LENGTH XLEN +#define CSR_DPC_DPC (((1L<<XLEN)-1) << CSR_DPC_DPC_OFFSET) +#define CSR_DSCRATCH0 0x7b2 +#define CSR_DSCRATCH1 0x7b3 +#define CSR_PRIV virtual +/* +* Contains the privilege level the hart was operating in when Debug +* Mode was entered. The encoding is described in Table +* \ref{tab:privlevel}. A user can write this value to change the +* hart's privilege level when exiting Debug Mode. + */ +#define CSR_PRIV_PRV_OFFSET 0 +#define CSR_PRIV_PRV_LENGTH 2 +#define CSR_PRIV_PRV (0x3 << CSR_PRIV_PRV_OFFSET) +#define CSR_TSELECT 0x7a0 +#define CSR_TSELECT_INDEX_OFFSET 0 +#define CSR_TSELECT_INDEX_LENGTH XLEN +#define CSR_TSELECT_INDEX (((1L<<XLEN)-1) << CSR_TSELECT_INDEX_OFFSET) +#define CSR_TDATA1 0x7a1 +/* +* 0: There is no trigger at this \Rtselect. +* +* 1: The trigger is a legacy SiFive address match trigger. These +* should not be implemented and aren't further documented here. +* +* 2: The trigger is an address/data match trigger. The remaining bits +* in this register act as described in \Rmcontrol. +* +* 3: The trigger is an instruction count trigger. The remaining bits +* in this register act as described in \Ricount. +* +* 15: This trigger exists (so enumeration shouldn't terminate), but +* is not currently available. +* +* Other values are reserved for future use. + */ +#define CSR_TDATA1_TYPE_OFFSET XLEN-4 +#define CSR_TDATA1_TYPE_LENGTH 4 +#define CSR_TDATA1_TYPE (0xfL << CSR_TDATA1_TYPE_OFFSET) +/* +* 0: Both Debug and M Mode can write the {\tt tdata} registers at the +* selected \Rtselect. +* +* 1: Only Debug Mode can write the {\tt tdata} registers at the +* selected \Rtselect. Writes from other modes are ignored. +* +* This bit is only writable from Debug Mode. + */ +#define CSR_TDATA1_HMODE_OFFSET XLEN-5 +#define CSR_TDATA1_HMODE_LENGTH 1 +#define CSR_TDATA1_HMODE (0x1L << CSR_TDATA1_HMODE_OFFSET) +/* +* Trigger-specific data. + */ +#define CSR_TDATA1_DATA_OFFSET 0 +#define CSR_TDATA1_DATA_LENGTH XLEN - 5 +#define CSR_TDATA1_DATA (((1L<<XLEN - 5)-1) << CSR_TDATA1_DATA_OFFSET) +#define CSR_TDATA2 0x7a2 +#define CSR_TDATA2_DATA_OFFSET 0 +#define CSR_TDATA2_DATA_LENGTH XLEN +#define CSR_TDATA2_DATA (((1L<<XLEN)-1) << CSR_TDATA2_DATA_OFFSET) +#define CSR_TDATA3 0x7a3 +#define CSR_TDATA3_DATA_OFFSET 0 +#define CSR_TDATA3_DATA_LENGTH XLEN +#define CSR_TDATA3_DATA (((1L<<XLEN)-1) << CSR_TDATA3_DATA_OFFSET) +#define CSR_MCONTROL 0x7a1 +#define CSR_MCONTROL_TYPE_OFFSET XLEN-4 +#define CSR_MCONTROL_TYPE_LENGTH 4 +#define CSR_MCONTROL_TYPE (0xfL << CSR_MCONTROL_TYPE_OFFSET) +#define CSR_MCONTROL_DMODE_OFFSET XLEN-5 +#define CSR_MCONTROL_DMODE_LENGTH 1 +#define CSR_MCONTROL_DMODE (0x1L << CSR_MCONTROL_DMODE_OFFSET) +/* +* Specifies the largest naturally aligned powers-of-two (NAPOT) range +* supported by the hardware. The value is the logarithm base 2 of the +* number of bytes in that range. A value of 0 indicates that only +* exact value matches are supported (one byte range). A value of 63 +* corresponds to the maximum NAPOT range, which is $2^{63}$ bytes in +* size. + */ +#define CSR_MCONTROL_MASKMAX_OFFSET XLEN-11 +#define CSR_MCONTROL_MASKMAX_LENGTH 6 +#define CSR_MCONTROL_MASKMAX (0x3fL << CSR_MCONTROL_MASKMAX_OFFSET) +/* +* 0: Perform a match on the virtual address. +* +* 1: Perform a match on the data value loaded/stored, or the +* instruction executed. + */ +#define CSR_MCONTROL_SELECT_OFFSET 19 +#define CSR_MCONTROL_SELECT_LENGTH 1 +#define CSR_MCONTROL_SELECT (0x1L << CSR_MCONTROL_SELECT_OFFSET) +/* +* 0: The action for this trigger will be taken just before the +* instruction that triggered it is executed, but after all preceding +* instructions are are committed. +* +* 1: The action for this trigger will be taken after the instruction +* that triggered it is executed. It should be taken before the next +* instruction is executed, but it is better to implement triggers and +* not implement that suggestion than to not implement them at all. +* +* Most hardware will only implement one timing or the other, possibly +* dependent on \Fselect, \Fexecute, \Fload, and \Fstore. This bit +* primarily exists for the hardware to communicate to the debugger +* what will happen. Hardware may implement the bit fully writable, in +* which case the debugger has a little more control. +* +* Data load triggers with \Ftiming of 0 will result in the same load +* happening again when the debugger lets the core run. For data load +* triggers, debuggers must first attempt to set the breakpoint with +* \Ftiming of 1. +* +* A chain of triggers that don't all have the same \Ftiming value +* will never fire (unless consecutive instructions match the +* appropriate triggers). + */ +#define CSR_MCONTROL_TIMING_OFFSET 18 +#define CSR_MCONTROL_TIMING_LENGTH 1 +#define CSR_MCONTROL_TIMING (0x1L << CSR_MCONTROL_TIMING_OFFSET) +/* +* Determines what happens when this trigger matches. +* +* 0: Raise a breakpoint exception. (Used when software wants to use +* the trigger module without an external debugger attached.) +* +* 1: Enter Debug Mode. (Only supported when \Fhmode is 1.) +* +* 2: Start tracing. +* +* 3: Stop tracing. +* +* 4: Emit trace data for this match. If it is a data access match, +* emit appropriate Load/Store Address/Data. If it is an instruction +* execution, emit its PC. +* +* Other values are reserved for future use. + */ +#define CSR_MCONTROL_ACTION_OFFSET 12 +#define CSR_MCONTROL_ACTION_LENGTH 6 +#define CSR_MCONTROL_ACTION (0x3fL << CSR_MCONTROL_ACTION_OFFSET) +/* +* 0: When this trigger matches, the configured action is taken. +* +* 1: While this trigger does not match, it prevents the trigger with +* the next index from matching. + */ +#define CSR_MCONTROL_CHAIN_OFFSET 11 +#define CSR_MCONTROL_CHAIN_LENGTH 1 +#define CSR_MCONTROL_CHAIN (0x1L << CSR_MCONTROL_CHAIN_OFFSET) +/* +* 0: Matches when the value equals \Rtdatatwo. +* +* 1: Matches when the top M bits of the value match the top M bits of +* \Rtdatatwo. M is XLEN-1 minus the index of the least-significant +* bit containing 0 in \Rtdatatwo. +* +* 2: Matches when the value is greater than or equal to \Rtdatatwo. +* +* 3: Matches when the value is less than \Rtdatatwo. +* +* 4: Matches when the lower half of the value equals the lower half +* of \Rtdatatwo after the lower half of the value is ANDed with the +* upper half of \Rtdatatwo. +* +* 5: Matches when the upper half of the value equals the lower half +* of \Rtdatatwo after the upper half of the value is ANDed with the +* upper half of \Rtdatatwo. +* +* Other values are reserved for future use. + */ +#define CSR_MCONTROL_MATCH_OFFSET 7 +#define CSR_MCONTROL_MATCH_LENGTH 4 +#define CSR_MCONTROL_MATCH (0xfL << CSR_MCONTROL_MATCH_OFFSET) +/* +* When set, enable this trigger in M mode. + */ +#define CSR_MCONTROL_M_OFFSET 6 +#define CSR_MCONTROL_M_LENGTH 1 +#define CSR_MCONTROL_M (0x1L << CSR_MCONTROL_M_OFFSET) +/* +* When set, enable this trigger in H mode. + */ +#define CSR_MCONTROL_H_OFFSET 5 +#define CSR_MCONTROL_H_LENGTH 1 +#define CSR_MCONTROL_H (0x1L << CSR_MCONTROL_H_OFFSET) +/* +* When set, enable this trigger in S mode. + */ +#define CSR_MCONTROL_S_OFFSET 4 +#define CSR_MCONTROL_S_LENGTH 1 +#define CSR_MCONTROL_S (0x1L << CSR_MCONTROL_S_OFFSET) +/* +* When set, enable this trigger in U mode. + */ +#define CSR_MCONTROL_U_OFFSET 3 +#define CSR_MCONTROL_U_LENGTH 1 +#define CSR_MCONTROL_U (0x1L << CSR_MCONTROL_U_OFFSET) +/* +* When set, the trigger fires on the virtual address or opcode of an +* instruction that is executed. + */ +#define CSR_MCONTROL_EXECUTE_OFFSET 2 +#define CSR_MCONTROL_EXECUTE_LENGTH 1 +#define CSR_MCONTROL_EXECUTE (0x1L << CSR_MCONTROL_EXECUTE_OFFSET) +/* +* When set, the trigger fires on the virtual address or data of a store. + */ +#define CSR_MCONTROL_STORE_OFFSET 1 +#define CSR_MCONTROL_STORE_LENGTH 1 +#define CSR_MCONTROL_STORE (0x1L << CSR_MCONTROL_STORE_OFFSET) +/* +* When set, the trigger fires on the virtual address or data of a load. + */ +#define CSR_MCONTROL_LOAD_OFFSET 0 +#define CSR_MCONTROL_LOAD_LENGTH 1 +#define CSR_MCONTROL_LOAD (0x1L << CSR_MCONTROL_LOAD_OFFSET) +#define CSR_ICOUNT 0x7a1 +#define CSR_ICOUNT_TYPE_OFFSET XLEN-4 +#define CSR_ICOUNT_TYPE_LENGTH 4 +#define CSR_ICOUNT_TYPE (0xfL << CSR_ICOUNT_TYPE_OFFSET) +#define CSR_ICOUNT_DMODE_OFFSET XLEN-5 +#define CSR_ICOUNT_DMODE_LENGTH 1 +#define CSR_ICOUNT_DMODE (0x1L << CSR_ICOUNT_DMODE_OFFSET) +/* +* When count is decremented to 0, the trigger fires. Instead of +* changing \Fcount from 1 to 0, it is also acceptable for hardware to +* clear \Fm, \Fh, \Fs, and \Fu. This allows \Fcount to be hard-wired +* to 1 if this register just exists for single step. + */ +#define CSR_ICOUNT_COUNT_OFFSET 10 +#define CSR_ICOUNT_COUNT_LENGTH 14 +#define CSR_ICOUNT_COUNT (0x3fffL << CSR_ICOUNT_COUNT_OFFSET) +/* +* When set, every instruction completed or exception taken in M mode decrements \Fcount +* by 1. + */ +#define CSR_ICOUNT_M_OFFSET 9 +#define CSR_ICOUNT_M_LENGTH 1 +#define CSR_ICOUNT_M (0x1L << CSR_ICOUNT_M_OFFSET) +/* +* When set, every instruction completed or exception taken in in H mode decrements \Fcount +* by 1. + */ +#define CSR_ICOUNT_H_OFFSET 8 +#define CSR_ICOUNT_H_LENGTH 1 +#define CSR_ICOUNT_H (0x1L << CSR_ICOUNT_H_OFFSET) +/* +* When set, every instruction completed or exception taken in S mode decrements \Fcount +* by 1. + */ +#define CSR_ICOUNT_S_OFFSET 7 +#define CSR_ICOUNT_S_LENGTH 1 +#define CSR_ICOUNT_S (0x1L << CSR_ICOUNT_S_OFFSET) +/* +* When set, every instruction completed or exception taken in U mode decrements \Fcount +* by 1. + */ +#define CSR_ICOUNT_U_OFFSET 6 +#define CSR_ICOUNT_U_LENGTH 1 +#define CSR_ICOUNT_U (0x1L << CSR_ICOUNT_U_OFFSET) +/* +* Determines what happens when this trigger matches. +* +* 0: Raise a breakpoint exception. (Used when software wants to use the +* trigger module without an external debugger attached.) +* +* 1: Enter Debug Mode. (Only supported when \Fhmode is 1.) +* +* 2: Start tracing. +* +* 3: Stop tracing. +* +* 4: Emit trace data for this match. If it is a data access match, +* emit appropriate Load/Store Address/Data. If it is an instruction +* execution, emit its PC. +* +* Other values are reserved for future use. + */ +#define CSR_ICOUNT_ACTION_OFFSET 0 +#define CSR_ICOUNT_ACTION_LENGTH 6 +#define CSR_ICOUNT_ACTION (0x3fL << CSR_ICOUNT_ACTION_OFFSET) +#define DMI_DMSTATUS 0x11 +/* +* This field is 1 when all currently selected harts have acknowledged the previous \Fresumereq. + */ +#define DMI_DMSTATUS_ALLRESUMEACK_OFFSET 17 +#define DMI_DMSTATUS_ALLRESUMEACK_LENGTH 1 +#define DMI_DMSTATUS_ALLRESUMEACK (0x1 << DMI_DMSTATUS_ALLRESUMEACK_OFFSET) +/* +* This field is 1 when any currently selected hart has acknowledged the previous \Fresumereq. + */ +#define DMI_DMSTATUS_ANYRESUMEACK_OFFSET 16 +#define DMI_DMSTATUS_ANYRESUMEACK_LENGTH 1 +#define DMI_DMSTATUS_ANYRESUMEACK (0x1 << DMI_DMSTATUS_ANYRESUMEACK_OFFSET) +/* +* This field is 1 when all currently selected harts do not exist in this system. + */ +#define DMI_DMSTATUS_ALLNONEXISTENT_OFFSET 15 +#define DMI_DMSTATUS_ALLNONEXISTENT_LENGTH 1 +#define DMI_DMSTATUS_ALLNONEXISTENT (0x1 << DMI_DMSTATUS_ALLNONEXISTENT_OFFSET) +/* +* This field is 1 when any currently selected hart does not exist in this system. + */ +#define DMI_DMSTATUS_ANYNONEXISTENT_OFFSET 14 +#define DMI_DMSTATUS_ANYNONEXISTENT_LENGTH 1 +#define DMI_DMSTATUS_ANYNONEXISTENT (0x1 << DMI_DMSTATUS_ANYNONEXISTENT_OFFSET) +/* +* This field is 1 when all currently selected harts are unavailable. + */ +#define DMI_DMSTATUS_ALLUNAVAIL_OFFSET 13 +#define DMI_DMSTATUS_ALLUNAVAIL_LENGTH 1 +#define DMI_DMSTATUS_ALLUNAVAIL (0x1 << DMI_DMSTATUS_ALLUNAVAIL_OFFSET) +/* +* This field is 1 when any currently selected hart is unavailable. + */ +#define DMI_DMSTATUS_ANYUNAVAIL_OFFSET 12 +#define DMI_DMSTATUS_ANYUNAVAIL_LENGTH 1 +#define DMI_DMSTATUS_ANYUNAVAIL (0x1 << DMI_DMSTATUS_ANYUNAVAIL_OFFSET) +/* +* This field is 1 when all currently selected harts are running. + */ +#define DMI_DMSTATUS_ALLRUNNING_OFFSET 11 +#define DMI_DMSTATUS_ALLRUNNING_LENGTH 1 +#define DMI_DMSTATUS_ALLRUNNING (0x1 << DMI_DMSTATUS_ALLRUNNING_OFFSET) +/* +* This field is 1 when any currently selected hart is running. + */ +#define DMI_DMSTATUS_ANYRUNNING_OFFSET 10 +#define DMI_DMSTATUS_ANYRUNNING_LENGTH 1 +#define DMI_DMSTATUS_ANYRUNNING (0x1 << DMI_DMSTATUS_ANYRUNNING_OFFSET) +/* +* This field is 1 when all currently selected harts are halted. + */ +#define DMI_DMSTATUS_ALLHALTED_OFFSET 9 +#define DMI_DMSTATUS_ALLHALTED_LENGTH 1 +#define DMI_DMSTATUS_ALLHALTED (0x1 << DMI_DMSTATUS_ALLHALTED_OFFSET) +/* +* This field is 1 when any currently selected hart is halted. + */ +#define DMI_DMSTATUS_ANYHALTED_OFFSET 8 +#define DMI_DMSTATUS_ANYHALTED_LENGTH 1 +#define DMI_DMSTATUS_ANYHALTED (0x1 << DMI_DMSTATUS_ANYHALTED_OFFSET) +/* +* 0 when authentication is required before using the DM. 1 when the +* authentication check has passed. On components that don't implement +* authentication, this bit must be preset as 1. + */ +#define DMI_DMSTATUS_AUTHENTICATED_OFFSET 7 +#define DMI_DMSTATUS_AUTHENTICATED_LENGTH 1 +#define DMI_DMSTATUS_AUTHENTICATED (0x1 << DMI_DMSTATUS_AUTHENTICATED_OFFSET) +/* +* 0: The authentication module is ready to process the next +* read/write to \Rauthdata. +* +* 1: The authentication module is busy. Accessing \Rauthdata results +* in unspecified behavior. +* +* \Fauthbusy only becomes set in immediate response to an access to +* \Rauthdata. + */ +#define DMI_DMSTATUS_AUTHBUSY_OFFSET 6 +#define DMI_DMSTATUS_AUTHBUSY_LENGTH 1 +#define DMI_DMSTATUS_AUTHBUSY (0x1 << DMI_DMSTATUS_AUTHBUSY_OFFSET) +#define DMI_DMSTATUS_CFGSTRVALID_OFFSET 4 +#define DMI_DMSTATUS_CFGSTRVALID_LENGTH 1 +#define DMI_DMSTATUS_CFGSTRVALID (0x1 << DMI_DMSTATUS_CFGSTRVALID_OFFSET) +/* +* Reserved for future use. Reads as 0. + */ +#define DMI_DMSTATUS_VERSIONHI_OFFSET 2 +#define DMI_DMSTATUS_VERSIONHI_LENGTH 2 +#define DMI_DMSTATUS_VERSIONHI (0x3 << DMI_DMSTATUS_VERSIONHI_OFFSET) +/* +* 00: There is no Debug Module present. +* +* 01: There is a Debug Module and it conforms to version 0.11 of this +* specification. +* +* 10: There is a Debug Module and it conforms to version 0.13 of this +* specification. +* +* 11: Reserved for future use. + */ +#define DMI_DMSTATUS_VERSIONLO_OFFSET 0 +#define DMI_DMSTATUS_VERSIONLO_LENGTH 2 +#define DMI_DMSTATUS_VERSIONLO (0x3 << DMI_DMSTATUS_VERSIONLO_OFFSET) +#define DMI_DMCONTROL 0x10 +/* +* Halt request signal for all currently selected harts. When set to 1, the +* hart will halt if it is not currently halted. +* Setting both \Fhaltreq and \Fresumereq leads to undefined behavior. +* +* Writes apply to the new value of \Fhartsel and \Fhasel. + */ +#define DMI_DMCONTROL_HALTREQ_OFFSET 31 +#define DMI_DMCONTROL_HALTREQ_LENGTH 1 +#define DMI_DMCONTROL_HALTREQ (0x1 << DMI_DMCONTROL_HALTREQ_OFFSET) +/* +* Resume request signal for all currently selected harts. When set to 1, +* the hart will resume if it is currently halted. +* Setting both \Fhaltreq and \Fresumereq leads to undefined behavior. +* +* Writes apply to the new value of \Fhartsel and \Fhasel. + */ +#define DMI_DMCONTROL_RESUMEREQ_OFFSET 30 +#define DMI_DMCONTROL_RESUMEREQ_LENGTH 1 +#define DMI_DMCONTROL_RESUMEREQ (0x1 << DMI_DMCONTROL_RESUMEREQ_OFFSET) +/* +* This optional bit controls reset to all the currently selected harts. +* To perform a reset the debugger writes 1, and then writes 0 to +* deassert the reset signal. +* +* If this feature is not implemented, the bit always stays 0, so +* after writing 1 the debugger can read the register back to see if +* the feature is supported. +* +* Writes apply to the new value of \Fhartsel and \Fhasel. + */ +#define DMI_DMCONTROL_HARTRESET_OFFSET 29 +#define DMI_DMCONTROL_HARTRESET_LENGTH 1 +#define DMI_DMCONTROL_HARTRESET (0x1 << DMI_DMCONTROL_HARTRESET_OFFSET) +/* +* Selects the definition of currently selected harts. +* +* 0: There is a single currently selected hart, that selected by \Fhartsel. +* +* 1: There may be multiple currently selected harts -- that selected by \Fhartsel, +* plus those selected by the hart array mask register. +* +* An implementation which does not implement the hart array mask register +* should tie this field to 0. A debugger which wishes to use the hart array +* mask register feature should set this bit and read back to see if the functionality +* is supported. + */ +#define DMI_DMCONTROL_HASEL_OFFSET 26 +#define DMI_DMCONTROL_HASEL_LENGTH 1 +#define DMI_DMCONTROL_HASEL (0x1 << DMI_DMCONTROL_HASEL_OFFSET) +/* +* The DM-specific index of the hart to select. This hart is always part of the +* currently selected harts. + */ +#define DMI_DMCONTROL_HARTSEL_OFFSET 16 +#define DMI_DMCONTROL_HARTSEL_LENGTH 10 +#define DMI_DMCONTROL_HARTSEL (0x3ff << DMI_DMCONTROL_HARTSEL_OFFSET) +/* +* This bit controls the reset signal from the DM to the rest of the +* system. To perform a reset the debugger writes 1, and then writes 0 +* to deassert the reset. + */ +#define DMI_DMCONTROL_NDMRESET_OFFSET 1 +#define DMI_DMCONTROL_NDMRESET_LENGTH 1 +#define DMI_DMCONTROL_NDMRESET (0x1 << DMI_DMCONTROL_NDMRESET_OFFSET) +/* +* This bit serves as a reset signal for the Debug Module itself. +* +* 0: The module's state, including authentication mechanism, +* takes its reset values (the \Fdmactive bit is the only bit which can +* be written to something other than its reset value). +* +* 1: The module functions normally. +* +* No other mechanism should exist that may result in resetting the +* Debug Module after power up, including the platform's system reset +* or Debug Transport reset signals. +* +* A debugger should pulse this bit low to ensure that the Debug +* Module is fully reset and ready to use. +* +* Implementations may use this bit to aid debugging, for example by +* preventing the Debug Module from being power gated while debugging +* is active. + */ +#define DMI_DMCONTROL_DMACTIVE_OFFSET 0 +#define DMI_DMCONTROL_DMACTIVE_LENGTH 1 +#define DMI_DMCONTROL_DMACTIVE (0x1 << DMI_DMCONTROL_DMACTIVE_OFFSET) +#define DMI_HARTINFO 0x12 +/* +* Number of {\tt dscratch} registers available for the debugger +* to use during program buffer execution, starting from \Rdscratchzero. +* The debugger can make no assumptions about the contents of these +* registers between commands. + */ +#define DMI_HARTINFO_NSCRATCH_OFFSET 20 +#define DMI_HARTINFO_NSCRATCH_LENGTH 4 +#define DMI_HARTINFO_NSCRATCH (0xf << DMI_HARTINFO_NSCRATCH_OFFSET) +/* +* 0: The {\tt data} registers are shadowed in the hart by CSR +* registers. Each CSR register is XLEN bits in size, and corresponds +* to a single argument, per Table~\ref{tab:datareg}. +* +* 1: The {\tt data} registers are shadowed in the hart's memory map. +* Each register takes up 4 bytes in the memory map. + */ +#define DMI_HARTINFO_DATAACCESS_OFFSET 16 +#define DMI_HARTINFO_DATAACCESS_LENGTH 1 +#define DMI_HARTINFO_DATAACCESS (0x1 << DMI_HARTINFO_DATAACCESS_OFFSET) +/* +* If \Fdataaccess is 0: Number of CSR registers dedicated to +* shadowing the {\tt data} registers. +* +* If \Fdataaccess is 1: Number of 32-bit words in the memory map +* dedicated to shadowing the {\tt data} registers. + */ +#define DMI_HARTINFO_DATASIZE_OFFSET 12 +#define DMI_HARTINFO_DATASIZE_LENGTH 4 +#define DMI_HARTINFO_DATASIZE (0xf << DMI_HARTINFO_DATASIZE_OFFSET) +/* +* If \Fdataaccess is 0: The number of the first CSR dedicated to +* shadowing the {\tt data} registers. +* +* If \Fdataaccess is 1: Signed address of RAM where the {\tt data} +* registers are shadowed. + */ +#define DMI_HARTINFO_DATAADDR_OFFSET 0 +#define DMI_HARTINFO_DATAADDR_LENGTH 12 +#define DMI_HARTINFO_DATAADDR (0xfff << DMI_HARTINFO_DATAADDR_OFFSET) +#define DMI_HALTSUM 0x13 +#define DMI_HALTSUM_HALT1023_992_OFFSET 31 +#define DMI_HALTSUM_HALT1023_992_LENGTH 1 +#define DMI_HALTSUM_HALT1023_992 (0x1 << DMI_HALTSUM_HALT1023_992_OFFSET) +#define DMI_HALTSUM_HALT991_960_OFFSET 30 +#define DMI_HALTSUM_HALT991_960_LENGTH 1 +#define DMI_HALTSUM_HALT991_960 (0x1 << DMI_HALTSUM_HALT991_960_OFFSET) +#define DMI_HALTSUM_HALT959_928_OFFSET 29 +#define DMI_HALTSUM_HALT959_928_LENGTH 1 +#define DMI_HALTSUM_HALT959_928 (0x1 << DMI_HALTSUM_HALT959_928_OFFSET) +#define DMI_HALTSUM_HALT927_896_OFFSET 28 +#define DMI_HALTSUM_HALT927_896_LENGTH 1 +#define DMI_HALTSUM_HALT927_896 (0x1 << DMI_HALTSUM_HALT927_896_OFFSET) +#define DMI_HALTSUM_HALT895_864_OFFSET 27 +#define DMI_HALTSUM_HALT895_864_LENGTH 1 +#define DMI_HALTSUM_HALT895_864 (0x1 << DMI_HALTSUM_HALT895_864_OFFSET) +#define DMI_HALTSUM_HALT863_832_OFFSET 26 +#define DMI_HALTSUM_HALT863_832_LENGTH 1 +#define DMI_HALTSUM_HALT863_832 (0x1 << DMI_HALTSUM_HALT863_832_OFFSET) +#define DMI_HALTSUM_HALT831_800_OFFSET 25 +#define DMI_HALTSUM_HALT831_800_LENGTH 1 +#define DMI_HALTSUM_HALT831_800 (0x1 << DMI_HALTSUM_HALT831_800_OFFSET) +#define DMI_HALTSUM_HALT799_768_OFFSET 24 +#define DMI_HALTSUM_HALT799_768_LENGTH 1 +#define DMI_HALTSUM_HALT799_768 (0x1 << DMI_HALTSUM_HALT799_768_OFFSET) +#define DMI_HALTSUM_HALT767_736_OFFSET 23 +#define DMI_HALTSUM_HALT767_736_LENGTH 1 +#define DMI_HALTSUM_HALT767_736 (0x1 << DMI_HALTSUM_HALT767_736_OFFSET) +#define DMI_HALTSUM_HALT735_704_OFFSET 22 +#define DMI_HALTSUM_HALT735_704_LENGTH 1 +#define DMI_HALTSUM_HALT735_704 (0x1 << DMI_HALTSUM_HALT735_704_OFFSET) +#define DMI_HALTSUM_HALT703_672_OFFSET 21 +#define DMI_HALTSUM_HALT703_672_LENGTH 1 +#define DMI_HALTSUM_HALT703_672 (0x1 << DMI_HALTSUM_HALT703_672_OFFSET) +#define DMI_HALTSUM_HALT671_640_OFFSET 20 +#define DMI_HALTSUM_HALT671_640_LENGTH 1 +#define DMI_HALTSUM_HALT671_640 (0x1 << DMI_HALTSUM_HALT671_640_OFFSET) +#define DMI_HALTSUM_HALT639_608_OFFSET 19 +#define DMI_HALTSUM_HALT639_608_LENGTH 1 +#define DMI_HALTSUM_HALT639_608 (0x1 << DMI_HALTSUM_HALT639_608_OFFSET) +#define DMI_HALTSUM_HALT607_576_OFFSET 18 +#define DMI_HALTSUM_HALT607_576_LENGTH 1 +#define DMI_HALTSUM_HALT607_576 (0x1 << DMI_HALTSUM_HALT607_576_OFFSET) +#define DMI_HALTSUM_HALT575_544_OFFSET 17 +#define DMI_HALTSUM_HALT575_544_LENGTH 1 +#define DMI_HALTSUM_HALT575_544 (0x1 << DMI_HALTSUM_HALT575_544_OFFSET) +#define DMI_HALTSUM_HALT543_512_OFFSET 16 +#define DMI_HALTSUM_HALT543_512_LENGTH 1 +#define DMI_HALTSUM_HALT543_512 (0x1 << DMI_HALTSUM_HALT543_512_OFFSET) +#define DMI_HALTSUM_HALT511_480_OFFSET 15 +#define DMI_HALTSUM_HALT511_480_LENGTH 1 +#define DMI_HALTSUM_HALT511_480 (0x1 << DMI_HALTSUM_HALT511_480_OFFSET) +#define DMI_HALTSUM_HALT479_448_OFFSET 14 +#define DMI_HALTSUM_HALT479_448_LENGTH 1 +#define DMI_HALTSUM_HALT479_448 (0x1 << DMI_HALTSUM_HALT479_448_OFFSET) +#define DMI_HALTSUM_HALT447_416_OFFSET 13 +#define DMI_HALTSUM_HALT447_416_LENGTH 1 +#define DMI_HALTSUM_HALT447_416 (0x1 << DMI_HALTSUM_HALT447_416_OFFSET) +#define DMI_HALTSUM_HALT415_384_OFFSET 12 +#define DMI_HALTSUM_HALT415_384_LENGTH 1 +#define DMI_HALTSUM_HALT415_384 (0x1 << DMI_HALTSUM_HALT415_384_OFFSET) +#define DMI_HALTSUM_HALT383_352_OFFSET 11 +#define DMI_HALTSUM_HALT383_352_LENGTH 1 +#define DMI_HALTSUM_HALT383_352 (0x1 << DMI_HALTSUM_HALT383_352_OFFSET) +#define DMI_HALTSUM_HALT351_320_OFFSET 10 +#define DMI_HALTSUM_HALT351_320_LENGTH 1 +#define DMI_HALTSUM_HALT351_320 (0x1 << DMI_HALTSUM_HALT351_320_OFFSET) +#define DMI_HALTSUM_HALT319_288_OFFSET 9 +#define DMI_HALTSUM_HALT319_288_LENGTH 1 +#define DMI_HALTSUM_HALT319_288 (0x1 << DMI_HALTSUM_HALT319_288_OFFSET) +#define DMI_HALTSUM_HALT287_256_OFFSET 8 +#define DMI_HALTSUM_HALT287_256_LENGTH 1 +#define DMI_HALTSUM_HALT287_256 (0x1 << DMI_HALTSUM_HALT287_256_OFFSET) +#define DMI_HALTSUM_HALT255_224_OFFSET 7 +#define DMI_HALTSUM_HALT255_224_LENGTH 1 +#define DMI_HALTSUM_HALT255_224 (0x1 << DMI_HALTSUM_HALT255_224_OFFSET) +#define DMI_HALTSUM_HALT223_192_OFFSET 6 +#define DMI_HALTSUM_HALT223_192_LENGTH 1 +#define DMI_HALTSUM_HALT223_192 (0x1 << DMI_HALTSUM_HALT223_192_OFFSET) +#define DMI_HALTSUM_HALT191_160_OFFSET 5 +#define DMI_HALTSUM_HALT191_160_LENGTH 1 +#define DMI_HALTSUM_HALT191_160 (0x1 << DMI_HALTSUM_HALT191_160_OFFSET) +#define DMI_HALTSUM_HALT159_128_OFFSET 4 +#define DMI_HALTSUM_HALT159_128_LENGTH 1 +#define DMI_HALTSUM_HALT159_128 (0x1 << DMI_HALTSUM_HALT159_128_OFFSET) +#define DMI_HALTSUM_HALT127_96_OFFSET 3 +#define DMI_HALTSUM_HALT127_96_LENGTH 1 +#define DMI_HALTSUM_HALT127_96 (0x1 << DMI_HALTSUM_HALT127_96_OFFSET) +#define DMI_HALTSUM_HALT95_64_OFFSET 2 +#define DMI_HALTSUM_HALT95_64_LENGTH 1 +#define DMI_HALTSUM_HALT95_64 (0x1 << DMI_HALTSUM_HALT95_64_OFFSET) +#define DMI_HALTSUM_HALT63_32_OFFSET 1 +#define DMI_HALTSUM_HALT63_32_LENGTH 1 +#define DMI_HALTSUM_HALT63_32 (0x1 << DMI_HALTSUM_HALT63_32_OFFSET) +#define DMI_HALTSUM_HALT31_0_OFFSET 0 +#define DMI_HALTSUM_HALT31_0_LENGTH 1 +#define DMI_HALTSUM_HALT31_0 (0x1 << DMI_HALTSUM_HALT31_0_OFFSET) +#define DMI_HAWINDOWSEL 0x14 +#define DMI_HAWINDOWSEL_HAWINDOWSEL_OFFSET 0 +#define DMI_HAWINDOWSEL_HAWINDOWSEL_LENGTH 5 +#define DMI_HAWINDOWSEL_HAWINDOWSEL (0x1f << DMI_HAWINDOWSEL_HAWINDOWSEL_OFFSET) +#define DMI_HAWINDOW 0x15 +#define DMI_HAWINDOW_MASKDATA_OFFSET 0 +#define DMI_HAWINDOW_MASKDATA_LENGTH 32 +#define DMI_HAWINDOW_MASKDATA (0xffffffff << DMI_HAWINDOW_MASKDATA_OFFSET) +#define DMI_ABSTRACTCS 0x16 +/* +* Size of the Program Buffer, in 32-bit words. Valid sizes are 0 - 16. +* +* TODO: Explain what can be done with each size of the buffer, to suggest +* why you would want more or less words. + */ +#define DMI_ABSTRACTCS_PROGSIZE_OFFSET 24 +#define DMI_ABSTRACTCS_PROGSIZE_LENGTH 5 +#define DMI_ABSTRACTCS_PROGSIZE (0x1f << DMI_ABSTRACTCS_PROGSIZE_OFFSET) +/* +* 1: An abstract command is currently being executed. +* +* This bit is set as soon as \Rcommand is written, and is +* not cleared until that command has completed. + */ +#define DMI_ABSTRACTCS_BUSY_OFFSET 12 +#define DMI_ABSTRACTCS_BUSY_LENGTH 1 +#define DMI_ABSTRACTCS_BUSY (0x1 << DMI_ABSTRACTCS_BUSY_OFFSET) +/* +* Gets set if an abstract command fails. The bits in this field remain set until +* they are cleared by writing 1 to them. No abstract command is +* started until the value is reset to 0. +* +* 0 (none): No error. +* +* 1 (busy): An abstract command was executing while \Rcommand or one +* of the {\tt data} registers was accessed. +* +* 2 (not supported): The requested command is not supported. A +* command that is not supported while the hart is running may be +* supported when it is halted. +* +* 3 (exception): An exception occurred while executing the command +* (eg. while executing the Program Buffer). +* +* 4 (halt/resume): An abstract command couldn't execute because the +* hart wasn't in the expected state (running/halted). +* +* 7 (other): The command failed for another reason. + */ +#define DMI_ABSTRACTCS_CMDERR_OFFSET 8 +#define DMI_ABSTRACTCS_CMDERR_LENGTH 3 +#define DMI_ABSTRACTCS_CMDERR (0x7 << DMI_ABSTRACTCS_CMDERR_OFFSET) +/* +* Number of {\tt data} registers that are implemented as part of the +* abstract command interface. Valid sizes are 0 - 8. + */ +#define DMI_ABSTRACTCS_DATACOUNT_OFFSET 0 +#define DMI_ABSTRACTCS_DATACOUNT_LENGTH 5 +#define DMI_ABSTRACTCS_DATACOUNT (0x1f << DMI_ABSTRACTCS_DATACOUNT_OFFSET) +#define DMI_COMMAND 0x17 +/* +* The type determines the overall functionality of this +* abstract command. + */ +#define DMI_COMMAND_CMDTYPE_OFFSET 24 +#define DMI_COMMAND_CMDTYPE_LENGTH 8 +#define DMI_COMMAND_CMDTYPE (0xff << DMI_COMMAND_CMDTYPE_OFFSET) +/* +* This field is interpreted in a command-specific manner, +* described for each abstract command. + */ +#define DMI_COMMAND_CONTROL_OFFSET 0 +#define DMI_COMMAND_CONTROL_LENGTH 24 +#define DMI_COMMAND_CONTROL (0xffffff << DMI_COMMAND_CONTROL_OFFSET) +#define DMI_ABSTRACTAUTO 0x18 +/* +* When a bit in this field is 1, read or write accesses the corresponding {\tt progbuf} word +* cause the command in \Rcommand to be executed again. + */ +#define DMI_ABSTRACTAUTO_AUTOEXECPROGBUF_OFFSET 16 +#define DMI_ABSTRACTAUTO_AUTOEXECPROGBUF_LENGTH 16 +#define DMI_ABSTRACTAUTO_AUTOEXECPROGBUF (0xffff << DMI_ABSTRACTAUTO_AUTOEXECPROGBUF_OFFSET) +/* +* When a bit in this field is 1, read or write accesses the corresponding {\tt data} word +* cause the command in \Rcommand to be executed again. + */ +#define DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET 0 +#define DMI_ABSTRACTAUTO_AUTOEXECDATA_LENGTH 12 +#define DMI_ABSTRACTAUTO_AUTOEXECDATA (0xfff << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET) +#define DMI_CFGSTRADDR0 0x19 +#define DMI_CFGSTRADDR0_ADDR_OFFSET 0 +#define DMI_CFGSTRADDR0_ADDR_LENGTH 32 +#define DMI_CFGSTRADDR0_ADDR (0xffffffff << DMI_CFGSTRADDR0_ADDR_OFFSET) +#define DMI_CFGSTRADDR1 0x1a +#define DMI_CFGSTRADDR2 0x1b +#define DMI_CFGSTRADDR3 0x1c +#define DMI_DATA0 0x04 +#define DMI_DATA0_DATA_OFFSET 0 +#define DMI_DATA0_DATA_LENGTH 32 +#define DMI_DATA0_DATA (0xffffffff << DMI_DATA0_DATA_OFFSET) +#define DMI_DATA11 0x0f +#define DMI_PROGBUF0 0x20 +#define DMI_PROGBUF0_DATA_OFFSET 0 +#define DMI_PROGBUF0_DATA_LENGTH 32 +#define DMI_PROGBUF0_DATA (0xffffffff << DMI_PROGBUF0_DATA_OFFSET) +#define DMI_PROGBUF15 0x2f +#define DMI_AUTHDATA 0x30 +#define DMI_AUTHDATA_DATA_OFFSET 0 +#define DMI_AUTHDATA_DATA_LENGTH 32 +#define DMI_AUTHDATA_DATA (0xffffffff << DMI_AUTHDATA_DATA_OFFSET) +#define DMI_SERCS 0x34 +/* +* Number of supported serial ports. + */ +#define DMI_SERCS_SERIALCOUNT_OFFSET 28 +#define DMI_SERCS_SERIALCOUNT_LENGTH 4 +#define DMI_SERCS_SERIALCOUNT (0xf << DMI_SERCS_SERIALCOUNT_OFFSET) +/* +* Select which serial port is accessed by \Rserrx and \Rsertx. + */ +#define DMI_SERCS_SERIAL_OFFSET 24 +#define DMI_SERCS_SERIAL_LENGTH 3 +#define DMI_SERCS_SERIAL (0x7 << DMI_SERCS_SERIAL_OFFSET) +#define DMI_SERCS_ERROR7_OFFSET 23 +#define DMI_SERCS_ERROR7_LENGTH 1 +#define DMI_SERCS_ERROR7 (0x1 << DMI_SERCS_ERROR7_OFFSET) +#define DMI_SERCS_VALID7_OFFSET 22 +#define DMI_SERCS_VALID7_LENGTH 1 +#define DMI_SERCS_VALID7 (0x1 << DMI_SERCS_VALID7_OFFSET) +#define DMI_SERCS_FULL7_OFFSET 21 +#define DMI_SERCS_FULL7_LENGTH 1 +#define DMI_SERCS_FULL7 (0x1 << DMI_SERCS_FULL7_OFFSET) +#define DMI_SERCS_ERROR6_OFFSET 20 +#define DMI_SERCS_ERROR6_LENGTH 1 +#define DMI_SERCS_ERROR6 (0x1 << DMI_SERCS_ERROR6_OFFSET) +#define DMI_SERCS_VALID6_OFFSET 19 +#define DMI_SERCS_VALID6_LENGTH 1 +#define DMI_SERCS_VALID6 (0x1 << DMI_SERCS_VALID6_OFFSET) +#define DMI_SERCS_FULL6_OFFSET 18 +#define DMI_SERCS_FULL6_LENGTH 1 +#define DMI_SERCS_FULL6 (0x1 << DMI_SERCS_FULL6_OFFSET) +#define DMI_SERCS_ERROR5_OFFSET 17 +#define DMI_SERCS_ERROR5_LENGTH 1 +#define DMI_SERCS_ERROR5 (0x1 << DMI_SERCS_ERROR5_OFFSET) +#define DMI_SERCS_VALID5_OFFSET 16 +#define DMI_SERCS_VALID5_LENGTH 1 +#define DMI_SERCS_VALID5 (0x1 << DMI_SERCS_VALID5_OFFSET) +#define DMI_SERCS_FULL5_OFFSET 15 +#define DMI_SERCS_FULL5_LENGTH 1 +#define DMI_SERCS_FULL5 (0x1 << DMI_SERCS_FULL5_OFFSET) +#define DMI_SERCS_ERROR4_OFFSET 14 +#define DMI_SERCS_ERROR4_LENGTH 1 +#define DMI_SERCS_ERROR4 (0x1 << DMI_SERCS_ERROR4_OFFSET) +#define DMI_SERCS_VALID4_OFFSET 13 +#define DMI_SERCS_VALID4_LENGTH 1 +#define DMI_SERCS_VALID4 (0x1 << DMI_SERCS_VALID4_OFFSET) +#define DMI_SERCS_FULL4_OFFSET 12 +#define DMI_SERCS_FULL4_LENGTH 1 +#define DMI_SERCS_FULL4 (0x1 << DMI_SERCS_FULL4_OFFSET) +#define DMI_SERCS_ERROR3_OFFSET 11 +#define DMI_SERCS_ERROR3_LENGTH 1 +#define DMI_SERCS_ERROR3 (0x1 << DMI_SERCS_ERROR3_OFFSET) +#define DMI_SERCS_VALID3_OFFSET 10 +#define DMI_SERCS_VALID3_LENGTH 1 +#define DMI_SERCS_VALID3 (0x1 << DMI_SERCS_VALID3_OFFSET) +#define DMI_SERCS_FULL3_OFFSET 9 +#define DMI_SERCS_FULL3_LENGTH 1 +#define DMI_SERCS_FULL3 (0x1 << DMI_SERCS_FULL3_OFFSET) +#define DMI_SERCS_ERROR2_OFFSET 8 +#define DMI_SERCS_ERROR2_LENGTH 1 +#define DMI_SERCS_ERROR2 (0x1 << DMI_SERCS_ERROR2_OFFSET) +#define DMI_SERCS_VALID2_OFFSET 7 +#define DMI_SERCS_VALID2_LENGTH 1 +#define DMI_SERCS_VALID2 (0x1 << DMI_SERCS_VALID2_OFFSET) +#define DMI_SERCS_FULL2_OFFSET 6 +#define DMI_SERCS_FULL2_LENGTH 1 +#define DMI_SERCS_FULL2 (0x1 << DMI_SERCS_FULL2_OFFSET) +#define DMI_SERCS_ERROR1_OFFSET 5 +#define DMI_SERCS_ERROR1_LENGTH 1 +#define DMI_SERCS_ERROR1 (0x1 << DMI_SERCS_ERROR1_OFFSET) +#define DMI_SERCS_VALID1_OFFSET 4 +#define DMI_SERCS_VALID1_LENGTH 1 +#define DMI_SERCS_VALID1 (0x1 << DMI_SERCS_VALID1_OFFSET) +#define DMI_SERCS_FULL1_OFFSET 3 +#define DMI_SERCS_FULL1_LENGTH 1 +#define DMI_SERCS_FULL1 (0x1 << DMI_SERCS_FULL1_OFFSET) +/* +* 1 when the debugger-to-core queue for serial port 0 has +* over or underflowed. This bit will remain set until it is reset by +* writing 1 to this bit. + */ +#define DMI_SERCS_ERROR0_OFFSET 2 +#define DMI_SERCS_ERROR0_LENGTH 1 +#define DMI_SERCS_ERROR0 (0x1 << DMI_SERCS_ERROR0_OFFSET) +/* +* 1 when the core-to-debugger queue for serial port 0 is not empty. + */ +#define DMI_SERCS_VALID0_OFFSET 1 +#define DMI_SERCS_VALID0_LENGTH 1 +#define DMI_SERCS_VALID0 (0x1 << DMI_SERCS_VALID0_OFFSET) +/* +* 1 when the debugger-to-core queue for serial port 0 is full. + */ +#define DMI_SERCS_FULL0_OFFSET 0 +#define DMI_SERCS_FULL0_LENGTH 1 +#define DMI_SERCS_FULL0 (0x1 << DMI_SERCS_FULL0_OFFSET) +#define DMI_SERTX 0x35 +#define DMI_SERTX_DATA_OFFSET 0 +#define DMI_SERTX_DATA_LENGTH 32 +#define DMI_SERTX_DATA (0xffffffff << DMI_SERTX_DATA_OFFSET) +#define DMI_SERRX 0x36 +#define DMI_SERRX_DATA_OFFSET 0 +#define DMI_SERRX_DATA_LENGTH 32 +#define DMI_SERRX_DATA (0xffffffff << DMI_SERRX_DATA_OFFSET) +#define DMI_SBCS 0x38 +/* +* When a 1 is written here, triggers a read at the address in {\tt +* sbaddress} using the access size set by \Fsbaccess. + */ +#define DMI_SBCS_SBSINGLEREAD_OFFSET 20 +#define DMI_SBCS_SBSINGLEREAD_LENGTH 1 +#define DMI_SBCS_SBSINGLEREAD (0x1 << DMI_SBCS_SBSINGLEREAD_OFFSET) +/* +* Select the access size to use for system bus accesses triggered by +* writes to the {\tt sbaddress} registers or \Rsbdatazero. +* +* 0: 8-bit +* +* 1: 16-bit +* +* 2: 32-bit +* +* 3: 64-bit +* +* 4: 128-bit +* +* If an unsupported system bus access size is written here, +* the DM may not perform the access, or may perform the access +* with any access size. + */ +#define DMI_SBCS_SBACCESS_OFFSET 17 +#define DMI_SBCS_SBACCESS_LENGTH 3 +#define DMI_SBCS_SBACCESS (0x7 << DMI_SBCS_SBACCESS_OFFSET) +/* +* When 1, the internal address value (used by the system bus master) +* is incremented by the access size (in bytes) selected in \Fsbaccess +* after every system bus access. + */ +#define DMI_SBCS_SBAUTOINCREMENT_OFFSET 16 +#define DMI_SBCS_SBAUTOINCREMENT_LENGTH 1 +#define DMI_SBCS_SBAUTOINCREMENT (0x1 << DMI_SBCS_SBAUTOINCREMENT_OFFSET) +/* +* When 1, every read from \Rsbdatazero automatically triggers a system +* bus read at the new address. + */ +#define DMI_SBCS_SBAUTOREAD_OFFSET 15 +#define DMI_SBCS_SBAUTOREAD_LENGTH 1 +#define DMI_SBCS_SBAUTOREAD (0x1 << DMI_SBCS_SBAUTOREAD_OFFSET) +/* +* When the debug module's system bus +* master causes a bus error, this field gets set. The bits in this +* field remain set until they are cleared by writing 1 to them. +* While this field is non-zero, no more system bus accesses can be +* initiated by the debug module. +* +* 0: There was no bus error. +* +* 1: There was a timeout. +* +* 2: A bad address was accessed. +* +* 3: There was some other error (eg. alignment). +* +* 4: The system bus master was busy when a one of the +* {\tt sbaddress} or {\tt sbdata} registers was written, +* or the {\tt sbdata} register was read when it had +* stale data. + */ +#define DMI_SBCS_SBERROR_OFFSET 12 +#define DMI_SBCS_SBERROR_LENGTH 3 +#define DMI_SBCS_SBERROR (0x7 << DMI_SBCS_SBERROR_OFFSET) +/* +* Width of system bus addresses in bits. (0 indicates there is no bus +* access support.) + */ +#define DMI_SBCS_SBASIZE_OFFSET 5 +#define DMI_SBCS_SBASIZE_LENGTH 7 +#define DMI_SBCS_SBASIZE (0x7f << DMI_SBCS_SBASIZE_OFFSET) +/* +* 1 when 128-bit system bus accesses are supported. + */ +#define DMI_SBCS_SBACCESS128_OFFSET 4 +#define DMI_SBCS_SBACCESS128_LENGTH 1 +#define DMI_SBCS_SBACCESS128 (0x1 << DMI_SBCS_SBACCESS128_OFFSET) +/* +* 1 when 64-bit system bus accesses are supported. + */ +#define DMI_SBCS_SBACCESS64_OFFSET 3 +#define DMI_SBCS_SBACCESS64_LENGTH 1 +#define DMI_SBCS_SBACCESS64 (0x1 << DMI_SBCS_SBACCESS64_OFFSET) +/* +* 1 when 32-bit system bus accesses are supported. + */ +#define DMI_SBCS_SBACCESS32_OFFSET 2 +#define DMI_SBCS_SBACCESS32_LENGTH 1 +#define DMI_SBCS_SBACCESS32 (0x1 << DMI_SBCS_SBACCESS32_OFFSET) +/* +* 1 when 16-bit system bus accesses are supported. + */ +#define DMI_SBCS_SBACCESS16_OFFSET 1 +#define DMI_SBCS_SBACCESS16_LENGTH 1 +#define DMI_SBCS_SBACCESS16 (0x1 << DMI_SBCS_SBACCESS16_OFFSET) +/* +* 1 when 8-bit system bus accesses are supported. + */ +#define DMI_SBCS_SBACCESS8_OFFSET 0 +#define DMI_SBCS_SBACCESS8_LENGTH 1 +#define DMI_SBCS_SBACCESS8 (0x1 << DMI_SBCS_SBACCESS8_OFFSET) +#define DMI_SBADDRESS0 0x39 +/* +* Accesses bits 31:0 of the internal address. + */ +#define DMI_SBADDRESS0_ADDRESS_OFFSET 0 +#define DMI_SBADDRESS0_ADDRESS_LENGTH 32 +#define DMI_SBADDRESS0_ADDRESS (0xffffffff << DMI_SBADDRESS0_ADDRESS_OFFSET) +#define DMI_SBADDRESS1 0x3a +/* +* Accesses bits 63:32 of the internal address (if the system address +* bus is that wide). + */ +#define DMI_SBADDRESS1_ADDRESS_OFFSET 0 +#define DMI_SBADDRESS1_ADDRESS_LENGTH 32 +#define DMI_SBADDRESS1_ADDRESS (0xffffffff << DMI_SBADDRESS1_ADDRESS_OFFSET) +#define DMI_SBADDRESS2 0x3b +/* +* Accesses bits 95:64 of the internal address (if the system address +* bus is that wide). + */ +#define DMI_SBADDRESS2_ADDRESS_OFFSET 0 +#define DMI_SBADDRESS2_ADDRESS_LENGTH 32 +#define DMI_SBADDRESS2_ADDRESS (0xffffffff << DMI_SBADDRESS2_ADDRESS_OFFSET) +#define DMI_SBDATA0 0x3c +/* +* Accesses bits 31:0 of the internal data. + */ +#define DMI_SBDATA0_DATA_OFFSET 0 +#define DMI_SBDATA0_DATA_LENGTH 32 +#define DMI_SBDATA0_DATA (0xffffffff << DMI_SBDATA0_DATA_OFFSET) +#define DMI_SBDATA1 0x3d +/* +* Accesses bits 63:32 of the internal data (if the system bus is +* that wide). + */ +#define DMI_SBDATA1_DATA_OFFSET 0 +#define DMI_SBDATA1_DATA_LENGTH 32 +#define DMI_SBDATA1_DATA (0xffffffff << DMI_SBDATA1_DATA_OFFSET) +#define DMI_SBDATA2 0x3e +/* +* Accesses bits 95:64 of the internal data (if the system bus is +* that wide). + */ +#define DMI_SBDATA2_DATA_OFFSET 0 +#define DMI_SBDATA2_DATA_LENGTH 32 +#define DMI_SBDATA2_DATA (0xffffffff << DMI_SBDATA2_DATA_OFFSET) +#define DMI_SBDATA3 0x3f +/* +* Accesses bits 127:96 of the internal data (if the system bus is +* that wide). + */ +#define DMI_SBDATA3_DATA_OFFSET 0 +#define DMI_SBDATA3_DATA_LENGTH 32 +#define DMI_SBDATA3_DATA (0xffffffff << DMI_SBDATA3_DATA_OFFSET) +#define TRACE 0x728 +/* +* 1 if the trace buffer has wrapped since the last time \Fdiscard was +* written. 0 otherwise. + */ +#define TRACE_WRAPPED_OFFSET 24 +#define TRACE_WRAPPED_LENGTH 1 +#define TRACE_WRAPPED (0x1 << TRACE_WRAPPED_OFFSET) +/* +* Emit Timestamp trace sequences. + */ +#define TRACE_EMITTIMESTAMP_OFFSET 23 +#define TRACE_EMITTIMESTAMP_LENGTH 1 +#define TRACE_EMITTIMESTAMP (0x1 << TRACE_EMITTIMESTAMP_OFFSET) +/* +* Emit Store Data trace sequences. + */ +#define TRACE_EMITSTOREDATA_OFFSET 22 +#define TRACE_EMITSTOREDATA_LENGTH 1 +#define TRACE_EMITSTOREDATA (0x1 << TRACE_EMITSTOREDATA_OFFSET) +/* +* Emit Load Data trace sequences. + */ +#define TRACE_EMITLOADDATA_OFFSET 21 +#define TRACE_EMITLOADDATA_LENGTH 1 +#define TRACE_EMITLOADDATA (0x1 << TRACE_EMITLOADDATA_OFFSET) +/* +* Emit Store Address trace sequences. + */ +#define TRACE_EMITSTOREADDR_OFFSET 20 +#define TRACE_EMITSTOREADDR_LENGTH 1 +#define TRACE_EMITSTOREADDR (0x1 << TRACE_EMITSTOREADDR_OFFSET) +/* +* Emit Load Address trace sequences. + */ +#define TRACE_EMITLOADADDR_OFFSET 19 +#define TRACE_EMITLOADADDR_LENGTH 1 +#define TRACE_EMITLOADADDR (0x1 << TRACE_EMITLOADADDR_OFFSET) +/* +* Emit Privilege Level trace sequences. + */ +#define TRACE_EMITPRIV_OFFSET 18 +#define TRACE_EMITPRIV_LENGTH 1 +#define TRACE_EMITPRIV (0x1 << TRACE_EMITPRIV_OFFSET) +/* +* Emit Branch Taken and Branch Not Taken trace sequences. + */ +#define TRACE_EMITBRANCH_OFFSET 17 +#define TRACE_EMITBRANCH_LENGTH 1 +#define TRACE_EMITBRANCH (0x1 << TRACE_EMITBRANCH_OFFSET) +/* +* Emit PC trace sequences. + */ +#define TRACE_EMITPC_OFFSET 16 +#define TRACE_EMITPC_LENGTH 1 +#define TRACE_EMITPC (0x1 << TRACE_EMITPC_OFFSET) +/* +* Determine what happens when the trace buffer is full. 0 means wrap +* and overwrite. 1 means turn off trace until \Fdiscard is written as 1. +* 2 means cause a trace full exception. 3 is reserved for future use. + */ +#define TRACE_FULLACTION_OFFSET 8 +#define TRACE_FULLACTION_LENGTH 2 +#define TRACE_FULLACTION (0x3 << TRACE_FULLACTION_OFFSET) +/* +* 0: Trace to a dedicated on-core RAM (which is not further defined in +* this spec). +* +* 1: Trace to RAM on the system bus. +* +* 2: Send trace data to a dedicated off-chip interface (which is not +* defined in this spec). This does not affect execution speed. +* +* 3: Reserved for future use. +* +* Options 0 and 1 slow down execution (eg. because of system bus +* contention). + */ +#define TRACE_DESTINATION_OFFSET 4 +#define TRACE_DESTINATION_LENGTH 2 +#define TRACE_DESTINATION (0x3 << TRACE_DESTINATION_OFFSET) +/* +* When 1, the trace logic may stall processor execution to ensure it +* can emit all the trace sequences required. When 0 individual trace +* sequences may be dropped. + */ +#define TRACE_STALL_OFFSET 2 +#define TRACE_STALL_LENGTH 1 +#define TRACE_STALL (0x1 << TRACE_STALL_OFFSET) +/* +* Writing 1 to this bit tells the trace logic that any trace +* collected is no longer required. When tracing to RAM, it resets the +* trace write pointer to the start of the memory, as well as +* \Fwrapped. + */ +#define TRACE_DISCARD_OFFSET 1 +#define TRACE_DISCARD_LENGTH 1 +#define TRACE_DISCARD (0x1 << TRACE_DISCARD_OFFSET) +#define TRACE_SUPPORTED_OFFSET 0 +#define TRACE_SUPPORTED_LENGTH 1 +#define TRACE_SUPPORTED (0x1 << TRACE_SUPPORTED_OFFSET) +#define TBUFSTART 0x729 +#define TBUFEND 0x72a +#define TBUFWRITE 0x72b +#define SHORTNAME 0x123 +/* +* Description of what this field is used for. + */ +#define SHORTNAME_FIELD_OFFSET 0 +#define SHORTNAME_FIELD_LENGTH 8 +#define SHORTNAME_FIELD (0xff << SHORTNAME_FIELD_OFFSET) +#define AC_ACCESS_REGISTER None +/* +* This is 0 to indicate Access Register Command. + */ +#define AC_ACCESS_REGISTER_CMDTYPE_OFFSET 24 +#define AC_ACCESS_REGISTER_CMDTYPE_LENGTH 8 +#define AC_ACCESS_REGISTER_CMDTYPE (0xff << AC_ACCESS_REGISTER_CMDTYPE_OFFSET) +/* +* 2: Access the lowest 32 bits of the register. +* +* 3: Access the lowest 64 bits of the register. +* +* 4: Access the lowest 128 bits of the register. +* +* If \Fsize specifies a size larger than the register's actual size, +* then the access must fail. If a register is accessible, then reads of \Fsize +* less than or equal to the register's actual size must be supported. + */ +#define AC_ACCESS_REGISTER_SIZE_OFFSET 20 +#define AC_ACCESS_REGISTER_SIZE_LENGTH 3 +#define AC_ACCESS_REGISTER_SIZE (0x7 << AC_ACCESS_REGISTER_SIZE_OFFSET) +/* +* When 1, execute the program in the Program Buffer exactly once +* after performing the transfer, if any. + */ +#define AC_ACCESS_REGISTER_POSTEXEC_OFFSET 18 +#define AC_ACCESS_REGISTER_POSTEXEC_LENGTH 1 +#define AC_ACCESS_REGISTER_POSTEXEC (0x1 << AC_ACCESS_REGISTER_POSTEXEC_OFFSET) +/* +* 0: Don't do the operation specified by \Fwrite. +* +* 1: Do the operation specified by \Fwrite. + */ +#define AC_ACCESS_REGISTER_TRANSFER_OFFSET 17 +#define AC_ACCESS_REGISTER_TRANSFER_LENGTH 1 +#define AC_ACCESS_REGISTER_TRANSFER (0x1 << AC_ACCESS_REGISTER_TRANSFER_OFFSET) +/* +* When \Ftransfer is set: +* 0: Copy data from the specified register into {\tt arg0} portion +* of {\tt data}. +* +* 1: Copy data from {\tt arg0} portion of {\tt data} into the +* specified register. + */ +#define AC_ACCESS_REGISTER_WRITE_OFFSET 16 +#define AC_ACCESS_REGISTER_WRITE_LENGTH 1 +#define AC_ACCESS_REGISTER_WRITE (0x1 << AC_ACCESS_REGISTER_WRITE_OFFSET) +/* +* Number of the register to access, as described in Table~\ref{tab:regno}. + */ +#define AC_ACCESS_REGISTER_REGNO_OFFSET 0 +#define AC_ACCESS_REGISTER_REGNO_LENGTH 16 +#define AC_ACCESS_REGISTER_REGNO (0xffff << AC_ACCESS_REGISTER_REGNO_OFFSET) +#define AC_QUICK_ACCESS None +/* +* This is 1 to indicate Quick Access command. + */ +#define AC_QUICK_ACCESS_CMDTYPE_OFFSET 24 +#define AC_QUICK_ACCESS_CMDTYPE_LENGTH 8 +#define AC_QUICK_ACCESS_CMDTYPE (0xff << AC_QUICK_ACCESS_CMDTYPE_OFFSET) diff --git a/riscv/debug_module.cc b/riscv/debug_module.cc index 8bcc60e..db035e3 100644 --- a/riscv/debug_module.cc +++ b/riscv/debug_module.cc @@ -1,49 +1,167 @@ #include <cassert> #include "debug_module.h" +#include "debug_defines.h" +#include "opcodes.h" #include "mmu.h" #include "debug_rom/debug_rom.h" +#include "debug_rom/debug_rom_defines.h" + +#if 1 +# define D(x) x +#else +# define D(x) +#endif + +///////////////////////// debug_module_t + +debug_module_t::debug_module_t(sim_t *sim) : sim(sim) +{ + dmcontrol = {0}; + + dmstatus = {0}; + dmstatus.authenticated = 1; + dmstatus.versionlo = 2; + + abstractcs = {0}; + abstractcs.progsize = progsize; + + abstractauto = {0}; + + memset(halted, 0, sizeof(halted)); + memset(debug_rom_flags, 0, sizeof(debug_rom_flags)); + memset(resumeack, 0, sizeof(resumeack)); + memset(program_buffer, 0, sizeof(program_buffer)); + memset(dmdata, 0, sizeof(dmdata)); + + write32(debug_rom_whereto, 0, + jal(ZERO, debug_abstract_start - DEBUG_ROM_WHERETO)); + + memset(debug_abstract, 0, sizeof(debug_abstract)); + +} + +void debug_module_t::reset() +{ + for (unsigned i = 0; i < sim->nprocs(); i++) { + processor_t *proc = sim->get_core(i); + if (proc) + proc->halt_request = false; + } + + dmcontrol = {0}; + + dmstatus = {0}; + dmstatus.authenticated = 1; + dmstatus.versionlo = 2; + + abstractcs = {0}; + abstractcs.datacount = sizeof(dmdata) / 4; + abstractcs.progsize = progsize; + + abstractauto = {0}; +} + +void debug_module_t::add_device(bus_t *bus) { + bus->add_device(DEBUG_START, this); +} bool debug_module_t::load(reg_t addr, size_t len, uint8_t* bytes) { addr = DEBUG_START + addr; - if (addr >= DEBUG_RAM_START && addr + len <= DEBUG_RAM_END) { - memcpy(bytes, debug_ram + addr - DEBUG_RAM_START, len); + if (addr >= DEBUG_ROM_ENTRY && + (addr + len) <= (DEBUG_ROM_ENTRY + debug_rom_raw_len)) { + memcpy(bytes, debug_rom_raw + addr - DEBUG_ROM_ENTRY, len); return true; } - if (addr >= DEBUG_ROM_START && addr + len <= DEBUG_ROM_END) { - memcpy(bytes, debug_rom_raw + addr - DEBUG_ROM_START, len); + if (addr >= DEBUG_ROM_WHERETO && (addr + len) <= (DEBUG_ROM_WHERETO + 4)) { + memcpy(bytes, debug_rom_whereto + addr - DEBUG_ROM_WHERETO, len); + return true; + } + + if (addr >= DEBUG_ROM_FLAGS && ((addr + len) <= DEBUG_ROM_FLAGS + 1024)) { + memcpy(bytes, debug_rom_flags + addr - DEBUG_ROM_FLAGS, len); + return true; + } + + if (addr >= debug_abstract_start && ((addr + len) <= (debug_abstract_start + sizeof(debug_abstract)))) { + memcpy(bytes, debug_abstract + addr - debug_abstract_start, len); + return true; + } + + if (addr >= debug_data_start && (addr + len) <= (debug_data_start + sizeof(dmdata))) { + memcpy(bytes, dmdata + addr - debug_data_start, len); + return true; + } + + if (addr >= debug_progbuf_start && ((addr + len) <= (debug_progbuf_start + sizeof(program_buffer)))) { + memcpy(bytes, program_buffer + addr - debug_progbuf_start, len); return true; } fprintf(stderr, "ERROR: invalid load from debug module: %zd bytes at 0x%016" PRIx64 "\n", len, addr); + return false; } bool debug_module_t::store(reg_t addr, size_t len, const uint8_t* bytes) { + + uint8_t id_bytes[4]; + uint32_t id = 0; + if (len == 4) { + memcpy(id_bytes, bytes, 4); + id = read32(id_bytes, 0); + } + addr = DEBUG_START + addr; + + if (addr >= debug_data_start && (addr + len) <= (debug_data_start + sizeof(dmdata))) { + memcpy(dmdata + addr - debug_data_start, bytes, len); + return true; + } + + if (addr >= debug_progbuf_start && ((addr + len) <= (debug_progbuf_start + sizeof(program_buffer)))) { + fprintf(stderr, "Successful write to program buffer %d bytes at %x\n", (int) len, (int) addr); + memcpy(program_buffer + addr - debug_progbuf_start, bytes, len); + + return true; + } - if (addr & (len-1)) { - fprintf(stderr, "ERROR: unaligned store to debug module: %zd bytes at 0x%016" - PRIx64 "\n", len, addr); - return false; + if (addr == DEBUG_ROM_HALTED) { + assert (len == 4); + halted[id] = true; + if (dmcontrol.hartsel == id) { + if (0 == (debug_rom_flags[id] & (1 << DEBUG_ROM_FLAG_GO))){ + if (dmcontrol.hartsel == id) { + abstractcs.busy = false; + } + } + } + return true; } - if (addr >= DEBUG_RAM_START && addr + len <= DEBUG_RAM_END) { - memcpy(debug_ram + addr - DEBUG_RAM_START, bytes, len); + if (addr == DEBUG_ROM_GOING) { + debug_rom_flags[dmcontrol.hartsel] &= ~(1 << DEBUG_ROM_FLAG_GO); return true; - } else if (len == 4 && addr == DEBUG_CLEARDEBINT) { - clear_interrupt(bytes[0] | (bytes[1] << 8) | - (bytes[2] << 16) | (bytes[3] << 24)); + } + + if (addr == DEBUG_ROM_RESUMING) { + assert (len == 4); + halted[id] = false; + resumeack[id] = true; + debug_rom_flags[id] &= ~(1 << DEBUG_ROM_FLAG_RESUME); return true; - } else if (len == 4 && addr == DEBUG_SETHALTNOT) { - set_halt_notification(bytes[0] | (bytes[1] << 8) | - (bytes[2] << 16) | (bytes[3] << 24)); + } + + if (addr == DEBUG_ROM_EXCEPTION) { + if (abstractcs.cmderr == CMDERR_NONE) { + abstractcs.cmderr = CMDERR_EXCEPTION; + } return true; } @@ -52,23 +170,303 @@ bool debug_module_t::store(reg_t addr, size_t len, const uint8_t* bytes) return false; } -void debug_module_t::ram_write32(unsigned int index, uint32_t value) +void debug_module_t::write32(uint8_t *memory, unsigned int index, uint32_t value) { - char* base = debug_ram + index * 4; + uint8_t* base = memory + index * 4; base[0] = value & 0xff; base[1] = (value >> 8) & 0xff; base[2] = (value >> 16) & 0xff; base[3] = (value >> 24) & 0xff; } -uint32_t debug_module_t::ram_read32(unsigned int index) +uint32_t debug_module_t::read32(uint8_t *memory, unsigned int index) { - // It'd be better for raw_page (and all memory) to be unsigned chars, but mem - // in sim_t is just chars, so I'm following that convention. - unsigned char* base = (unsigned char*) (debug_ram + index * 4); + uint8_t* base = memory + index * 4; uint32_t value = ((uint32_t) base[0]) | (((uint32_t) base[1]) << 8) | (((uint32_t) base[2]) << 16) | (((uint32_t) base[3]) << 24); return value; } + +processor_t *debug_module_t::current_proc() const +{ + processor_t *proc = NULL; + try { + proc = sim->get_core(dmcontrol.hartsel); + } catch (const std::out_of_range&) { + } + return proc; +} + +bool debug_module_t::dmi_read(unsigned address, uint32_t *value) +{ + uint32_t result = 0; + D(fprintf(stderr, "dmi_read(0x%x) -> ", address)); + if (address >= DMI_DATA0 && address < DMI_DATA0 + abstractcs.datacount) { + unsigned i = address - DMI_DATA0; + result = read32(dmdata, i); + if (abstractcs.busy) { + result = -1; + fprintf(stderr, "\ndmi_read(0x%02x (data[%d]) -> -1 because abstractcs.busy==true\n", address, i); + } + + if (abstractcs.busy && abstractcs.cmderr == CMDERR_NONE) { + abstractcs.cmderr = CMDERR_BUSY; + } + + if (!abstractcs.busy && ((abstractauto.autoexecdata >> i) & 1)) { + perform_abstract_command(); + } + } else if (address >= DMI_PROGBUF0 && address < DMI_PROGBUF0 + progsize) { + unsigned i = address - DMI_PROGBUF0; + result = read32(program_buffer, i); + if (abstractcs.busy) { + result = -1; + fprintf(stderr, "\ndmi_read(0x%02x (progbuf[%d]) -> -1 because abstractcs.busy==true\n", address, i); + } + if (!abstractcs.busy && ((abstractauto.autoexecprogbuf >> i) & 1)) { + perform_abstract_command(); + } + + } else { + switch (address) { + case DMI_DMCONTROL: + { + processor_t *proc = current_proc(); + if (proc) + dmcontrol.haltreq = proc->halt_request; + + result = set_field(result, DMI_DMCONTROL_HALTREQ, dmcontrol.haltreq); + result = set_field(result, DMI_DMCONTROL_RESUMEREQ, dmcontrol.resumereq); + result = set_field(result, DMI_DMCONTROL_HARTSEL, dmcontrol.hartsel); + result = set_field(result, DMI_DMCONTROL_HARTRESET, dmcontrol.hartreset); + result = set_field(result, DMI_DMCONTROL_NDMRESET, dmcontrol.ndmreset); + result = set_field(result, DMI_DMCONTROL_DMACTIVE, dmcontrol.dmactive); + } + break; + case DMI_DMSTATUS: + { + processor_t *proc = current_proc(); + + dmstatus.allnonexistant = false; + dmstatus.allunavail = false; + dmstatus.allrunning = false; + dmstatus.allhalted = false; + dmstatus.allresumeack = false; + if (proc) { + if (halted[dmcontrol.hartsel]) { + dmstatus.allhalted = true; + } else { + dmstatus.allrunning = true; + } + } else { + dmstatus.allnonexistant = true; + } + dmstatus.anynonexistant = dmstatus.allnonexistant; + dmstatus.anyunavail = dmstatus.allunavail; + dmstatus.anyrunning = dmstatus.allrunning; + dmstatus.anyhalted = dmstatus.allhalted; + if (proc) { + if (resumeack[dmcontrol.hartsel]) { + dmstatus.allresumeack = true; + } else { + dmstatus.allresumeack = false; + } + } else { + dmstatus.allresumeack = false; + } + + result = set_field(result, DMI_DMSTATUS_ALLNONEXISTENT, dmstatus.allnonexistant); + result = set_field(result, DMI_DMSTATUS_ALLUNAVAIL, dmstatus.allunavail); + result = set_field(result, DMI_DMSTATUS_ALLRUNNING, dmstatus.allrunning); + result = set_field(result, DMI_DMSTATUS_ALLHALTED, dmstatus.allhalted); + result = set_field(result, DMI_DMSTATUS_ALLRESUMEACK, dmstatus.allresumeack); + result = set_field(result, DMI_DMSTATUS_ANYNONEXISTENT, dmstatus.anynonexistant); + result = set_field(result, DMI_DMSTATUS_ANYUNAVAIL, dmstatus.anyunavail); + result = set_field(result, DMI_DMSTATUS_ANYRUNNING, dmstatus.anyrunning); + result = set_field(result, DMI_DMSTATUS_ANYHALTED, dmstatus.anyhalted); + result = set_field(result, DMI_DMSTATUS_ANYRESUMEACK, dmstatus.anyresumeack); + result = set_field(result, DMI_DMSTATUS_AUTHENTICATED, dmstatus.authenticated); + result = set_field(result, DMI_DMSTATUS_AUTHBUSY, dmstatus.authbusy); + result = set_field(result, DMI_DMSTATUS_VERSIONHI, dmstatus.versionhi); + result = set_field(result, DMI_DMSTATUS_VERSIONLO, dmstatus.versionlo); + } + break; + case DMI_ABSTRACTCS: + result = set_field(result, DMI_ABSTRACTCS_CMDERR, abstractcs.cmderr); + result = set_field(result, DMI_ABSTRACTCS_BUSY, abstractcs.busy); + result = set_field(result, DMI_ABSTRACTCS_DATACOUNT, abstractcs.datacount); + result = set_field(result, DMI_ABSTRACTCS_PROGSIZE, abstractcs.progsize); + break; + case DMI_ABSTRACTAUTO: + result = set_field(result, DMI_ABSTRACTAUTO_AUTOEXECPROGBUF, abstractauto.autoexecprogbuf); + result = set_field(result, DMI_ABSTRACTAUTO_AUTOEXECDATA, abstractauto.autoexecdata); + break; + case DMI_COMMAND: + result = 0; + break; + case DMI_HARTINFO: + result = set_field(result, DMI_HARTINFO_NSCRATCH, 1); + result = set_field(result, DMI_HARTINFO_DATAACCESS, 1); + result = set_field(result, DMI_HARTINFO_DATASIZE, abstractcs.datacount); + result = set_field(result, DMI_HARTINFO_DATAADDR, debug_data_start); + break; + default: + result = 0; + D(fprintf(stderr, "Unexpected. Returning Error.")); + return false; + } + } + D(fprintf(stderr, "0x%x\n", result)); + *value = result; + return true; +} + +bool debug_module_t::perform_abstract_command() +{ + if (abstractcs.cmderr != CMDERR_NONE) + return true; + if (abstractcs.busy) { + abstractcs.cmderr = CMDERR_BUSY; + return true; + } + + if ((command >> 24) == 0) { + // register access + unsigned size = get_field(command, AC_ACCESS_REGISTER_SIZE); + bool write = get_field(command, AC_ACCESS_REGISTER_WRITE); + unsigned regno = get_field(command, AC_ACCESS_REGISTER_REGNO); + + if (!halted[dmcontrol.hartsel]) { + abstractcs.cmderr = CMDERR_HALTRESUME; + return true; + } + + if (get_field(command, AC_ACCESS_REGISTER_TRANSFER)) { + + if (regno < 0x1000 || regno >= 0x1020) { + abstractcs.cmderr = CMDERR_NOTSUP; + return true; + } + + unsigned regnum = regno - 0x1000; + + switch (size) { + case 2: + if (write) + write32(debug_abstract, 0, lw(regnum, ZERO, debug_data_start)); + else + write32(debug_abstract, 0, sw(regnum, ZERO, debug_data_start)); + break; + case 3: + if (write) + write32(debug_abstract, 0, ld(regnum, ZERO, debug_data_start)); + else + write32(debug_abstract, 0, sd(regnum, ZERO, debug_data_start)); + break; + /* + case 4: + if (write) + write32(debug_rom_code, 0, lq(regnum, ZERO, debug_data_start)); + else + write32(debug_rom_code, 0, sq(regnum, ZERO, debug_data_start)); + break; + */ + default: + abstractcs.cmderr = CMDERR_NOTSUP; + return true; + } + } else { + //NOP + write32(debug_abstract, 0, addi(ZERO, ZERO, 0)); + } + + if (get_field(command, AC_ACCESS_REGISTER_POSTEXEC)) { + // Since the next instruction is what we will use, just use nother NOP + // to get there. + write32(debug_abstract, 1, addi(ZERO, ZERO, 0)); + } else { + write32(debug_abstract, 1, ebreak()); + } + + debug_rom_flags[dmcontrol.hartsel] |= 1 << DEBUG_ROM_FLAG_GO; + + abstractcs.busy = true; + } else { + abstractcs.cmderr = CMDERR_NOTSUP; + } + return true; +} + +bool debug_module_t::dmi_write(unsigned address, uint32_t value) +{ + D(fprintf(stderr, "dmi_write(0x%x, 0x%x)\n", address, value)); + if (address >= DMI_DATA0 && address < DMI_DATA0 + abstractcs.datacount) { + unsigned i = address - DMI_DATA0; + if (!abstractcs.busy) + write32(dmdata, address - DMI_DATA0, value); + + if (abstractcs.busy && abstractcs.cmderr == CMDERR_NONE) { + abstractcs.cmderr = CMDERR_BUSY; + } + + if (!abstractcs.busy && ((abstractauto.autoexecdata >> i) & 1)) { + perform_abstract_command(); + } + return true; + + } else if (address >= DMI_PROGBUF0 && address < DMI_PROGBUF0 + progsize) { + unsigned i = address - DMI_PROGBUF0; + + if (!abstractcs.busy) + write32(program_buffer, i, value); + + if (!abstractcs.busy && ((abstractauto.autoexecprogbuf >> i) & 1)) { + perform_abstract_command(); + } + return true; + + } else { + switch (address) { + case DMI_DMCONTROL: + { + dmcontrol.dmactive = get_field(value, DMI_DMCONTROL_DMACTIVE); + if (dmcontrol.dmactive) { + dmcontrol.haltreq = get_field(value, DMI_DMCONTROL_HALTREQ); + dmcontrol.resumereq = get_field(value, DMI_DMCONTROL_RESUMEREQ); + dmcontrol.ndmreset = get_field(value, DMI_DMCONTROL_NDMRESET); + dmcontrol.hartsel = get_field(value, DMI_DMCONTROL_HARTSEL); + } else { + reset(); + } + processor_t *proc = current_proc(); + if (proc) { + proc->halt_request = dmcontrol.haltreq; + if (dmcontrol.resumereq) { + debug_rom_flags[dmcontrol.hartsel] |= (1 << DEBUG_ROM_FLAG_RESUME); + resumeack[dmcontrol.hartsel] = false; + } + if (dmcontrol.ndmreset) { + proc->reset(); + } + } + } + return true; + + case DMI_COMMAND: + command = value; + return perform_abstract_command(); + + case DMI_ABSTRACTCS: + abstractcs.cmderr = (cmderr_t) (((uint32_t) (abstractcs.cmderr)) & (~(uint32_t)(get_field(value, DMI_ABSTRACTCS_CMDERR)))); + return true; + + case DMI_ABSTRACTAUTO: + abstractauto.autoexecprogbuf = get_field(value, DMI_ABSTRACTAUTO_AUTOEXECPROGBUF); + abstractauto.autoexecdata = get_field(value, DMI_ABSTRACTAUTO_AUTOEXECDATA); + break; + } + } + return false; +} diff --git a/riscv/debug_module.h b/riscv/debug_module.h index 53b32db..f0d2a47 100644 --- a/riscv/debug_module.h +++ b/riscv/debug_module.h @@ -6,41 +6,104 @@ #include "devices.h" +class sim_t; + +typedef struct { + bool haltreq; + bool resumereq; + unsigned hartsel; + bool hartreset; + bool dmactive; + bool ndmreset; +} dmcontrol_t; + +typedef struct { + bool allnonexistant; + bool anynonexistant; + bool allunavail; + bool anyunavail; + bool allrunning; + bool anyrunning; + bool allhalted; + bool anyhalted; + bool allresumeack; + bool anyresumeack; + bool authenticated; + bool authbusy; + bool cfgstrvalid; + unsigned versionhi; + unsigned versionlo; +} dmstatus_t; + +typedef enum cmderr { + CMDERR_NONE = 0, + CMDERR_BUSY = 1, + CMDERR_NOTSUP = 2, + CMDERR_EXCEPTION = 3, + CMDERR_HALTRESUME = 4, + CMDERR_OTHER = 7 +} cmderr_t; + +typedef struct { + bool busy; + unsigned datacount; + unsigned progsize; + cmderr_t cmderr; +} abstractcs_t; + +typedef struct { + unsigned autoexecprogbuf; + unsigned autoexecdata; +} abstractauto_t; + class debug_module_t : public abstract_device_t { public: + debug_module_t(sim_t *sim); + + void add_device(bus_t *bus); + bool load(reg_t addr, size_t len, uint8_t* bytes); bool store(reg_t addr, size_t len, const uint8_t* bytes); - void ram_write32(unsigned int index, uint32_t value); - uint32_t ram_read32(unsigned int index); - - void set_interrupt(uint32_t hartid) { - interrupt.insert(hartid); - } - void clear_interrupt(uint32_t hartid) { - interrupt.erase(hartid); - } - bool get_interrupt(uint32_t hartid) const { - return interrupt.find(hartid) != interrupt.end(); - } - - void set_halt_notification(uint32_t hartid) { - halt_notification.insert(hartid); - } - void clear_halt_notification(uint32_t hartid) { - halt_notification.erase(hartid); - } - bool get_halt_notification(uint32_t hartid) const { - return halt_notification.find(hartid) != halt_notification.end(); - } + // Debug Module Interface that the debugger (in our case through JTAG DTM) + // uses to access the DM. + // Return true for success, false for failure. + bool dmi_read(unsigned address, uint32_t *value); + bool dmi_write(unsigned address, uint32_t value); private: - // Track which interrupts from module to debugger are set. - std::set<uint32_t> interrupt; - // Track which halt notifications from debugger to module are set. - std::set<uint32_t> halt_notification; - char debug_ram[DEBUG_RAM_SIZE]; + static const unsigned datasize = 2; + static const unsigned progsize = 16; + static const unsigned debug_data_start = 0x380; + static const unsigned debug_progbuf_start = debug_data_start - progsize*4; + + static const unsigned debug_abstract_size = 2; + static const unsigned debug_abstract_start = debug_progbuf_start - debug_abstract_size*4; + + sim_t *sim; + + uint8_t debug_rom_whereto[4]; + uint8_t debug_abstract[debug_abstract_size * 4]; + uint8_t program_buffer[progsize * 4]; + uint8_t dmdata[datasize * 4]; + + bool halted[1024]; + bool resumeack[1024]; + uint8_t debug_rom_flags[1024]; + + void write32(uint8_t *rom, unsigned int index, uint32_t value); + uint32_t read32(uint8_t *rom, unsigned int index); + + dmcontrol_t dmcontrol; + dmstatus_t dmstatus; + abstractcs_t abstractcs; + abstractauto_t abstractauto; + uint32_t command; + + processor_t *current_proc() const; + void reset(); + bool perform_abstract_command(); }; #endif diff --git a/riscv/decode.h b/riscv/decode.h index b607bf3..9dcd809 100644 --- a/riscv/decode.h +++ b/riscv/decode.h @@ -7,17 +7,22 @@ # error spike requires a two''s-complement c++ implementation #endif +#ifdef WORDS_BIGENDIAN +# error spike requires a little-endian host +#endif + #include <cstdint> #include <string.h> #include <strings.h> #include "encoding.h" #include "config.h" #include "common.h" +#include "softfloat_types.h" +#include "specialize.h" #include <cinttypes> typedef int64_t sreg_t; typedef uint64_t reg_t; -typedef uint64_t freg_t; const int NXPR = 32; const int NFPR = 32; @@ -130,7 +135,7 @@ private: #ifndef RISCV_ENABLE_COMMITLOG # define WRITE_REG(reg, value) STATE.XPR.write(reg, value) -# define WRITE_FREG(reg, value) DO_WRITE_FREG(reg, value) +# define WRITE_FREG(reg, value) DO_WRITE_FREG(reg, freg(value)) #else # define WRITE_REG(reg, value) ({ \ reg_t wdata = (value); /* value may have side effects */ \ @@ -138,8 +143,8 @@ private: STATE.XPR.write(reg, wdata); \ }) # define WRITE_FREG(reg, value) ({ \ - freg_t wdata = (value); /* value may have side effects */ \ - STATE.log_reg_write = (commit_log_reg_t){((reg) << 1) | 1, wdata}; \ + freg_t wdata = freg(value); /* value may have side effects */ \ + STATE.log_reg_write = (commit_log_reg_t){((reg) << 1) | 1, wdata.v}; \ DO_WRITE_FREG(reg, wdata); \ }) #endif @@ -170,13 +175,13 @@ private: #define JUMP_TARGET (pc + insn.uj_imm()) #define RM ({ int rm = insn.rm(); \ if(rm == 7) rm = STATE.frm; \ - if(rm > 4) throw trap_illegal_instruction(); \ + if(rm > 4) throw trap_illegal_instruction(0); \ rm; }) #define get_field(reg, mask) (((reg) & (decltype(reg))(mask)) / ((mask) & ~((mask) << 1))) #define set_field(reg, mask, val) (((reg) & ~(decltype(reg))(mask)) | (((decltype(reg))(val) * ((mask) & ~((mask) << 1))) & (decltype(reg))(mask))) -#define require(x) if (unlikely(!(x))) throw trap_illegal_instruction() +#define require(x) if (unlikely(!(x))) throw trap_illegal_instruction(0) #define require_privilege(p) require(STATE.prv >= (p)) #define require_rv64 require(xlen == 64) #define require_rv32 require(xlen == 32) @@ -202,9 +207,10 @@ private: } while(0) #define set_pc_and_serialize(x) \ - do { set_pc(x); /* check alignment */ \ + do { reg_t __npc = (x); \ + set_pc(__npc); /* check alignment */ \ npc = PC_SERIALIZE_AFTER; \ - STATE.pc = (x); \ + STATE.pc = __npc; \ } while(0) /* Sentinel PC values to serialize simulator pipeline */ @@ -213,8 +219,23 @@ private: #define invalid_pc(pc) ((pc) & 1) /* Convenience wrappers to simplify softfloat code sequences */ -#define f32(x) ((float32_t){(uint32_t)x}) -#define f64(x) ((float64_t){(uint64_t)x}) +#define isBoxedF32(r) (((r) & 0xffffffff00000000) == 0xffffffff00000000) +#define unboxF32(r) (isBoxedF32(r) ? (r) : defaultNaNF32UI) +#define unboxF64(r) (r) +struct freg_t { uint64_t v; }; +inline float32_t f32(uint32_t v) { return { v }; } +inline float64_t f64(uint64_t v) { return { v }; } +inline float32_t f32(freg_t r) { return f32(unboxF32(r.v)); } +inline float64_t f64(freg_t r) { return f64(unboxF64(r.v)); } +inline freg_t freg(float32_t f) { return { ((decltype(freg_t::v))-1 << 32) | f.v }; } +inline freg_t freg(float64_t f) { return { f.v }; } +inline freg_t freg(freg_t f) { return f; } +#define F64_SIGN ((decltype(freg_t::v))1 << 63) +#define F32_SIGN ((decltype(freg_t::v))1 << 31) +#define fsgnj32(a, b, n, x) \ + f32((f32(a).v & ~F32_SIGN) | ((((x) ? f32(a).v : (n) ? F32_SIGN : 0) ^ f32(b).v) & F32_SIGN)) +#define fsgnj64(a, b, n, x) \ + f64((f64(a).v & ~F64_SIGN) | ((((x) ? f64(a).v : (n) ? F64_SIGN : 0) ^ f64(b).v) & F64_SIGN)) #define validate_csr(which, write) ({ \ if (!STATE.serialized) return PC_SERIALIZE_BEFORE; \ @@ -222,20 +243,11 @@ private: unsigned csr_priv = get_field((which), 0x300); \ unsigned csr_read_only = get_field((which), 0xC00) == 3; \ if (((write) && csr_read_only) || STATE.prv < csr_priv) \ - throw trap_illegal_instruction(); \ + throw trap_illegal_instruction(0); \ (which); }) +// Seems that 0x0 doesn't work. #define DEBUG_START 0x100 -#define DEBUG_ROM_START 0x800 -#define DEBUG_ROM_RESUME (DEBUG_ROM_START + 4) -#define DEBUG_ROM_EXCEPTION (DEBUG_ROM_START + 8) -#define DEBUG_ROM_END (DEBUG_ROM_START + debug_rom_raw_len) -#define DEBUG_RAM_START 0x400 -#define DEBUG_RAM_SIZE 64 -#define DEBUG_RAM_END (DEBUG_RAM_START + DEBUG_RAM_SIZE) -#define DEBUG_END 0xfff -#define DEBUG_CLEARDEBINT 0x100 -#define DEBUG_SETHALTNOT 0x10c -#define DEBUG_SIZE (DEBUG_END - DEBUG_START + 1) +#define DEBUG_END (0x1000 - 1) #endif diff --git a/riscv/devices.cc b/riscv/devices.cc index c7a63b0..15115c8 100644 --- a/riscv/devices.cc +++ b/riscv/devices.cc @@ -20,3 +20,11 @@ bool bus_t::store(reg_t addr, size_t len, const uint8_t* bytes) return false; return it->second->store(addr - -it->first, len, bytes); } + +std::pair<reg_t, abstract_device_t*> bus_t::find_device(reg_t addr) +{ + auto it = devices.lower_bound(-addr); + if (it == devices.end() || addr < -it->first) + return std::make_pair((reg_t)0, (abstract_device_t*)NULL); + return std::make_pair(-it->first, it->second); +} diff --git a/riscv/devices.h b/riscv/devices.h index 64ab79b..e4df6c9 100644 --- a/riscv/devices.h +++ b/riscv/devices.h @@ -22,6 +22,8 @@ class bus_t : public abstract_device_t { bool store(reg_t addr, size_t len, const uint8_t* bytes); void add_device(reg_t addr, abstract_device_t* dev); + std::pair<reg_t, abstract_device_t*> find_device(reg_t addr); + private: std::map<reg_t, abstract_device_t*> devices; }; @@ -36,17 +38,40 @@ class rom_device_t : public abstract_device_t { std::vector<char> data; }; -class rtc_t : public abstract_device_t { +class mem_t : public abstract_device_t { + public: + mem_t(size_t size) : len(size) { + data = (char*)calloc(1, size); + if (!data) + throw std::runtime_error("couldn't allocate " + std::to_string(size) + " bytes of target memory"); + } + mem_t(const mem_t& that) = delete; + ~mem_t() { free(data); } + + bool load(reg_t addr, size_t len, uint8_t* bytes) { return false; } + bool store(reg_t addr, size_t len, const uint8_t* bytes) { return false; } + char* contents() { return data; } + size_t size() { return len; } + + private: + char* data; + size_t len; +}; + +class clint_t : public abstract_device_t { public: - rtc_t(std::vector<processor_t*>&); + clint_t(std::vector<processor_t*>&); bool load(reg_t addr, size_t len, uint8_t* bytes); bool store(reg_t addr, size_t len, const uint8_t* bytes); - size_t size() { return regs.size() * sizeof(regs[0]); } + size_t size() { return CLINT_SIZE; } void increment(reg_t inc); private: + typedef uint64_t mtime_t; + typedef uint64_t mtimecmp_t; + typedef uint32_t msip_t; std::vector<processor_t*>& procs; - std::vector<uint64_t> regs; - uint64_t time() { return regs[0]; } + mtime_t mtime; + std::vector<mtimecmp_t> mtimecmp; }; #endif diff --git a/riscv/encoding.h b/riscv/encoding.h index 35e0f9f..8ec1345 100644 --- a/riscv/encoding.h +++ b/riscv/encoding.h @@ -17,10 +17,14 @@ #define MSTATUS_FS 0x00006000 #define MSTATUS_XS 0x00018000 #define MSTATUS_MPRV 0x00020000 -#define MSTATUS_PUM 0x00040000 +#define MSTATUS_SUM 0x00040000 #define MSTATUS_MXR 0x00080000 -#define MSTATUS_VM 0x1F000000 +#define MSTATUS_TVM 0x00100000 +#define MSTATUS_TW 0x00200000 +#define MSTATUS_TSR 0x00400000 #define MSTATUS32_SD 0x80000000 +#define MSTATUS_UXL 0x0000000300000000 +#define MSTATUS_SXL 0x0000000C00000000 #define MSTATUS64_SD 0x8000000000000000 #define SSTATUS_UIE 0x00000001 @@ -30,8 +34,10 @@ #define SSTATUS_SPP 0x00000100 #define SSTATUS_FS 0x00006000 #define SSTATUS_XS 0x00018000 -#define SSTATUS_PUM 0x00040000 +#define SSTATUS_SUM 0x00040000 +#define SSTATUS_MXR 0x00080000 #define SSTATUS32_SD 0x80000000 +#define SSTATUS_UXL 0x0000000300000000 #define SSTATUS64_SD 0x8000000000000000 #define DCSR_XDEBUGVER (3U<<30) @@ -107,12 +113,30 @@ #define PRV_H 2 #define PRV_M 3 -#define VM_MBARE 0 -#define VM_MBB 1 -#define VM_MBBID 2 -#define VM_SV32 8 -#define VM_SV39 9 -#define VM_SV48 10 +#define SPTBR32_MODE 0x80000000 +#define SPTBR32_ASID 0x7FC00000 +#define SPTBR32_PPN 0x003FFFFF +#define SPTBR64_MODE 0xF000000000000000 +#define SPTBR64_ASID 0x0FFFF00000000000 +#define SPTBR64_PPN 0x00000FFFFFFFFFFF + +#define SPTBR_MODE_OFF 0 +#define SPTBR_MODE_SV32 1 +#define SPTBR_MODE_SV39 8 +#define SPTBR_MODE_SV48 9 +#define SPTBR_MODE_SV57 10 +#define SPTBR_MODE_SV64 11 + +#define PMP_R 0x01 +#define PMP_W 0x02 +#define PMP_X 0x04 +#define PMP_A 0x18 +#define PMP_L 0x80 +#define PMP_SHIFT 2 + +#define PMP_TOR 0x08 +#define PMP_NA4 0x10 +#define PMP_NAPOT 0x18 #define IRQ_S_SOFT 1 #define IRQ_H_SOFT 2 @@ -127,9 +151,8 @@ #define IRQ_HOST 13 #define DEFAULT_RSTVEC 0x00001000 -#define DEFAULT_NMIVEC 0x00001004 -#define DEFAULT_MTVEC 0x00001010 -#define CONFIG_STRING_ADDR 0x0000100C +#define CLINT_BASE 0x02000000 +#define CLINT_SIZE 0x000c0000 #define EXT_IO_BASE 0x40000000 #define DRAM_BASE 0x80000000 @@ -150,14 +173,16 @@ #ifdef __riscv -#ifdef __riscv64 +#if __riscv_xlen == 64 # define MSTATUS_SD MSTATUS64_SD # define SSTATUS_SD SSTATUS64_SD # define RISCV_PGLEVEL_BITS 9 +# define SPTBR_MODE SPTBR64_MODE #else # define MSTATUS_SD MSTATUS32_SD # define SSTATUS_SD SSTATUS32_SD # define RISCV_PGLEVEL_BITS 10 +# define SPTBR_MODE SPTBR32_MODE #endif #define RISCV_PGSHIFT 12 #define RISCV_PGSIZE (1 << RISCV_PGSHIFT) @@ -171,30 +196,18 @@ __tmp; }) #define write_csr(reg, val) ({ \ - if (__builtin_constant_p(val) && (unsigned long)(val) < 32) \ - asm volatile ("csrw " #reg ", %0" :: "i"(val)); \ - else \ - asm volatile ("csrw " #reg ", %0" :: "r"(val)); }) + asm volatile ("csrw " #reg ", %0" :: "rK"(val)); }) #define swap_csr(reg, val) ({ unsigned long __tmp; \ - if (__builtin_constant_p(val) && (unsigned long)(val) < 32) \ - asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "i"(val)); \ - else \ - asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "r"(val)); \ + asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "rK"(val)); \ __tmp; }) #define set_csr(reg, bit) ({ unsigned long __tmp; \ - if (__builtin_constant_p(bit) && (unsigned long)(bit) < 32) \ - asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "i"(bit)); \ - else \ - asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "r"(bit)); \ + asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \ __tmp; }) #define clear_csr(reg, bit) ({ unsigned long __tmp; \ - if (__builtin_constant_p(bit) && (unsigned long)(bit) < 32) \ - asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "i"(bit)); \ - else \ - asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "r"(bit)); \ + asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \ __tmp; }) #define rdtime() read_csr(time) @@ -208,7 +221,7 @@ #endif #endif -/* Automatically generated by parse-opcodes */ +/* Automatically generated by parse-opcodes. */ #ifndef RISCV_ENCODING_H #define RISCV_ENCODING_H #define MATCH_BEQ 0x63 @@ -391,14 +404,12 @@ #define MASK_URET 0xffffffff #define MATCH_SRET 0x10200073 #define MASK_SRET 0xffffffff -#define MATCH_HRET 0x20200073 -#define MASK_HRET 0xffffffff #define MATCH_MRET 0x30200073 #define MASK_MRET 0xffffffff #define MATCH_DRET 0x7b200073 #define MASK_DRET 0xffffffff -#define MATCH_SFENCE_VM 0x10400073 -#define MASK_SFENCE_VM 0xfff07fff +#define MATCH_SFENCE_VMA 0x12000073 +#define MASK_SFENCE_VMA 0xfe007fff #define MATCH_WFI 0x10500073 #define MASK_WFI 0xffffffff #define MATCH_CSRRW 0x1073 @@ -457,6 +468,34 @@ #define MASK_FCVT_D_S 0xfff0007f #define MATCH_FSQRT_D 0x5a000053 #define MASK_FSQRT_D 0xfff0007f +#define MATCH_FADD_Q 0x6000053 +#define MASK_FADD_Q 0xfe00007f +#define MATCH_FSUB_Q 0xe000053 +#define MASK_FSUB_Q 0xfe00007f +#define MATCH_FMUL_Q 0x16000053 +#define MASK_FMUL_Q 0xfe00007f +#define MATCH_FDIV_Q 0x1e000053 +#define MASK_FDIV_Q 0xfe00007f +#define MATCH_FSGNJ_Q 0x26000053 +#define MASK_FSGNJ_Q 0xfe00707f +#define MATCH_FSGNJN_Q 0x26001053 +#define MASK_FSGNJN_Q 0xfe00707f +#define MATCH_FSGNJX_Q 0x26002053 +#define MASK_FSGNJX_Q 0xfe00707f +#define MATCH_FMIN_Q 0x2e000053 +#define MASK_FMIN_Q 0xfe00707f +#define MATCH_FMAX_Q 0x2e001053 +#define MASK_FMAX_Q 0xfe00707f +#define MATCH_FCVT_S_Q 0x40300053 +#define MASK_FCVT_S_Q 0xfff0007f +#define MATCH_FCVT_Q_S 0x46000053 +#define MASK_FCVT_Q_S 0xfff0007f +#define MATCH_FCVT_D_Q 0x42300053 +#define MASK_FCVT_D_Q 0xfff0007f +#define MATCH_FCVT_Q_D 0x46100053 +#define MASK_FCVT_Q_D 0xfff0007f +#define MATCH_FSQRT_Q 0x5e000053 +#define MASK_FSQRT_Q 0xfff0007f #define MATCH_FLE_S 0xa0000053 #define MASK_FLE_S 0xfe00707f #define MATCH_FLT_S 0xa0001053 @@ -469,6 +508,12 @@ #define MASK_FLT_D 0xfe00707f #define MATCH_FEQ_D 0xa2002053 #define MASK_FEQ_D 0xfe00707f +#define MATCH_FLE_Q 0xa6000053 +#define MASK_FLE_Q 0xfe00707f +#define MATCH_FLT_Q 0xa6001053 +#define MASK_FLT_Q 0xfe00707f +#define MATCH_FEQ_Q 0xa6002053 +#define MASK_FEQ_Q 0xfe00707f #define MATCH_FCVT_W_S 0xc0000053 #define MASK_FCVT_W_S 0xfff0007f #define MATCH_FCVT_WU_S 0xc0100053 @@ -477,8 +522,8 @@ #define MASK_FCVT_L_S 0xfff0007f #define MATCH_FCVT_LU_S 0xc0300053 #define MASK_FCVT_LU_S 0xfff0007f -#define MATCH_FMV_X_S 0xe0000053 -#define MASK_FMV_X_S 0xfff0707f +#define MATCH_FMV_X_W 0xe0000053 +#define MASK_FMV_X_W 0xfff0707f #define MATCH_FCLASS_S 0xe0001053 #define MASK_FCLASS_S 0xfff0707f #define MATCH_FCVT_W_D 0xc2000053 @@ -493,6 +538,18 @@ #define MASK_FMV_X_D 0xfff0707f #define MATCH_FCLASS_D 0xe2001053 #define MASK_FCLASS_D 0xfff0707f +#define MATCH_FCVT_W_Q 0xc6000053 +#define MASK_FCVT_W_Q 0xfff0007f +#define MATCH_FCVT_WU_Q 0xc6100053 +#define MASK_FCVT_WU_Q 0xfff0007f +#define MATCH_FCVT_L_Q 0xc6200053 +#define MASK_FCVT_L_Q 0xfff0007f +#define MATCH_FCVT_LU_Q 0xc6300053 +#define MASK_FCVT_LU_Q 0xfff0007f +#define MATCH_FMV_X_Q 0xe6000053 +#define MASK_FMV_X_Q 0xfff0707f +#define MATCH_FCLASS_Q 0xe6001053 +#define MASK_FCLASS_Q 0xfff0707f #define MATCH_FCVT_S_W 0xd0000053 #define MASK_FCVT_S_W 0xfff0007f #define MATCH_FCVT_S_WU 0xd0100053 @@ -501,8 +558,8 @@ #define MASK_FCVT_S_L 0xfff0007f #define MATCH_FCVT_S_LU 0xd0300053 #define MASK_FCVT_S_LU 0xfff0007f -#define MATCH_FMV_S_X 0xf0000053 -#define MASK_FMV_S_X 0xfff0707f +#define MATCH_FMV_W_X 0xf0000053 +#define MASK_FMV_W_X 0xfff0707f #define MATCH_FCVT_D_W 0xd2000053 #define MASK_FCVT_D_W 0xfff0007f #define MATCH_FCVT_D_WU 0xd2100053 @@ -513,14 +570,28 @@ #define MASK_FCVT_D_LU 0xfff0007f #define MATCH_FMV_D_X 0xf2000053 #define MASK_FMV_D_X 0xfff0707f +#define MATCH_FCVT_Q_W 0xd6000053 +#define MASK_FCVT_Q_W 0xfff0007f +#define MATCH_FCVT_Q_WU 0xd6100053 +#define MASK_FCVT_Q_WU 0xfff0007f +#define MATCH_FCVT_Q_L 0xd6200053 +#define MASK_FCVT_Q_L 0xfff0007f +#define MATCH_FCVT_Q_LU 0xd6300053 +#define MASK_FCVT_Q_LU 0xfff0007f +#define MATCH_FMV_Q_X 0xf6000053 +#define MASK_FMV_Q_X 0xfff0707f #define MATCH_FLW 0x2007 #define MASK_FLW 0x707f #define MATCH_FLD 0x3007 #define MASK_FLD 0x707f +#define MATCH_FLQ 0x4007 +#define MASK_FLQ 0x707f #define MATCH_FSW 0x2027 #define MASK_FSW 0x707f #define MATCH_FSD 0x3027 #define MASK_FSD 0x707f +#define MATCH_FSQ 0x4027 +#define MASK_FSQ 0x707f #define MATCH_FMADD_S 0x43 #define MASK_FMADD_S 0x600007f #define MATCH_FMSUB_S 0x47 @@ -537,6 +608,14 @@ #define MASK_FNMSUB_D 0x600007f #define MATCH_FNMADD_D 0x200004f #define MASK_FNMADD_D 0x600007f +#define MATCH_FMADD_Q 0x6000043 +#define MASK_FMADD_Q 0x600007f +#define MATCH_FMSUB_Q 0x6000047 +#define MASK_FMSUB_Q 0x600007f +#define MATCH_FNMSUB_Q 0x600004b +#define MASK_FNMSUB_Q 0x600007f +#define MATCH_FNMADD_Q 0x600004f +#define MASK_FNMADD_Q 0x600007f #define MATCH_C_NOP 0x1 #define MASK_C_NOP 0xffff #define MATCH_C_ADDI16SP 0x6101 @@ -707,6 +786,7 @@ #define CSR_SSTATUS 0x100 #define CSR_SIE 0x104 #define CSR_STVEC 0x105 +#define CSR_SCOUNTEREN 0x106 #define CSR_SSCRATCH 0x140 #define CSR_SEPC 0x141 #define CSR_SCAUSE 0x142 @@ -719,11 +799,32 @@ #define CSR_MIDELEG 0x303 #define CSR_MIE 0x304 #define CSR_MTVEC 0x305 +#define CSR_MCOUNTEREN 0x306 #define CSR_MSCRATCH 0x340 #define CSR_MEPC 0x341 #define CSR_MCAUSE 0x342 #define CSR_MBADADDR 0x343 #define CSR_MIP 0x344 +#define CSR_PMPCFG0 0x3a0 +#define CSR_PMPCFG1 0x3a1 +#define CSR_PMPCFG2 0x3a2 +#define CSR_PMPCFG3 0x3a3 +#define CSR_PMPADDR0 0x3b0 +#define CSR_PMPADDR1 0x3b1 +#define CSR_PMPADDR2 0x3b2 +#define CSR_PMPADDR3 0x3b3 +#define CSR_PMPADDR4 0x3b4 +#define CSR_PMPADDR5 0x3b5 +#define CSR_PMPADDR6 0x3b6 +#define CSR_PMPADDR7 0x3b7 +#define CSR_PMPADDR8 0x3b8 +#define CSR_PMPADDR9 0x3b9 +#define CSR_PMPADDR10 0x3ba +#define CSR_PMPADDR11 0x3bb +#define CSR_PMPADDR12 0x3bc +#define CSR_PMPADDR13 0x3bd +#define CSR_PMPADDR14 0x3be +#define CSR_PMPADDR15 0x3bf #define CSR_TSELECT 0x7a0 #define CSR_TDATA1 0x7a1 #define CSR_TDATA2 0x7a2 @@ -762,8 +863,6 @@ #define CSR_MHPMCOUNTER29 0xb1d #define CSR_MHPMCOUNTER30 0xb1e #define CSR_MHPMCOUNTER31 0xb1f -#define CSR_MUCOUNTEREN 0x320 -#define CSR_MSCOUNTEREN 0x321 #define CSR_MHPMEVENT3 0x323 #define CSR_MHPMEVENT4 0x324 #define CSR_MHPMEVENT5 0x325 @@ -861,17 +960,20 @@ #define CSR_MHPMCOUNTER30H 0xb9e #define CSR_MHPMCOUNTER31H 0xb9f #define CAUSE_MISALIGNED_FETCH 0x0 -#define CAUSE_FAULT_FETCH 0x1 +#define CAUSE_FETCH_ACCESS 0x1 #define CAUSE_ILLEGAL_INSTRUCTION 0x2 #define CAUSE_BREAKPOINT 0x3 #define CAUSE_MISALIGNED_LOAD 0x4 -#define CAUSE_FAULT_LOAD 0x5 +#define CAUSE_LOAD_ACCESS 0x5 #define CAUSE_MISALIGNED_STORE 0x6 -#define CAUSE_FAULT_STORE 0x7 +#define CAUSE_STORE_ACCESS 0x7 #define CAUSE_USER_ECALL 0x8 #define CAUSE_SUPERVISOR_ECALL 0x9 #define CAUSE_HYPERVISOR_ECALL 0xa #define CAUSE_MACHINE_ECALL 0xb +#define CAUSE_FETCH_PAGE_FAULT 0xc +#define CAUSE_LOAD_PAGE_FAULT 0xd +#define CAUSE_STORE_PAGE_FAULT 0xf #endif #ifdef DECLARE_INSN DECLARE_INSN(beq, MATCH_BEQ, MASK_BEQ) @@ -964,10 +1066,9 @@ DECLARE_INSN(ecall, MATCH_ECALL, MASK_ECALL) DECLARE_INSN(ebreak, MATCH_EBREAK, MASK_EBREAK) DECLARE_INSN(uret, MATCH_URET, MASK_URET) DECLARE_INSN(sret, MATCH_SRET, MASK_SRET) -DECLARE_INSN(hret, MATCH_HRET, MASK_HRET) DECLARE_INSN(mret, MATCH_MRET, MASK_MRET) DECLARE_INSN(dret, MATCH_DRET, MASK_DRET) -DECLARE_INSN(sfence_vm, MATCH_SFENCE_VM, MASK_SFENCE_VM) +DECLARE_INSN(sfence_vma, MATCH_SFENCE_VMA, MASK_SFENCE_VMA) DECLARE_INSN(wfi, MATCH_WFI, MASK_WFI) DECLARE_INSN(csrrw, MATCH_CSRRW, MASK_CSRRW) DECLARE_INSN(csrrs, MATCH_CSRRS, MASK_CSRRS) @@ -997,17 +1098,34 @@ DECLARE_INSN(fmax_d, MATCH_FMAX_D, MASK_FMAX_D) DECLARE_INSN(fcvt_s_d, MATCH_FCVT_S_D, MASK_FCVT_S_D) DECLARE_INSN(fcvt_d_s, MATCH_FCVT_D_S, MASK_FCVT_D_S) DECLARE_INSN(fsqrt_d, MATCH_FSQRT_D, MASK_FSQRT_D) +DECLARE_INSN(fadd_q, MATCH_FADD_Q, MASK_FADD_Q) +DECLARE_INSN(fsub_q, MATCH_FSUB_Q, MASK_FSUB_Q) +DECLARE_INSN(fmul_q, MATCH_FMUL_Q, MASK_FMUL_Q) +DECLARE_INSN(fdiv_q, MATCH_FDIV_Q, MASK_FDIV_Q) +DECLARE_INSN(fsgnj_q, MATCH_FSGNJ_Q, MASK_FSGNJ_Q) +DECLARE_INSN(fsgnjn_q, MATCH_FSGNJN_Q, MASK_FSGNJN_Q) +DECLARE_INSN(fsgnjx_q, MATCH_FSGNJX_Q, MASK_FSGNJX_Q) +DECLARE_INSN(fmin_q, MATCH_FMIN_Q, MASK_FMIN_Q) +DECLARE_INSN(fmax_q, MATCH_FMAX_Q, MASK_FMAX_Q) +DECLARE_INSN(fcvt_s_q, MATCH_FCVT_S_Q, MASK_FCVT_S_Q) +DECLARE_INSN(fcvt_q_s, MATCH_FCVT_Q_S, MASK_FCVT_Q_S) +DECLARE_INSN(fcvt_d_q, MATCH_FCVT_D_Q, MASK_FCVT_D_Q) +DECLARE_INSN(fcvt_q_d, MATCH_FCVT_Q_D, MASK_FCVT_Q_D) +DECLARE_INSN(fsqrt_q, MATCH_FSQRT_Q, MASK_FSQRT_Q) DECLARE_INSN(fle_s, MATCH_FLE_S, MASK_FLE_S) DECLARE_INSN(flt_s, MATCH_FLT_S, MASK_FLT_S) DECLARE_INSN(feq_s, MATCH_FEQ_S, MASK_FEQ_S) DECLARE_INSN(fle_d, MATCH_FLE_D, MASK_FLE_D) DECLARE_INSN(flt_d, MATCH_FLT_D, MASK_FLT_D) DECLARE_INSN(feq_d, MATCH_FEQ_D, MASK_FEQ_D) +DECLARE_INSN(fle_q, MATCH_FLE_Q, MASK_FLE_Q) +DECLARE_INSN(flt_q, MATCH_FLT_Q, MASK_FLT_Q) +DECLARE_INSN(feq_q, MATCH_FEQ_Q, MASK_FEQ_Q) DECLARE_INSN(fcvt_w_s, MATCH_FCVT_W_S, MASK_FCVT_W_S) DECLARE_INSN(fcvt_wu_s, MATCH_FCVT_WU_S, MASK_FCVT_WU_S) DECLARE_INSN(fcvt_l_s, MATCH_FCVT_L_S, MASK_FCVT_L_S) DECLARE_INSN(fcvt_lu_s, MATCH_FCVT_LU_S, MASK_FCVT_LU_S) -DECLARE_INSN(fmv_x_s, MATCH_FMV_X_S, MASK_FMV_X_S) +DECLARE_INSN(fmv_x_w, MATCH_FMV_X_W, MASK_FMV_X_W) DECLARE_INSN(fclass_s, MATCH_FCLASS_S, MASK_FCLASS_S) DECLARE_INSN(fcvt_w_d, MATCH_FCVT_W_D, MASK_FCVT_W_D) DECLARE_INSN(fcvt_wu_d, MATCH_FCVT_WU_D, MASK_FCVT_WU_D) @@ -1015,20 +1133,33 @@ DECLARE_INSN(fcvt_l_d, MATCH_FCVT_L_D, MASK_FCVT_L_D) DECLARE_INSN(fcvt_lu_d, MATCH_FCVT_LU_D, MASK_FCVT_LU_D) DECLARE_INSN(fmv_x_d, MATCH_FMV_X_D, MASK_FMV_X_D) DECLARE_INSN(fclass_d, MATCH_FCLASS_D, MASK_FCLASS_D) +DECLARE_INSN(fcvt_w_q, MATCH_FCVT_W_Q, MASK_FCVT_W_Q) +DECLARE_INSN(fcvt_wu_q, MATCH_FCVT_WU_Q, MASK_FCVT_WU_Q) +DECLARE_INSN(fcvt_l_q, MATCH_FCVT_L_Q, MASK_FCVT_L_Q) +DECLARE_INSN(fcvt_lu_q, MATCH_FCVT_LU_Q, MASK_FCVT_LU_Q) +DECLARE_INSN(fmv_x_q, MATCH_FMV_X_Q, MASK_FMV_X_Q) +DECLARE_INSN(fclass_q, MATCH_FCLASS_Q, MASK_FCLASS_Q) DECLARE_INSN(fcvt_s_w, MATCH_FCVT_S_W, MASK_FCVT_S_W) DECLARE_INSN(fcvt_s_wu, MATCH_FCVT_S_WU, MASK_FCVT_S_WU) DECLARE_INSN(fcvt_s_l, MATCH_FCVT_S_L, MASK_FCVT_S_L) DECLARE_INSN(fcvt_s_lu, MATCH_FCVT_S_LU, MASK_FCVT_S_LU) -DECLARE_INSN(fmv_s_x, MATCH_FMV_S_X, MASK_FMV_S_X) +DECLARE_INSN(fmv_w_x, MATCH_FMV_W_X, MASK_FMV_W_X) DECLARE_INSN(fcvt_d_w, MATCH_FCVT_D_W, MASK_FCVT_D_W) DECLARE_INSN(fcvt_d_wu, MATCH_FCVT_D_WU, MASK_FCVT_D_WU) DECLARE_INSN(fcvt_d_l, MATCH_FCVT_D_L, MASK_FCVT_D_L) DECLARE_INSN(fcvt_d_lu, MATCH_FCVT_D_LU, MASK_FCVT_D_LU) DECLARE_INSN(fmv_d_x, MATCH_FMV_D_X, MASK_FMV_D_X) +DECLARE_INSN(fcvt_q_w, MATCH_FCVT_Q_W, MASK_FCVT_Q_W) +DECLARE_INSN(fcvt_q_wu, MATCH_FCVT_Q_WU, MASK_FCVT_Q_WU) +DECLARE_INSN(fcvt_q_l, MATCH_FCVT_Q_L, MASK_FCVT_Q_L) +DECLARE_INSN(fcvt_q_lu, MATCH_FCVT_Q_LU, MASK_FCVT_Q_LU) +DECLARE_INSN(fmv_q_x, MATCH_FMV_Q_X, MASK_FMV_Q_X) DECLARE_INSN(flw, MATCH_FLW, MASK_FLW) DECLARE_INSN(fld, MATCH_FLD, MASK_FLD) +DECLARE_INSN(flq, MATCH_FLQ, MASK_FLQ) DECLARE_INSN(fsw, MATCH_FSW, MASK_FSW) DECLARE_INSN(fsd, MATCH_FSD, MASK_FSD) +DECLARE_INSN(fsq, MATCH_FSQ, MASK_FSQ) DECLARE_INSN(fmadd_s, MATCH_FMADD_S, MASK_FMADD_S) DECLARE_INSN(fmsub_s, MATCH_FMSUB_S, MASK_FMSUB_S) DECLARE_INSN(fnmsub_s, MATCH_FNMSUB_S, MASK_FNMSUB_S) @@ -1037,6 +1168,10 @@ DECLARE_INSN(fmadd_d, MATCH_FMADD_D, MASK_FMADD_D) DECLARE_INSN(fmsub_d, MATCH_FMSUB_D, MASK_FMSUB_D) DECLARE_INSN(fnmsub_d, MATCH_FNMSUB_D, MASK_FNMSUB_D) DECLARE_INSN(fnmadd_d, MATCH_FNMADD_D, MASK_FNMADD_D) +DECLARE_INSN(fmadd_q, MATCH_FMADD_Q, MASK_FMADD_Q) +DECLARE_INSN(fmsub_q, MATCH_FMSUB_Q, MASK_FMSUB_Q) +DECLARE_INSN(fnmsub_q, MATCH_FNMSUB_Q, MASK_FNMSUB_Q) +DECLARE_INSN(fnmadd_q, MATCH_FNMADD_Q, MASK_FNMADD_Q) DECLARE_INSN(c_nop, MATCH_C_NOP, MASK_C_NOP) DECLARE_INSN(c_addi16sp, MATCH_C_ADDI16SP, MASK_C_ADDI16SP) DECLARE_INSN(c_jr, MATCH_C_JR, MASK_C_JR) @@ -1143,6 +1278,7 @@ DECLARE_CSR(hpmcounter31, CSR_HPMCOUNTER31) DECLARE_CSR(sstatus, CSR_SSTATUS) DECLARE_CSR(sie, CSR_SIE) DECLARE_CSR(stvec, CSR_STVEC) +DECLARE_CSR(scounteren, CSR_SCOUNTEREN) DECLARE_CSR(sscratch, CSR_SSCRATCH) DECLARE_CSR(sepc, CSR_SEPC) DECLARE_CSR(scause, CSR_SCAUSE) @@ -1155,11 +1291,32 @@ DECLARE_CSR(medeleg, CSR_MEDELEG) DECLARE_CSR(mideleg, CSR_MIDELEG) DECLARE_CSR(mie, CSR_MIE) DECLARE_CSR(mtvec, CSR_MTVEC) +DECLARE_CSR(mcounteren, CSR_MCOUNTEREN) DECLARE_CSR(mscratch, CSR_MSCRATCH) DECLARE_CSR(mepc, CSR_MEPC) DECLARE_CSR(mcause, CSR_MCAUSE) DECLARE_CSR(mbadaddr, CSR_MBADADDR) DECLARE_CSR(mip, CSR_MIP) +DECLARE_CSR(pmpcfg0, CSR_PMPCFG0) +DECLARE_CSR(pmpcfg1, CSR_PMPCFG1) +DECLARE_CSR(pmpcfg2, CSR_PMPCFG2) +DECLARE_CSR(pmpcfg3, CSR_PMPCFG3) +DECLARE_CSR(pmpaddr0, CSR_PMPADDR0) +DECLARE_CSR(pmpaddr1, CSR_PMPADDR1) +DECLARE_CSR(pmpaddr2, CSR_PMPADDR2) +DECLARE_CSR(pmpaddr3, CSR_PMPADDR3) +DECLARE_CSR(pmpaddr4, CSR_PMPADDR4) +DECLARE_CSR(pmpaddr5, CSR_PMPADDR5) +DECLARE_CSR(pmpaddr6, CSR_PMPADDR6) +DECLARE_CSR(pmpaddr7, CSR_PMPADDR7) +DECLARE_CSR(pmpaddr8, CSR_PMPADDR8) +DECLARE_CSR(pmpaddr9, CSR_PMPADDR9) +DECLARE_CSR(pmpaddr10, CSR_PMPADDR10) +DECLARE_CSR(pmpaddr11, CSR_PMPADDR11) +DECLARE_CSR(pmpaddr12, CSR_PMPADDR12) +DECLARE_CSR(pmpaddr13, CSR_PMPADDR13) +DECLARE_CSR(pmpaddr14, CSR_PMPADDR14) +DECLARE_CSR(pmpaddr15, CSR_PMPADDR15) DECLARE_CSR(tselect, CSR_TSELECT) DECLARE_CSR(tdata1, CSR_TDATA1) DECLARE_CSR(tdata2, CSR_TDATA2) @@ -1198,8 +1355,6 @@ DECLARE_CSR(mhpmcounter28, CSR_MHPMCOUNTER28) DECLARE_CSR(mhpmcounter29, CSR_MHPMCOUNTER29) DECLARE_CSR(mhpmcounter30, CSR_MHPMCOUNTER30) DECLARE_CSR(mhpmcounter31, CSR_MHPMCOUNTER31) -DECLARE_CSR(mucounteren, CSR_MUCOUNTEREN) -DECLARE_CSR(mscounteren, CSR_MSCOUNTEREN) DECLARE_CSR(mhpmevent3, CSR_MHPMEVENT3) DECLARE_CSR(mhpmevent4, CSR_MHPMEVENT4) DECLARE_CSR(mhpmevent5, CSR_MHPMEVENT5) @@ -1299,15 +1454,18 @@ DECLARE_CSR(mhpmcounter31h, CSR_MHPMCOUNTER31H) #endif #ifdef DECLARE_CAUSE DECLARE_CAUSE("misaligned fetch", CAUSE_MISALIGNED_FETCH) -DECLARE_CAUSE("fault fetch", CAUSE_FAULT_FETCH) +DECLARE_CAUSE("fetch access", CAUSE_FETCH_ACCESS) DECLARE_CAUSE("illegal instruction", CAUSE_ILLEGAL_INSTRUCTION) DECLARE_CAUSE("breakpoint", CAUSE_BREAKPOINT) DECLARE_CAUSE("misaligned load", CAUSE_MISALIGNED_LOAD) -DECLARE_CAUSE("fault load", CAUSE_FAULT_LOAD) +DECLARE_CAUSE("load access", CAUSE_LOAD_ACCESS) DECLARE_CAUSE("misaligned store", CAUSE_MISALIGNED_STORE) -DECLARE_CAUSE("fault store", CAUSE_FAULT_STORE) +DECLARE_CAUSE("store access", CAUSE_STORE_ACCESS) DECLARE_CAUSE("user_ecall", CAUSE_USER_ECALL) DECLARE_CAUSE("supervisor_ecall", CAUSE_SUPERVISOR_ECALL) DECLARE_CAUSE("hypervisor_ecall", CAUSE_HYPERVISOR_ECALL) DECLARE_CAUSE("machine_ecall", CAUSE_MACHINE_ECALL) +DECLARE_CAUSE("fetch page fault", CAUSE_FETCH_PAGE_FAULT) +DECLARE_CAUSE("load page fault", CAUSE_LOAD_PAGE_FAULT) +DECLARE_CAUSE("store page fault", CAUSE_STORE_PAGE_FAULT) #endif diff --git a/riscv/execute.cc b/riscv/execute.cc index ebc9dc7..303effe 100644 --- a/riscv/execute.cc +++ b/riscv/execute.cc @@ -63,16 +63,12 @@ bool processor_t::slow_path() void processor_t::step(size_t n) { if (state.dcsr.cause == DCSR_CAUSE_NONE) { - // TODO: get_interrupt() isn't super fast. Does that matter? - if (sim->debug_module.get_interrupt(id)) { + if (halt_request) { enter_debug_mode(DCSR_CAUSE_DEBUGINT); - } else if (state.dcsr.halt) { + } // !!!The halt bit in DCSR is deprecated. + else if (state.dcsr.halt) { enter_debug_mode(DCSR_CAUSE_HALT); } - } else { - // In Debug Mode, just do 11 steps at a time. Otherwise we're going to be - // spinning the rest of the time anyway. - n = std::min(n, (size_t) 11); } while (n > 0) { @@ -84,7 +80,7 @@ void processor_t::step(size_t n) if (unlikely(invalid_pc(pc))) { \ switch (pc) { \ case PC_SERIALIZE_BEFORE: state.serialized = true; break; \ - case PC_SERIALIZE_AFTER: instret++; break; \ + case PC_SERIALIZE_AFTER: n = ++instret; break; \ default: abort(); \ } \ pc = state.pc; \ @@ -120,6 +116,15 @@ void processor_t::step(size_t n) // enter_debug_mode changed state.pc, so we can't just continue. break; } + + if (unlikely(state.pc >= DEBUG_START && + state.pc < DEBUG_END)) { + // We're waiting for the debugger to tell us something. + return; + } + + + } } else while (instret < n) diff --git a/riscv/extension.cc b/riscv/extension.cc index a34dd80..520c2ed 100644 --- a/riscv/extension.cc +++ b/riscv/extension.cc @@ -9,7 +9,7 @@ extension_t::~extension_t() void extension_t::illegal_instruction() { - throw trap_illegal_instruction(); + throw trap_illegal_instruction(0); } void extension_t::raise_interrupt() diff --git a/riscv/gdbserver.cc b/riscv/gdbserver.cc deleted file mode 100644 index 79284eb..0000000 --- a/riscv/gdbserver.cc +++ /dev/null @@ -1,2232 +0,0 @@ -#include <arpa/inet.h> -#include <errno.h> -#include <fcntl.h> -#include <stdlib.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include <algorithm> -#include <cassert> -#include <cinttypes> -#include <cstdio> -#include <vector> - -#include "disasm.h" -#include "sim.h" -#include "gdbserver.h" -#include "mmu.h" -#include "encoding.h" - -//////////////////////////////////////// Utility Functions - -#undef DEBUG -#ifdef DEBUG -# define D(x) x -#else -# define D(x) -#endif // DEBUG - -void die(const char* msg) -{ - fprintf(stderr, "gdbserver code died: %s\n", msg); - abort(); -} - -// gdb's register list is defined in riscv_gdb_reg_names gdb/riscv-tdep.c in -// its source tree. We must interpret the numbers the same here. -enum { - REG_XPR0 = 0, - REG_XPR31 = 31, - REG_PC = 32, - REG_FPR0 = 33, - REG_FPR31 = 64, - REG_CSR0 = 65, - REG_MSTATUS = CSR_MSTATUS + REG_CSR0, - REG_CSR4095 = 4160, - REG_PRIV = 4161 -}; - -//////////////////////////////////////// Functions to generate RISC-V opcodes. - -// TODO: Does this already exist somewhere? - -#define ZERO 0 -// Using regnames.cc as source. The RVG Calling Convention of the 2.0 RISC-V -// spec says it should be 2 and 3. -#define S0 8 -#define S1 9 -static uint32_t bits(uint32_t value, unsigned int hi, unsigned int lo) { - return (value >> lo) & ((1 << (hi+1-lo)) - 1); -} - -static uint32_t bit(uint32_t value, unsigned int b) { - return (value >> b) & 1; -} - -static uint32_t jal(unsigned int rd, uint32_t imm) { - return (bit(imm, 20) << 31) | - (bits(imm, 10, 1) << 21) | - (bit(imm, 11) << 20) | - (bits(imm, 19, 12) << 12) | - (rd << 7) | - MATCH_JAL; -} - -static uint32_t csrsi(unsigned int csr, uint16_t imm) { - return (csr << 20) | - (bits(imm, 4, 0) << 15) | - MATCH_CSRRSI; -} - -static uint32_t csrci(unsigned int csr, uint16_t imm) { - return (csr << 20) | - (bits(imm, 4, 0) << 15) | - MATCH_CSRRCI; -} - -static uint32_t csrr(unsigned int rd, unsigned int csr) { - return (csr << 20) | (rd << 7) | MATCH_CSRRS; -} - -static uint32_t csrw(unsigned int source, unsigned int csr) { - return (csr << 20) | (source << 15) | MATCH_CSRRW; -} - -static uint32_t fence_i() -{ - return MATCH_FENCE_I; -} - -static uint32_t sb(unsigned int src, unsigned int base, uint16_t offset) -{ - return (bits(offset, 11, 5) << 25) | - (src << 20) | - (base << 15) | - (bits(offset, 4, 0) << 7) | - MATCH_SB; -} - -static uint32_t sh(unsigned int src, unsigned int base, uint16_t offset) -{ - return (bits(offset, 11, 5) << 25) | - (src << 20) | - (base << 15) | - (bits(offset, 4, 0) << 7) | - MATCH_SH; -} - -static uint32_t sw(unsigned int src, unsigned int base, uint16_t offset) -{ - return (bits(offset, 11, 5) << 25) | - (src << 20) | - (base << 15) | - (bits(offset, 4, 0) << 7) | - MATCH_SW; -} - -static uint32_t sd(unsigned int src, unsigned int base, uint16_t offset) -{ - return (bits(offset, 11, 5) << 25) | - (bits(src, 4, 0) << 20) | - (base << 15) | - (bits(offset, 4, 0) << 7) | - MATCH_SD; -} - -static uint32_t sq(unsigned int src, unsigned int base, uint16_t offset) -{ -#if 0 - return (bits(offset, 11, 5) << 25) | - (bits(src, 4, 0) << 20) | - (base << 15) | - (bits(offset, 4, 0) << 7) | - MATCH_SQ; -#else - abort(); -#endif -} - -static uint32_t lq(unsigned int rd, unsigned int base, uint16_t offset) -{ -#if 0 - return (bits(offset, 11, 0) << 20) | - (base << 15) | - (bits(rd, 4, 0) << 7) | - MATCH_LQ; -#else - abort(); -#endif -} - -static uint32_t ld(unsigned int rd, unsigned int base, uint16_t offset) -{ - return (bits(offset, 11, 0) << 20) | - (base << 15) | - (bits(rd, 4, 0) << 7) | - MATCH_LD; -} - -static uint32_t lw(unsigned int rd, unsigned int base, uint16_t offset) -{ - return (bits(offset, 11, 0) << 20) | - (base << 15) | - (bits(rd, 4, 0) << 7) | - MATCH_LW; -} - -static uint32_t lh(unsigned int rd, unsigned int base, uint16_t offset) -{ - return (bits(offset, 11, 0) << 20) | - (base << 15) | - (bits(rd, 4, 0) << 7) | - MATCH_LH; -} - -static uint32_t lb(unsigned int rd, unsigned int base, uint16_t offset) -{ - return (bits(offset, 11, 0) << 20) | - (base << 15) | - (bits(rd, 4, 0) << 7) | - MATCH_LB; -} - -static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset) -{ - return (bits(offset, 11, 5) << 25) | - (bits(src, 4, 0) << 20) | - (base << 15) | - (bits(offset, 4, 0) << 7) | - MATCH_FSW; -} - -static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset) -{ - return (bits(offset, 11, 5) << 25) | - (bits(src, 4, 0) << 20) | - (base << 15) | - (bits(offset, 4, 0) << 7) | - MATCH_FSD; -} - -static uint32_t flw(unsigned int dest, unsigned int base, uint16_t offset) -{ - return (bits(offset, 11, 0) << 20) | - (base << 15) | - (bits(dest, 4, 0) << 7) | - MATCH_FLW; -} - -static uint32_t fld(unsigned int dest, unsigned int base, uint16_t offset) -{ - return (bits(offset, 11, 0) << 20) | - (base << 15) | - (bits(dest, 4, 0) << 7) | - MATCH_FLD; -} - -static uint32_t addi(unsigned int dest, unsigned int src, uint16_t imm) -{ - return (bits(imm, 11, 0) << 20) | - (src << 15) | - (dest << 7) | - MATCH_ADDI; -} - -static uint32_t ori(unsigned int dest, unsigned int src, uint16_t imm) -{ - return (bits(imm, 11, 0) << 20) | - (src << 15) | - (dest << 7) | - MATCH_ORI; -} - -static uint32_t xori(unsigned int dest, unsigned int src, uint16_t imm) -{ - return (bits(imm, 11, 0) << 20) | - (src << 15) | - (dest << 7) | - MATCH_XORI; -} - -static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt) -{ - return (bits(shamt, 4, 0) << 20) | - (src << 15) | - (dest << 7) | - MATCH_SRLI; -} - - -static uint32_t nop() -{ - return addi(0, 0, 0); -} - -template <typename T> -unsigned int circular_buffer_t<T>::size() const -{ - if (end >= start) - return end - start; - else - return end + capacity - start; -} - -template <typename T> -void circular_buffer_t<T>::consume(unsigned int bytes) -{ - start = (start + bytes) % capacity; -} - -template <typename T> -unsigned int circular_buffer_t<T>::contiguous_empty_size() const -{ - if (end >= start) - if (start == 0) - return capacity - end - 1; - else - return capacity - end; - else - return start - end - 1; -} - -template <typename T> -unsigned int circular_buffer_t<T>::contiguous_data_size() const -{ - if (end >= start) - return end - start; - else - return capacity - start; -} - -template <typename T> -void circular_buffer_t<T>::data_added(unsigned int bytes) -{ - end += bytes; - assert(end <= capacity); - if (end == capacity) - end = 0; -} - -template <typename T> -void circular_buffer_t<T>::reset() -{ - start = 0; - end = 0; -} - -template <typename T> -void circular_buffer_t<T>::append(const T *src, unsigned int count) -{ - unsigned int copy = std::min(count, contiguous_empty_size()); - memcpy(contiguous_empty(), src, copy * sizeof(T)); - data_added(copy); - count -= copy; - if (count > 0) { - assert(count < contiguous_empty_size()); - memcpy(contiguous_empty(), src+copy, count * sizeof(T)); - data_added(count); - } -} - -////////////////////////////// Debug Operations - -class halt_op_t : public operation_t -{ - public: - halt_op_t(gdbserver_t& gdbserver, bool send_status=false) : - operation_t(gdbserver), send_status(send_status), - state(ST_ENTER) {}; - - void write_dpc_program() { - gs.dr_write32(0, csrsi(CSR_DCSR, DCSR_HALT)); - gs.dr_write32(1, csrr(S0, CSR_DPC)); - gs.dr_write_store(2, S0, SLOT_DATA0); - gs.dr_write_jump(3); - gs.set_interrupt(0); - } - - bool perform_step(unsigned int step) { - switch (state) { - gs.tselect_valid = false; - case ST_ENTER: - if (gs.xlen == 0) { - gs.dr_write32(0, xori(S1, ZERO, -1)); - gs.dr_write32(1, srli(S1, S1, 31)); - // 0x00000001 0x00000001:ffffffff 0x00000001:ffffffff:ffffffff:ffffffff - gs.dr_write32(2, sw(S1, ZERO, DEBUG_RAM_START)); - gs.dr_write32(3, srli(S1, S1, 31)); - // 0x00000000 0x00000000:00000003 0x00000000:00000003:ffffffff:ffffffff - gs.dr_write32(4, sw(S1, ZERO, DEBUG_RAM_START + 4)); - gs.dr_write_jump(5); - gs.set_interrupt(0); - state = ST_XLEN; - - } else { - write_dpc_program(); - state = ST_DPC; - } - return false; - - case ST_XLEN: - { - uint32_t word0 = gs.dr_read32(0); - uint32_t word1 = gs.dr_read32(1); - - if (word0 == 1 && word1 == 0) { - gs.xlen = 32; - } else if (word0 == 0xffffffff && word1 == 3) { - gs.xlen = 64; - } else if (word0 == 0xffffffff && word1 == 0xffffffff) { - gs.xlen = 128; - } - - write_dpc_program(); - state = ST_DPC; - return false; - } - - case ST_DPC: - gs.dpc = gs.dr_read(SLOT_DATA0); - gs.dr_write32(0, csrr(S0, CSR_MSTATUS)); - gs.dr_write_store(1, S0, SLOT_DATA0); - gs.dr_write_jump(2); - gs.set_interrupt(0); - state = ST_MSTATUS; - return false; - - case ST_MSTATUS: - gs.mstatus = gs.dr_read(SLOT_DATA0); - gs.mstatus_dirty = false; - gs.dr_write32(0, csrr(S0, CSR_DCSR)); - gs.dr_write32(1, sw(S0, 0, (uint16_t) DEBUG_RAM_START + 16)); - gs.dr_write_jump(2); - gs.set_interrupt(0); - state = ST_DCSR; - return false; - - case ST_DCSR: - gs.dcsr = gs.dr_read32(4); - - gs.sptbr_valid = false; - gs.pte_cache.clear(); - - if (send_status) { - switch (get_field(gs.dcsr, DCSR_CAUSE)) { - case DCSR_CAUSE_NONE: - fprintf(stderr, "Internal error. Processor halted without reason.\n"); - abort(); - - case DCSR_CAUSE_DEBUGINT: - gs.send_packet("S02"); // Pretend program received SIGINT. - break; - - case DCSR_CAUSE_HWBP: - case DCSR_CAUSE_STEP: - case DCSR_CAUSE_HALT: - // There's no gdb code for this. - gs.send_packet("T05"); - break; - case DCSR_CAUSE_SWBP: - gs.send_packet("T05swbreak:;"); - break; - } - } - - return true; - - default: - assert(0); - } - } - - private: - bool send_status; - enum { - ST_ENTER, - ST_XLEN, - ST_DPC, - ST_MSTATUS, - ST_DCSR - } state; -}; - -class continue_op_t : public operation_t -{ - public: - continue_op_t(gdbserver_t& gdbserver, bool single_step) : - operation_t(gdbserver), single_step(single_step) {}; - - bool perform_step(unsigned int step) { - D(fprintf(stderr, "continue step %d\n", step)); - switch (step) { - case 0: - gs.dr_write_load(0, S0, SLOT_DATA0); - gs.dr_write32(1, csrw(S0, CSR_DPC)); - // TODO: Isn't there a fence.i in Debug ROM already? - if (gs.fence_i_required) { - gs.dr_write32(2, fence_i()); - gs.dr_write_jump(3); - gs.fence_i_required = false; - } else { - gs.dr_write_jump(2); - } - gs.dr_write(SLOT_DATA0, gs.dpc); - gs.set_interrupt(0); - return false; - - case 1: - gs.dr_write_load(0, S0, SLOT_DATA0); - gs.dr_write32(1, csrw(S0, CSR_MSTATUS)); - gs.dr_write_jump(2); - gs.dr_write(SLOT_DATA0, gs.mstatus); - gs.set_interrupt(0); - return false; - - case 2: - gs.dr_write32(0, lw(S0, 0, (uint16_t) DEBUG_RAM_START+16)); - gs.dr_write32(1, csrw(S0, CSR_DCSR)); - gs.dr_write_jump(2); - - reg_t dcsr = set_field(gs.dcsr, DCSR_HALT, 0); - dcsr = set_field(dcsr, DCSR_STEP, single_step); - // Software breakpoints should go here. - dcsr = set_field(dcsr, DCSR_EBREAKM, 1); - dcsr = set_field(dcsr, DCSR_EBREAKH, 1); - dcsr = set_field(dcsr, DCSR_EBREAKS, 1); - dcsr = set_field(dcsr, DCSR_EBREAKU, 1); - gs.dr_write32(4, dcsr); - - gs.set_interrupt(0); - return true; - } - return false; - } - - private: - bool single_step; -}; - -class general_registers_read_op_t : public operation_t -{ - // Register order that gdb expects is: - // "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", - // "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", - // "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", - // "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", - - // Each byte of register data is described by two hex digits. The bytes with - // the register are transmitted in target byte order. The size of each - // register and their position within the ‘g’ packet are determined by the - // gdb internal gdbarch functions DEPRECATED_REGISTER_RAW_SIZE and - // gdbarch_register_name. - - public: - general_registers_read_op_t(gdbserver_t& gdbserver) : - operation_t(gdbserver) {}; - - bool perform_step(unsigned int step) - { - D(fprintf(stderr, "register_read step %d\n", step)); - if (step == 0) { - gs.start_packet(); - - // x0 is always zero. - if (gs.xlen == 32) { - gs.send((uint32_t) 0); - } else { - gs.send((uint64_t) 0); - } - - gs.dr_write_store(0, 1, SLOT_DATA0); - gs.dr_write_store(1, 2, SLOT_DATA1); - gs.dr_write_jump(2); - gs.set_interrupt(0); - return false; - } - - if (gs.xlen == 32) { - gs.send((uint32_t) gs.dr_read(SLOT_DATA0)); - } else { - gs.send((uint64_t) gs.dr_read(SLOT_DATA0)); - } - if (step >= 16) { - gs.end_packet(); - return true; - } - - if (gs.xlen == 32) { - gs.send((uint32_t) gs.dr_read(SLOT_DATA1)); - } else { - gs.send((uint64_t) gs.dr_read(SLOT_DATA1)); - } - - unsigned int current_reg = 2 * step + 1; - unsigned int i = 0; - if (current_reg == S1) { - gs.dr_write_load(i++, S1, SLOT_DATA_LAST); - } - gs.dr_write_store(i++, current_reg, SLOT_DATA0); - if (current_reg + 1 == S0) { - gs.dr_write32(i++, csrr(S0, CSR_DSCRATCH)); - } - if (step < 15) { - gs.dr_write_store(i++, current_reg+1, SLOT_DATA1); - } - gs.dr_write_jump(i); - gs.set_interrupt(0); - - return false; - } -}; - -class register_read_op_t : public operation_t -{ - public: - register_read_op_t(gdbserver_t& gdbserver, unsigned int reg) : - operation_t(gdbserver), reg(reg) {}; - - bool perform_step(unsigned int step) - { - switch (step) { - case 0: - if (reg >= REG_XPR0 && reg <= REG_XPR31) { - unsigned int i = 0; - if (reg == S0) { - gs.dr_write32(i++, csrr(S0, CSR_DSCRATCH)); - } - if (gs.xlen == 32) { - gs.dr_write32(i++, sw(reg - REG_XPR0, 0, (uint16_t) DEBUG_RAM_START + 16)); - } else { - gs.dr_write32(i++, sd(reg - REG_XPR0, 0, (uint16_t) DEBUG_RAM_START + 16)); - } - gs.dr_write_jump(i); - } else if (reg == REG_PC) { - gs.start_packet(); - if (gs.xlen == 32) { - gs.send((uint32_t) gs.dpc); - } else { - gs.send(gs.dpc); - } - gs.end_packet(); - return true; - } else if (reg >= REG_FPR0 && reg <= REG_FPR31) { - gs.dr_write_load(0, S0, SLOT_DATA1); - gs.dr_write(SLOT_DATA1, set_field(gs.mstatus, MSTATUS_FS, 1)); - gs.dr_write32(1, csrw(S0, CSR_MSTATUS)); - gs.mstatus_dirty = true; - if (gs.xlen == 32) { - gs.dr_write32(2, fsw(reg - REG_FPR0, 0, (uint16_t) DEBUG_RAM_START + 16)); - } else { - gs.dr_write32(2, fsd(reg - REG_FPR0, 0, (uint16_t) DEBUG_RAM_START + 16)); - } - gs.dr_write_jump(3); - } else if (reg == REG_MSTATUS) { - gs.start_packet(); - if (gs.xlen == 32) { - gs.send((uint32_t) gs.mstatus); - } else { - gs.send(gs.mstatus); - } - gs.end_packet(); - return true; - } else if (reg >= REG_CSR0 && reg <= REG_CSR4095) { - gs.dr_write32(0, csrr(S0, reg - REG_CSR0)); - gs.dr_write_store(1, S0, SLOT_DATA0); - gs.dr_write_jump(2); - // If we hit an exception reading the CSR, we'll end up returning ~0 as - // the register's value, which is what we want. (Right?) - gs.dr_write(SLOT_DATA0, ~(uint64_t) 0); - } else if (reg == REG_PRIV) { - gs.start_packet(); - gs.send((uint8_t) get_field(gs.dcsr, DCSR_PRV)); - gs.end_packet(); - return true; - } else { - gs.send_packet("E02"); - return true; - } - gs.set_interrupt(0); - return false; - - case 1: - { - unsigned result = gs.dr_read32(DEBUG_RAM_SIZE / 4 - 1); - if (result) { - gs.send_packet("E03"); - return true; - } - gs.start_packet(); - if (gs.xlen == 32) { - gs.send(gs.dr_read32(4)); - } else { - gs.send(gs.dr_read(SLOT_DATA0)); - } - gs.end_packet(); - return true; - } - } - return false; - } - - private: - unsigned int reg; -}; - -class register_write_op_t : public operation_t -{ - public: - register_write_op_t(gdbserver_t& gdbserver, unsigned int reg, reg_t value) : - operation_t(gdbserver), reg(reg), value(value) {}; - - bool perform_step(unsigned int step) - { - switch (step) { - case 0: - gs.dr_write_load(0, S0, SLOT_DATA0); - gs.dr_write(SLOT_DATA0, value); - if (reg == S0) { - gs.dr_write32(1, csrw(S0, CSR_DSCRATCH)); - gs.dr_write_jump(2); - } else if (reg == S1) { - gs.dr_write_store(1, S0, SLOT_DATA_LAST); - gs.dr_write_jump(2); - } else if (reg >= REG_XPR0 && reg <= REG_XPR31) { - gs.dr_write32(1, addi(reg, S0, 0)); - gs.dr_write_jump(2); - } else if (reg == REG_PC) { - gs.dpc = value; - return true; - } else if (reg >= REG_FPR0 && reg <= REG_FPR31) { - gs.dr_write_load(0, S0, SLOT_DATA1); - gs.dr_write(SLOT_DATA1, set_field(gs.mstatus, MSTATUS_FS, 1)); - gs.dr_write32(1, csrw(S0, CSR_MSTATUS)); - gs.mstatus_dirty = true; - if (gs.xlen == 32) { - gs.dr_write32(2, flw(reg - REG_FPR0, 0, (uint16_t) DEBUG_RAM_START + 16)); - } else { - gs.dr_write32(2, fld(reg - REG_FPR0, 0, (uint16_t) DEBUG_RAM_START + 16)); - } - gs.dr_write_jump(3); - } else if (reg == REG_MSTATUS) { - gs.mstatus = value; - gs.mstatus_dirty = true; - return true; - } else if (reg >= REG_CSR0 && reg <= REG_CSR4095) { - gs.dr_write32(1, csrw(S0, reg - REG_CSR0)); - gs.dr_write_jump(2); - if (reg == REG_CSR0 + CSR_SPTBR) { - gs.sptbr = value; - gs.sptbr_valid = true; - } - } else if (reg == REG_PRIV) { - gs.dcsr = set_field(gs.dcsr, DCSR_PRV, value); - return true; - } else { - gs.send_packet("E02"); - return true; - } - gs.set_interrupt(0); - return false; - - case 1: - { - unsigned result = gs.dr_read32(DEBUG_RAM_SIZE / 4 - 1); - if (result) { - gs.send_packet("E03"); - return true; - } - gs.send_packet("OK"); - return true; - } - } - - assert(0); - } - - private: - unsigned int reg; - reg_t value; -}; - -class memory_read_op_t : public operation_t -{ - public: - // Read length bytes from vaddr, storing the result into data. - // If data is NULL, send the result straight to gdb. - memory_read_op_t(gdbserver_t& gdbserver, reg_t vaddr, unsigned int length, - unsigned char *data=NULL) : - operation_t(gdbserver), vaddr(vaddr), length(length), data(data), index(0) - { - buf = new uint8_t[length]; - }; - - ~memory_read_op_t() - { - delete[] buf; - } - - bool perform_step(unsigned int step) - { - if (step == 0) { - // address goes in S0 - paddr = gs.translate(vaddr); - access_size = gs.find_access_size(paddr, length); - - gs.dr_write_load(0, S0, SLOT_DATA0); - switch (access_size) { - case 1: - gs.dr_write32(1, lb(S1, S0, 0)); - break; - case 2: - gs.dr_write32(1, lh(S1, S0, 0)); - break; - case 4: - gs.dr_write32(1, lw(S1, S0, 0)); - break; - case 8: - gs.dr_write32(1, ld(S1, S0, 0)); - break; - } - gs.dr_write_store(2, S1, SLOT_DATA1); - gs.dr_write_jump(3); - gs.dr_write(SLOT_DATA0, paddr); - gs.set_interrupt(0); - - return false; - } - - if (gs.dr_read32(DEBUG_RAM_SIZE / 4 - 1)) { - // Note that OpenOCD doesn't report this error to gdb by default. They - // think it can mess up stack tracing. So far I haven't seen any - // problems. - gs.send_packet("E99"); - return true; - } - - reg_t value = gs.dr_read(SLOT_DATA1); - for (unsigned int i = 0; i < access_size; i++) { - if (data) { - *(data++) = value & 0xff; - D(fprintf(stderr, "%02x", (unsigned int) (value & 0xff))); - } else { - buf[index++] = value & 0xff; - } - value >>= 8; - } - if (data) { - D(fprintf(stderr, "\n")); - } - length -= access_size; - paddr += access_size; - - if (length == 0) { - if (!data) { - gs.start_packet(); - char buffer[3]; - for (unsigned int i = 0; i < index; i++) { - sprintf(buffer, "%02x", (unsigned int) buf[i]); - gs.send(buffer); - } - gs.end_packet(); - } - return true; - } else { - gs.dr_write(SLOT_DATA0, paddr); - gs.set_interrupt(0); - return false; - } - } - - private: - reg_t vaddr; - unsigned int length; - unsigned char* data; - reg_t paddr; - unsigned int access_size; - unsigned int index; - uint8_t *buf; -}; - -class memory_write_op_t : public operation_t -{ - public: - memory_write_op_t(gdbserver_t& gdbserver, reg_t vaddr, unsigned int length, - const unsigned char *data) : - operation_t(gdbserver), vaddr(vaddr), offset(0), length(length), data(data) {}; - - ~memory_write_op_t() { - delete[] data; - } - - bool perform_step(unsigned int step) - { - reg_t paddr = gs.translate(vaddr); - - unsigned int data_offset; - switch (gs.xlen) { - case 32: - data_offset = slot_offset32[SLOT_DATA1]; - break; - case 64: - data_offset = slot_offset64[SLOT_DATA1]; - break; - case 128: - data_offset = slot_offset128[SLOT_DATA1]; - break; - default: - abort(); - } - - if (step == 0) { - access_size = gs.find_access_size(paddr, length); - - D(fprintf(stderr, "write to 0x%" PRIx64 " -> 0x%" PRIx64 " (access=%d): ", - vaddr, paddr, access_size)); - for (unsigned int i = 0; i < length; i++) { - D(fprintf(stderr, "%02x", data[i])); - } - D(fprintf(stderr, "\n")); - - // address goes in S0 - gs.dr_write_load(0, S0, SLOT_DATA0); - switch (access_size) { - case 1: - gs.dr_write32(1, lb(S1, 0, (uint16_t) DEBUG_RAM_START + 4*data_offset)); - gs.dr_write32(2, sb(S1, S0, 0)); - gs.dr_write32(data_offset, data[0]); - break; - case 2: - gs.dr_write32(1, lh(S1, 0, (uint16_t) DEBUG_RAM_START + 4*data_offset)); - gs.dr_write32(2, sh(S1, S0, 0)); - gs.dr_write32(data_offset, data[0] | (data[1] << 8)); - break; - case 4: - gs.dr_write32(1, lw(S1, 0, (uint16_t) DEBUG_RAM_START + 4*data_offset)); - gs.dr_write32(2, sw(S1, S0, 0)); - gs.dr_write32(data_offset, data[0] | (data[1] << 8) | - (data[2] << 16) | (data[3] << 24)); - break; - case 8: - gs.dr_write32(1, ld(S1, 0, (uint16_t) DEBUG_RAM_START + 4*data_offset)); - gs.dr_write32(2, sd(S1, S0, 0)); - gs.dr_write32(data_offset, data[0] | (data[1] << 8) | - (data[2] << 16) | (data[3] << 24)); - gs.dr_write32(data_offset+1, data[4] | (data[5] << 8) | - (data[6] << 16) | (data[7] << 24)); - break; - default: - fprintf(stderr, "gdbserver error: write %d bytes to 0x%016" PRIx64 - " -> 0x%016" PRIx64 "; access_size=%d\n", - length, vaddr, paddr, access_size); - gs.send_packet("E12"); - return true; - } - gs.dr_write_jump(3); - gs.dr_write(SLOT_DATA0, paddr); - gs.set_interrupt(0); - - return false; - } - - if (gs.dr_read32(DEBUG_RAM_SIZE / 4 - 1)) { - gs.send_packet("E98"); - return true; - } - - offset += access_size; - if (offset >= length) { - gs.send_packet("OK"); - return true; - } else { - const unsigned char *d = data + offset; - switch (access_size) { - case 1: - gs.dr_write32(data_offset, d[0]); - break; - case 2: - gs.dr_write32(data_offset, d[0] | (d[1] << 8)); - break; - case 4: - gs.dr_write32(data_offset, d[0] | (d[1] << 8) | - (d[2] << 16) | (d[3] << 24)); - break; - case 8: - gs.dr_write32(data_offset, d[0] | (d[1] << 8) | - (d[2] << 16) | (d[3] << 24)); - gs.dr_write32(data_offset+1, d[4] | (d[5] << 8) | - (d[6] << 16) | (d[7] << 24)); - break; - default: - gs.send_packet("E13"); - return true; - } - gs.dr_write(SLOT_DATA0, paddr + offset); - gs.set_interrupt(0); - return false; - } - } - - private: - reg_t vaddr; - unsigned int offset; - unsigned int length; - unsigned int access_size; - const unsigned char *data; -}; - -class collect_translation_info_op_t : public operation_t -{ - public: - // Read sufficient information from the target into gdbserver structures so - // that it's possible to translate vaddr, vaddr+length, and all addresses - // in between to physical addresses. - collect_translation_info_op_t(gdbserver_t& gdbserver, reg_t vaddr, size_t length) : - operation_t(gdbserver), state(STATE_START), vaddr(vaddr), length(length) {}; - - bool perform_step(unsigned int step) - { - unsigned int vm = gs.virtual_memory(); - - if (step == 0) { - switch (vm) { - case VM_MBARE: - // Nothing to be done. - return true; - - case VM_SV32: - levels = 2; - ptidxbits = 10; - ptesize = 4; - break; - case VM_SV39: - levels = 3; - ptidxbits = 9; - ptesize = 8; - break; - case VM_SV48: - levels = 4; - ptidxbits = 9; - ptesize = 8; - break; - - default: - { - char buf[100]; - sprintf(buf, "VM mode %d is not supported by gdbserver.cc.", vm); - die(buf); - return true; // die doesn't return, but gcc doesn't know that. - } - } - } - - // Perform any reads from the just-completed action. - switch (state) { - case STATE_START: - break; - case STATE_READ_SPTBR: - gs.sptbr = gs.dr_read(SLOT_DATA0); - gs.sptbr_valid = true; - break; - case STATE_READ_PTE: - if (ptesize == 4) { - gs.pte_cache[pte_addr] = gs.dr_read32(4); - } else { - gs.pte_cache[pte_addr] = ((uint64_t) gs.dr_read32(5) << 32) | - gs.dr_read32(4); - } - D(fprintf(stderr, "pte_cache[0x%" PRIx64 "] = 0x%" PRIx64 "\n", pte_addr, - gs.pte_cache[pte_addr])); - break; - } - - // Set up the next action. - // We only get here for VM_SV32/39/38. - - if (!gs.sptbr_valid) { - state = STATE_READ_SPTBR; - gs.dr_write32(0, csrr(S0, CSR_SPTBR)); - gs.dr_write_store(1, S0, SLOT_DATA0); - gs.dr_write_jump(2); - gs.set_interrupt(0); - return false; - } - - reg_t base = gs.sptbr << PGSHIFT; - int ptshift = (levels - 1) * ptidxbits; - for (unsigned int i = 0; i < levels; i++, ptshift -= ptidxbits) { - reg_t idx = (vaddr >> (PGSHIFT + ptshift)) & ((1 << ptidxbits) - 1); - - pte_addr = base + idx * ptesize; - auto it = gs.pte_cache.find(pte_addr); - if (it == gs.pte_cache.end()) { - state = STATE_READ_PTE; - if (ptesize == 4) { - gs.dr_write32(0, lw(S0, 0, (uint16_t) DEBUG_RAM_START + 16)); - gs.dr_write32(1, lw(S1, S0, 0)); - gs.dr_write32(2, sw(S1, 0, (uint16_t) DEBUG_RAM_START + 16)); - } else { - assert(gs.xlen >= 64); - gs.dr_write32(0, ld(S0, 0, (uint16_t) DEBUG_RAM_START + 16)); - gs.dr_write32(1, ld(S1, S0, 0)); - gs.dr_write32(2, sd(S1, 0, (uint16_t) DEBUG_RAM_START + 16)); - } - gs.dr_write_jump(3); - gs.dr_write32(4, pte_addr); - gs.dr_write32(5, pte_addr >> 32); - gs.set_interrupt(0); - return false; - } - - reg_t pte = gs.pte_cache[pte_addr]; - reg_t ppn = pte >> PTE_PPN_SHIFT; - - if (PTE_TABLE(pte)) { // next level of page table - base = ppn << PGSHIFT; - } else { - // We've collected all the data required for the translation. - return true; - } - } - fprintf(stderr, - "ERROR: gdbserver couldn't find appropriate PTEs to translate 0x%016" PRIx64 "\n", - vaddr); - return true; - } - - private: - enum { - STATE_START, - STATE_READ_SPTBR, - STATE_READ_PTE - } state; - reg_t vaddr; - size_t length; - unsigned int levels; - unsigned int ptidxbits; - unsigned int ptesize; - reg_t pte_addr; -}; - -class hardware_breakpoint_insert_op_t : public operation_t -{ - public: - hardware_breakpoint_insert_op_t(gdbserver_t& gdbserver, - hardware_breakpoint_t bp) : - operation_t(gdbserver), state(STATE_START), bp(bp) {}; - - void write_new_index_program() - { - gs.dr_write_load(0, S0, SLOT_DATA1); - gs.dr_write32(1, csrw(S0, CSR_TSELECT)); - gs.dr_write32(2, csrr(S0, CSR_TSELECT)); - gs.dr_write_store(3, S0, SLOT_DATA1); - gs.dr_write_jump(4); - gs.dr_write(SLOT_DATA1, bp.index); - } - - bool perform_step(unsigned int step) - { - switch (state) { - case STATE_START: - bp.index = 0; - write_new_index_program(); - state = STATE_CHECK_INDEX; - break; - - case STATE_CHECK_INDEX: - if (gs.dr_read(SLOT_DATA1) != bp.index) { - // We've exhausted breakpoints without finding an appropriate one. - gs.send_packet("E58"); - return true; - } - - gs.dr_write32(0, csrr(S0, CSR_TDATA1)); - gs.dr_write_store(1, S0, SLOT_DATA0); - gs.dr_write_jump(2); - state = STATE_CHECK_MCONTROL; - break; - - case STATE_CHECK_MCONTROL: - { - reg_t mcontrol = gs.dr_read(SLOT_DATA0); - unsigned int type = mcontrol >> (gs.xlen - 4); - if (type == 0) { - // We've exhausted breakpoints without finding an appropriate one. - gs.send_packet("E58"); - return true; - } - - if (type == 2 && - !get_field(mcontrol, MCONTROL_EXECUTE) && - !get_field(mcontrol, MCONTROL_LOAD) && - !get_field(mcontrol, MCONTROL_STORE)) { - // Found an unused trigger. - gs.dr_write_load(0, S0, SLOT_DATA1); - gs.dr_write32(1, csrw(S0, CSR_TDATA1)); - gs.dr_write_jump(2); - mcontrol = set_field(0, MCONTROL_ACTION, MCONTROL_ACTION_DEBUG_MODE); - mcontrol = set_field(mcontrol, MCONTROL_DMODE(gs.xlen), 1); - mcontrol = set_field(mcontrol, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL); - mcontrol = set_field(mcontrol, MCONTROL_M, 1); - mcontrol = set_field(mcontrol, MCONTROL_H, 1); - mcontrol = set_field(mcontrol, MCONTROL_S, 1); - mcontrol = set_field(mcontrol, MCONTROL_U, 1); - mcontrol = set_field(mcontrol, MCONTROL_EXECUTE, bp.execute); - mcontrol = set_field(mcontrol, MCONTROL_LOAD, bp.load); - mcontrol = set_field(mcontrol, MCONTROL_STORE, bp.store); - // For store triggers it's nicer to fire just before the - // instruction than just after. However, gdb doesn't clear the - // breakpoints and step before resuming from a store trigger. - // That means that without extra code, you'll keep hitting the - // same watchpoint over and over again. That's not useful at all. - // Instead of fixing this the right way, just set timing=1 for - // those triggers. - if (bp.load || bp.store) - mcontrol = set_field(mcontrol, MCONTROL_TIMING, 1); - - gs.dr_write(SLOT_DATA1, mcontrol); - state = STATE_WRITE_ADDRESS; - } else { - bp.index++; - write_new_index_program(); - state = STATE_CHECK_INDEX; - } - } - break; - - case STATE_WRITE_ADDRESS: - { - gs.dr_write_load(0, S0, SLOT_DATA1); - gs.dr_write32(1, csrw(S0, CSR_TDATA2)); - gs.dr_write_jump(2); - gs.dr_write(SLOT_DATA1, bp.vaddr); - gs.set_interrupt(0); - gs.send_packet("OK"); - - gs.hardware_breakpoints.insert(bp); - - return true; - } - } - - gs.set_interrupt(0); - return false; - } - - private: - enum { - STATE_START, - STATE_CHECK_INDEX, - STATE_CHECK_MCONTROL, - STATE_WRITE_ADDRESS - } state; - hardware_breakpoint_t bp; -}; - -class maybe_save_tselect_op_t : public operation_t -{ - public: - maybe_save_tselect_op_t(gdbserver_t& gdbserver) : operation_t(gdbserver) {}; - bool perform_step(unsigned int step) { - if (gs.tselect_valid) - return true; - - switch (step) { - case 0: - gs.dr_write32(0, csrr(S0, CSR_TDATA1)); - gs.dr_write_store(1, S0, SLOT_DATA0); - gs.dr_write_jump(2); - gs.set_interrupt(0); - return false; - case 1: - gs.tselect = gs.dr_read(SLOT_DATA0); - gs.tselect_valid = true; - break; - } - return true; - } -}; - -class maybe_restore_tselect_op_t : public operation_t -{ - public: - maybe_restore_tselect_op_t(gdbserver_t& gdbserver) : operation_t(gdbserver) {}; - bool perform_step(unsigned int step) { - if (gs.tselect_valid) { - gs.dr_write_load(0, S0, SLOT_DATA1); - gs.dr_write32(1, csrw(S0, CSR_TSELECT)); - gs.dr_write_jump(2); - gs.dr_write(SLOT_DATA1, gs.tselect); - } - return true; - } -}; - -class maybe_restore_mstatus_op_t : public operation_t -{ - public: - maybe_restore_mstatus_op_t(gdbserver_t& gdbserver) : operation_t(gdbserver) {}; - bool perform_step(unsigned int step) { - if (gs.mstatus_dirty) { - gs.dr_write_load(0, S0, SLOT_DATA1); - gs.dr_write32(1, csrw(S0, CSR_MSTATUS)); - gs.dr_write_jump(2); - gs.dr_write(SLOT_DATA1, gs.mstatus); - } - return true; - } -}; - -class hardware_breakpoint_remove_op_t : public operation_t -{ - public: - hardware_breakpoint_remove_op_t(gdbserver_t& gdbserver, - hardware_breakpoint_t bp) : - operation_t(gdbserver), bp(bp) {}; - - bool perform_step(unsigned int step) { - gs.dr_write32(0, addi(S0, ZERO, bp.index)); - gs.dr_write32(1, csrw(S0, CSR_TSELECT)); - gs.dr_write32(2, csrw(ZERO, CSR_TDATA1)); - gs.dr_write_jump(3); - gs.set_interrupt(0); - return true; - } - - private: - hardware_breakpoint_t bp; -}; - -////////////////////////////// gdbserver itself - -gdbserver_t::gdbserver_t(uint16_t port, sim_t *sim) : - xlen(0), - sim(sim), - client_fd(0), - // gdb likes to send 0x100000 bytes at once when downloading. - recv_buf(0x180000), send_buf(64 * 1024) -{ - socket_fd = socket(AF_INET, SOCK_STREAM, 0); - if (socket_fd == -1) { - fprintf(stderr, "failed to make socket: %s (%d)\n", strerror(errno), errno); - abort(); - } - - fcntl(socket_fd, F_SETFL, O_NONBLOCK); - int reuseaddr = 1; - if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, - sizeof(int)) == -1) { - fprintf(stderr, "failed setsockopt: %s (%d)\n", strerror(errno), errno); - abort(); - } - - struct sockaddr_in addr; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = htons(port); - - if (bind(socket_fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { - fprintf(stderr, "failed to bind socket: %s (%d)\n", strerror(errno), errno); - abort(); - } - - if (listen(socket_fd, 1) == -1) { - fprintf(stderr, "failed to listen on socket: %s (%d)\n", strerror(errno), errno); - abort(); - } -} - -unsigned int gdbserver_t::find_access_size(reg_t address, int length) -{ - reg_t composite = address | length; - if ((composite & 0x7) == 0 && xlen >= 64) - return 8; - if ((composite & 0x3) == 0) - return 4; - return 1; -} - -reg_t gdbserver_t::translate(reg_t vaddr) -{ - unsigned int vm = virtual_memory(); - unsigned int levels, ptidxbits, ptesize; - - switch (vm) { - case VM_MBARE: - return vaddr; - - case VM_SV32: - levels = 2; - ptidxbits = 10; - ptesize = 4; - break; - case VM_SV39: - levels = 3; - ptidxbits = 9; - ptesize = 8; - break; - case VM_SV48: - levels = 4; - ptidxbits = 9; - ptesize = 8; - break; - - default: - { - char buf[100]; - sprintf(buf, "VM mode %d is not supported by gdbserver.cc.", vm); - die(buf); - return true; // die doesn't return, but gcc doesn't know that. - } - } - - // Handle page tables here. There's a bunch of duplicated code with - // collect_translation_info_op_t. :-( - reg_t base = sptbr << PGSHIFT; - int ptshift = (levels - 1) * ptidxbits; - for (unsigned int i = 0; i < levels; i++, ptshift -= ptidxbits) { - reg_t idx = (vaddr >> (PGSHIFT + ptshift)) & ((1 << ptidxbits) - 1); - - reg_t pte_addr = base + idx * ptesize; - auto it = pte_cache.find(pte_addr); - if (it == pte_cache.end()) { - fprintf(stderr, "ERROR: gdbserver tried to translate 0x%016" PRIx64 - " without first collecting the relevant PTEs.\n", vaddr); - die("gdbserver_t::translate()"); - } - - reg_t pte = pte_cache[pte_addr]; - reg_t ppn = pte >> PTE_PPN_SHIFT; - - if (PTE_TABLE(pte)) { // next level of page table - base = ppn << PGSHIFT; - } else { - // We've collected all the data required for the translation. - reg_t vpn = vaddr >> PGSHIFT; - reg_t paddr = (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT; - paddr += vaddr & (PGSIZE-1); - D(fprintf(stderr, "gdbserver translate 0x%" PRIx64 " -> 0x%" PRIx64 "\n", vaddr, paddr)); - return paddr; - } - } - - fprintf(stderr, "ERROR: gdbserver tried to translate 0x%016" PRIx64 - " but the relevant PTEs are invalid.\n", vaddr); - // TODO: Is it better to throw an exception here? - return -1; -} - -unsigned int gdbserver_t::privilege_mode() -{ - unsigned int mode = get_field(dcsr, DCSR_PRV); - if (get_field(mstatus, MSTATUS_MPRV)) - mode = get_field(mstatus, MSTATUS_MPP); - return mode; -} - -unsigned int gdbserver_t::virtual_memory() -{ - unsigned int mode = privilege_mode(); - if (mode == PRV_M) - return VM_MBARE; - return get_field(mstatus, MSTATUS_VM); -} - -void gdbserver_t::dr_write32(unsigned int index, uint32_t value) -{ - sim->debug_module.ram_write32(index, value); -} - -void gdbserver_t::dr_write64(unsigned int index, uint64_t value) -{ - dr_write32(index, value); - dr_write32(index+1, value >> 32); -} - -void gdbserver_t::dr_write(enum slot slot, uint64_t value) -{ - switch (xlen) { - case 32: - dr_write32(slot_offset32[slot], value); - break; - case 64: - dr_write64(slot_offset64[slot], value); - break; - case 128: - default: - abort(); - } -} - -void gdbserver_t::dr_write_jump(unsigned int index) -{ - dr_write32(index, jal(0, - (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*index)))); -} - -void gdbserver_t::dr_write_store(unsigned int index, unsigned int reg, enum slot slot) -{ - assert(slot != SLOT_INST0 || index > 2); - assert(slot != SLOT_DATA0 || index < 4 || index > 6); - assert(slot != SLOT_DATA1 || index < 5 || index > 10); - assert(slot != SLOT_DATA_LAST || index < 6 || index > 14); - switch (xlen) { - case 32: - return dr_write32(index, - sw(reg, 0, (uint16_t) DEBUG_RAM_START + 4 * slot_offset32[slot])); - case 64: - return dr_write32(index, - sd(reg, 0, (uint16_t) DEBUG_RAM_START + 4 * slot_offset64[slot])); - case 128: - return dr_write32(index, - sq(reg, 0, (uint16_t) DEBUG_RAM_START + 4 * slot_offset128[slot])); - default: - fprintf(stderr, "xlen is %d!\n", xlen); - abort(); - } -} - -void gdbserver_t::dr_write_load(unsigned int index, unsigned int reg, enum slot slot) -{ - switch (xlen) { - case 32: - return dr_write32(index, - lw(reg, 0, (uint16_t) DEBUG_RAM_START + 4 * slot_offset32[slot])); - case 64: - return dr_write32(index, - ld(reg, 0, (uint16_t) DEBUG_RAM_START + 4 * slot_offset64[slot])); - case 128: - return dr_write32(index, - lq(reg, 0, (uint16_t) DEBUG_RAM_START + 4 * slot_offset128[slot])); - default: - fprintf(stderr, "xlen is %d!\n", xlen); - abort(); - } -} - -uint32_t gdbserver_t::dr_read32(unsigned int index) -{ - uint32_t value = sim->debug_module.ram_read32(index); - D(fprintf(stderr, "read32(%d) -> 0x%x\n", index, value)); - return value; -} - -uint64_t gdbserver_t::dr_read64(unsigned int index) -{ - return ((uint64_t) dr_read32(index+1) << 32) | dr_read32(index); -} - -uint64_t gdbserver_t::dr_read(enum slot slot) -{ - switch (xlen) { - case 32: - return dr_read32(slot_offset32[slot]); - case 64: - return dr_read64(slot_offset64[slot]); - case 128: - abort(); - default: - abort(); - } -} - -void gdbserver_t::add_operation(operation_t* operation) -{ - operation_queue.push(operation); -} - -void gdbserver_t::accept() -{ - client_fd = ::accept(socket_fd, NULL, NULL); - if (client_fd == -1) { - if (errno == EAGAIN) { - // No client waiting to connect right now. - } else { - fprintf(stderr, "failed to accept on socket: %s (%d)\n", strerror(errno), - errno); - abort(); - } - } else { - fcntl(client_fd, F_SETFL, O_NONBLOCK); - - expect_ack = false; - extended_mode = false; - - // gdb wants the core to be halted when it attaches. - add_operation(new halt_op_t(*this)); - } -} - -void gdbserver_t::read() -{ - // Reading from a non-blocking socket still blocks if there is no data - // available. - - size_t count = recv_buf.contiguous_empty_size(); - ssize_t bytes = ::read(client_fd, recv_buf.contiguous_empty(), count); - if (bytes == -1) { - if (errno == EAGAIN) { - // We'll try again the next call. - } else { - fprintf(stderr, "failed to read on socket: %s (%d)\n", strerror(errno), errno); - abort(); - } - } else if (bytes == 0) { - // The remote disconnected. - client_fd = 0; - processor_t *p = sim->get_core(0); - // TODO p->set_halted(false, HR_NONE); - recv_buf.reset(); - send_buf.reset(); - } else { - recv_buf.data_added(bytes); - } -} - -void gdbserver_t::write() -{ - if (send_buf.empty()) - return; - - while (!send_buf.empty()) { - unsigned int count = send_buf.contiguous_data_size(); - assert(count > 0); - ssize_t bytes = ::write(client_fd, send_buf.contiguous_data(), count); - if (bytes == -1) { - fprintf(stderr, "failed to write to socket: %s (%d)\n", strerror(errno), errno); - abort(); - } else if (bytes == 0) { - // Client can't take any more data right now. - break; - } else { - D(fprintf(stderr, "wrote %zd bytes: ", bytes)); - for (int i = 0; i < bytes; i++) { - D(fprintf(stderr, "%c", send_buf[i])); - } - D(fprintf(stderr, "\n")); - send_buf.consume(bytes); - } - } -} - -void print_packet(const std::vector<uint8_t> &packet) -{ - for (uint8_t c : packet) { - if (c >= ' ' and c <= '~') - fprintf(stderr, "%c", c); - else - fprintf(stderr, "\\x%02x", c); - } - fprintf(stderr, "\n"); -} - -uint8_t compute_checksum(const std::vector<uint8_t> &packet) -{ - uint8_t checksum = 0; - for (auto i = packet.begin() + 1; i != packet.end() - 3; i++ ) { - checksum += *i; - } - return checksum; -} - -uint8_t character_hex_value(uint8_t character) -{ - if (character >= '0' && character <= '9') - return character - '0'; - if (character >= 'a' && character <= 'f') - return 10 + character - 'a'; - if (character >= 'A' && character <= 'F') - return 10 + character - 'A'; - return 0xff; -} - -uint8_t extract_checksum(const std::vector<uint8_t> &packet) -{ - return character_hex_value(*(packet.end() - 1)) + - 16 * character_hex_value(*(packet.end() - 2)); -} - -void gdbserver_t::process_requests() -{ - // See https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html - - while (!recv_buf.empty()) { - std::vector<uint8_t> packet; - for (unsigned int i = 0; i < recv_buf.size(); i++) { - uint8_t b = recv_buf[i]; - - if (packet.empty() && expect_ack && b == '+') { - recv_buf.consume(1); - break; - } - - if (packet.empty() && b == 3) { - D(fprintf(stderr, "Received interrupt\n")); - recv_buf.consume(1); - handle_interrupt(); - break; - } - - if (b == '$') { - // Start of new packet. - if (!packet.empty()) { - fprintf(stderr, "Received malformed %zd-byte packet from debug client: ", - packet.size()); - print_packet(packet); - recv_buf.consume(i); - break; - } - } - - packet.push_back(b); - - // Packets consist of $<packet-data>#<checksum> - // where <checksum> is - if (packet.size() >= 4 && - packet[packet.size()-3] == '#') { - handle_packet(packet); - recv_buf.consume(i+1); - break; - } - } - // There's a partial packet in the buffer. Wait until we get more data to - // process it. - if (packet.size()) { - break; - } - } - - if (recv_buf.full()) { - fprintf(stderr, - "Receive buffer is full, but no complete packet was found!\n"); - for (unsigned line = 0; line < 8; line++) { - for (unsigned i = 0; i < 16; i++) { - fprintf(stderr, "%02x ", recv_buf.entry(line * 16 + i)); - } - for (unsigned i = 0; i < 16; i++) { - uint8_t e = recv_buf.entry(line * 16 + i); - if (e >= ' ' && e <= '~') - fprintf(stderr, "%c", e); - else - fprintf(stderr, "."); - } - fprintf(stderr, "\n"); - } - assert(!recv_buf.full()); - } -} - -void gdbserver_t::handle_halt_reason(const std::vector<uint8_t> &packet) -{ - send_packet("S00"); -} - -void gdbserver_t::handle_general_registers_read(const std::vector<uint8_t> &packet) -{ - add_operation(new general_registers_read_op_t(*this)); -} - -void gdbserver_t::set_interrupt(uint32_t hartid) { - sim->debug_module.set_interrupt(hartid); -} - -// First byte is the most-significant one. -// Eg. "08675309" becomes 0x08675309. -uint64_t consume_hex_number(std::vector<uint8_t>::const_iterator &iter, - std::vector<uint8_t>::const_iterator end) -{ - uint64_t value = 0; - - while (iter != end) { - uint8_t c = *iter; - uint64_t c_value = character_hex_value(c); - if (c_value > 15) - break; - iter++; - value <<= 4; - value += c_value; - } - return value; -} - -// First byte is the least-significant one. -// Eg. "08675309" becomes 0x09536708 -uint64_t gdbserver_t::consume_hex_number_le( - std::vector<uint8_t>::const_iterator &iter, - std::vector<uint8_t>::const_iterator end) -{ - uint64_t value = 0; - unsigned int shift = 4; - - while (iter != end) { - uint8_t c = *iter; - uint64_t c_value = character_hex_value(c); - if (c_value > 15) - break; - iter++; - value |= c_value << shift; - if ((shift % 8) == 0) - shift += 12; - else - shift -= 4; - } - if (shift > (xlen+4)) { - fprintf(stderr, - "gdb sent too many data bytes. That means it thinks XLEN is greater " - "than %d.\nTo fix that, tell gdb: set arch riscv:rv%d\n", - xlen, xlen); - } - return value; -} - -void consume_string(std::string &str, std::vector<uint8_t>::const_iterator &iter, - std::vector<uint8_t>::const_iterator end, uint8_t separator) -{ - while (iter != end && *iter != separator) { - str.append(1, (char) *iter); - iter++; - } -} - -void gdbserver_t::handle_register_read(const std::vector<uint8_t> &packet) -{ - // p n - - std::vector<uint8_t>::const_iterator iter = packet.begin() + 2; - unsigned int n = consume_hex_number(iter, packet.end()); - if (*iter != '#') - return send_packet("E01"); - - add_operation(new register_read_op_t(*this, n)); -} - -void gdbserver_t::handle_register_write(const std::vector<uint8_t> &packet) -{ - // P n...=r... - - std::vector<uint8_t>::const_iterator iter = packet.begin() + 2; - unsigned int n = consume_hex_number(iter, packet.end()); - if (*iter != '=') - return send_packet("E05"); - iter++; - - reg_t value = consume_hex_number_le(iter, packet.end()); - if (*iter != '#') - return send_packet("E06"); - - processor_t *p = sim->get_core(0); - - add_operation(new register_write_op_t(*this, n, value)); -} - -void gdbserver_t::handle_memory_read(const std::vector<uint8_t> &packet) -{ - // m addr,length - std::vector<uint8_t>::const_iterator iter = packet.begin() + 2; - reg_t address = consume_hex_number(iter, packet.end()); - if (*iter != ',') - return send_packet("E10"); - iter++; - reg_t length = consume_hex_number(iter, packet.end()); - if (*iter != '#') - return send_packet("E11"); - - add_operation(new collect_translation_info_op_t(*this, address, length)); - add_operation(new memory_read_op_t(*this, address, length)); -} - -void gdbserver_t::handle_memory_binary_write(const std::vector<uint8_t> &packet) -{ - // X addr,length:XX... - std::vector<uint8_t>::const_iterator iter = packet.begin() + 2; - reg_t address = consume_hex_number(iter, packet.end()); - if (*iter != ',') - return send_packet("E20"); - iter++; - reg_t length = consume_hex_number(iter, packet.end()); - if (*iter != ':') - return send_packet("E21"); - iter++; - - if (length == 0) { - return send_packet("OK"); - } - - unsigned char *data = new unsigned char[length]; - for (unsigned int i = 0; i < length; i++) { - if (iter == packet.end()) { - return send_packet("E22"); - } - uint8_t c = *iter; - iter++; - if (c == '}') { - // The binary data representation uses 7d (ascii ‘}’) as an escape - // character. Any escaped byte is transmitted as the escape character - // followed by the original character XORed with 0x20. For example, the - // byte 0x7d would be transmitted as the two bytes 0x7d 0x5d. The bytes - // 0x23 (ascii ‘#’), 0x24 (ascii ‘$’), and 0x7d (ascii ‘}’) must always - // be escaped. - if (iter == packet.end()) { - return send_packet("E23"); - } - c = (*iter) ^ 0x20; - iter++; - } - data[i] = c; - } - if (*iter != '#') - return send_packet("E4b"); // EOVERFLOW - - add_operation(new collect_translation_info_op_t(*this, address, length)); - add_operation(new memory_write_op_t(*this, address, length, data)); -} - -void gdbserver_t::handle_continue(const std::vector<uint8_t> &packet) -{ - // c [addr] - processor_t *p = sim->get_core(0); - if (packet[2] != '#') { - std::vector<uint8_t>::const_iterator iter = packet.begin() + 2; - dpc = consume_hex_number(iter, packet.end()); - if (*iter != '#') - return send_packet("E30"); - } - - add_operation(new maybe_restore_tselect_op_t(*this)); - add_operation(new maybe_restore_mstatus_op_t(*this)); - add_operation(new continue_op_t(*this, false)); -} - -void gdbserver_t::handle_step(const std::vector<uint8_t> &packet) -{ - // s [addr] - if (packet[2] != '#') { - std::vector<uint8_t>::const_iterator iter = packet.begin() + 2; - die("handle_step"); - //p->state.pc = consume_hex_number(iter, packet.end()); - if (*iter != '#') - return send_packet("E40"); - } - - add_operation(new maybe_restore_tselect_op_t(*this)); - add_operation(new continue_op_t(*this, true)); -} - -void gdbserver_t::handle_kill(const std::vector<uint8_t> &packet) -{ - // k - // The exact effect of this packet is not specified. - // Looks like OpenOCD disconnects? - // TODO -} - -void gdbserver_t::handle_extended(const std::vector<uint8_t> &packet) -{ - // Enable extended mode. In extended mode, the remote server is made - // persistent. The ‘R’ packet is used to restart the program being debugged. - send_packet("OK"); - extended_mode = true; -} - -void gdbserver_t::software_breakpoint_insert(reg_t vaddr, unsigned int size) -{ - fence_i_required = true; - add_operation(new collect_translation_info_op_t(*this, vaddr, size)); - unsigned char* inst = new unsigned char[4]; - if (size == 2) { - inst[0] = MATCH_C_EBREAK & 0xff; - inst[1] = (MATCH_C_EBREAK >> 8) & 0xff; - } else { - inst[0] = MATCH_EBREAK & 0xff; - inst[1] = (MATCH_EBREAK >> 8) & 0xff; - inst[2] = (MATCH_EBREAK >> 16) & 0xff; - inst[3] = (MATCH_EBREAK >> 24) & 0xff; - } - - software_breakpoint_t bp = { - .vaddr = vaddr, - .size = size - }; - software_breakpoints[vaddr] = bp; - add_operation(new memory_read_op_t(*this, bp.vaddr, bp.size, - software_breakpoints[bp.vaddr].instruction)); - add_operation(new memory_write_op_t(*this, bp.vaddr, bp.size, inst)); -} - -void gdbserver_t::software_breakpoint_remove(reg_t vaddr, unsigned int size) -{ - fence_i_required = true; - add_operation(new collect_translation_info_op_t(*this, vaddr, size)); - - software_breakpoint_t found_bp = software_breakpoints[vaddr]; - unsigned char* instruction = new unsigned char[4]; - memcpy(instruction, found_bp.instruction, 4); - add_operation(new memory_write_op_t(*this, found_bp.vaddr, - found_bp.size, instruction)); - software_breakpoints.erase(vaddr); -} - -void gdbserver_t::hardware_breakpoint_insert(const hardware_breakpoint_t &bp) -{ - add_operation(new maybe_save_tselect_op_t(*this)); - add_operation(new hardware_breakpoint_insert_op_t(*this, bp)); -} - -void gdbserver_t::hardware_breakpoint_remove(const hardware_breakpoint_t &bp) -{ - add_operation(new maybe_save_tselect_op_t(*this)); - hardware_breakpoint_t found = *hardware_breakpoints.find(bp); - add_operation(new hardware_breakpoint_remove_op_t(*this, found)); -} - -void gdbserver_t::handle_breakpoint(const std::vector<uint8_t> &packet) -{ - // insert: Z type,addr,length - // remove: z type,addr,length - - // type: 0 - software breakpoint, 1 - hardware breakpoint, 2 - write - // watchpoint, 3 - read watchpoint, 4 - access watchpoint; addr is address; - // length is in bytes. For a software breakpoint, length specifies the size - // of the instruction to be patched. For hardware breakpoints and watchpoints - // length specifies the memory region to be monitored. To avoid potential - // problems with duplicate packets, the operations should be implemented in - // an idempotent way. - - bool insert = (packet[1] == 'Z'); - std::vector<uint8_t>::const_iterator iter = packet.begin() + 2; - gdb_breakpoint_type_t type = static_cast<gdb_breakpoint_type_t>( - consume_hex_number(iter, packet.end())); - if (*iter != ',') - return send_packet("E50"); - iter++; - reg_t address = consume_hex_number(iter, packet.end()); - if (*iter != ',') - return send_packet("E51"); - iter++; - unsigned int size = consume_hex_number(iter, packet.end()); - // There may be more options after a ; here, but we don't support that. - if (*iter != '#') - return send_packet("E52"); - - switch (type) { - case GB_SOFTWARE: - if (size != 2 && size != 4) { - return send_packet("E53"); - } - if (insert) { - software_breakpoint_insert(address, size); - } else { - software_breakpoint_remove(address, size); - } - break; - - case GB_HARDWARE: - case GB_WRITE: - case GB_READ: - case GB_ACCESS: - { - hardware_breakpoint_t bp = { - .vaddr = address, - .size = size - }; - bp.load = (type == GB_READ || type == GB_ACCESS); - bp.store = (type == GB_WRITE || type == GB_ACCESS); - bp.execute = (type == GB_HARDWARE || type == GB_ACCESS); - if (insert) { - hardware_breakpoint_insert(bp); - // Insert might fail if there's no space, so the insert operation will - // send its own OK (or not). - return; - } else { - hardware_breakpoint_remove(bp); - } - } - break; - - default: - return send_packet("E56"); - } - - return send_packet("OK"); -} - -void gdbserver_t::handle_query(const std::vector<uint8_t> &packet) -{ - std::string name; - std::vector<uint8_t>::const_iterator iter = packet.begin() + 2; - - consume_string(name, iter, packet.end(), ':'); - if (iter != packet.end()) - iter++; - if (name == "Supported") { - start_packet(); - while (iter != packet.end()) { - std::string feature; - consume_string(feature, iter, packet.end(), ';'); - if (iter != packet.end()) - iter++; - if (feature == "swbreak+") { - send("swbreak+;"); - } - } - send("PacketSize=131072;"); - return end_packet(); - } - - D(fprintf(stderr, "Unsupported query %s\n", name.c_str())); - return send_packet(""); -} - -void gdbserver_t::handle_packet(const std::vector<uint8_t> &packet) -{ - if (compute_checksum(packet) != extract_checksum(packet)) { - fprintf(stderr, "Received %zd-byte packet with invalid checksum\n", packet.size()); - fprintf(stderr, "Computed checksum: %x\n", compute_checksum(packet)); - print_packet(packet); - send("-"); - return; - } - - D(fprintf(stderr, "Received %zd-byte packet from debug client: ", packet.size())); - D(print_packet(packet)); - send("+"); - - switch (packet[1]) { - case '!': - return handle_extended(packet); - case '?': - return handle_halt_reason(packet); - case 'g': - return handle_general_registers_read(packet); -// case 'k': -// return handle_kill(packet); - case 'm': - return handle_memory_read(packet); -// case 'M': -// return handle_memory_write(packet); - case 'X': - return handle_memory_binary_write(packet); - case 'p': - return handle_register_read(packet); - case 'P': - return handle_register_write(packet); - case 'c': - return handle_continue(packet); - case 's': - return handle_step(packet); - case 'z': - case 'Z': - return handle_breakpoint(packet); - case 'q': - case 'Q': - return handle_query(packet); - } - - // Not supported. - D(fprintf(stderr, "** Unsupported packet: ")); - D(print_packet(packet)); - send_packet(""); -} - -void gdbserver_t::handle_interrupt() -{ - processor_t *p = sim->get_core(0); - add_operation(new halt_op_t(*this, true)); -} - -void gdbserver_t::handle() -{ - if (client_fd > 0) { - processor_t *p = sim->get_core(0); - - bool interrupt = sim->debug_module.get_interrupt(0); - - if (!interrupt && !operation_queue.empty()) { - operation_t *operation = operation_queue.front(); - if (operation->step()) { - operation_queue.pop(); - delete operation; - } - } - - bool halt_notification = sim->debug_module.get_halt_notification(0); - if (halt_notification) { - sim->debug_module.clear_halt_notification(0); - add_operation(new halt_op_t(*this, true)); - } - - this->read(); - this->write(); - - } else { - this->accept(); - } - - if (operation_queue.empty()) { - this->process_requests(); - } -} - -void gdbserver_t::send(const char* msg) -{ - unsigned int length = strlen(msg); - for (const char *c = msg; *c; c++) - running_checksum += *c; - send_buf.append((const uint8_t *) msg, length); -} - -void gdbserver_t::send(uint64_t value) -{ - char buffer[3]; - for (unsigned int i = 0; i < 8; i++) { - sprintf(buffer, "%02x", (int) (value & 0xff)); - send(buffer); - value >>= 8; - } -} - -void gdbserver_t::send(uint32_t value) -{ - char buffer[3]; - for (unsigned int i = 0; i < 4; i++) { - sprintf(buffer, "%02x", (int) (value & 0xff)); - send(buffer); - value >>= 8; - } -} - -void gdbserver_t::send(uint8_t value) -{ - char buffer[3]; - sprintf(buffer, "%02x", (int) value); - send(buffer); -} - -void gdbserver_t::send_packet(const char* data) -{ - start_packet(); - send(data); - end_packet(); - expect_ack = true; -} - -void gdbserver_t::start_packet() -{ - send("$"); - running_checksum = 0; -} - -void gdbserver_t::end_packet(const char* data) -{ - if (data) { - send(data); - } - - char checksum_string[4]; - sprintf(checksum_string, "#%02x", running_checksum); - send(checksum_string); - expect_ack = true; -} diff --git a/riscv/gdbserver.h b/riscv/gdbserver.h deleted file mode 100644 index 79748b1..0000000 --- a/riscv/gdbserver.h +++ /dev/null @@ -1,269 +0,0 @@ -#ifndef _RISCV_GDBSERVER_H -#define _RISCV_GDBSERVER_H - -#include <map> -#include <queue> - -#include <stdint.h> - -class sim_t; - -template <typename T> -class circular_buffer_t -{ -public: - // The buffer can store capacity-1 data elements. - circular_buffer_t(unsigned int capacity) : data(new T[capacity]), - start(0), end(0), capacity(capacity) {} - circular_buffer_t() : start(0), end(0), capacity(0) {} - ~circular_buffer_t() { delete[] data; } - - T *data; - unsigned int start; // Data start, inclusive. - unsigned int end; // Data end, exclusive. - unsigned int capacity; // Size of the buffer. - unsigned int size() const; - bool empty() const { return start == end; } - bool full() const { return ((end+1) % capacity) == start; } - T entry(unsigned index) { return data[(start + index) % capacity]; } - - // Return size and address of the block of RAM where more data can be copied - // to be added to the buffer. - unsigned int contiguous_empty_size() const; - T *contiguous_empty() { return data + end; } - void data_added(unsigned int bytes); - - unsigned int contiguous_data_size() const; - T *contiguous_data() { return data + start; } - // Tell the buffer that some bytes were consumed from the start of the - // buffer. - void consume(unsigned int bytes); - - void reset(); - - T operator[](unsigned int i) const { return data[(start + i) % capacity]; } - - void append(const T *src, unsigned int count); -}; - -// Class to track software breakpoints that we set. -class software_breakpoint_t -{ - public: - reg_t vaddr; - unsigned int size; - unsigned char instruction[4]; -}; - -class hardware_breakpoint_t -{ - public: - reg_t vaddr; - unsigned int size; - unsigned int index; - bool load, store, execute; -}; - -struct hardware_breakpoint_compare_t { - bool operator()(const hardware_breakpoint_t& a, const hardware_breakpoint_t& b) const { - if (a.vaddr != b.vaddr) - return a.vaddr < b.vaddr; - return a.size < b.size; - } -}; - -class gdbserver_t; - -class operation_t -{ - public: - operation_t(gdbserver_t& gdbserver) : gs(gdbserver), current_step(0) {} - virtual ~operation_t() {} - - bool step() { - bool result = perform_step(current_step); - current_step++; - return result; - } - - // Perform the next step of this operation (which is probably to write to - // Debug RAM and assert the debug interrupt). - // Return true if this operation is complete. In that case the object will - // be deleted. - // Return false if more steps are required the next time the debug - // interrupt is clear. - virtual bool perform_step(unsigned int step) = 0; - - protected: - gdbserver_t& gs; - unsigned int current_step; -}; - -/* - * word 32 64 128 - * 0 inst/0 inst/0 inst/0 - * 1 inst inst/0 inst/0 - * 2 inst inst inst/0 - * 3 inst inst inst/0 - * 4 data0 data0 data0 - * 5 data1 data0 data0 - * 6 data2 data1 data0 - * 7 data1 data0 - * 8 data2 data1 - * 9 data2 data1 - * 10 data1 - * 11 data1 - * 12 data2 - * 13 data2 - * 14 data2 - * 15 data2 - */ -enum slot { - SLOT_INST0, - SLOT_DATA0, - SLOT_DATA1, - SLOT_DATA_LAST, -}; - -static const unsigned int slot_offset32[] = {0, 4, 5, DEBUG_RAM_SIZE/4 - 1}; -static const unsigned int slot_offset64[] = {0, 4, 6, DEBUG_RAM_SIZE/4 - 2}; -static const unsigned int slot_offset128[] = {0, 4, 8, DEBUG_RAM_SIZE/4 - 4}; - -typedef enum { - GB_SOFTWARE = 0, - GB_HARDWARE = 1, - GB_WRITE = 2, - GB_READ = 3, - GB_ACCESS = 4, -} gdb_breakpoint_type_t; - -class gdbserver_t -{ -public: - // Create a new server, listening for connections from localhost on the given - // port. - gdbserver_t(uint16_t port, sim_t *sim); - - // Process all pending messages from a client. - void handle(); - - void handle_packet(const std::vector<uint8_t> &packet); - void handle_interrupt(); - - void software_breakpoint_remove(reg_t vaddr, unsigned int size); - void software_breakpoint_insert(reg_t vaddr, unsigned int size); - void hardware_breakpoint_remove(const hardware_breakpoint_t &bp); - void hardware_breakpoint_insert(const hardware_breakpoint_t &bp); - - void handle_breakpoint(const std::vector<uint8_t> &packet); - void handle_continue(const std::vector<uint8_t> &packet); - void handle_extended(const std::vector<uint8_t> &packet); - void handle_general_registers_read(const std::vector<uint8_t> &packet); - void continue_general_registers_read(); - void handle_halt_reason(const std::vector<uint8_t> &packet); - void handle_kill(const std::vector<uint8_t> &packet); - void handle_memory_binary_write(const std::vector<uint8_t> &packet); - void handle_memory_read(const std::vector<uint8_t> &packet); - void handle_query(const std::vector<uint8_t> &packet); - void handle_register_read(const std::vector<uint8_t> &packet); - void continue_register_read(); - void handle_register_write(const std::vector<uint8_t> &packet); - void handle_step(const std::vector<uint8_t> &packet); - - bool connected() const { return client_fd > 0; } - - // TODO: Move this into its own packet sending class? - // Add the given message to send_buf. - void send(const char* msg); - // Hex-encode a 64-bit value, and send it to gcc in target byte order (little - // endian). - void send(uint64_t value); - // Hex-encode a 32-bit value, and send it to gcc in target byte order (little - // endian). - void send(uint32_t value); - // Hex-encode an 8-bit value, and send it to gcc. - void send(uint8_t value); - void send_packet(const char* data); - uint8_t running_checksum; - // Send "$" and clear running checksum. - void start_packet(); - // Send "#" and checksum. - void end_packet(const char* data=NULL); - - // Write value to the index'th word in Debug RAM. - void dr_write32(unsigned int index, uint32_t value); - void dr_write64(unsigned int index, uint64_t value); - void dr_write(enum slot slot, uint64_t value); - // Write jump-to-resume instruction to the index'th word in Debug RAM. - void dr_write_jump(unsigned int index); - // Write an xlen-bit store instruction. - void dr_write_store(unsigned int index, unsigned int reg, enum slot); - void dr_write_load(unsigned int index, unsigned int reg, enum slot); - uint32_t dr_read32(unsigned int index); - uint64_t dr_read64(unsigned int index); - uint64_t dr_read(enum slot slot); - - uint64_t consume_hex_number_le(std::vector<uint8_t>::const_iterator &iter, - std::vector<uint8_t>::const_iterator end); - - // Return access size to use when writing length bytes to address, so that - // every write will be aligned. - unsigned int find_access_size(reg_t address, int length); - - void set_interrupt(uint32_t hartid); - - // Members that ought to be privated, but that we'd really like to access - // from operation classes. - reg_t dpc; - reg_t dcsr; - reg_t mstatus; - bool mstatus_dirty; - reg_t sptbr; - bool sptbr_valid; - reg_t tselect; - bool tselect_valid; - bool fence_i_required; - - std::map<reg_t, reg_t> pte_cache; - - reg_t translate(reg_t vaddr); - // Return the PRV_x that is used when the code under debug performs a memory - // access. - unsigned int privilege_mode(); - // Return the VM_x that is used when the code under debug performs a memory - // access. - unsigned int virtual_memory(); - - unsigned int xlen; - - std::set<hardware_breakpoint_t, hardware_breakpoint_compare_t> - hardware_breakpoints; - -private: - sim_t *sim; - int socket_fd; - int client_fd; - circular_buffer_t<uint8_t> recv_buf; - circular_buffer_t<uint8_t> send_buf; - - bool expect_ack; - bool extended_mode; - // Used to track whether we think the target is running. If we think it is - // but it isn't, we need to tell gdb about it. - bool running; - - std::map<reg_t, software_breakpoint_t> software_breakpoints; - - // Read pending data from the client. - void read(); - void write(); - // Accept a new client if there isn't one already connected. - void accept(); - // Process all complete requests in recv_buf. - void process_requests(); - - std::queue<operation_t*> operation_queue; - void add_operation(operation_t* operation); -}; - -#endif diff --git a/riscv/insns/c_ebreak.h b/riscv/insns/c_ebreak.h index a17200f..128b86b 100644 --- a/riscv/insns/c_ebreak.h +++ b/riscv/insns/c_ebreak.h @@ -1,2 +1,2 @@ require_extension('C'); -throw trap_breakpoint(); +throw trap_breakpoint(pc); diff --git a/riscv/insns/c_fld.h b/riscv/insns/c_fld.h index 10d14f8..319615b 100644 --- a/riscv/insns/c_fld.h +++ b/riscv/insns/c_fld.h @@ -1,4 +1,4 @@ require_extension('C'); require_extension('D'); require_fp; -WRITE_RVC_FRS2S(MMU.load_int64(RVC_RS1S + insn.rvc_ld_imm())); +WRITE_RVC_FRS2S(f64(MMU.load_uint64(RVC_RS1S + insn.rvc_ld_imm()))); diff --git a/riscv/insns/c_fldsp.h b/riscv/insns/c_fldsp.h index 8b1e19f..534eef7 100644 --- a/riscv/insns/c_fldsp.h +++ b/riscv/insns/c_fldsp.h @@ -1,4 +1,4 @@ require_extension('C'); require_extension('D'); require_fp; -WRITE_FRD(MMU.load_int64(RVC_SP + insn.rvc_ldsp_imm())); +WRITE_FRD(f64(MMU.load_uint64(RVC_SP + insn.rvc_ldsp_imm()))); diff --git a/riscv/insns/c_flw.h b/riscv/insns/c_flw.h index 0be27a9..682566c 100644 --- a/riscv/insns/c_flw.h +++ b/riscv/insns/c_flw.h @@ -2,7 +2,7 @@ require_extension('C'); if (xlen == 32) { require_extension('F'); require_fp; - WRITE_RVC_FRS2S(MMU.load_int32(RVC_RS1S + insn.rvc_lw_imm())); + WRITE_RVC_FRS2S(f32(MMU.load_uint32(RVC_RS1S + insn.rvc_lw_imm()))); } else { // c.ld WRITE_RVC_RS2S(MMU.load_int64(RVC_RS1S + insn.rvc_ld_imm())); } diff --git a/riscv/insns/c_flwsp.h b/riscv/insns/c_flwsp.h index 26a4721..79058c4 100644 --- a/riscv/insns/c_flwsp.h +++ b/riscv/insns/c_flwsp.h @@ -2,7 +2,7 @@ require_extension('C'); if (xlen == 32) { require_extension('F'); require_fp; - WRITE_FRD(MMU.load_int32(RVC_SP + insn.rvc_lwsp_imm())); + WRITE_FRD(f32(MMU.load_uint32(RVC_SP + insn.rvc_lwsp_imm()))); } else { // c.ldsp require(insn.rvc_rd() != 0); WRITE_RD(MMU.load_int64(RVC_SP + insn.rvc_ldsp_imm())); diff --git a/riscv/insns/c_fsd.h b/riscv/insns/c_fsd.h index 84f1a7f..8743266 100644 --- a/riscv/insns/c_fsd.h +++ b/riscv/insns/c_fsd.h @@ -1,4 +1,4 @@ require_extension('C'); require_extension('D'); require_fp; -MMU.store_uint64(RVC_RS1S + insn.rvc_ld_imm(), RVC_FRS2S); +MMU.store_uint64(RVC_RS1S + insn.rvc_ld_imm(), RVC_FRS2S.v); diff --git a/riscv/insns/c_fsdsp.h b/riscv/insns/c_fsdsp.h index 5c5c680..f62f8ff 100644 --- a/riscv/insns/c_fsdsp.h +++ b/riscv/insns/c_fsdsp.h @@ -1,4 +1,4 @@ require_extension('C'); require_extension('D'); require_fp; -MMU.store_uint64(RVC_SP + insn.rvc_sdsp_imm(), RVC_FRS2); +MMU.store_uint64(RVC_SP + insn.rvc_sdsp_imm(), RVC_FRS2.v); diff --git a/riscv/insns/c_fsw.h b/riscv/insns/c_fsw.h index 8923fef..b924a46 100644 --- a/riscv/insns/c_fsw.h +++ b/riscv/insns/c_fsw.h @@ -2,7 +2,7 @@ require_extension('C'); if (xlen == 32) { require_extension('F'); require_fp; - MMU.store_uint32(RVC_RS1S + insn.rvc_lw_imm(), RVC_FRS2S); + MMU.store_uint32(RVC_RS1S + insn.rvc_lw_imm(), RVC_FRS2S.v); } else { // c.sd MMU.store_uint64(RVC_RS1S + insn.rvc_ld_imm(), RVC_RS2S); } diff --git a/riscv/insns/c_fswsp.h b/riscv/insns/c_fswsp.h index c13aa12..011de55 100644 --- a/riscv/insns/c_fswsp.h +++ b/riscv/insns/c_fswsp.h @@ -2,7 +2,7 @@ require_extension('C'); if (xlen == 32) { require_extension('F'); require_fp; - MMU.store_uint32(RVC_SP + insn.rvc_swsp_imm(), RVC_FRS2); + MMU.store_uint32(RVC_SP + insn.rvc_swsp_imm(), RVC_FRS2.v); } else { // c.sdsp MMU.store_uint64(RVC_SP + insn.rvc_sdsp_imm(), RVC_RS2); } diff --git a/riscv/insns/c_li.h b/riscv/insns/c_li.h index 844686d..f9fd66b 100644 --- a/riscv/insns/c_li.h +++ b/riscv/insns/c_li.h @@ -1,3 +1,2 @@ require_extension('C'); -require(insn.rvc_rd() != 0); WRITE_RD(insn.rvc_imm()); diff --git a/riscv/insns/c_lui.h b/riscv/insns/c_lui.h index 130aaed..75d8eb8 100644 --- a/riscv/insns/c_lui.h +++ b/riscv/insns/c_lui.h @@ -3,6 +3,6 @@ if (insn.rvc_rd() == 2) { // c.addi16sp require(insn.rvc_addi16sp_imm() != 0); WRITE_REG(X_SP, sext_xlen(RVC_SP + insn.rvc_addi16sp_imm())); } else { - require(insn.rvc_rd() != 0); + require(insn.rvc_imm() != 0); WRITE_RD(insn.rvc_imm() << 12); } diff --git a/riscv/insns/ebreak.h b/riscv/insns/ebreak.h index c22776c..736cebe 100644 --- a/riscv/insns/ebreak.h +++ b/riscv/insns/ebreak.h @@ -1 +1 @@ -throw trap_breakpoint(); +throw trap_breakpoint(pc); diff --git a/riscv/insns/fadd_d.h b/riscv/insns/fadd_d.h index 9990174..4a436e2 100644 --- a/riscv/insns/fadd_d.h +++ b/riscv/insns/fadd_d.h @@ -1,5 +1,5 @@ require_extension('D'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f64_add(f64(FRS1), f64(FRS2)).v); +WRITE_FRD(f64_add(f64(FRS1), f64(FRS2))); set_fp_exceptions; diff --git a/riscv/insns/fadd_s.h b/riscv/insns/fadd_s.h index cdef36a..cc18d58 100644 --- a/riscv/insns/fadd_s.h +++ b/riscv/insns/fadd_s.h @@ -1,5 +1,5 @@ require_extension('F'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f32_add(f32(FRS1), f32(FRS2)).v); +WRITE_FRD(f32_add(f32(FRS1), f32(FRS2))); set_fp_exceptions; diff --git a/riscv/insns/fcvt_d_l.h b/riscv/insns/fcvt_d_l.h index fece227..08716cf 100644 --- a/riscv/insns/fcvt_d_l.h +++ b/riscv/insns/fcvt_d_l.h @@ -2,5 +2,5 @@ require_extension('D'); require_rv64; require_fp; softfloat_roundingMode = RM; -WRITE_FRD(i64_to_f64(RS1).v); +WRITE_FRD(i64_to_f64(RS1)); set_fp_exceptions; diff --git a/riscv/insns/fcvt_d_lu.h b/riscv/insns/fcvt_d_lu.h index 775c7ae..306d7fe 100644 --- a/riscv/insns/fcvt_d_lu.h +++ b/riscv/insns/fcvt_d_lu.h @@ -2,5 +2,5 @@ require_extension('D'); require_rv64; require_fp; softfloat_roundingMode = RM; -WRITE_FRD(ui64_to_f64(RS1).v); +WRITE_FRD(ui64_to_f64(RS1)); set_fp_exceptions; diff --git a/riscv/insns/fcvt_d_s.h b/riscv/insns/fcvt_d_s.h index ec778cc..5f805b0 100644 --- a/riscv/insns/fcvt_d_s.h +++ b/riscv/insns/fcvt_d_s.h @@ -1,5 +1,5 @@ require_extension('D'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f32_to_f64(f32(FRS1)).v); +WRITE_FRD(f32_to_f64(f32(FRS1))); set_fp_exceptions; diff --git a/riscv/insns/fcvt_d_w.h b/riscv/insns/fcvt_d_w.h index 753250d..4c4861c 100644 --- a/riscv/insns/fcvt_d_w.h +++ b/riscv/insns/fcvt_d_w.h @@ -1,5 +1,5 @@ require_extension('D'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(i32_to_f64((int32_t)RS1).v); +WRITE_FRD(i32_to_f64((int32_t)RS1)); set_fp_exceptions; diff --git a/riscv/insns/fcvt_d_wu.h b/riscv/insns/fcvt_d_wu.h index af893b3..1dbf218 100644 --- a/riscv/insns/fcvt_d_wu.h +++ b/riscv/insns/fcvt_d_wu.h @@ -1,5 +1,5 @@ require_extension('D'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(ui32_to_f64((uint32_t)RS1).v); +WRITE_FRD(ui32_to_f64((uint32_t)RS1)); set_fp_exceptions; diff --git a/riscv/insns/fcvt_s_d.h b/riscv/insns/fcvt_s_d.h index 211bbba..4033335 100644 --- a/riscv/insns/fcvt_s_d.h +++ b/riscv/insns/fcvt_s_d.h @@ -1,5 +1,5 @@ require_extension('D'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f64_to_f32(f64(FRS1)).v); +WRITE_FRD(f64_to_f32(f64(FRS1))); set_fp_exceptions; diff --git a/riscv/insns/fcvt_s_l.h b/riscv/insns/fcvt_s_l.h index 1c0581a..9abcc80 100644 --- a/riscv/insns/fcvt_s_l.h +++ b/riscv/insns/fcvt_s_l.h @@ -2,5 +2,5 @@ require_extension('F'); require_rv64; require_fp; softfloat_roundingMode = RM; -WRITE_FRD(i64_to_f32(RS1).v); +WRITE_FRD(i64_to_f32(RS1)); set_fp_exceptions; diff --git a/riscv/insns/fcvt_s_lu.h b/riscv/insns/fcvt_s_lu.h index e9bf78e..70c676e 100644 --- a/riscv/insns/fcvt_s_lu.h +++ b/riscv/insns/fcvt_s_lu.h @@ -2,5 +2,5 @@ require_extension('F'); require_rv64; require_fp; softfloat_roundingMode = RM; -WRITE_FRD(ui64_to_f32(RS1).v); +WRITE_FRD(ui64_to_f32(RS1)); set_fp_exceptions; diff --git a/riscv/insns/fcvt_s_w.h b/riscv/insns/fcvt_s_w.h index 9411cbd..1ddabd8 100644 --- a/riscv/insns/fcvt_s_w.h +++ b/riscv/insns/fcvt_s_w.h @@ -1,5 +1,5 @@ require_extension('F'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(i32_to_f32((int32_t)RS1).v); +WRITE_FRD(i32_to_f32((int32_t)RS1)); set_fp_exceptions; diff --git a/riscv/insns/fcvt_s_wu.h b/riscv/insns/fcvt_s_wu.h index a6cf836..c1394c3 100644 --- a/riscv/insns/fcvt_s_wu.h +++ b/riscv/insns/fcvt_s_wu.h @@ -1,5 +1,5 @@ require_extension('F'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(ui32_to_f32((uint32_t)RS1).v); +WRITE_FRD(ui32_to_f32((uint32_t)RS1)); set_fp_exceptions; diff --git a/riscv/insns/fdiv_d.h b/riscv/insns/fdiv_d.h index d8943de..ae7911a 100644 --- a/riscv/insns/fdiv_d.h +++ b/riscv/insns/fdiv_d.h @@ -1,5 +1,5 @@ require_extension('D'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f64_div(f64(FRS1), f64(FRS2)).v); +WRITE_FRD(f64_div(f64(FRS1), f64(FRS2))); set_fp_exceptions; diff --git a/riscv/insns/fdiv_s.h b/riscv/insns/fdiv_s.h index 66ac48d..c74ff04 100644 --- a/riscv/insns/fdiv_s.h +++ b/riscv/insns/fdiv_s.h @@ -1,5 +1,5 @@ require_extension('F'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f32_div(f32(FRS1), f32(FRS2)).v); +WRITE_FRD(f32_div(f32(FRS1), f32(FRS2))); set_fp_exceptions; diff --git a/riscv/insns/fld.h b/riscv/insns/fld.h index 0b50b8a..4dea1d4 100644 --- a/riscv/insns/fld.h +++ b/riscv/insns/fld.h @@ -1,3 +1,3 @@ require_extension('D'); require_fp; -WRITE_FRD(MMU.load_int64(RS1 + insn.i_imm())); +WRITE_FRD(f64(MMU.load_uint64(RS1 + insn.i_imm()))); diff --git a/riscv/insns/flw.h b/riscv/insns/flw.h index 489e743..6129754 100644 --- a/riscv/insns/flw.h +++ b/riscv/insns/flw.h @@ -1,3 +1,3 @@ require_extension('F'); require_fp; -WRITE_FRD(MMU.load_uint32(RS1 + insn.i_imm())); +WRITE_FRD(f32(MMU.load_uint32(RS1 + insn.i_imm()))); diff --git a/riscv/insns/fmadd_d.h b/riscv/insns/fmadd_d.h index 98f1cbc..ab22beb 100644 --- a/riscv/insns/fmadd_d.h +++ b/riscv/insns/fmadd_d.h @@ -1,5 +1,5 @@ require_extension('D'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f64_mulAdd(f64(FRS1), f64(FRS2), f64(FRS3)).v); +WRITE_FRD(f64_mulAdd(f64(FRS1), f64(FRS2), f64(FRS3))); set_fp_exceptions; diff --git a/riscv/insns/fmadd_s.h b/riscv/insns/fmadd_s.h index a78ed25..e919190 100644 --- a/riscv/insns/fmadd_s.h +++ b/riscv/insns/fmadd_s.h @@ -1,5 +1,5 @@ require_extension('F'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f32_mulAdd(f32(FRS1), f32(FRS2), f32(FRS3)).v); +WRITE_FRD(f32_mulAdd(f32(FRS1), f32(FRS2), f32(FRS3))); set_fp_exceptions; diff --git a/riscv/insns/fmax_d.h b/riscv/insns/fmax_d.h index 56c9c7a..9c8e5b3 100644 --- a/riscv/insns/fmax_d.h +++ b/riscv/insns/fmax_d.h @@ -1,6 +1,6 @@ require_extension('D'); require_fp; -WRITE_FRD(f64_le_quiet(f64(FRS2), f64(FRS1)) || isNaNF64UI(FRS2) ? FRS1 : FRS2); -if ((isNaNF64UI(FRS1) && isNaNF64UI(FRS2)) || softfloat_exceptionFlags) - WRITE_FRD(defaultNaNF64UI); +WRITE_FRD(f64_le_quiet(f64(FRS2), f64(FRS1)) || isNaNF64UI(f64(FRS2).v) ? FRS1 : FRS2); +if ((isNaNF64UI(f64(FRS1).v) && isNaNF64UI(f64(FRS2).v)) || softfloat_exceptionFlags) + WRITE_FRD(f64(defaultNaNF64UI)); set_fp_exceptions; diff --git a/riscv/insns/fmax_s.h b/riscv/insns/fmax_s.h index bf90356..2f570ea 100644 --- a/riscv/insns/fmax_s.h +++ b/riscv/insns/fmax_s.h @@ -1,6 +1,6 @@ require_extension('F'); require_fp; -WRITE_FRD(f32_le_quiet(f32(FRS2), f32(FRS1)) || isNaNF32UI(FRS2) ? FRS1 : FRS2); -if ((isNaNF32UI(FRS1) && isNaNF32UI(FRS2)) || softfloat_exceptionFlags) - WRITE_FRD(defaultNaNF32UI); +WRITE_FRD(f32_le_quiet(f32(FRS2), f32(FRS1)) || isNaNF32UI(f32(FRS2).v) ? FRS1 : FRS2); +if ((isNaNF32UI(f32(FRS1).v) && isNaNF32UI(f32(FRS2).v)) || softfloat_exceptionFlags) + WRITE_FRD(f32(defaultNaNF32UI)); set_fp_exceptions; diff --git a/riscv/insns/fmin_d.h b/riscv/insns/fmin_d.h index 2a1755e..cd40e15 100644 --- a/riscv/insns/fmin_d.h +++ b/riscv/insns/fmin_d.h @@ -1,6 +1,6 @@ require_extension('D'); require_fp; -WRITE_FRD(f64_lt_quiet(f64(FRS1), f64(FRS2)) || isNaNF64UI(FRS2) ? FRS1 : FRS2); -if ((isNaNF64UI(FRS1) && isNaNF64UI(FRS2)) || softfloat_exceptionFlags) - WRITE_FRD(defaultNaNF64UI); +WRITE_FRD(f64_lt_quiet(f64(FRS1), f64(FRS2)) || isNaNF64UI(f64(FRS2).v) ? FRS1 : FRS2); +if ((isNaNF64UI(f64(FRS1).v) && isNaNF64UI(f64(FRS2).v)) || softfloat_exceptionFlags) + WRITE_FRD(f64(defaultNaNF64UI)); set_fp_exceptions; diff --git a/riscv/insns/fmin_s.h b/riscv/insns/fmin_s.h index 831a7a2..b813f45 100644 --- a/riscv/insns/fmin_s.h +++ b/riscv/insns/fmin_s.h @@ -1,6 +1,6 @@ require_extension('F'); require_fp; -WRITE_FRD(f32_lt_quiet(f32(FRS1), f32(FRS2)) || isNaNF32UI(FRS2) ? FRS1 : FRS2); -if ((isNaNF32UI(FRS1) && isNaNF32UI(FRS2)) || softfloat_exceptionFlags) - WRITE_FRD(defaultNaNF32UI); +WRITE_FRD(f32_lt_quiet(f32(FRS1), f32(FRS2)) || isNaNF32UI(f32(FRS2).v) ? FRS1 : FRS2); +if ((isNaNF32UI(f32(FRS1).v) && isNaNF32UI(f32(FRS2).v)) || softfloat_exceptionFlags) + WRITE_FRD(f32(defaultNaNF32UI)); set_fp_exceptions; diff --git a/riscv/insns/fmsub_d.h b/riscv/insns/fmsub_d.h index afcea88..5b5bc0f 100644 --- a/riscv/insns/fmsub_d.h +++ b/riscv/insns/fmsub_d.h @@ -1,5 +1,5 @@ require_extension('D'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f64_mulAdd(f64(FRS1), f64(FRS2), f64(FRS3 ^ (uint64_t)INT64_MIN)).v); +WRITE_FRD(f64_mulAdd(f64(FRS1), f64(FRS2), f64(f64(FRS3).v ^ F64_SIGN))); set_fp_exceptions; diff --git a/riscv/insns/fmsub_s.h b/riscv/insns/fmsub_s.h index 45945da..d46c887 100644 --- a/riscv/insns/fmsub_s.h +++ b/riscv/insns/fmsub_s.h @@ -1,5 +1,5 @@ require_extension('F'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f32_mulAdd(f32(FRS1), f32(FRS2), f32(FRS3 ^ (uint32_t)INT32_MIN)).v); +WRITE_FRD(f32_mulAdd(f32(FRS1), f32(FRS2), f32(f32(FRS3).v ^ F32_SIGN))); set_fp_exceptions; diff --git a/riscv/insns/fmul_d.h b/riscv/insns/fmul_d.h index 04e7402..9189d8d 100644 --- a/riscv/insns/fmul_d.h +++ b/riscv/insns/fmul_d.h @@ -1,5 +1,5 @@ require_extension('D'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f64_mul(f64(FRS1), f64(FRS2)).v); +WRITE_FRD(f64_mul(f64(FRS1), f64(FRS2))); set_fp_exceptions; diff --git a/riscv/insns/fmul_s.h b/riscv/insns/fmul_s.h index 9ae7b3c..145d5ce 100644 --- a/riscv/insns/fmul_s.h +++ b/riscv/insns/fmul_s.h @@ -1,5 +1,5 @@ require_extension('F'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f32_mul(f32(FRS1), f32(FRS2)).v); +WRITE_FRD(f32_mul(f32(FRS1), f32(FRS2))); set_fp_exceptions; diff --git a/riscv/insns/fmv_d_x.h b/riscv/insns/fmv_d_x.h index c3f6049..0bff5fb 100644 --- a/riscv/insns/fmv_d_x.h +++ b/riscv/insns/fmv_d_x.h @@ -1,4 +1,4 @@ require_extension('D'); require_rv64; require_fp; -WRITE_FRD(RS1); +WRITE_FRD(f64(RS1)); diff --git a/riscv/insns/fmv_s_x.h b/riscv/insns/fmv_w_x.h index 2daf6da..5f71323 100644 --- a/riscv/insns/fmv_s_x.h +++ b/riscv/insns/fmv_w_x.h @@ -1,3 +1,3 @@ require_extension('F'); require_fp; -WRITE_FRD(zext32(RS1)); +WRITE_FRD(f32(RS1)); diff --git a/riscv/insns/fmv_x_d.h b/riscv/insns/fmv_x_d.h index b97d7f5..da8e72a 100644 --- a/riscv/insns/fmv_x_d.h +++ b/riscv/insns/fmv_x_d.h @@ -1,4 +1,4 @@ require_extension('D'); require_rv64; require_fp; -WRITE_RD(FRS1); +WRITE_RD(FRS1.v); diff --git a/riscv/insns/fmv_x_s.h b/riscv/insns/fmv_x_w.h index 1bee87f..b722479 100644 --- a/riscv/insns/fmv_x_s.h +++ b/riscv/insns/fmv_x_w.h @@ -1,3 +1,3 @@ require_extension('F'); require_fp; -WRITE_RD(sext32(FRS1)); +WRITE_RD(sext32(FRS1.v)); diff --git a/riscv/insns/fnmadd_d.h b/riscv/insns/fnmadd_d.h index d6e1f04..e8dd743 100644 --- a/riscv/insns/fnmadd_d.h +++ b/riscv/insns/fnmadd_d.h @@ -1,5 +1,5 @@ require_extension('D'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f64_mulAdd(f64(FRS1 ^ (uint64_t)INT64_MIN), f64(FRS2), f64(FRS3 ^ (uint64_t)INT64_MIN)).v); +WRITE_FRD(f64_mulAdd(f64(f64(FRS1).v ^ F64_SIGN), f64(FRS2), f64(f64(FRS3).v ^ F64_SIGN))); set_fp_exceptions; diff --git a/riscv/insns/fnmadd_s.h b/riscv/insns/fnmadd_s.h index 0d0b2e9..1c2996e 100644 --- a/riscv/insns/fnmadd_s.h +++ b/riscv/insns/fnmadd_s.h @@ -1,5 +1,5 @@ require_extension('F'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f32_mulAdd(f32(FRS1 ^ (uint32_t)INT32_MIN), f32(FRS2), f32(FRS3 ^ (uint32_t)INT32_MIN)).v); +WRITE_FRD(f32_mulAdd(f32(f32(FRS1).v ^ F32_SIGN), f32(FRS2), f32(f32(FRS3).v ^ F32_SIGN))); set_fp_exceptions; diff --git a/riscv/insns/fnmsub_d.h b/riscv/insns/fnmsub_d.h index ee74cab..c29a0b9 100644 --- a/riscv/insns/fnmsub_d.h +++ b/riscv/insns/fnmsub_d.h @@ -1,5 +1,5 @@ require_extension('D'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f64_mulAdd(f64(FRS1 ^ (uint64_t)INT64_MIN), f64(FRS2), f64(FRS3)).v); +WRITE_FRD(f64_mulAdd(f64(f64(FRS1).v ^ F64_SIGN), f64(FRS2), f64(FRS3))); set_fp_exceptions; diff --git a/riscv/insns/fnmsub_s.h b/riscv/insns/fnmsub_s.h index 3e0b8ea..4c61fc7 100644 --- a/riscv/insns/fnmsub_s.h +++ b/riscv/insns/fnmsub_s.h @@ -1,5 +1,5 @@ require_extension('F'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f32_mulAdd(f32(FRS1 ^ (uint32_t)INT32_MIN), f32(FRS2), f32(FRS3)).v); +WRITE_FRD(f32_mulAdd(f32(f32(FRS1).v ^ F32_SIGN), f32(FRS2), f32(FRS3))); set_fp_exceptions; diff --git a/riscv/insns/fsd.h b/riscv/insns/fsd.h index 63cc8e5..679cc95 100644 --- a/riscv/insns/fsd.h +++ b/riscv/insns/fsd.h @@ -1,3 +1,3 @@ require_extension('D'); require_fp; -MMU.store_uint64(RS1 + insn.s_imm(), FRS2); +MMU.store_uint64(RS1 + insn.s_imm(), FRS2.v); diff --git a/riscv/insns/fsgnj_d.h b/riscv/insns/fsgnj_d.h index 52648a1..78f9ce7 100644 --- a/riscv/insns/fsgnj_d.h +++ b/riscv/insns/fsgnj_d.h @@ -1,3 +1,3 @@ require_extension('D'); require_fp; -WRITE_FRD((FRS1 &~ INT64_MIN) | (FRS2 & INT64_MIN)); +WRITE_FRD(fsgnj64(FRS1, FRS2, false, false)); diff --git a/riscv/insns/fsgnj_s.h b/riscv/insns/fsgnj_s.h index 4c91ff3..c1a70cb 100644 --- a/riscv/insns/fsgnj_s.h +++ b/riscv/insns/fsgnj_s.h @@ -1,3 +1,3 @@ require_extension('F'); require_fp; -WRITE_FRD((FRS1 &~ (uint32_t)INT32_MIN) | (FRS2 & (uint32_t)INT32_MIN)); +WRITE_FRD(fsgnj32(FRS1, FRS2, false, false)); diff --git a/riscv/insns/fsgnjn_d.h b/riscv/insns/fsgnjn_d.h index cdec924..f02c311 100644 --- a/riscv/insns/fsgnjn_d.h +++ b/riscv/insns/fsgnjn_d.h @@ -1,3 +1,3 @@ require_extension('D'); require_fp; -WRITE_FRD((FRS1 &~ INT64_MIN) | ((~FRS2) & INT64_MIN)); +WRITE_FRD(fsgnj64(FRS1, FRS2, true, false)); diff --git a/riscv/insns/fsgnjn_s.h b/riscv/insns/fsgnjn_s.h index f91a7b0..35906d6 100644 --- a/riscv/insns/fsgnjn_s.h +++ b/riscv/insns/fsgnjn_s.h @@ -1,3 +1,3 @@ require_extension('F'); require_fp; -WRITE_FRD((FRS1 &~ (uint32_t)INT32_MIN) | ((~FRS2) & (uint32_t)INT32_MIN)); +WRITE_FRD(fsgnj32(FRS1, FRS2, true, false)); diff --git a/riscv/insns/fsgnjx_d.h b/riscv/insns/fsgnjx_d.h index b09d24c..c121737 100644 --- a/riscv/insns/fsgnjx_d.h +++ b/riscv/insns/fsgnjx_d.h @@ -1,3 +1,3 @@ require_extension('D'); require_fp; -WRITE_FRD(FRS1 ^ (FRS2 & INT64_MIN)); +WRITE_FRD(fsgnj64(FRS1, FRS2, false, true)); diff --git a/riscv/insns/fsgnjx_s.h b/riscv/insns/fsgnjx_s.h index 1fd2de6..4d5c624 100644 --- a/riscv/insns/fsgnjx_s.h +++ b/riscv/insns/fsgnjx_s.h @@ -1,3 +1,3 @@ require_extension('F'); require_fp; -WRITE_FRD(FRS1 ^ (FRS2 & (uint32_t)INT32_MIN)); +WRITE_FRD(fsgnj32(FRS1, FRS2, false, true)); diff --git a/riscv/insns/fsqrt_d.h b/riscv/insns/fsqrt_d.h index 45f37ce..da138ba 100644 --- a/riscv/insns/fsqrt_d.h +++ b/riscv/insns/fsqrt_d.h @@ -1,5 +1,5 @@ require_extension('D'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f64_sqrt(f64(FRS1)).v); +WRITE_FRD(f64_sqrt(f64(FRS1))); set_fp_exceptions; diff --git a/riscv/insns/fsqrt_s.h b/riscv/insns/fsqrt_s.h index f3b3956..7476846 100644 --- a/riscv/insns/fsqrt_s.h +++ b/riscv/insns/fsqrt_s.h @@ -1,5 +1,5 @@ require_extension('F'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f32_sqrt(f32(FRS1)).v); +WRITE_FRD(f32_sqrt(f32(FRS1))); set_fp_exceptions; diff --git a/riscv/insns/fsub_d.h b/riscv/insns/fsub_d.h index 487743e..1418a06 100644 --- a/riscv/insns/fsub_d.h +++ b/riscv/insns/fsub_d.h @@ -1,5 +1,5 @@ require_extension('D'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f64_sub(f64(FRS1), f64(FRS2)).v); +WRITE_FRD(f64_sub(f64(FRS1), f64(FRS2))); set_fp_exceptions; diff --git a/riscv/insns/fsub_s.h b/riscv/insns/fsub_s.h index e7a7cf1..f6183ea 100644 --- a/riscv/insns/fsub_s.h +++ b/riscv/insns/fsub_s.h @@ -1,5 +1,5 @@ require_extension('F'); require_fp; softfloat_roundingMode = RM; -WRITE_FRD(f32_sub(f32(FRS1), f32(FRS2)).v); +WRITE_FRD(f32_sub(f32(FRS1), f32(FRS2))); set_fp_exceptions; diff --git a/riscv/insns/fsw.h b/riscv/insns/fsw.h index 3135e9b..42fc683 100644 --- a/riscv/insns/fsw.h +++ b/riscv/insns/fsw.h @@ -1,3 +1,3 @@ require_extension('F'); require_fp; -MMU.store_uint32(RS1 + insn.s_imm(), FRS2); +MMU.store_uint32(RS1 + insn.s_imm(), FRS2.v); diff --git a/riscv/insns/mret.h b/riscv/insns/mret.h index f3c4414..96933cf 100644 --- a/riscv/insns/mret.h +++ b/riscv/insns/mret.h @@ -2,7 +2,7 @@ require_privilege(PRV_M); set_pc_and_serialize(p->get_state()->mepc); reg_t s = STATE.mstatus; reg_t prev_prv = get_field(s, MSTATUS_MPP); -s = set_field(s, MSTATUS_UIE << prev_prv, get_field(s, MSTATUS_MPIE)); +s = set_field(s, MSTATUS_MIE, get_field(s, MSTATUS_MPIE)); s = set_field(s, MSTATUS_MPIE, 1); s = set_field(s, MSTATUS_MPP, PRV_U); p->set_privilege(prev_prv); diff --git a/riscv/insns/sfence_vm.h b/riscv/insns/sfence_vm.h deleted file mode 100644 index 35ff5dd..0000000 --- a/riscv/insns/sfence_vm.h +++ /dev/null @@ -1,2 +0,0 @@ -require_privilege(PRV_S); -MMU.flush_tlb(); diff --git a/riscv/insns/sfence_vma.h b/riscv/insns/sfence_vma.h new file mode 100644 index 0000000..fc4625f --- /dev/null +++ b/riscv/insns/sfence_vma.h @@ -0,0 +1,2 @@ +require_privilege(get_field(STATE.mstatus, MSTATUS_TVM) ? PRV_M : PRV_S); +MMU.flush_tlb(); diff --git a/riscv/insns/sret.h b/riscv/insns/sret.h index b593198..ae841de 100644 --- a/riscv/insns/sret.h +++ b/riscv/insns/sret.h @@ -1,8 +1,8 @@ -require_privilege(PRV_S); +require_privilege(get_field(STATE.mstatus, MSTATUS_TSR) ? PRV_M : PRV_S); set_pc_and_serialize(p->get_state()->sepc); reg_t s = STATE.mstatus; reg_t prev_prv = get_field(s, MSTATUS_SPP); -s = set_field(s, MSTATUS_UIE << prev_prv, get_field(s, MSTATUS_SPIE)); +s = set_field(s, MSTATUS_SIE, get_field(s, MSTATUS_SPIE)); s = set_field(s, MSTATUS_SPIE, 1); s = set_field(s, MSTATUS_SPP, PRV_U); p->set_privilege(prev_prv); diff --git a/riscv/insns/wfi.h b/riscv/insns/wfi.h index 6f037f8..16de594 100644 --- a/riscv/insns/wfi.h +++ b/riscv/insns/wfi.h @@ -1 +1,2 @@ -// nop +require_privilege(get_field(STATE.mstatus, MSTATUS_TW) ? PRV_M : PRV_S); +set_pc_and_serialize(npc); diff --git a/riscv/interactive.cc b/riscv/interactive.cc index 748f454..31b9162 100644 --- a/riscv/interactive.cc +++ b/riscv/interactive.cc @@ -18,12 +18,14 @@ #include <vector> #include <algorithm> +DECLARE_TRAP(-1, interactive) + processor_t *sim_t::get_core(const std::string& i) { char *ptr; unsigned long p = strtoul(i.c_str(), &ptr, 10); - if (*ptr || p >= num_cores()) - throw trap_illegal_instruction(); + if (*ptr || p >= procs.size()) + throw trap_interactive(); return get_core(p); } @@ -64,6 +66,7 @@ void sim_t::interactive() funcs["r"] = funcs["run"]; funcs["rs"] = &sim_t::interactive_run_silent; funcs["reg"] = &sim_t::interactive_reg; + funcs["freg"] = &sim_t::interactive_freg; funcs["fregs"] = &sim_t::interactive_fregs; funcs["fregd"] = &sim_t::interactive_fregd; funcs["pc"] = &sim_t::interactive_pc; @@ -161,7 +164,7 @@ void sim_t::interactive_quit(const std::string& cmd, const std::vector<std::stri reg_t sim_t::get_pc(const std::vector<std::string>& args) { if(args.size() != 1) - throw trap_illegal_instruction(); + throw trap_interactive(); processor_t *p = get_core(args[0]); return p->state.pc; @@ -175,7 +178,7 @@ void sim_t::interactive_pc(const std::string& cmd, const std::vector<std::string reg_t sim_t::get_reg(const std::vector<std::string>& args) { if(args.size() != 2) - throw trap_illegal_instruction(); + throw trap_interactive(); processor_t *p = get_core(args[0]); @@ -192,22 +195,22 @@ reg_t sim_t::get_reg(const std::vector<std::string>& args) } if (r >= NXPR) - throw trap_illegal_instruction(); + throw trap_interactive(); return p->state.XPR[r]; } -reg_t sim_t::get_freg(const std::vector<std::string>& args) +freg_t sim_t::get_freg(const std::vector<std::string>& args) { if(args.size() != 2) - throw trap_illegal_instruction(); + throw trap_interactive(); processor_t *p = get_core(args[0]); int r = std::find(fpr_name, fpr_name + NFPR, args[1]) - fpr_name; if (r == NFPR) r = atoi(args[1].c_str()); if (r >= NFPR) - throw trap_illegal_instruction(); + throw trap_interactive(); return p->state.FPR[r]; } @@ -229,11 +232,16 @@ void sim_t::interactive_reg(const std::string& cmd, const std::vector<std::strin union fpr { - reg_t r; + freg_t r; float s; double d; }; +void sim_t::interactive_freg(const std::string& cmd, const std::vector<std::string>& args) +{ + fprintf(stderr, "0x%016" PRIx64 "\n", get_freg(args).v); +} + void sim_t::interactive_fregs(const std::string& cmd, const std::vector<std::string>& args) { fpr f; @@ -251,7 +259,7 @@ void sim_t::interactive_fregd(const std::string& cmd, const std::vector<std::str reg_t sim_t::get_mem(const std::vector<std::string>& args) { if(args.size() != 1 && args.size() != 2) - throw trap_illegal_instruction(); + throw trap_interactive(); std::string addr_str = args[0]; mmu_t* mmu = debug_mmu; @@ -293,7 +301,7 @@ void sim_t::interactive_mem(const std::string& cmd, const std::vector<std::strin void sim_t::interactive_str(const std::string& cmd, const std::vector<std::string>& args) { if(args.size() != 1) - throw trap_illegal_instruction(); + throw trap_interactive(); reg_t addr = strtol(args[0].c_str(),NULL,16); diff --git a/riscv/jtag_dtm.cc b/riscv/jtag_dtm.cc new file mode 100644 index 0000000..cd3f3ee --- /dev/null +++ b/riscv/jtag_dtm.cc @@ -0,0 +1,180 @@ +#include <stdio.h> + +#include "decode.h" +#include "jtag_dtm.h" +#include "debug_module.h" +#include "debug_defines.h" + +#if 0 +# define D(x) x +#else +# define D(x) +#endif + +enum { + IR_IDCODE=1, + IR_DTMCONTROL=0x10, + IR_DBUS=0x11 +}; + +#define DTMCONTROL_VERSION 0xf +#define DTMCONTROL_ABITS (0x3f << 4) +#define DTMCONTROL_DBUSSTAT (3<<10) +#define DTMCONTROL_IDLE (7<<12) +#define DTMCONTROL_DBUSRESET (1<<16) + +#define DMI_OP 3 +#define DMI_DATA (0xffffffffL<<2) +#define DMI_ADDRESS ((1L<<(abits+34)) - (1L<<34)) + +#define DMI_OP_STATUS_SUCCESS 0 +#define DMI_OP_STATUS_RESERVED 1 +#define DMI_OP_STATUS_FAILED 2 +#define DMI_OP_STATUS_BUSY 3 + +#define DMI_OP_NOP 0 +#define DMI_OP_READ 1 +#define DMI_OP_WRITE 2 +#define DMI_OP_RESERVED 3 + +jtag_dtm_t::jtag_dtm_t(debug_module_t *dm) : + dm(dm), + _tck(false), _tms(false), _tdi(false), _tdo(false), + dtmcontrol((abits << DTM_DTMCS_ABITS_OFFSET) | 1), + dmi(DMI_OP_STATUS_FAILED << DTM_DMI_OP_OFFSET), + _state(TEST_LOGIC_RESET) +{ +} + +void jtag_dtm_t::reset() { + _state = TEST_LOGIC_RESET; +} + +void jtag_dtm_t::set_pins(bool tck, bool tms, bool tdi) { + const jtag_state_t next[16][2] = { + /* TEST_LOGIC_RESET */ { RUN_TEST_IDLE, TEST_LOGIC_RESET }, + /* RUN_TEST_IDLE */ { RUN_TEST_IDLE, SELECT_DR_SCAN }, + /* SELECT_DR_SCAN */ { CAPTURE_DR, SELECT_IR_SCAN }, + /* CAPTURE_DR */ { SHIFT_DR, EXIT1_DR }, + /* SHIFT_DR */ { SHIFT_DR, EXIT1_DR }, + /* EXIT1_DR */ { PAUSE_DR, UPDATE_DR }, + /* PAUSE_DR */ { PAUSE_DR, EXIT2_DR }, + /* EXIT2_DR */ { SHIFT_DR, UPDATE_DR }, + /* UPDATE_DR */ { RUN_TEST_IDLE, SELECT_DR_SCAN }, + /* SELECT_IR_SCAN */ { CAPTURE_IR, TEST_LOGIC_RESET }, + /* CAPTURE_IR */ { SHIFT_IR, EXIT1_IR }, + /* SHIFT_IR */ { SHIFT_IR, EXIT1_IR }, + /* EXIT1_IR */ { PAUSE_IR, UPDATE_IR }, + /* PAUSE_IR */ { PAUSE_IR, EXIT2_IR }, + /* EXIT2_IR */ { SHIFT_IR, UPDATE_IR }, + /* UPDATE_IR */ { RUN_TEST_IDLE, SELECT_DR_SCAN } + }; + + if (!_tck && tck) { + // Positive clock edge. + + switch (_state) { + case SHIFT_DR: + dr >>= 1; + dr |= (uint64_t) _tdi << (dr_length-1); + break; + case SHIFT_IR: + ir >>= 1; + ir |= _tdi << (ir_length-1); + break; + default: + break; + } + _state = next[_state][_tms]; + switch (_state) { + case TEST_LOGIC_RESET: + ir = IR_IDCODE; + break; + case CAPTURE_DR: + capture_dr(); + break; + case SHIFT_DR: + _tdo = dr & 1; + break; + case UPDATE_DR: + update_dr(); + break; + case CAPTURE_IR: + break; + case SHIFT_IR: + _tdo = ir & 1; + break; + case UPDATE_IR: + break; + default: + break; + } + } + + D(fprintf(stderr, "state=%2d, tdi=%d, tdo=%d, tms=%d, tck=%d, ir=0x%02x, " + "dr=0x%lx\n", + _state, _tdi, _tdo, _tms, _tck, ir, dr)); + + _tck = tck; + _tms = tms; + _tdi = tdi; +} + +void jtag_dtm_t::capture_dr() +{ + switch (ir) { + case IR_IDCODE: + dr = idcode; + dr_length = 32; + break; + case IR_DTMCONTROL: + dr = dtmcontrol; + dr_length = 32; + break; + case IR_DBUS: + dr = dmi; + dr_length = abits + 34; + break; + default: + D(fprintf(stderr, "Unsupported IR: 0x%x\n", ir)); + break; + } + D(fprintf(stderr, "Capture DR; IR=0x%x, DR=0x%lx (%d bits)\n", + ir, dr, dr_length)); +} + +void jtag_dtm_t::update_dr() +{ + D(fprintf(stderr, "Update DR; IR=0x%x, DR=0x%lx (%d bits)\n", + ir, dr, dr_length)); + switch (ir) { + case IR_DBUS: + { + unsigned op = get_field(dr, DMI_OP); + uint32_t data = get_field(dr, DMI_DATA); + unsigned address = get_field(dr, DMI_ADDRESS); + + dmi = dr; + + bool success = true; + if (op == DMI_OP_READ) { + uint32_t value; + if (dm->dmi_read(address, &value)) { + dmi = set_field(dmi, DMI_DATA, value); + } else { + success = false; + } + } else if (op == DMI_OP_WRITE) { + success = dm->dmi_write(address, data); + } + + if (success) { + dmi = set_field(dmi, DMI_OP, DMI_OP_STATUS_SUCCESS); + } else { + dmi = set_field(dmi, DMI_OP, DMI_OP_STATUS_FAILED); + } + D(fprintf(stderr, "dmi=0x%lx\n", dmi)); + } + break; + } +} diff --git a/riscv/jtag_dtm.h b/riscv/jtag_dtm.h new file mode 100644 index 0000000..063e3f4 --- /dev/null +++ b/riscv/jtag_dtm.h @@ -0,0 +1,61 @@ +#ifndef JTAG_DTM_H +#define JTAG_DTM_H + +#include <stdint.h> + +class debug_module_t; + +typedef enum { + TEST_LOGIC_RESET, + RUN_TEST_IDLE, + SELECT_DR_SCAN, + CAPTURE_DR, + SHIFT_DR, + EXIT1_DR, + PAUSE_DR, + EXIT2_DR, + UPDATE_DR, + SELECT_IR_SCAN, + CAPTURE_IR, + SHIFT_IR, + EXIT1_IR, + PAUSE_IR, + EXIT2_IR, + UPDATE_IR +} jtag_state_t; + +class jtag_dtm_t +{ + static const unsigned idcode = 0xdeadbeef; + + public: + jtag_dtm_t(debug_module_t *dm); + void reset(); + + void set_pins(bool tck, bool tms, bool tdi); + + bool tdo() const { return _tdo; } + + jtag_state_t state() const { return _state; } + + private: + debug_module_t *dm; + bool _tck, _tms, _tdi, _tdo; + uint32_t ir; + const unsigned ir_length = 5; + uint64_t dr; + unsigned dr_length; + + // abits must come before dtmcontrol so it can easily be used in the + // constructor. + const unsigned abits = 6; + uint32_t dtmcontrol; + uint64_t dmi; + + jtag_state_t _state; + + void capture_dr(); + void update_dr(); +}; + +#endif diff --git a/riscv/mmu.cc b/riscv/mmu.cc index 878d849..76a6ab1 100644 --- a/riscv/mmu.cc +++ b/riscv/mmu.cc @@ -43,33 +43,21 @@ reg_t mmu_t::translate(reg_t addr, access_type type) if (!proc->state.dcsr.cause && get_field(proc->state.mstatus, MSTATUS_MPRV)) mode = get_field(proc->state.mstatus, MSTATUS_MPP); } - if (get_field(proc->state.mstatus, MSTATUS_VM) == VM_MBARE) - mode = PRV_M; - if (mode == PRV_M) { - reg_t msb_mask = (reg_t(2) << (proc->xlen-1))-1; // zero-extend from xlen - return addr & msb_mask; - } return walk(addr, type, mode) | (addr & (PGSIZE-1)); } -const uint16_t* mmu_t::fetch_slow_path(reg_t vaddr) +tlb_entry_t mmu_t::fetch_slow_path(reg_t vaddr) { reg_t paddr = translate(vaddr, FETCH); - // mmu_t::walk() returns -1 if it can't find a match. Of course -1 could also - // be a valid address. - if (paddr == ~(reg_t) 0 && vaddr != ~(reg_t) 0) { - throw trap_instruction_access_fault(vaddr); - } - - if (sim->addr_is_mem(paddr)) { - refill_tlb(vaddr, paddr, FETCH); - return (const uint16_t*)sim->addr_to_mem(paddr); + if (auto host_addr = sim->addr_to_mem(paddr)) { + return refill_tlb(vaddr, paddr, host_addr, FETCH); } else { if (!sim->mmio_load(paddr, sizeof fetch_temp, (uint8_t*)&fetch_temp)) throw trap_instruction_access_fault(vaddr); - return &fetch_temp; + tlb_entry_t entry = {(char*)&fetch_temp - vaddr, paddr - vaddr}; + return entry; } } @@ -103,12 +91,12 @@ void mmu_t::load_slow_path(reg_t addr, reg_t len, uint8_t* bytes) { reg_t paddr = translate(addr, LOAD); - if (sim->addr_is_mem(paddr)) { - memcpy(bytes, sim->addr_to_mem(paddr), len); + if (auto host_addr = sim->addr_to_mem(paddr)) { + memcpy(bytes, host_addr, len); if (tracer.interested_in_range(paddr, paddr + PGSIZE, LOAD)) tracer.trace(paddr, len, LOAD); else - refill_tlb(addr, paddr, LOAD); + refill_tlb(addr, paddr, host_addr, LOAD); } else if (!sim->mmio_load(paddr, len, bytes)) { throw trap_load_access_fault(addr); } @@ -132,18 +120,18 @@ void mmu_t::store_slow_path(reg_t addr, reg_t len, const uint8_t* bytes) throw *matched_trigger; } - if (sim->addr_is_mem(paddr)) { - memcpy(sim->addr_to_mem(paddr), bytes, len); + if (auto host_addr = sim->addr_to_mem(paddr)) { + memcpy(host_addr, bytes, len); if (tracer.interested_in_range(paddr, paddr + PGSIZE, STORE)) tracer.trace(paddr, len, STORE); else - refill_tlb(addr, paddr, STORE); + refill_tlb(addr, paddr, host_addr, STORE); } else if (!sim->mmio_store(paddr, len, bytes)) { throw trap_store_access_fault(addr); } } -void mmu_t::refill_tlb(reg_t vaddr, reg_t paddr, access_type type) +tlb_entry_t mmu_t::refill_tlb(reg_t vaddr, reg_t paddr, char* host_addr, access_type type) { reg_t idx = (vaddr >> PGSHIFT) % TLB_ENTRIES; reg_t expected_tag = vaddr >> PGSHIFT; @@ -164,48 +152,44 @@ void mmu_t::refill_tlb(reg_t vaddr, reg_t paddr, access_type type) else if (type == STORE) tlb_store_tag[idx] = expected_tag; else tlb_load_tag[idx] = expected_tag; - tlb_data[idx] = sim->addr_to_mem(paddr) - vaddr; + tlb_entry_t entry = {host_addr - vaddr, paddr - vaddr}; + tlb_data[idx] = entry; + return entry; } reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode) { - int levels, ptidxbits, ptesize; - switch (get_field(proc->get_state()->mstatus, MSTATUS_VM)) - { - case VM_SV32: levels = 2; ptidxbits = 10; ptesize = 4; break; - case VM_SV39: levels = 3; ptidxbits = 9; ptesize = 8; break; - case VM_SV48: levels = 4; ptidxbits = 9; ptesize = 8; break; - default: abort(); - } + vm_info vm = decode_vm_info(proc->max_xlen, mode, proc->get_state()->sptbr); + if (vm.levels == 0) + return addr & ((reg_t(2) << (proc->xlen-1))-1); // zero-extend from xlen bool supervisor = mode == PRV_S; - bool pum = get_field(proc->state.mstatus, MSTATUS_PUM); + bool sum = get_field(proc->state.mstatus, MSTATUS_SUM); bool mxr = get_field(proc->state.mstatus, MSTATUS_MXR); // verify bits xlen-1:va_bits-1 are all equal - int va_bits = PGSHIFT + levels * ptidxbits; + int va_bits = PGSHIFT + vm.levels * vm.idxbits; reg_t mask = (reg_t(1) << (proc->xlen - (va_bits-1))) - 1; reg_t masked_msbs = (addr >> (va_bits-1)) & mask; if (masked_msbs != 0 && masked_msbs != mask) - return -1; + vm.levels = 0; - reg_t base = proc->get_state()->sptbr << PGSHIFT; - int ptshift = (levels - 1) * ptidxbits; - for (int i = 0; i < levels; i++, ptshift -= ptidxbits) { - reg_t idx = (addr >> (PGSHIFT + ptshift)) & ((1 << ptidxbits) - 1); + reg_t base = vm.ptbase; + for (int i = vm.levels - 1; i >= 0; i--) { + int ptshift = i * vm.idxbits; + reg_t idx = (addr >> (PGSHIFT + ptshift)) & ((1 << vm.idxbits) - 1); // check that physical address of PTE is legal - reg_t pte_addr = base + idx * ptesize; - if (!sim->addr_is_mem(pte_addr)) - break; + auto ppte = sim->addr_to_mem(base + idx * vm.ptesize); + if (!ppte) + throw trap_load_access_fault(addr); - void* ppte = sim->addr_to_mem(pte_addr); - reg_t pte = ptesize == 4 ? *(uint32_t*)ppte : *(uint64_t*)ppte; + reg_t pte = vm.ptesize == 4 ? *(uint32_t*)ppte : *(uint64_t*)ppte; reg_t ppn = pte >> PTE_PPN_SHIFT; if (PTE_TABLE(pte)) { // next level of page table base = ppn << PGSHIFT; - } else if ((pte & PTE_U) ? supervisor && pum : !supervisor) { + } else if ((pte & PTE_U) ? supervisor && !sum : !supervisor) { break; } else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) { break; @@ -213,9 +197,18 @@ reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode) type == LOAD ? !(pte & PTE_R) && !(mxr && (pte & PTE_X)) : !((pte & PTE_R) && (pte & PTE_W))) { break; + } else if ((ppn & ((reg_t(1) << ptshift) - 1)) != 0) { + break; } else { + reg_t ad = PTE_A | ((type == STORE) * PTE_D); +#ifdef RISCV_ENABLE_DIRTY // set accessed and possibly dirty bits. - *(uint32_t*)ppte |= PTE_A | ((type == STORE) * PTE_D); + *(uint32_t*)ppte |= ad; +#else + // take exception if access or possibly dirty bit is not set. + if ((pte & ad) != ad) + break; +#endif // for superpage mappings, make a fake leaf PTE for the TLB's benefit. reg_t vpn = addr >> PGSHIFT; reg_t value = (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT; @@ -223,7 +216,13 @@ reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode) } } - return -1; +fail: + switch (type) { + case FETCH: throw trap_instruction_page_fault(addr); + case LOAD: throw trap_load_page_fault(addr); + case STORE: throw trap_store_page_fault(addr); + default: abort(); + } } void mmu_t::register_memtracer(memtracer_t* t) diff --git a/riscv/mmu.h b/riscv/mmu.h index 34bcf99..f70a969 100644 --- a/riscv/mmu.h +++ b/riscv/mmu.h @@ -30,6 +30,11 @@ struct icache_entry_t { insn_fetch_t data; }; +struct tlb_entry_t { + char* host_offset; + reg_t target_offset; +}; + class trigger_matched_t { public: @@ -51,16 +56,38 @@ public: mmu_t(sim_t* sim, processor_t* proc); ~mmu_t(); + inline reg_t misaligned_load(reg_t addr, size_t size) + { +#ifdef RISCV_ENABLE_MISALIGNED + reg_t res = 0; + for (size_t i = 0; i < size; i++) + res += (reg_t)load_uint8(addr + i) << (i * 8); + return res; +#else + throw trap_load_address_misaligned(addr); +#endif + } + + inline void misaligned_store(reg_t addr, reg_t data, size_t size) + { +#ifdef RISCV_ENABLE_MISALIGNED + for (size_t i = 0; i < size; i++) + store_uint8(addr + i, data >> (i * 8)); +#else + throw trap_store_address_misaligned(addr); +#endif + } + // template for functions that load an aligned value from memory #define load_func(type) \ inline type##_t load_##type(reg_t addr) { \ - if (addr & (sizeof(type##_t)-1)) \ - throw trap_load_address_misaligned(addr); \ + if (unlikely(addr & (sizeof(type##_t)-1))) \ + return misaligned_load(addr, sizeof(type##_t)); \ reg_t vpn = addr >> PGSHIFT; \ if (likely(tlb_load_tag[vpn % TLB_ENTRIES] == vpn)) \ - return *(type##_t*)(tlb_data[vpn % TLB_ENTRIES] + addr); \ + return *(type##_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr); \ if (unlikely(tlb_load_tag[vpn % TLB_ENTRIES] == (vpn | TLB_CHECK_TRIGGERS))) { \ - type##_t data = *(type##_t*)(tlb_data[vpn % TLB_ENTRIES] + addr); \ + type##_t data = *(type##_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr); \ if (!matched_trigger) { \ matched_trigger = trigger_exception(OPERATION_LOAD, addr, data); \ if (matched_trigger) \ @@ -88,18 +115,18 @@ public: // template for functions that store an aligned value to memory #define store_func(type) \ void store_##type(reg_t addr, type##_t val) { \ - if (addr & (sizeof(type##_t)-1)) \ - throw trap_store_address_misaligned(addr); \ + if (unlikely(addr & (sizeof(type##_t)-1))) \ + return misaligned_store(addr, val, sizeof(type##_t)); \ reg_t vpn = addr >> PGSHIFT; \ if (likely(tlb_store_tag[vpn % TLB_ENTRIES] == vpn)) \ - *(type##_t*)(tlb_data[vpn % TLB_ENTRIES] + addr) = val; \ + *(type##_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr) = val; \ else if (unlikely(tlb_store_tag[vpn % TLB_ENTRIES] == (vpn | TLB_CHECK_TRIGGERS))) { \ if (!matched_trigger) { \ matched_trigger = trigger_exception(OPERATION_STORE, addr, val); \ if (matched_trigger) \ throw *matched_trigger; \ } \ - *(type##_t*)(tlb_data[vpn % TLB_ENTRIES] + addr) = val; \ + *(type##_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr) = val; \ } \ else \ store_slow_path(addr, sizeof(type##_t), (const uint8_t*)&val); \ @@ -115,6 +142,9 @@ public: auto lhs = load_##type(addr); \ store_##type(addr, f(lhs)); \ return lhs; \ + } catch (trap_load_page_fault& t) { \ + /* AMO faults should be reported as store faults */ \ + throw trap_store_page_fault(t.get_badaddr()); \ } catch (trap_load_access_fault& t) { \ /* AMO faults should be reported as store faults */ \ throw trap_store_access_fault(t.get_badaddr()); \ @@ -140,29 +170,29 @@ public: inline icache_entry_t* refill_icache(reg_t addr, icache_entry_t* entry) { - const uint16_t* iaddr = translate_insn_addr(addr); - insn_bits_t insn = *iaddr; + auto tlb_entry = translate_insn_addr(addr); + insn_bits_t insn = *(uint16_t*)(tlb_entry.host_offset + addr); int length = insn_length(insn); if (likely(length == 4)) { - insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr(addr + 2) << 16; + insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr_to_host(addr + 2) << 16; } else if (length == 2) { insn = (int16_t)insn; } else if (length == 6) { - insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr(addr + 4) << 32; - insn |= (insn_bits_t)*(const uint16_t*)translate_insn_addr(addr + 2) << 16; + insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr_to_host(addr + 4) << 32; + insn |= (insn_bits_t)*(const uint16_t*)translate_insn_addr_to_host(addr + 2) << 16; } else { static_assert(sizeof(insn_bits_t) == 8, "insn_bits_t must be uint64_t"); - insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr(addr + 6) << 48; - insn |= (insn_bits_t)*(const uint16_t*)translate_insn_addr(addr + 4) << 32; - insn |= (insn_bits_t)*(const uint16_t*)translate_insn_addr(addr + 2) << 16; + insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr_to_host(addr + 6) << 48; + insn |= (insn_bits_t)*(const uint16_t*)translate_insn_addr_to_host(addr + 4) << 32; + insn |= (insn_bits_t)*(const uint16_t*)translate_insn_addr_to_host(addr + 2) << 16; } insn_fetch_t fetch = {proc->decode_insn(insn), insn}; entry->tag = addr; entry->data = fetch; - reg_t paddr = sim->mem_to_addr((char*)iaddr); + reg_t paddr = tlb_entry.target_offset + addr;; if (tracer.interested_in_range(paddr, paddr + 1, FETCH)) { entry->tag = -1; tracer.trace(paddr, length, FETCH); @@ -203,39 +233,43 @@ private: // If a TLB tag has TLB_CHECK_TRIGGERS set, then the MMU must check for a // trigger match before completing an access. static const reg_t TLB_CHECK_TRIGGERS = reg_t(1) << 63; - char* tlb_data[TLB_ENTRIES]; + tlb_entry_t tlb_data[TLB_ENTRIES]; reg_t tlb_insn_tag[TLB_ENTRIES]; reg_t tlb_load_tag[TLB_ENTRIES]; reg_t tlb_store_tag[TLB_ENTRIES]; // finish translation on a TLB miss and update the TLB - void refill_tlb(reg_t vaddr, reg_t paddr, access_type type); + tlb_entry_t refill_tlb(reg_t vaddr, reg_t paddr, char* host_addr, access_type type); const char* fill_from_mmio(reg_t vaddr, reg_t paddr); // perform a page table walk for a given VA; set referenced/dirty bits reg_t walk(reg_t addr, access_type type, reg_t prv); // handle uncommon cases: TLB misses, page faults, MMIO - const uint16_t* fetch_slow_path(reg_t addr); + tlb_entry_t fetch_slow_path(reg_t addr); void load_slow_path(reg_t addr, reg_t len, uint8_t* bytes); void store_slow_path(reg_t addr, reg_t len, const uint8_t* bytes); reg_t translate(reg_t addr, access_type type); // ITLB lookup - inline const uint16_t* translate_insn_addr(reg_t addr) { + inline tlb_entry_t translate_insn_addr(reg_t addr) { reg_t vpn = addr >> PGSHIFT; if (likely(tlb_insn_tag[vpn % TLB_ENTRIES] == vpn)) - return (uint16_t*)(tlb_data[vpn % TLB_ENTRIES] + addr); + return tlb_data[vpn % TLB_ENTRIES]; if (unlikely(tlb_insn_tag[vpn % TLB_ENTRIES] == (vpn | TLB_CHECK_TRIGGERS))) { - uint16_t* ptr = (uint16_t*)(tlb_data[vpn % TLB_ENTRIES] + addr); + uint16_t* ptr = (uint16_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr); int match = proc->trigger_match(OPERATION_EXECUTE, addr, *ptr); if (match >= 0) throw trigger_matched_t(match, OPERATION_EXECUTE, addr, *ptr); - return ptr; + return tlb_data[vpn % TLB_ENTRIES]; } return fetch_slow_path(addr); } + inline const uint16_t* translate_insn_addr_to_host(reg_t addr) { + return (uint16_t*)(translate_insn_addr(addr).host_offset + addr); + } + inline trigger_matched_t *trigger_exception(trigger_operation_t operation, reg_t address, reg_t data) { @@ -260,4 +294,35 @@ private: friend class processor_t; }; +struct vm_info { + int levels; + int idxbits; + int ptesize; + reg_t ptbase; +}; + +inline vm_info decode_vm_info(int xlen, reg_t prv, reg_t sptbr) +{ + if (prv == PRV_M) { + return {0, 0, 0, 0}; + } else if (prv <= PRV_S && xlen == 32) { + switch (get_field(sptbr, SPTBR32_MODE)) { + case SPTBR_MODE_OFF: return {0, 0, 0, 0}; + case SPTBR_MODE_SV32: return {2, 10, 4, (sptbr & SPTBR32_PPN) << PGSHIFT}; + default: abort(); + } + } else if (prv <= PRV_S && xlen == 64) { + switch (get_field(sptbr, SPTBR64_MODE)) { + case SPTBR_MODE_OFF: return {0, 0, 0, 0}; + case SPTBR_MODE_SV39: return {3, 9, 8, (sptbr & SPTBR64_PPN) << PGSHIFT}; + case SPTBR_MODE_SV48: return {4, 9, 8, (sptbr & SPTBR64_PPN) << PGSHIFT}; + case SPTBR_MODE_SV57: return {5, 9, 8, (sptbr & SPTBR64_PPN) << PGSHIFT}; + case SPTBR_MODE_SV64: return {6, 9, 8, (sptbr & SPTBR64_PPN) << PGSHIFT}; + default: abort(); + } + } else { + abort(); + } +} + #endif diff --git a/riscv/opcodes.h b/riscv/opcodes.h new file mode 100644 index 0000000..34c089e --- /dev/null +++ b/riscv/opcodes.h @@ -0,0 +1,244 @@ +#include "encoding.h" + +#define ZERO 0 +#define T0 5 +#define S0 8 +#define S1 9 + +static uint32_t bits(uint32_t value, unsigned int hi, unsigned int lo) { + return (value >> lo) & ((1 << (hi+1-lo)) - 1); +} + +static uint32_t bit(uint32_t value, unsigned int b) { + return (value >> b) & 1; +} + +static uint32_t jal(unsigned int rd, uint32_t imm) __attribute__ ((unused)); +static uint32_t jal(unsigned int rd, uint32_t imm) { + return (bit(imm, 20) << 31) | + (bits(imm, 10, 1) << 21) | + (bit(imm, 11) << 20) | + (bits(imm, 19, 12) << 12) | + (rd << 7) | + MATCH_JAL; +} + +static uint32_t csrsi(unsigned int csr, uint16_t imm) __attribute__ ((unused)); +static uint32_t csrsi(unsigned int csr, uint16_t imm) { + return (csr << 20) | + (bits(imm, 4, 0) << 15) | + MATCH_CSRRSI; +} + +static uint32_t sw(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); +static uint32_t sw(unsigned int src, unsigned int base, uint16_t offset) +{ + return (bits(offset, 11, 5) << 25) | + (src << 20) | + (base << 15) | + (bits(offset, 4, 0) << 7) | + MATCH_SW; +} + +static uint32_t sd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); +static uint32_t sd(unsigned int src, unsigned int base, uint16_t offset) +{ + return (bits(offset, 11, 5) << 25) | + (src << 20) | + (base << 15) | + (bits(offset, 4, 0) << 7) | + MATCH_SD; +} + +static uint32_t sh(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); +static uint32_t sh(unsigned int src, unsigned int base, uint16_t offset) +{ + return (bits(offset, 11, 5) << 25) | + (src << 20) | + (base << 15) | + (bits(offset, 4, 0) << 7) | + MATCH_SH; +} + +static uint32_t sb(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); +static uint32_t sb(unsigned int src, unsigned int base, uint16_t offset) +{ + return (bits(offset, 11, 5) << 25) | + (src << 20) | + (base << 15) | + (bits(offset, 4, 0) << 7) | + MATCH_SB; +} + +static uint32_t ld(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused)); +static uint32_t ld(unsigned int rd, unsigned int base, uint16_t offset) +{ + return (bits(offset, 11, 0) << 20) | + (base << 15) | + (bits(rd, 4, 0) << 7) | + MATCH_LD; +} + +static uint32_t lw(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused)); +static uint32_t lw(unsigned int rd, unsigned int base, uint16_t offset) +{ + return (bits(offset, 11, 0) << 20) | + (base << 15) | + (bits(rd, 4, 0) << 7) | + MATCH_LW; +} + +static uint32_t lh(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused)); +static uint32_t lh(unsigned int rd, unsigned int base, uint16_t offset) +{ + return (bits(offset, 11, 0) << 20) | + (base << 15) | + (bits(rd, 4, 0) << 7) | + MATCH_LH; +} + +static uint32_t lb(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused)); +static uint32_t lb(unsigned int rd, unsigned int base, uint16_t offset) +{ + return (bits(offset, 11, 0) << 20) | + (base << 15) | + (bits(rd, 4, 0) << 7) | + MATCH_LB; +} + +static uint32_t csrw(unsigned int source, unsigned int csr) __attribute__ ((unused)); +static uint32_t csrw(unsigned int source, unsigned int csr) { + return (csr << 20) | (source << 15) | MATCH_CSRRW; +} + +static uint32_t addi(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused)); +static uint32_t addi(unsigned int dest, unsigned int src, uint16_t imm) +{ + return (bits(imm, 11, 0) << 20) | + (src << 15) | + (dest << 7) | + MATCH_ADDI; +} + +static uint32_t csrr(unsigned int rd, unsigned int csr) __attribute__ ((unused)); +static uint32_t csrr(unsigned int rd, unsigned int csr) { + return (csr << 20) | (rd << 7) | MATCH_CSRRS; +} + +static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); +static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset) +{ + return (bits(offset, 11, 5) << 25) | + (bits(src, 4, 0) << 20) | + (base << 15) | + (bits(offset, 4, 0) << 7) | + MATCH_FSW; +} + +static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); +static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset) +{ + return (bits(offset, 11, 5) << 25) | + (bits(src, 4, 0) << 20) | + (base << 15) | + (bits(offset, 4, 0) << 7) | + MATCH_FSD; +} + +static uint32_t flw(unsigned int dest, unsigned int base, uint16_t offset) __attribute__ ((unused)); +static uint32_t flw(unsigned int dest, unsigned int base, uint16_t offset) +{ + return (bits(offset, 11, 0) << 20) | + (base << 15) | + (bits(dest, 4, 0) << 7) | + MATCH_FLW; +} + +static uint32_t fld(unsigned int dest, unsigned int base, uint16_t offset) __attribute__ ((unused)); +static uint32_t fld(unsigned int dest, unsigned int base, uint16_t offset) +{ + return (bits(offset, 11, 0) << 20) | + (base << 15) | + (bits(dest, 4, 0) << 7) | + MATCH_FLD; +} + +static uint32_t ebreak(void) __attribute__ ((unused)); +static uint32_t ebreak(void) { return MATCH_EBREAK; } +static uint32_t ebreak_c(void) __attribute__ ((unused)); +static uint32_t ebreak_c(void) { return MATCH_C_EBREAK; } + +static uint32_t dret(void) __attribute__ ((unused)); +static uint32_t dret(void) { return MATCH_DRET; } + +static uint32_t fence_i(void) __attribute__ ((unused)); +static uint32_t fence_i(void) +{ + return MATCH_FENCE_I; +} + +/* +static uint32_t lui(unsigned int dest, uint32_t imm) __attribute__ ((unused)); +static uint32_t lui(unsigned int dest, uint32_t imm) +{ + return (bits(imm, 19, 0) << 12) | + (dest << 7) | + MATCH_LUI; +} + +static uint32_t csrci(unsigned int csr, uint16_t imm) __attribute__ ((unused)); +static uint32_t csrci(unsigned int csr, uint16_t imm) { + return (csr << 20) | + (bits(imm, 4, 0) << 15) | + MATCH_CSRRCI; +} + +static uint32_t li(unsigned int dest, uint16_t imm) __attribute__ ((unused)); +static uint32_t li(unsigned int dest, uint16_t imm) +{ + return addi(dest, 0, imm); +} + +static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); +static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset) +{ + return (bits(offset, 11, 5) << 25) | + (bits(src, 4, 0) << 20) | + (base << 15) | + (bits(offset, 4, 0) << 7) | + MATCH_FSD; +} + +static uint32_t ori(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused)); +static uint32_t ori(unsigned int dest, unsigned int src, uint16_t imm) +{ + return (bits(imm, 11, 0) << 20) | + (src << 15) | + (dest << 7) | + MATCH_ORI; +} + +static uint32_t nop(void) __attribute__ ((unused)); +static uint32_t nop(void) +{ + return addi(0, 0, 0); +} +*/ + +static uint32_t xori(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused)); +static uint32_t xori(unsigned int dest, unsigned int src, uint16_t imm) +{ + return (bits(imm, 11, 0) << 20) | + (src << 15) | + (dest << 7) | + MATCH_XORI; +} + +static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt) __attribute__ ((unused)); +static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt) +{ + return (bits(shamt, 4, 0) << 20) | + (src << 15) | + (dest << 7) | + MATCH_SRLI; +} diff --git a/riscv/processor.cc b/riscv/processor.cc index 9a6a4e2..1e3573d 100644 --- a/riscv/processor.cc +++ b/riscv/processor.cc @@ -7,7 +7,6 @@ #include "sim.h" #include "mmu.h" #include "disasm.h" -#include "gdbserver.h" #include <cinttypes> #include <cmath> #include <cstdlib> @@ -22,7 +21,8 @@ processor_t::processor_t(const char* isa, sim_t* sim, uint32_t id, bool halt_on_reset) - : debug(false), sim(sim), ext(NULL), id(id), halt_on_reset(halt_on_reset) + : debug(false), halt_request(false), sim(sim), ext(NULL), id(id), + halt_on_reset(halt_on_reset) { parse_isa_string(isa); register_base_instructions(); @@ -118,7 +118,6 @@ void state_t::reset() memset(this, 0, sizeof(*this)); prv = PRV_M; pc = DEFAULT_RSTVEC; - mtvec = DEFAULT_MTVEC; load_reservation = -1; tselect = 0; for (unsigned int i = 0; i < num_triggers; i++) @@ -172,12 +171,22 @@ void processor_t::take_interrupt(reg_t pending_interrupts) reg_t sie = get_field(state.mstatus, MSTATUS_SIE); reg_t s_enabled = state.prv < PRV_S || (state.prv == PRV_S && sie); - enabled_interrupts |= pending_interrupts & state.mideleg & -s_enabled; + if (enabled_interrupts == 0) + enabled_interrupts = pending_interrupts & state.mideleg & -s_enabled; if (enabled_interrupts) throw trap_t(((reg_t)1 << (max_xlen-1)) | ctz(enabled_interrupts)); } +static int xlen_to_uxl(int xlen) +{ + if (xlen == 32) + return 1; + if (xlen == 64) + return 2; + abort(); +} + void processor_t::set_privilege(reg_t prv) { assert(prv <= PRV_M); @@ -193,7 +202,7 @@ void processor_t::enter_debug_mode(uint8_t cause) state.dcsr.prv = state.prv; set_privilege(PRV_M); state.dpc = state.pc; - state.pc = DEBUG_ROM_START; + state.pc = debug_rom_entry(); } void processor_t::take_trap(trap_t& t, reg_t epc) @@ -206,6 +215,15 @@ void processor_t::take_trap(trap_t& t, reg_t epc) t.get_badaddr()); } + if (state.dcsr.cause) { + if (t.cause() == CAUSE_BREAKPOINT) { + state.pc = debug_rom_entry(); + } else { + state.pc = DEBUG_ROM_TVEC; + } + return; + } + if (t.cause() == CAUSE_BREAKPOINT && ( (state.prv == PRV_M && state.dcsr.ebreakm) || (state.prv == PRV_H && state.dcsr.ebreakh) || @@ -215,15 +233,11 @@ void processor_t::take_trap(trap_t& t, reg_t epc) return; } - if (state.dcsr.cause) { - state.pc = DEBUG_ROM_EXCEPTION; - return; - } - // by default, trap to M-mode, unless delegated to S-mode reg_t bit = t.cause(); reg_t deleg = state.medeleg; - if (bit & ((reg_t)1 << (max_xlen-1))) + bool interrupt = (bit & ((reg_t)1 << (max_xlen-1))) != 0; + if (interrupt) deleg = state.mideleg, bit &= ~((reg_t)1 << (max_xlen-1)); if (state.prv <= PRV_S && bit < max_xlen && ((deleg >> bit) & 1)) { // handle the trap in S-mode @@ -234,20 +248,21 @@ void processor_t::take_trap(trap_t& t, reg_t epc) state.sbadaddr = t.get_badaddr(); reg_t s = state.mstatus; - s = set_field(s, MSTATUS_SPIE, get_field(s, MSTATUS_UIE << state.prv)); + s = set_field(s, MSTATUS_SPIE, get_field(s, MSTATUS_SIE)); s = set_field(s, MSTATUS_SPP, state.prv); s = set_field(s, MSTATUS_SIE, 0); set_csr(CSR_MSTATUS, s); set_privilege(PRV_S); } else { - state.pc = state.mtvec; + reg_t vector = (state.mtvec & 1) && interrupt ? 4*bit : 0; + state.pc = (state.mtvec & ~(reg_t)1) + vector; state.mepc = epc; state.mcause = t.cause(); if (t.has_badaddr()) state.mbadaddr = t.get_badaddr(); reg_t s = state.mstatus; - s = set_field(s, MSTATUS_MPIE, get_field(s, MSTATUS_UIE << state.prv)); + s = set_field(s, MSTATUS_MPIE, get_field(s, MSTATUS_MIE)); s = set_field(s, MSTATUS_MPP, state.prv); s = set_field(s, MSTATUS_MIE, 0); set_csr(CSR_MSTATUS, s); @@ -259,18 +274,23 @@ void processor_t::take_trap(trap_t& t, reg_t epc) void processor_t::disasm(insn_t insn) { + static uint64_t last_pc = 1, last_bits; + static uint64_t executions = 1; + uint64_t bits = insn.bits() & ((1ULL << (8 * insn_length(insn.bits()))) - 1); - fprintf(stderr, "core %3d: 0x%016" PRIx64 " (0x%08" PRIx64 ") %s\n", - id, state.pc, bits, disassembler->disassemble(insn).c_str()); -} + if (last_pc != state.pc || last_bits != bits) { + if (executions != 1) { + fprintf(stderr, "core %3d: Executed %" PRIx64 " times\n", id, executions); + } -static bool validate_vm(int max_xlen, reg_t vm) -{ - if (max_xlen == 64 && (vm == VM_SV39 || vm == VM_SV48)) - return true; - if (max_xlen == 32 && vm == VM_SV32) - return true; - return vm == VM_MBARE; + fprintf(stderr, "core %3d: 0x%016" PRIx64 " (0x%08" PRIx64 ") %s\n", + id, state.pc, bits, disassembler->disassemble(insn).c_str()); + last_pc = state.pc; + last_bits = bits; + executions = 1; + } else { + executions++; + } } int processor_t::paddr_bits() @@ -301,15 +321,14 @@ void processor_t::set_csr(int which, reg_t val) break; case CSR_MSTATUS: { if ((val ^ state.mstatus) & - (MSTATUS_VM | MSTATUS_MPP | MSTATUS_MPRV | MSTATUS_PUM | MSTATUS_MXR)) + (MSTATUS_MPP | MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_MXR)) mmu->flush_tlb(); reg_t mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE - | MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_PUM - | MSTATUS_MPP | MSTATUS_MXR | (ext ? MSTATUS_XS : 0); - - if (validate_vm(max_xlen, get_field(val, MSTATUS_VM))) - mask |= MSTATUS_VM; + | MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM + | MSTATUS_MPP | MSTATUS_MXR | MSTATUS_TW | MSTATUS_TVM + | MSTATUS_TSR | MSTATUS_UXL | MSTATUS_SXL | + (ext ? MSTATUS_XS : 0); state.mstatus = (state.mstatus & ~mask) | (val & mask); @@ -320,8 +339,9 @@ void processor_t::set_csr(int which, reg_t val) else state.mstatus = set_field(state.mstatus, MSTATUS64_SD, dirty); - // spike supports the notion of xlen < max_xlen, but current priv spec - // doesn't provide a mechanism to run RV32 software on an RV64 machine + state.mstatus = set_field(state.mstatus, MSTATUS_UXL, xlen_to_uxl(max_xlen)); + state.mstatus = set_field(state.mstatus, MSTATUS_SXL, xlen_to_uxl(max_xlen)); + // U-XLEN == S-XLEN == M-XLEN xlen = max_xlen; break; } @@ -355,15 +375,15 @@ void processor_t::set_csr(int which, reg_t val) case CSR_MCYCLEH: state.minstret = (val << 32) | (state.minstret << 32 >> 32); break; - case CSR_MUCOUNTEREN: - state.mucounteren = val; + case CSR_SCOUNTEREN: + state.scounteren = val; break; - case CSR_MSCOUNTEREN: - state.mscounteren = val; + case CSR_MCOUNTEREN: + state.mcounteren = val; break; case CSR_SSTATUS: { reg_t mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_SPP | SSTATUS_FS - | SSTATUS_XS | SSTATUS_PUM; + | SSTATUS_XS | SSTATUS_SUM | SSTATUS_MXR; return set_csr(CSR_MSTATUS, (state.mstatus & ~mask) | (val & mask)); } case CSR_SIP: { @@ -374,8 +394,13 @@ void processor_t::set_csr(int which, reg_t val) return set_csr(CSR_MIE, (state.mie & ~state.mideleg) | (val & state.mideleg)); case CSR_SPTBR: { - // upper bits of sptbr are the ASID; we only support ASID = 0 - state.sptbr = val & (((reg_t)1 << (paddr_bits() - PGSHIFT)) - 1); + mmu->flush_tlb(); + if (max_xlen == 32) + state.sptbr = val & (SPTBR32_PPN | SPTBR32_MODE); + if (max_xlen == 64 && (get_field(val, SPTBR64_MODE) == SPTBR_MODE_OFF || + get_field(val, SPTBR64_MODE) == SPTBR_MODE_SV39 || + get_field(val, SPTBR64_MODE) == SPTBR_MODE_SV48)) + state.sptbr = val & (SPTBR64_PPN | SPTBR64_MODE); break; } case CSR_SEPC: state.sepc = val; break; @@ -384,7 +409,7 @@ void processor_t::set_csr(int which, reg_t val) case CSR_SCAUSE: state.scause = val; break; case CSR_SBADADDR: state.sbadaddr = val; break; case CSR_MEPC: state.mepc = val; break; - case CSR_MTVEC: state.mtvec = val >> 2 << 2; break; + case CSR_MTVEC: state.mtvec = val & ~(reg_t)2; break; case CSR_MSCRATCH: state.mscratch = val; break; case CSR_MCAUSE: state.mcause = val; break; case CSR_MBADADDR: state.mbadaddr = val; break; @@ -463,8 +488,11 @@ void processor_t::set_csr(int which, reg_t val) reg_t processor_t::get_csr(int which) { - reg_t ctr_en = state.prv == PRV_U ? state.mucounteren : - state.prv == PRV_S ? state.mscounteren : -1U; + uint32_t ctr_en = -1; + if (state.prv < PRV_M) + ctr_en &= state.mcounteren; + if (state.prv < PRV_S) + ctr_en &= state.scounteren; bool ctr_ok = (ctr_en >> (which & 31)) & 1; if (ctr_ok) { @@ -475,7 +503,7 @@ reg_t processor_t::get_csr(int which) } if (which >= CSR_MHPMCOUNTER3 && which <= CSR_MHPMCOUNTER31) return 0; - if (xlen == 32 && which >= CSR_MHPMCOUNTER3 && which <= CSR_MHPMCOUNTER31) + if (xlen == 32 && which >= CSR_MHPMCOUNTER3H && which <= CSR_MHPMCOUNTER31H) return 0; if (which >= CSR_MHPMEVENT3 && which <= CSR_MHPMEVENT31) return 0; @@ -510,11 +538,11 @@ reg_t processor_t::get_csr(int which) if (xlen == 32) return state.minstret >> 32; break; - case CSR_MUCOUNTEREN: return state.mucounteren; - case CSR_MSCOUNTEREN: return state.mscounteren; + case CSR_SCOUNTEREN: return state.scounteren; + case CSR_MCOUNTEREN: return state.mcounteren; case CSR_SSTATUS: { reg_t mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_SPP | SSTATUS_FS - | SSTATUS_XS | SSTATUS_PUM; + | SSTATUS_XS | SSTATUS_SUM | SSTATUS_UXL; reg_t sstatus = state.mstatus & mask; if ((sstatus & SSTATUS_FS) == SSTATUS_FS || (sstatus & SSTATUS_XS) == SSTATUS_XS) @@ -530,7 +558,10 @@ reg_t processor_t::get_csr(int which) if (max_xlen > xlen) return state.scause | ((state.scause >> (max_xlen-1)) << (xlen-1)); return state.scause; - case CSR_SPTBR: return state.sptbr; + case CSR_SPTBR: + if (get_field(state.mstatus, MSTATUS_TVM)) + require_privilege(PRV_M); + return state.sptbr; case CSR_SSCRATCH: return state.sscratch; case CSR_MSTATUS: return state.mstatus; case CSR_MIP: return state.mip; @@ -584,19 +615,15 @@ reg_t processor_t::get_csr(int which) { uint32_t v = 0; v = set_field(v, DCSR_XDEBUGVER, 1); - v = set_field(v, DCSR_NDRESET, 0); - v = set_field(v, DCSR_FULLRESET, 0); - v = set_field(v, DCSR_PRV, state.dcsr.prv); - v = set_field(v, DCSR_STEP, state.dcsr.step); - v = set_field(v, DCSR_DEBUGINT, sim->debug_module.get_interrupt(id)); - v = set_field(v, DCSR_STOPCYCLE, 0); - v = set_field(v, DCSR_STOPTIME, 0); v = set_field(v, DCSR_EBREAKM, state.dcsr.ebreakm); v = set_field(v, DCSR_EBREAKH, state.dcsr.ebreakh); v = set_field(v, DCSR_EBREAKS, state.dcsr.ebreaks); v = set_field(v, DCSR_EBREAKU, state.dcsr.ebreaku); - v = set_field(v, DCSR_HALT, state.dcsr.halt); + v = set_field(v, DCSR_STOPCYCLE, 0); + v = set_field(v, DCSR_STOPTIME, 0); v = set_field(v, DCSR_CAUSE, state.dcsr.cause); + v = set_field(v, DCSR_STEP, state.dcsr.step); + v = set_field(v, DCSR_PRV, state.dcsr.prv); return v; } case CSR_DPC: @@ -604,12 +631,12 @@ reg_t processor_t::get_csr(int which) case CSR_DSCRATCH: return state.dscratch; } - throw trap_illegal_instruction(); + throw trap_illegal_instruction(0); } reg_t illegal_instruction(processor_t* p, insn_t insn, reg_t pc) { - throw trap_illegal_instruction(); + throw trap_illegal_instruction(0); } insn_func_t processor_t::decode_insn(insn_t insn) @@ -692,6 +719,17 @@ void processor_t::register_base_instructions() bool processor_t::load(reg_t addr, size_t len, uint8_t* bytes) { + switch (addr) + { + case 0: + if (len <= 4) { + memset(bytes, 0, len); + bytes[0] = get_field(state.mip, MIP_MSIP); + return true; + } + break; + } + return false; } @@ -700,14 +738,14 @@ bool processor_t::store(reg_t addr, size_t len, const uint8_t* bytes) switch (addr) { case 0: - state.mip &= ~MIP_MSIP; - if (bytes[0] & 1) - state.mip |= MIP_MSIP; - return true; - - default: - return false; + if (len <= 4) { + state.mip = set_field(state.mip, MIP_MSIP, bytes[0]); + return true; + } + break; } + + return false; } void processor_t::trigger_updated() diff --git a/riscv/processor.h b/riscv/processor.h index 0224f10..071f458 100644 --- a/riscv/processor.h +++ b/riscv/processor.h @@ -8,6 +8,7 @@ #include <string> #include <vector> #include <map> +#include "debug_rom/debug_rom_defines.h" class processor_t; class mmu_t; @@ -105,8 +106,8 @@ struct state_t reg_t mip; reg_t medeleg; reg_t mideleg; - uint32_t mucounteren; - uint32_t mscounteren; + uint32_t mcounteren; + uint32_t scounteren; reg_t sepc; reg_t sbadaddr; reg_t sscratch; @@ -191,6 +192,14 @@ public: bool debug; // When true, take the slow simulation path. bool slow_path(); + bool halted() { return state.dcsr.cause ? true : false; } + bool halt_request; + // The unique debug rom address that this hart jumps to when entering debug + // mode. Rely on the fact that spike hart IDs start at 0 and are consecutive. + uint32_t debug_rom_entry() { + fprintf(stderr, "Debug_rom_entry called for id %d = %x\n", id, DEBUG_ROM_ENTRY + 4*id); + return DEBUG_ROM_ENTRY + 4 * id; + } // Return the index of a trigger that matched, or -1. inline int trigger_match(trigger_operation_t operation, reg_t address, reg_t data) @@ -306,7 +315,7 @@ private: friend class sim_t; friend class mmu_t; - friend class rtc_t; + friend class clint_t; friend class extension_t; void parse_isa_string(const char* isa); diff --git a/riscv/remote_bitbang.cc b/riscv/remote_bitbang.cc new file mode 100644 index 0000000..21306dd --- /dev/null +++ b/riscv/remote_bitbang.cc @@ -0,0 +1,180 @@ +#include <arpa/inet.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <algorithm> +#include <cassert> +#include <cstdio> + +#include "remote_bitbang.h" + +#if 1 +# define D(x) x +#else +# define D(x) +#endif + +/////////// remote_bitbang_t + +remote_bitbang_t::remote_bitbang_t(uint16_t port, jtag_dtm_t *tap) : + tap(tap), + socket_fd(0), + client_fd(0), + recv_start(0), + recv_end(0) +{ + socket_fd = socket(AF_INET, SOCK_STREAM, 0); + if (socket_fd == -1) { + fprintf(stderr, "remote_bitbang failed to make socket: %s (%d)\n", + strerror(errno), errno); + abort(); + } + + fcntl(socket_fd, F_SETFL, O_NONBLOCK); + int reuseaddr = 1; + if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, + sizeof(int)) == -1) { + fprintf(stderr, "remote_bitbang failed setsockopt: %s (%d)\n", + strerror(errno), errno); + abort(); + } + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(port); + + if (bind(socket_fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { + fprintf(stderr, "remote_bitbang failed to bind socket: %s (%d)\n", + strerror(errno), errno); + abort(); + } + + if (listen(socket_fd, 1) == -1) { + fprintf(stderr, "remote_bitbang failed to listen on socket: %s (%d)\n", + strerror(errno), errno); + abort(); + } + + socklen_t addrlen = sizeof(addr); + if (getsockname(socket_fd, (struct sockaddr *) &addr, &addrlen) == -1) { + fprintf(stderr, "remote_bitbang getsockname failed: %s (%d)\n", + strerror(errno), errno); + abort(); + } + + printf("Listening for remote bitbang connection on port %d.\n", + ntohs(addr.sin_port)); + fflush(stdout); +} + +void remote_bitbang_t::accept() +{ + client_fd = ::accept(socket_fd, NULL, NULL); + if (client_fd == -1) { + if (errno == EAGAIN) { + // No client waiting to connect right now. + } else { + fprintf(stderr, "failed to accept on socket: %s (%d)\n", strerror(errno), + errno); + abort(); + } + } else { + fcntl(client_fd, F_SETFL, O_NONBLOCK); + } +} + +void remote_bitbang_t::tick() +{ + if (client_fd > 0) { + execute_commands(); + } else { + this->accept(); + } +} + +void remote_bitbang_t::execute_commands() +{ + static char send_buf[buf_size]; + unsigned total_processed = 0; + bool quit = false; + bool in_rti = tap->state() == RUN_TEST_IDLE; + bool entered_rti = false; + while (1) { + if (recv_start < recv_end) { + unsigned send_offset = 0; + while (recv_start < recv_end) { + uint8_t command = recv_buf[recv_start]; + + switch (command) { + case 'B': /* fprintf(stderr, "*BLINK*\n"); */ break; + case 'b': /* fprintf(stderr, "_______\n"); */ break; + case 'r': tap->reset(); break; + case '0': tap->set_pins(0, 0, 0); break; + case '1': tap->set_pins(0, 0, 1); break; + case '2': tap->set_pins(0, 1, 0); break; + case '3': tap->set_pins(0, 1, 1); break; + case '4': tap->set_pins(1, 0, 0); break; + case '5': tap->set_pins(1, 0, 1); break; + case '6': tap->set_pins(1, 1, 0); break; + case '7': tap->set_pins(1, 1, 1); break; + case 'R': send_buf[send_offset++] = tap->tdo() ? '1' : '0'; break; + case 'Q': quit = true; break; + default: + fprintf(stderr, "remote_bitbang got unsupported command '%c'\n", + command); + } + recv_start++; + total_processed++; + if (!in_rti && tap->state() == RUN_TEST_IDLE) { + entered_rti = true; + break; + } + in_rti = false; + } + unsigned sent = 0; + while (sent < send_offset) { + ssize_t bytes = write(client_fd, send_buf + sent, send_offset); + if (bytes == -1) { + fprintf(stderr, "failed to write to socket: %s (%d)\n", strerror(errno), errno); + abort(); + } + sent += bytes; + } + } + + if (total_processed > buf_size || quit || entered_rti) { + // Don't go forever, because that could starve the main simulation. + break; + } + + recv_start = 0; + recv_end = read(client_fd, recv_buf, buf_size); + + if (recv_end == -1) { + if (errno == EAGAIN) { + break; + } else { + fprintf(stderr, "remote_bitbang failed to read on socket: %s (%d)\n", + strerror(errno), errno); + abort(); + } + } + + if (quit) { + fprintf(stderr, "Remote Bitbang received 'Q'\n"); + } + + if (recv_end == 0 || quit) { + // The remote disconnected. + fprintf(stderr, "Received nothing. Quitting.\n"); + close(client_fd); + client_fd = 0; + break; + } + } +} diff --git a/riscv/remote_bitbang.h b/riscv/remote_bitbang.h new file mode 100644 index 0000000..1db4d55 --- /dev/null +++ b/riscv/remote_bitbang.h @@ -0,0 +1,34 @@ +#ifndef REMOTE_BITBANG_H +#define REMOTE_BITBANG_H + +#include <stdint.h> + +#include "jtag_dtm.h" + +class remote_bitbang_t +{ +public: + // Create a new server, listening for connections from localhost on the given + // port. + remote_bitbang_t(uint16_t port, jtag_dtm_t *tap); + + // Do a bit of work. + void tick(); + +private: + jtag_dtm_t *tap; + + int socket_fd; + int client_fd; + + static const ssize_t buf_size = 64 * 1024; + char recv_buf[buf_size]; + ssize_t recv_start, recv_end; + + // Check for a client connecting, and accept if there is one. + void accept(); + // Execute any commands the client has for us. + void execute_commands(); +}; + +#endif diff --git a/riscv/riscv.ac b/riscv/riscv.ac index 7b48be6..68bcdb5 100644 --- a/riscv/riscv.ac +++ b/riscv/riscv.ac @@ -32,3 +32,13 @@ AC_ARG_ENABLE([histogram], AS_HELP_STRING([--enable-histogram], [Enable PC histo AS_IF([test "x$enable_histogram" = "xyes"], [ AC_DEFINE([RISCV_ENABLE_HISTOGRAM],,[Enable PC histogram generation]) ]) + +AC_ARG_ENABLE([dirty], AS_HELP_STRING([--enable-dirty], [Enable hardware management of PTE accessed and dirty bits])) +AS_IF([test "x$enable_dirty" = "xyes"], [ + AC_DEFINE([RISCV_ENABLE_DIRTY],,[Enable hardware management of PTE accessed and dirty bits]) +]) + +AC_ARG_ENABLE([misaligned], AS_HELP_STRING([--enable-misaligned], [Enable hardware support for misaligned loads and stores])) +AS_IF([test "x$enable_misaligned" = "xyes"], [ + AC_DEFINE([RISCV_ENABLE_MISALIGNED],,[Enable hardware support for misaligned loads and stores]) +]) diff --git a/riscv/riscv.mk.in b/riscv/riscv.mk.in index 552187a..05e316a 100644 --- a/riscv/riscv.mk.in +++ b/riscv/riscv.mk.in @@ -23,8 +23,9 @@ riscv_hdrs = \ rocc.h \ insn_template.h \ mulhi.h \ - gdbserver.h \ debug_module.h \ + remote_bitbang.h \ + jtag_dtm.h \ riscv_precompiled_hdrs = \ insn_template.h \ @@ -44,9 +45,10 @@ riscv_srcs = \ regnames.cc \ devices.cc \ rom.cc \ - rtc.cc \ - gdbserver.cc \ + clint.cc \ debug_module.cc \ + remote_bitbang.cc \ + jtag_dtm.cc \ $(riscv_gen_srcs) \ riscv_test_srcs = @@ -180,9 +182,9 @@ riscv_insn_list = \ fmul_d \ fmul_s \ fmv_d_x \ - fmv_s_x \ + fmv_w_x \ fmv_x_d \ - fmv_x_s \ + fmv_x_w \ fnmadd_d \ fnmadd_s \ fnmsub_d \ @@ -227,7 +229,7 @@ riscv_insn_list = \ sc_d \ sc_w \ sd \ - sfence_vm \ + sfence_vma \ sh \ sll \ slli \ diff --git a/riscv/rtc.cc b/riscv/rtc.cc deleted file mode 100644 index 22f318c..0000000 --- a/riscv/rtc.cc +++ /dev/null @@ -1,34 +0,0 @@ -#include "devices.h" -#include "processor.h" - -rtc_t::rtc_t(std::vector<processor_t*>& procs) - : procs(procs), regs(1 + procs.size()) -{ -} - -bool rtc_t::load(reg_t addr, size_t len, uint8_t* bytes) -{ - if (addr + len > size()) - return false; - memcpy(bytes, (uint8_t*)®s[0] + addr, len); - return true; -} - -bool rtc_t::store(reg_t addr, size_t len, const uint8_t* bytes) -{ - if (addr + len > size() || addr < 8) - return false; - memcpy((uint8_t*)®s[0] + addr, bytes, len); - increment(0); - return true; -} - -void rtc_t::increment(reg_t inc) -{ - regs[0] += inc; - for (size_t i = 0; i < procs.size(); i++) { - procs[i]->state.mip &= ~MIP_MTIP; - if (regs[0] >= regs[1+i]) - procs[i]->state.mip |= MIP_MTIP; - } -} diff --git a/riscv/sim.cc b/riscv/sim.cc index b455105..42d60a1 100644 --- a/riscv/sim.cc +++ b/riscv/sim.cc @@ -2,7 +2,7 @@ #include "sim.h" #include "mmu.h" -#include "gdbserver.h" +#include "remote_bitbang.h" #include <map> #include <iostream> #include <sstream> @@ -10,6 +10,9 @@ #include <cstdlib> #include <cassert> #include <signal.h> +#include <unistd.h> +#include <sys/wait.h> +#include <sys/types.h> volatile bool ctrlc_pressed = false; static void handle_signal(int sig) @@ -20,28 +23,19 @@ static void handle_signal(int sig) signal(sig, &handle_signal); } -sim_t::sim_t(const char* isa, size_t nprocs, size_t mem_mb, bool halted, +sim_t::sim_t(const char* isa, size_t nprocs, bool halted, reg_t start_pc, + std::vector<std::pair<reg_t, mem_t*>> mems, const std::vector<std::string>& args) - : htif_t(args), procs(std::max(nprocs, size_t(1))), - current_step(0), current_proc(0), debug(false), gdbserver(NULL) + : htif_t(args), debug_module(this), mems(mems), procs(std::max(nprocs, size_t(1))), + start_pc(start_pc), + current_step(0), current_proc(0), debug(false), remote_bitbang(NULL) { signal(SIGINT, &handle_signal); - // allocate target machine's memory, shrinking it as necessary - // until the allocation succeeds - size_t memsz0 = (size_t)mem_mb << 20; - size_t quantum = 1L << 20; - if (memsz0 == 0) - memsz0 = (size_t)((sizeof(size_t) == 8 ? 4096 : 2048) - 256) << 20; - memsz = memsz0; - while ((mem = (char*)calloc(1, memsz)) == NULL) - memsz = (size_t)(memsz*0.9)/quantum*quantum; + for (auto& x : mems) + bus.add_device(x.first, x.second); - if (memsz != memsz0) - fprintf(stderr, "warning: only got %zu bytes of target mem (wanted %zu)\n", - memsz, memsz0); - - bus.add_device(DEBUG_START, &debug_module); + debug_module.add_device(&bus); debug_mmu = new mmu_t(this, NULL); @@ -49,8 +43,8 @@ sim_t::sim_t(const char* isa, size_t nprocs, size_t mem_mb, bool halted, procs[i] = new processor_t(isa, this, i, halted); } - rtc.reset(new rtc_t(procs)); - make_config_string(); + clint.reset(new clint_t(procs)); + bus.add_device(CLINT_BASE, clint.get()); } sim_t::~sim_t() @@ -58,7 +52,6 @@ sim_t::~sim_t() for (size_t i = 0; i < procs.size(); i++) delete procs[i]; delete debug_mmu; - free(mem); } void sim_thread_main(void* arg) @@ -77,8 +70,8 @@ void sim_t::main() interactive(); else step(INTERLEAVE); - if (gdbserver) { - gdbserver->handle(); + if (remote_bitbang) { + remote_bitbang->tick(); } } } @@ -104,7 +97,7 @@ void sim_t::step(size_t n) procs[current_proc]->yield_load_reservation(); if (++current_proc == procs.size()) { current_proc = 0; - rtc->increment(INTERLEAVE / INSNS_PER_RTC_TICK); + clint->increment(INTERLEAVE / INSNS_PER_RTC_TICK); } host->switch_to(); @@ -150,66 +143,193 @@ bool sim_t::mmio_store(reg_t addr, size_t len, const uint8_t* bytes) return bus.store(addr, len, bytes); } -void sim_t::make_config_string() +static std::string dts_compile(const std::string& dts) { - reg_t rtc_addr = EXT_IO_BASE; - bus.add_device(rtc_addr, rtc.get()); + // Convert the DTS to DTB + int dts_pipe[2]; + pid_t dts_pid; - const int align = 0x1000; - reg_t cpu_addr = rtc_addr + ((rtc->size() - 1) / align + 1) * align; - reg_t cpu_size = align; - - uint32_t reset_vec[8] = { - 0x297 + DRAM_BASE - DEFAULT_RSTVEC, // reset vector - 0x00028067, // jump straight to DRAM_BASE - 0x00000000, // reserved - 0, // config string pointer - 0, 0, 0, 0 // trap vector + if (pipe(dts_pipe) != 0 || (dts_pid = fork()) < 0) { + std::cerr << "Failed to fork dts child: " << strerror(errno) << std::endl; + exit(1); + } + + // Child process to output dts + if (dts_pid == 0) { + close(dts_pipe[0]); + int step, len = dts.length(); + const char *buf = dts.c_str(); + for (int done = 0; done < len; done += step) { + step = write(dts_pipe[1], buf+done, len-done); + if (step == -1) { + std::cerr << "Failed to write dts: " << strerror(errno) << std::endl; + exit(1); + } + } + close(dts_pipe[1]); + exit(0); + } + + pid_t dtb_pid; + int dtb_pipe[2]; + if (pipe(dtb_pipe) != 0 || (dtb_pid = fork()) < 0) { + std::cerr << "Failed to fork dtb child: " << strerror(errno) << std::endl; + exit(1); + } + + // Child process to output dtb + if (dtb_pid == 0) { + dup2(dts_pipe[0], 0); + dup2(dtb_pipe[1], 1); + close(dts_pipe[0]); + close(dts_pipe[1]); + close(dtb_pipe[0]); + close(dtb_pipe[1]); + execl(DTC, DTC, "-O", "dtb", 0); + std::cerr << "Failed to run " DTC ": " << strerror(errno) << std::endl; + exit(1); + } + + close(dts_pipe[1]); + close(dts_pipe[0]); + close(dtb_pipe[1]); + + // Read-out dtb + std::stringstream dtb; + + int got; + char buf[4096]; + while ((got = read(dtb_pipe[0], buf, sizeof(buf))) > 0) { + dtb.write(buf, got); + } + if (got == -1) { + std::cerr << "Failed to read dtb: " << strerror(errno) << std::endl; + exit(1); + } + close(dtb_pipe[0]); + + // Reap children + int status; + waitpid(dts_pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + std::cerr << "Child dts process failed" << std::endl; + exit(1); + } + waitpid(dtb_pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + std::cerr << "Child dtb process failed" << std::endl; + exit(1); + } + + return dtb.str(); +} + +void sim_t::make_dtb() +{ + const int reset_vec_size = 8; + + start_pc = start_pc == reg_t(-1) ? get_entry_point() : start_pc; + reg_t pc_delta = start_pc - DEFAULT_RSTVEC; + reg_t pc_delta_hi = (pc_delta + 0x800U) & ~reg_t(0xfffU); + reg_t pc_delta_lo = pc_delta - pc_delta_hi; + if ((pc_delta_hi >> 31) != 0 && (pc_delta_hi >> 31) != reg_t(-1) >> 31) { + fprintf(stderr, "initial pc %" PRIx64 " out of range\n", pc_delta); + abort(); + } + + uint32_t reset_vec[reset_vec_size] = { + 0x297 + uint32_t(pc_delta_hi), // auipc t0, &pc + 0x597, // auipc a1, &dtb + 0x58593 + ((reset_vec_size - 1) * 4 << 20), // addi a1, a1, &dtb + 0xf1402573, // csrr a0, mhartid + 0x28067 + uint32_t(pc_delta_lo << 20) // jalr zero, t0, &pc }; - reset_vec[3] = DEFAULT_RSTVEC + sizeof(reset_vec); // config string pointer std::vector<char> rom((char*)reset_vec, (char*)reset_vec + sizeof(reset_vec)); std::stringstream s; - s << std::hex << - "platform {\n" - " vendor ucb;\n" - " arch spike;\n" - "};\n" - "rtc {\n" - " addr 0x" << rtc_addr << ";\n" - "};\n" - "ram {\n" - " 0 {\n" - " addr 0x" << DRAM_BASE << ";\n" - " size 0x" << memsz << ";\n" - " };\n" - "};\n" - "core {\n"; + s << std::dec << + "/dts-v1/;\n" + "\n" + "/ {\n" + " #address-cells = <2>;\n" + " #size-cells = <2>;\n" + " compatible = \"ucbbar,spike-bare-dev\";\n" + " model = \"ucbbar,spike-bare\";\n" + " cpus {\n" + " #address-cells = <1>;\n" + " #size-cells = <0>;\n" + " timebase-frequency = <" << (CPU_HZ/INSNS_PER_RTC_TICK) << ">;\n"; for (size_t i = 0; i < procs.size(); i++) { - s << - " " << i << " {\n" - " " << "0 {\n" << // hart 0 on core i - " isa " << procs[i]->isa_string << ";\n" - " timecmp 0x" << (rtc_addr + 8*(1+i)) << ";\n" - " ipi 0x" << cpu_addr << ";\n" - " };\n" - " };\n"; - bus.add_device(cpu_addr, procs[i]); - cpu_addr += cpu_size; + s << " CPU" << i << ": cpu@" << i << " {\n" + " device_type = \"cpu\";\n" + " reg = <" << i << ">;\n" + " status = \"okay\";\n" + " compatible = \"riscv\";\n" + " riscv,isa = \"" << procs[i]->isa_string << "\";\n" + " mmu-type = \"riscv," << (procs[i]->max_xlen <= 32 ? "sv32" : "sv48") << "\";\n" + " clock-frequency = <" << CPU_HZ << ">;\n" + " CPU" << i << "_intc: interrupt-controller {\n" + " #interrupt-cells = <1>;\n" + " interrupt-controller;\n" + " compatible = \"riscv,cpu-intc\";\n" + " };\n" + " };\n"; } - s << "};\n"; - - config_string = s.str(); - rom.insert(rom.end(), config_string.begin(), config_string.end()); - rom.resize((rom.size() / align + 1) * align); + s << " };\n"; + for (auto& m : mems) { + s << std::hex << + " memory@" << m.first << " {\n" + " device_type = \"memory\";\n" + " reg = <0x" << (m.first >> 32) << " 0x" << (m.first & (uint32_t)-1) << + " 0x" << (m.second->size() >> 32) << " 0x" << (m.second->size() & (uint32_t)-1) << ">;\n" + " };\n"; + } + s << " soc {\n" + " #address-cells = <2>;\n" + " #size-cells = <2>;\n" + " compatible = \"ucbbar,spike-bare-soc\", \"simple-bus\";\n" + " ranges;\n" + " clint@" << CLINT_BASE << " {\n" + " compatible = \"riscv,clint0\";\n" + " interrupts-extended = <" << std::dec; + for (size_t i = 0; i < procs.size(); i++) + s << "&CPU" << i << "_intc 3 &CPU" << i << "_intc 7 "; + reg_t clintbs = CLINT_BASE; + reg_t clintsz = CLINT_SIZE; + s << std::hex << ">;\n" + " reg = <0x" << (clintbs >> 32) << " 0x" << (clintbs & (uint32_t)-1) << + " 0x" << (clintsz >> 32) << " 0x" << (clintsz & (uint32_t)-1) << ">;\n" + " };\n" + " };\n" + "};\n"; + + dts = s.str(); + std::string dtb = dts_compile(dts); + + rom.insert(rom.end(), dtb.begin(), dtb.end()); + const int align = 0x1000; + rom.resize((rom.size() + align - 1) / align * align); boot_rom.reset(new rom_device_t(rom)); bus.add_device(DEFAULT_RSTVEC, boot_rom.get()); } +char* sim_t::addr_to_mem(reg_t addr) { + auto desc = bus.find_device(addr); + if (auto mem = dynamic_cast<mem_t*>(desc.second)) + if (addr - desc.first < mem->size()) + return mem->contents() + (addr - desc.first); + return NULL; +} + // htif +void sim_t::reset() +{ + make_dtb(); +} + void sim_t::idle() { target.switch_to(); diff --git a/riscv/sim.h b/riscv/sim.h index 5d165c9..9372cc1 100644 --- a/riscv/sim.h +++ b/riscv/sim.h @@ -13,13 +13,14 @@ #include <memory> class mmu_t; -class gdbserver_t; +class remote_bitbang_t; // this class encapsulates the processors and memory in a RISC-V machine. class sim_t : public htif_t { public: - sim_t(const char* isa, size_t _nprocs, size_t mem_mb, bool halted, + sim_t(const char* isa, size_t _nprocs, bool halted, reg_t start_pc, + std::vector<std::pair<reg_t, mem_t*>> mems, const std::vector<std::string>& args); ~sim_t(); @@ -29,41 +30,42 @@ public: void set_log(bool value); void set_histogram(bool value); void set_procs_debug(bool value); - void set_gdbserver(gdbserver_t* gdbserver) { this->gdbserver = gdbserver; } - const char* get_config_string() { return config_string.c_str(); } + void set_remote_bitbang(remote_bitbang_t* remote_bitbang) { + this->remote_bitbang = remote_bitbang; + } + const char* get_dts() { if (dts.empty()) reset(); return dts.c_str(); } processor_t* get_core(size_t i) { return procs.at(i); } + unsigned nprocs() const { return procs.size(); } + + debug_module_t debug_module; private: - char* mem; // main memory - size_t memsz; // memory size in bytes + std::vector<std::pair<reg_t, mem_t*>> mems; mmu_t* debug_mmu; // debug port into main memory std::vector<processor_t*> procs; - std::string config_string; + reg_t start_pc; + std::string dts; std::unique_ptr<rom_device_t> boot_rom; - std::unique_ptr<rtc_t> rtc; + std::unique_ptr<clint_t> clint; bus_t bus; - debug_module_t debug_module; processor_t* get_core(const std::string& i); void step(size_t n); // step through simulation static const size_t INTERLEAVE = 5000; 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; size_t current_proc; bool debug; bool log; bool histogram_enabled; // provide a histogram of PCs - gdbserver_t* gdbserver; + remote_bitbang_t* remote_bitbang; // memory-mapped I/O routines - bool addr_is_mem(reg_t addr) { - return addr >= DRAM_BASE && addr < DRAM_BASE + memsz; - } - char* addr_to_mem(reg_t addr) { return mem + addr - DRAM_BASE; } - reg_t mem_to_addr(char* x) { return x - mem + DRAM_BASE; } + char* addr_to_mem(reg_t addr); bool mmio_load(reg_t addr, size_t len, uint8_t* bytes); bool mmio_store(reg_t addr, size_t len, const uint8_t* bytes); - void make_config_string(); + void make_dtb(); // presents a prompt for introspection into the simulation void interactive(); @@ -75,6 +77,7 @@ private: void interactive_run_noisy(const std::string& cmd, const std::vector<std::string>& args); void interactive_run_silent(const std::string& cmd, const std::vector<std::string>& args); void interactive_reg(const std::string& cmd, const std::vector<std::string>& args); + void interactive_freg(const std::string& cmd, const std::vector<std::string>& args); void interactive_fregs(const std::string& cmd, const std::vector<std::string>& args); void interactive_fregd(const std::string& cmd, const std::vector<std::string>& args); void interactive_pc(const std::string& cmd, const std::vector<std::string>& args); @@ -82,13 +85,12 @@ private: void interactive_str(const std::string& cmd, const std::vector<std::string>& args); void interactive_until(const std::string& cmd, const std::vector<std::string>& args); reg_t get_reg(const std::vector<std::string>& args); - reg_t get_freg(const std::vector<std::string>& args); + freg_t get_freg(const std::vector<std::string>& args); reg_t get_mem(const std::vector<std::string>& args); reg_t get_pc(const std::vector<std::string>& args); friend class processor_t; friend class mmu_t; - friend class gdbserver_t; // htif friend void sim_thread_main(void*); @@ -96,7 +98,7 @@ private: context_t* host; context_t target; - void reset() { } + void reset(); void idle(); void read_chunk(addr_t taddr, size_t len, void* dst); void write_chunk(addr_t taddr, size_t len, const void* src); diff --git a/riscv/trap.h b/riscv/trap.h index 7f35c5f..91e5223 100644 --- a/riscv/trap.h +++ b/riscv/trap.h @@ -45,16 +45,19 @@ class mem_trap_t : public trap_t }; DECLARE_MEM_TRAP(CAUSE_MISALIGNED_FETCH, instruction_address_misaligned) -DECLARE_MEM_TRAP(CAUSE_FAULT_FETCH, instruction_access_fault) -DECLARE_TRAP(CAUSE_ILLEGAL_INSTRUCTION, illegal_instruction) -DECLARE_TRAP(CAUSE_BREAKPOINT, breakpoint) +DECLARE_MEM_TRAP(CAUSE_FETCH_ACCESS, instruction_access_fault) +DECLARE_MEM_TRAP(CAUSE_ILLEGAL_INSTRUCTION, illegal_instruction) +DECLARE_MEM_TRAP(CAUSE_BREAKPOINT, breakpoint) DECLARE_MEM_TRAP(CAUSE_MISALIGNED_LOAD, load_address_misaligned) DECLARE_MEM_TRAP(CAUSE_MISALIGNED_STORE, store_address_misaligned) -DECLARE_MEM_TRAP(CAUSE_FAULT_LOAD, load_access_fault) -DECLARE_MEM_TRAP(CAUSE_FAULT_STORE, store_access_fault) +DECLARE_MEM_TRAP(CAUSE_LOAD_ACCESS, load_access_fault) +DECLARE_MEM_TRAP(CAUSE_STORE_ACCESS, store_access_fault) DECLARE_TRAP(CAUSE_USER_ECALL, user_ecall) DECLARE_TRAP(CAUSE_SUPERVISOR_ECALL, supervisor_ecall) DECLARE_TRAP(CAUSE_HYPERVISOR_ECALL, hypervisor_ecall) DECLARE_TRAP(CAUSE_MACHINE_ECALL, machine_ecall) +DECLARE_MEM_TRAP(CAUSE_FETCH_PAGE_FAULT, instruction_page_fault) +DECLARE_MEM_TRAP(CAUSE_LOAD_PAGE_FAULT, load_page_fault) +DECLARE_MEM_TRAP(CAUSE_STORE_PAGE_FAULT, store_page_fault) #endif diff --git a/spike_main/disasm.cc b/spike_main/disasm.cc index bdbef9c..56c6fe6 100644 --- a/spike_main/disasm.cc +++ b/spike_main/disasm.cc @@ -418,7 +418,6 @@ disassembler_t::disassembler_t(int xlen) DEFINE_NOARG(ebreak); DEFINE_NOARG(uret); DEFINE_NOARG(sret); - DEFINE_NOARG(hret); DEFINE_NOARG(mret); DEFINE_NOARG(fence); DEFINE_NOARG(fence_i); @@ -457,13 +456,13 @@ disassembler_t::disassembler_t(int xlen) DEFINE_XFTYPE(fcvt_s_w); DEFINE_XFTYPE(fcvt_s_wu); DEFINE_XFTYPE(fcvt_s_wu); - DEFINE_XFTYPE(fmv_s_x); + DEFINE_XFTYPE(fmv_w_x); DEFINE_FXTYPE(fcvt_l_s); DEFINE_FXTYPE(fcvt_lu_s); DEFINE_FXTYPE(fcvt_w_s); DEFINE_FXTYPE(fcvt_wu_s); DEFINE_FXTYPE(fclass_s); - DEFINE_FXTYPE(fmv_x_s); + DEFINE_FXTYPE(fmv_x_w); DEFINE_FXTYPE(feq_s); DEFINE_FXTYPE(flt_s); DEFINE_FXTYPE(fle_s); diff --git a/spike_main/spike.cc b/spike_main/spike.cc index 424bf37..23f8e49 100644 --- a/spike_main/spike.cc +++ b/spike_main/spike.cc @@ -2,7 +2,7 @@ #include "sim.h" #include "mmu.h" -#include "gdbserver.h" +#include "remote_bitbang.h" #include "cachesim.h" #include "extension.h" #include <dlfcn.h> @@ -18,38 +18,72 @@ static void help() fprintf(stderr, "usage: spike [host options] <target program> [target options]\n"); fprintf(stderr, "Host Options:\n"); fprintf(stderr, " -p<n> Simulate <n> processors [default 1]\n"); - fprintf(stderr, " -m<n> Provide <n> MiB of target memory [default 4096]\n"); + fprintf(stderr, " -m<n> Provide <n> MiB of target memory [default 2048]\n"); + fprintf(stderr, " -m<a:m,b:n,...> Provide memory regions of size m and n bytes\n"); + fprintf(stderr, " at base addresses a and b (with 4 KiB alignment)\n"); fprintf(stderr, " -d Interactive debug mode\n"); fprintf(stderr, " -g Track histogram of PCs\n"); fprintf(stderr, " -l Generate a log of execution\n"); fprintf(stderr, " -h Print this help message\n"); fprintf(stderr, " -H Start halted, allowing a debugger to connect\n"); fprintf(stderr, " --isa=<name> RISC-V ISA string [default %s]\n", DEFAULT_ISA); + fprintf(stderr, " --pc=<address> Override ELF entry point\n"); fprintf(stderr, " --ic=<S>:<W>:<B> Instantiate a cache model with S sets,\n"); fprintf(stderr, " --dc=<S>:<W>:<B> W ways, and B-byte blocks (with S and\n"); fprintf(stderr, " --l2=<S>:<W>:<B> B both powers of 2).\n"); fprintf(stderr, " --extension=<name> Specify RoCC Extension\n"); fprintf(stderr, " --extlib=<name> Shared library to load\n"); - fprintf(stderr, " --gdb-port=<port> Listen on <port> for gdb to connect\n"); - fprintf(stderr, " --dump-config-string Print platform configuration string and exit\n"); + fprintf(stderr, " --rbb-port=<port> Listen on <port> for remote bitbang connection\n"); + fprintf(stderr, " --dump-dts Print device tree string and exit\n"); exit(1); } +static std::vector<std::pair<reg_t, mem_t*>> make_mems(const char* arg) +{ + // handle legacy mem argument + char* p; + auto mb = strtoull(arg, &p, 0); + if (*p == 0) { + reg_t size = reg_t(mb) << 20; + return std::vector<std::pair<reg_t, mem_t*>>(1, std::make_pair(reg_t(DRAM_BASE), new mem_t(size))); + } + + // handle base/size tuples + std::vector<std::pair<reg_t, mem_t*>> res; + while (true) { + auto base = strtoull(arg, &p, 0); + if (!*p || *p != ':') + help(); + auto size = strtoull(p + 1, &p, 0); + if ((size | base) % PGSIZE != 0) + help(); + res.push_back(std::make_pair(reg_t(base), new mem_t(size))); + if (!*p) + break; + if (*p != ',') + help(); + arg = p + 1; + } + return res; +} + int main(int argc, char** argv) { bool debug = false; bool halted = false; bool histogram = false; bool log = false; - bool dump_config_string = false; + bool dump_dts = false; size_t nprocs = 1; - size_t mem_mb = 0; + reg_t start_pc = reg_t(-1); + std::vector<std::pair<reg_t, mem_t*>> mems; std::unique_ptr<icache_sim_t> ic; std::unique_ptr<dcache_sim_t> dc; std::unique_ptr<cache_sim_t> l2; std::function<extension_t*()> extension; const char* isa = DEFAULT_ISA; - uint16_t gdb_port = 0; + uint16_t rbb_port = 0; + bool use_rbb = false; option_parser_t parser; parser.help(&help); @@ -58,16 +92,17 @@ int main(int argc, char** argv) parser.option('g', 0, 0, [&](const char* s){histogram = true;}); parser.option('l', 0, 0, [&](const char* s){log = true;}); parser.option('p', 0, 1, [&](const char* s){nprocs = atoi(s);}); - parser.option('m', 0, 1, [&](const char* s){mem_mb = atoi(s);}); + parser.option('m', 0, 1, [&](const char* s){mems = make_mems(s);}); // I wanted to use --halted, but for some reason that doesn't work. parser.option('H', 0, 0, [&](const char* s){halted = true;}); - parser.option(0, "gdb-port", 1, [&](const char* s){gdb_port = atoi(s);}); + parser.option(0, "rbb-port", 1, [&](const char* s){use_rbb = true; rbb_port = atoi(s);}); + parser.option(0, "pc", 1, [&](const char* s){start_pc = strtoull(s, 0, 0);}); parser.option(0, "ic", 1, [&](const char* s){ic.reset(new icache_sim_t(s));}); parser.option(0, "dc", 1, [&](const char* s){dc.reset(new dcache_sim_t(s));}); parser.option(0, "l2", 1, [&](const char* s){l2.reset(cache_sim_t::construct(s, "L2$"));}); parser.option(0, "isa", 1, [&](const char* s){isa = s;}); parser.option(0, "extension", 1, [&](const char* s){extension = find_extension(s);}); - parser.option(0, "dump-config-string", 0, [&](const char *s){dump_config_string = true;}); + parser.option(0, "dump-dts", 0, [&](const char *s){dump_dts = true;}); parser.option(0, "extlib", 1, [&](const char *s){ void *lib = dlopen(s, RTLD_NOW | RTLD_GLOBAL); if (lib == NULL) { @@ -78,15 +113,19 @@ int main(int argc, char** argv) auto argv1 = parser.parse(argv); std::vector<std::string> htif_args(argv1, (const char*const*)argv + argc); - sim_t s(isa, nprocs, mem_mb, halted, htif_args); - std::unique_ptr<gdbserver_t> gdbserver; - if (gdb_port) { - gdbserver = std::unique_ptr<gdbserver_t>(new gdbserver_t(gdb_port, &s)); - s.set_gdbserver(&(*gdbserver)); + if (mems.empty()) + mems = make_mems("2048"); + + sim_t s(isa, nprocs, halted, start_pc, mems, htif_args); + std::unique_ptr<remote_bitbang_t> remote_bitbang((remote_bitbang_t *) NULL); + std::unique_ptr<jtag_dtm_t> jtag_dtm(new jtag_dtm_t(&s.debug_module)); + if (use_rbb) { + remote_bitbang.reset(new remote_bitbang_t(rbb_port, &(*jtag_dtm))); + s.set_remote_bitbang(&(*remote_bitbang)); } - if (dump_config_string) { - printf("%s", s.get_config_string()); + if (dump_dts) { + printf("%s", s.get_dts()); return 0; } |