From 619d782174d7b9489ca4ad6302f6e5f6d77f4265 Mon Sep 17 00:00:00 2001 From: Eyck Jentzsch Date: Tue, 7 Oct 2025 16:13:47 +0200 Subject: [PATCH 01/77] initial version of multi-threading implementation --- CMakeLists.txt | 1 + src/sysc/core2sc_adapter.h | 65 ++++- src/sysc/core_complex.cpp | 7 - src/sysc/core_complex_mt.cpp | 526 +++++++++++++++++++++++++++++++++++ src/sysc/core_complex_mt.h | 226 +++++++++++++++ src/sysc/sc2core_if.h | 2 + 6 files changed, 815 insertions(+), 12 deletions(-) create mode 100644 src/sysc/core_complex_mt.cpp create mode 100644 src/sysc/core_complex_mt.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 377c6fd..08c10b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -261,6 +261,7 @@ if(TARGET scc-sysc) project(dbt-rise-riscv_sc VERSION 1.0.0) set(LIB_SOURCES src/sysc/core_complex.cpp + src/sysc/core_complex_mt.cpp src/sysc/register_cores.cpp src/sysc/register_rv32gcv.cpp src/sysc/register_rv64gcv.cpp diff --git a/src/sysc/core2sc_adapter.h b/src/sysc/core2sc_adapter.h index 368a5ac..353bb81 100644 --- a/src/sysc/core2sc_adapter.h +++ b/src/sysc/core2sc_adapter.h @@ -35,15 +35,18 @@ #ifndef _SYSC_CORE2SC_ADAPTER_H_ #define _SYSC_CORE2SC_ADAPTER_H_ -#include "core_complex.h" +#include "core_complex_if.h" #include "sc2core_if.h" #include #include #include #include #include +#include #include +#include #include + namespace sysc { template class core2sc_adapter : public PLAT, public sc2core_if { public: @@ -51,6 +54,7 @@ template class core2sc_adapter : public PLAT, public sc2core_if using core = typename PLAT::core; using reg_t = typename PLAT::reg_t; using phys_addr_t = typename PLAT::phys_addr_t; + using mutex_t = std::shared_mutex; core2sc_adapter(sysc::riscv::core_complex_if* owner) : owner(owner) { this->csr_rd_cb[iss::arch::time] = MK_CSR_RD_CB(read_time); @@ -68,6 +72,18 @@ template class core2sc_adapter : public PLAT, public sc2core_if this->register_csr_wr = util::delegate::from(this); } + void setup_mt() override { + this->set_hartid = util::delegate::from(this); + this->set_irq_count = util::delegate::from(this); + this->get_mode = util::delegate::from(this); + this->get_state = util::delegate::from(this); + this->get_interrupt_execution = util::delegate::from(this); + this->set_interrupt_execution = util::delegate::from(this); + this->local_irq = util::delegate::from(this); + this->register_csr_rd = util::delegate::from(this); + this->register_csr_wr = util::delegate::from(this); + } + virtual ~core2sc_adapter() {} void notify_phase(iss::arch_if::exec_phase p) { @@ -88,8 +104,9 @@ template class core2sc_adapter : public PLAT, public sc2core_if std::stringstream s; s << "[p:" << lvl[this->reg.PRIV] << ";s:0x" << std::hex << std::setfill('0') << std::setw(sizeof(reg_t) * 2) << (reg_t)this->state.mstatus << std::dec << ";c:" << this->reg.icount + this->cycle_offset << "]"; - SCCDEBUG(owner->hier_name()) << "disass: " << "0x" << std::setw(16) << std::right << std::setfill('0') << std::hex << pc - << "\t\t" << std::setw(40) << std::setfill(' ') << std::left << instr << s.str(); + SCCDEBUG(owner->hier_name()) << "disass: " + << "0x" << std::setw(16) << std::right << std::setfill('0') << std::hex << pc << "\t\t" + << std::setw(40) << std::setfill(' ') << std::left << instr << s.str(); } }; @@ -172,22 +189,46 @@ template class core2sc_adapter : public PLAT, public sc2core_if SCCDEBUG(owner->hier_name()) << "Sleeping until interrupt"; PLAT::wait_until(flags); while(this->reg.pending_trap == 0 && (this->csr[iss::arch::mip] & this->csr[iss::arch::mie]) == 0) { - sc_core::wait(wfi_evt); + sc_core::wait(wfi_evt.event()); } } private: void _set_mhartid(unsigned id) { PLAT::set_mhartid(id); } + void _set_mhartid_mt(unsigned id) { + std::unique_lock lock(sync_mtx); + PLAT::set_mhartid(id); + } void _set_irq_num(unsigned num) { PLAT::set_irq_num(num); } + void _set_irq_num_mt(unsigned num) { + std::unique_lock lock(sync_mtx); + PLAT::set_irq_num(num); + } uint32_t _get_mode() { return this->reg.PRIV; } + uint32_t _get_mode_mt() { + std::shared_lock lock(sync_mtx); + return this->reg.PRIV; + } void _set_interrupt_execution(bool v) { this->interrupt_sim = v ? 1 : 0; } + void _set_interrupt_execution_mt(bool v) { + std::unique_lock lock(sync_mtx); + this->interrupt_sim = v ? 1 : 0; + } bool _get_interrupt_execution() { return this->interrupt_sim; } + bool _get_interrupt_execution_mt() { + std::shared_lock lock(sync_mtx); + return this->interrupt_sim; + } uint64_t _get_state() { return this->state.mstatus.backing.val; } + uint64_t _get_state_mt() { + std::shared_lock lock(sync_mtx); + return this->state.mstatus.backing.val; + } void _local_irq(short id, bool value) { reg_t mask = 0; @@ -215,6 +256,10 @@ template class core2sc_adapter : public PLAT, public sc2core_if if(value) SCCTRACE(owner->hier_name()) << "Triggering interrupt " << id << " Pending trap: " << this->reg.pending_trap; } + void _local_irq_mt(short id, bool value) { + std::unique_lock lock(sync_mtx); + _local_irq(id, value); + } void _register_csr_rd(unsigned addr, rd_csr_f cb) { std::function lambda = [cb](unsigned addr, reg_t& r) -> iss::status { @@ -225,15 +270,25 @@ template class core2sc_adapter : public PLAT, public sc2core_if }; this->register_csr(addr, lambda); } + void _register_csr_rd_mt(unsigned addr, rd_csr_f cb) { + std::unique_lock lock(sync_mtx); + _register_csr_rd(addr, cb); + } + void _register_csr_wr(unsigned addr, wr_csr_f cb) { std::function lambda = [cb](unsigned addr, reg_t r) -> iss::status { return cb(addr, r); }; this->register_csr(addr, lambda); } + void _register_csr_wr_mt(unsigned addr, wr_csr_f cb) { + std::unique_lock lock(sync_mtx); + _register_csr_wr(addr, cb); + } sysc::riscv::core_complex_if* const owner{nullptr}; - sc_core::sc_event wfi_evt; + scc::async_event wfi_evt; unsigned to_host_wr_cnt = 0; bool first{true}; + mutex_t sync_mtx; }; } // namespace sysc #endif /* _SYSC_CORE2SC_ADAPTER_H_ */ diff --git a/src/sysc/core_complex.cpp b/src/sysc/core_complex.cpp index 728048e..9ce5f92 100644 --- a/src/sysc/core_complex.cpp +++ b/src/sysc/core_complex.cpp @@ -51,15 +51,8 @@ #include #include #include - // clang-format on -#define STR(X) #X -#define CREATE_CORE(CN) \ - if(type == STR(CN)) { \ - std::tie(cpu, vm) = create_core(backend, gdb_port, hart_id); \ - } else - #ifdef HAS_SCV #include #else diff --git a/src/sysc/core_complex_mt.cpp b/src/sysc/core_complex_mt.cpp new file mode 100644 index 0000000..c4136b9 --- /dev/null +++ b/src/sysc/core_complex_mt.cpp @@ -0,0 +1,526 @@ +/******************************************************************************* + * Copyright (C) 2017, 2018 MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + *******************************************************************************/ + +// clang-format off +#include "core_complex_mt.h" +#include +#include +#include +#include +#include +#include +#include +#include "iss_factory.h" +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif +#include +#include +#include +#include +#include +#include +// clang-format on + +#ifdef HAS_SCV +#include +#else +#include +using namespace scv_tr; +#endif + +#define GET_PROP_VALUE(P) P.get_value() + +#ifdef _MSC_VER +// not #if defined(_WIN32) || defined(_WIN64) because we have strncasecmp in mingw +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +namespace sysc { +namespace riscv { +using namespace std; +using namespace iss; +using namespace logging; +using namespace sc_core; + +namespace { +iss::debugger::encoder_decoder encdec; +std::array lvl = {{'U', 'S', 'H', 'M'}}; +} // namespace + +template +int core_complex_mt::cmd_sysc(int argc, char* argv[], debugger::out_func of, debugger::data_func df, + debugger::target_adapter_if* tgt_adapter) { + if(argc > 1) { + if(strcasecmp(argv[1], "print_time") == 0) { + std::string t = sc_time_stamp().to_string(); + of(t.c_str()); + std::array buf; + encdec.enc_string(t.c_str(), buf.data(), 63); + df(buf.data()); + return Ok; + } else if(strcasecmp(argv[1], "break") == 0) { + sc_time t; + if(argc == 4) { + t = scc::parse_from_string(argv[2], argv[3]); + } else if(argc == 3) { + t = scc::parse_from_string(argv[2]); + } else + return Err; + // no check needed as it is only called if debug server is active + tgt_adapter->add_break_condition([t]() -> unsigned { + SCCTRACE() << "Checking condition at " << sc_time_stamp(); + return sc_time_stamp() >= t ? std::numeric_limits::max() : 0; + }); + return Ok; + } + return Err; + } + return Err; +} + +template void core_complex_mt::reset(uint64_t addr) { vm->reset(addr); } + +template inline std::pair core_complex_mt::load_file(std::string const& name) { + iss::arch_if* cc = vm->get_arch(); + return cc->load_file(name); +}; + +template +void core_complex_mt::create_cpu(std::string const& type, std::string const& backend, unsigned gdb_port, uint32_t hart_id) { + auto& f = sysc::iss_factory::instance(); + if(type.size() == 0 || type == "?") { + std::unordered_map> core_by_backend; + for(auto& e : f.get_names()) { + auto p = e.find(':'); + assert(p != std::string::npos); + core_by_backend[e.substr(p + 1)].push_back(e.substr(0, p)); + } + std::ostringstream os; + os << "Available implementations\n"; + os << "=========================\n"; + for(auto& e : core_by_backend) { + std::sort(std::begin(e.second), std::end(e.second)); + if(os.str().size()) + os << "\n"; + os << " backend " << e.first << ":\n - " << util::join(e.second, "\n - "); + } + SCCINFO(SCMOD) << "\n" << os.str(); + sc_core::sc_stop(); + } else if(type.find(':') == std::string::npos) { + std::tie(core, vm) = f.create(type + ":" + backend, gdb_port, this); + } else { + auto base_isa = type.substr(0, 5); + if(base_isa == "tgc5d" || base_isa == "tgc5e") { + std::tie(core, vm) = f.create(type + "_clic_pmp:" + backend, gdb_port, this); + } else { + std::tie(core, vm) = f.create(type + ":" + backend, gdb_port, this); + } + } + if(!core) { + if(type != "?") + SCCFATAL() << "Could not create cpu for isa " << type << " and backend " << backend; + } else if(!vm) { + if(type != "?") + SCCFATAL() << "Could not create vm for isa " << type << " and backend " << backend; + } else { + core->set_hartid(hart_id); + auto* srv = debugger::server::get(); + if(srv) + tgt_adapter = srv->get_target(0); // FIXME: add core_id + if(tgt_adapter) + tgt_adapter->add_custom_command({"sysc", + [this](int argc, char* argv[], debugger::out_func of, debugger::data_func df) -> int { + return cmd_sysc(argc, argv, of, df, tgt_adapter); + }, + "SystemC sub-commands: break