/******************************************************************************* * 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. * *******************************************************************************/ #include "sysc/core_complex.h" #ifdef CORE_TGC_B #include "iss/arch/riscv_hart_m_p.h" #include "iss/arch/tgc_b.h" using tgc_b_plat_type = iss::arch::riscv_hart_m_p; #endif #include "iss/arch/riscv_hart_m_p.h" #include "iss/arch/tgc_c.h" using tgc_c_plat_type = iss::arch::riscv_hart_m_p; #ifdef CORE_TGC_D #include "iss/arch/riscv_hart_mu_p.h" #include "iss/arch/tgc_d.h" using tgc_d_plat_type = iss::arch::riscv_hart_mu_p; #endif #include "iss/debugger/encoderdecoder.h" #include "iss/debugger/gdb_session.h" #include "iss/debugger/server.h" #include "iss/debugger/target_adapter_if.h" #include "iss/iss.h" #include "iss/vm_types.h" #include "scc/report.h" #include #include #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 WITH_SCV #include #include #endif namespace sysc { namespace tgfs { 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'}}; } template class core_wrapper_t : public PLAT { public: using reg_t = typename arch::traits::reg_t; using phys_addr_t = typename arch::traits::phys_addr_t; using heart_state_t = typename PLAT::hart_state_type; core_wrapper_t(core_complex *owner) : owner(owner) { } uint32_t get_mode() { return this->reg.PRIV; } inline void set_interrupt_execution(bool v) { this->interrupt_sim = v?1:0; } inline bool get_interrupt_execution() { return this->interrupt_sim; } heart_state_t &get_state() { return this->state; } void notify_phase(iss::arch_if::exec_phase p) override { if (p == iss::arch_if::ISTART) owner->sync(this->reg.icount); } sync_type needed_sync() const override { return PRE_SYNC; } void disass_output(uint64_t pc, const std::string instr) override { if (INFO <= Log>::reporting_level() && Output2FILE::stream()) { 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 << "]"; Log>().get(INFO, "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(); } owner->disass_output(pc, instr); }; status read_mem(phys_addr_t addr, unsigned length, uint8_t *const data) override { if (addr.access && access_type::DEBUG) return owner->read_mem_dbg(addr.val, length, data) ? Ok : Err; else { return owner->read_mem(addr.val, length, data, addr.access && access_type::FETCH) ? Ok : Err; } } status write_mem(phys_addr_t addr, unsigned length, const uint8_t *const data) override { if (addr.access && access_type::DEBUG) return owner->write_mem_dbg(addr.val, length, data) ? Ok : Err; else { auto res = owner->write_mem(addr.val, length, data) ? Ok : Err; // clear MTIP on mtimecmp write if (addr.val == 0x2004000) { reg_t val; this->read_csr(arch::mip, val); if (val & (1ULL << 7)) this->write_csr(arch::mip, val & ~(1ULL << 7)); } return res; } } status read_csr(unsigned addr, reg_t &val) override { if((addr==arch::time || addr==arch::timeh) && owner->mtime_o.get_interface(0)){ uint64_t time_val; bool ret = owner->mtime_o->nb_peek(time_val); if (addr == iss::arch::time) { val = static_cast(time_val); } else if (addr == iss::arch::timeh) { if (sizeof(reg_t) != 4) return iss::Err; val = static_cast(time_val >> 32); } return ret?Ok:Err; } else { return PLAT::read_csr(addr, val); } } void wait_until(uint64_t flags) override { SCCDEBUG(owner->name()) << "Sleeping until interrupt"; do { sc_core::wait(wfi_evt); } while (this->reg.pending_trap == 0); PLAT::wait_until(flags); } void local_irq(short id, bool value) { reg_t mask = 0; switch (id) { case 16: // SW mask = 1 << 3; break; case 17: // timer mask = 1 << 7; break; case 18: // external mask = 1 << 11; break; default: /* do nothing*/ break; } if (value) { this->csr[arch::mip] |= mask; wfi_evt.notify(); } else this->csr[arch::mip] &= ~mask; this->check_interrupt(); if(value) SCCTRACE(owner->name()) << "Triggering interrupt " << id << " Pending trap: " << this->reg.pending_trap; } private: core_complex *const owner; sc_event wfi_evt; }; int 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; } using cpu_ptr = std::unique_ptr; using vm_ptr= std::unique_ptr; class core_wrapper { public: core_wrapper(core_complex *owner) : owner(owner) { } void reset(uint64_t addr){vm->reset(addr);} inline void start(){vm->start();} inline std::pair load_file(std::string const& name){ return cpu->load_file(name);}; std::function get_mode; std::function get_state; std::function get_interrupt_execution; std::function set_interrupt_execution; std::function local_irq; template std::tuple create_core(std::string const& backend, unsigned gdb_port, uint32_t hart_id){ auto* lcpu = new core_wrapper_t(owner); lcpu->set_mhartid(hart_id); get_mode = [lcpu]() { return lcpu->get_mode(); }; get_state = [lcpu]() { return lcpu->get_state().mstatus.backing.val; }; get_interrupt_execution = [lcpu]() { return lcpu->get_interrupt_execution(); }; set_interrupt_execution = [lcpu](bool b) { return lcpu->set_interrupt_execution(b); }; local_irq = [lcpu](short s, bool b) { return lcpu->local_irq(s, b); }; if(backend == "interp") return {cpu_ptr{lcpu}, vm_ptr{iss::interp::create(static_cast(lcpu), gdb_port)}}; #ifdef WITH_LLVM if(backend == "llvm") return {cpu_ptr{lcpu}, vm_ptr{iss::llvm::create(lcpu, gdb_port)}}; #endif #ifdef WITH_TCC if(backend == "tcc") s return {cpu_ptr{lcpu}, vm_ptr{iss::tcc::create(lcpu, gdb_port)}}; #endif return {nullptr, nullptr}; } void create_cpu(std::string const& type, std::string const& backend, unsigned gdb_port, uint32_t hart_id){ CREATE_CORE(tgc_c) #ifdef CORE_TGC_B CREATE_CORE(tgc_b) #endif #ifdef CORE_TGC_D CREATE_CORE(tgc_d) #endif { LOG(ERROR) << "Illegal argument value for core type: " << type << std::endl; } auto *srv = debugger::server::get(); if (srv) tgt_adapter = srv->get_target(); 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