From 9f523d8a66a3741f12a053ebe89a491e749e7acd Mon Sep 17 00:00:00 2001 From: Martin Erhart Date: Wed, 2 Jul 2025 11:18:23 +0100 Subject: [PATCH] Support label-based sideband commands for printing register contents --- fesvr/htif.cc | 45 +++++++++++++++++++++++++++++++++++++++------ fesvr/htif.h | 11 ++++++++++- riscv/execute.cc | 8 ++++++++ riscv/sim.cc | 40 ++++++++++++++++++++++++++++++++++++++++ riscv/sim.h | 9 +++++++++ riscv/simif.h | 6 ++++++ 6 files changed, 112 insertions(+), 7 deletions(-) diff --git a/fesvr/htif.cc b/fesvr/htif.cc index 15f79bf5e6..5ae02efdc8 100644 --- a/fesvr/htif.cc +++ b/fesvr/htif.cc @@ -80,6 +80,22 @@ htif_t::~htif_t() { for (auto d : dynamic_devices) delete d; + + if (intrinsic_label_log_stream) { + intrinsic_label_log_stream->close(); + delete intrinsic_label_log_stream; + } +} + +std::ostream &htif_t::log_label_intrinsic_output(void) { + if (intrinsic_label_log_stream) + return *intrinsic_label_log_stream; + + if (intrinsic_label_log_file.empty()) + return std::cout; + + intrinsic_label_log_stream = new std::ofstream(intrinsic_label_log_file); + return *intrinsic_label_log_stream; } void htif_t::start() @@ -183,11 +199,8 @@ void htif_t::load_symbols(std::map& symbols) fprintf(stderr, "warning: tohost and fromhost symbols not in ELF; can't communicate with target\n"); } - for (auto i : symbols) { - auto it = addr2symbol.find(i.second); - if ( it == addr2symbol.end()) - addr2symbol[i.second] = i.first; - } + for (auto i : symbols) + addr2symbol[i.second].push_back(i.first); } void htif_t::load_program() @@ -211,7 +224,20 @@ const char* htif_t::get_symbol(uint64_t addr) if(it == addr2symbol.end()) return nullptr; - return it->second.c_str(); + if (it->second.empty()) + return nullptr; + + return it->second[0].c_str(); +} + +std::vector htif_t::get_all_symbols(uint64_t addr) +{ + auto it = addr2symbol.find(addr); + + if(it == addr2symbol.end()) + return std::vector(); + + return it->second; } bool htif_t::should_exit() const { @@ -362,6 +388,9 @@ void htif_t::parse_arguments(int argc, char ** argv) case HTIF_LONG_OPTIONS_OPTIND + 7: symbol_elfs.push_back(optarg); break; + case HTIF_LONG_OPTIONS_OPTIND + 8: + intrinsic_label_log_file = optarg; + break; case '?': if (!opterr) break; @@ -408,6 +437,10 @@ void htif_t::parse_arguments(int argc, char ** argv) c = HTIF_LONG_OPTIONS_OPTIND + 7; optarg = optarg + 12; } + else if (arg.find("+intrinsic-label-log=") == 0) { + c = HTIF_LONG_OPTIONS_OPTIND + 8; + optarg = optarg + 21; + } else if (arg.find("+permissive-off") == 0) { if (opterr) throw std::invalid_argument("Found +permissive-off when not parsing permissively"); diff --git a/fesvr/htif.h b/fesvr/htif.h index 380e24aff7..a90a17a00d 100644 --- a/fesvr/htif.h +++ b/fesvr/htif.h @@ -79,6 +79,9 @@ class htif_t : public chunked_memif_t // Given an address, return symbol from addr2symbol map const char* get_symbol(uint64_t addr); + std::vector get_all_symbols(uint64_t addr); + + std::ostream &log_label_intrinsic_output(void); // Return true if the simulation should exit due to a signal, // or end-of-test from HTIF, or an instruction limit. @@ -112,7 +115,10 @@ class htif_t : public chunked_memif_t std::vector payloads; std::vector symbol_elfs; - std::map addr2symbol; + std::map> addr2symbol; + + std::string intrinsic_label_log_file; + std::ofstream *intrinsic_label_log_stream = nullptr; friend class memif_t; friend class syscall_t; @@ -142,6 +148,8 @@ class htif_t : public chunked_memif_t +payload=PATH\n\ --symbol-elf=PATH Populate the symbol table with the ELF file at PATH\n\ +symbol-elf=PATH\n\ + --intrinsic-label-log=PATH File for output caused by intrinsic labels\n\ + +intrinsic-label-log=PATH\n\ \n\ HOST OPTIONS (currently unsupported)\n\ --disk=DISK Add DISK device. Use a ramdisk since this isn't\n\ @@ -162,6 +170,7 @@ TARGET (RISC-V BINARY) OPTIONS\n\ {"signature-granularity", required_argument, 0, HTIF_LONG_OPTIONS_OPTIND + 5 }, \ {"target-argument", required_argument, 0, HTIF_LONG_OPTIONS_OPTIND + 6 }, \ {"symbol-elf", required_argument, 0, HTIF_LONG_OPTIONS_OPTIND + 7 }, \ +{"intrinsic-label-log", required_argument, 0, HTIF_LONG_OPTIONS_OPTIND + 8 }, \ {0, 0, 0, 0} #endif // __HTIF_H diff --git a/riscv/execute.cc b/riscv/execute.cc index 1b572a7b72..646255213a 100644 --- a/riscv/execute.cc +++ b/riscv/execute.cc @@ -289,6 +289,8 @@ void processor_t::step(size_t n) throw wait_for_interrupt_t(); } + sim->process_intrinsic_labels_pre_addr(state, state.pc); + in_wfi = false; insn_fetch_t fetch = mmu->load_insn(pc); if (debug && !state.serialized) @@ -296,6 +298,8 @@ void processor_t::step(size_t n) pc = execute_insn_logged(this, pc, fetch); advance_pc(); + sim->process_intrinsic_labels_post_addr(state, state.pc); + // Resume from debug mode in critical error if (state.critical_error && !state.debug_mode) { if (state.dcsr->read() & DCSR_CETRIG) { @@ -310,6 +314,8 @@ void processor_t::step(size_t n) } else while (instret < n) { + sim->process_intrinsic_labels_pre_addr(state, state.pc); + // Main simulation loop, fast path. for (auto ic_entry = _mmu->access_icache(pc); ; ) { auto fetch = ic_entry->data; @@ -324,6 +330,8 @@ void processor_t::step(size_t n) } advance_pc(); + + sim->process_intrinsic_labels_post_addr(state, state.pc); } } catch(trap_t& t) diff --git a/riscv/sim.cc b/riscv/sim.cc index 388d729027..a7e58cb762 100644 --- a/riscv/sim.cc +++ b/riscv/sim.cc @@ -416,6 +416,46 @@ const char* sim_t::get_symbol(uint64_t paddr) return htif_t::get_symbol(paddr); } +std::vector sim_t::get_all_symbols(uint64_t paddr) +{ + return htif_t::get_all_symbols(paddr); +} + +void sim_t::process_label_intrinsic(const state_t &state, const std::string &sym) { + size_t dot_pos = sym.find('.'); + auto prefix = sym.substr(0, dot_pos); + if (prefix == "printreg") + process_print_register_label_intrinsic(state, sym.substr(dot_pos + 1)); +} + +void sim_t::process_print_register_label_intrinsic(const state_t &state, const std::string &sym) { + size_t dot_pos = sym.find('.'); + if (dot_pos != std::string::npos) { + std::string reg_str = sym.substr(1, dot_pos - 1); + std::string id_str = sym.substr(dot_pos + 1, std::string::npos - dot_pos - 1); + + int reg_num = std::stoi(reg_str); + if (reg_num >= 0 && reg_num < 32) + htif_t::log_label_intrinsic_output() << id_str << "=0x" << std::hex << state.XPR[reg_num] << std::endl; + } +} + +void sim_t::process_intrinsic_labels_pre_addr(const state_t &state, uint64_t addr) { + for (const std::string &sym : get_all_symbols(addr)) { + std::string prefix("spike.pre."); + if (sym.substr(0, prefix.size()) == prefix) + process_label_intrinsic(state, sym.substr(prefix.size())); + } +} + +void sim_t::process_intrinsic_labels_post_addr(const state_t &state, uint64_t addr) { + for (const std::string &sym : get_all_symbols(addr)) { + std::string prefix("spike.post."); + if (sym.substr(0, prefix.size()) == prefix) + process_label_intrinsic(state, sym.substr(prefix.size())); + } +} + // htif void sim_t::reset() diff --git a/riscv/sim.h b/riscv/sim.h index da04a88284..05f6d8874d 100644 --- a/riscv/sim.h +++ b/riscv/sim.h @@ -112,6 +112,10 @@ class sim_t : public htif_t, public simif_t void set_rom(); virtual const char* get_symbol(uint64_t paddr) override; + virtual std::vector get_all_symbols(uint64_t paddr) override; + + virtual void process_intrinsic_labels_pre_addr(const state_t &state, uint64_t addr) override; + virtual void process_intrinsic_labels_post_addr(const state_t &state, uint64_t addr) override; // presents a prompt for introspection into the simulation void interactive(); @@ -157,6 +161,11 @@ class sim_t : public htif_t, public simif_t virtual size_t chunk_max_size() override { return 8; } virtual endianness_t get_target_endianness() const override; +private: + // label intrinsic handling + void process_label_intrinsic(const state_t &state, const std::string &sym); + void process_print_register_label_intrinsic(const state_t &state, const std::string &sym); + public: // Initialize this after procs, because in debug_module_t::reset() we // enumerate processors, which segfaults if procs hasn't been initialized diff --git a/riscv/simif.h b/riscv/simif.h index aeab5dba77..25c4c07c3f 100644 --- a/riscv/simif.h +++ b/riscv/simif.h @@ -8,6 +8,7 @@ #include "cfg.h" class processor_t; +struct state_t; class mmu_t; // this is the interface to the simulator used by the processors and memory @@ -24,10 +25,15 @@ class simif_t // Callback for processors to let the simulation know they were reset. virtual void proc_reset(unsigned id) = 0; + // used to process intrinsic labels + virtual void process_intrinsic_labels_pre_addr(const state_t &state, uint64_t addr) = 0; + virtual void process_intrinsic_labels_post_addr(const state_t &state, uint64_t addr) = 0; + virtual const cfg_t &get_cfg() const = 0; virtual const std::map& get_harts() const = 0; virtual const char* get_symbol(uint64_t paddr) = 0; + virtual std::vector get_all_symbols(uint64_t paddr) = 0; virtual ~simif_t() = default;