/******************************************************************************* * 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" #include "iss/arch/riscv_hart_m_p.h" #include "iss/arch/tgf_c.h" #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 #ifdef WITH_SCV #include #include #endif namespace sysc { namespace SiFive { using namespace std; using namespace iss; using namespace logging; using namespace sc_core; namespace { iss::debugger::encoder_decoder encdec; } using core_type = iss::arch::tgf_c; namespace { std::array lvl = {{'U', 'S', 'H', 'M'}}; std::array trap_str = { { "Instruction address misaligned", "Instruction access fault", "Illegal instruction", "Breakpoint", "Load address misaligned", "Load access fault", "Store/AMO address misaligned", "Store/AMO access fault", "Environment call from U-mode", "Environment call from S-mode", "Reserved", "Environment call from M-mode", "Instruction page fault", "Load page fault", "Reserved", "Store/AMO page fault" } }; std::array irq_str = { { "User software interrupt", "Supervisor software interrupt", "Reserved", "Machine software interrupt", "User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt", "User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt" } }; } class core_wrapper : public iss::arch::riscv_hart_m_p { public: using base_type = arch::riscv_hart_m_p; using phys_addr_t = typename arch::traits::phys_addr_t; core_wrapper(core_complex *owner) : owner(owner) { } uint32_t get_mode() { return this->reg.machine_state; } inline void set_interrupt_execution(bool v) { this->interrupt_sim = v?1:0; } inline bool get_interrupt_execution() { return this->interrupt_sim; } base_type::hart_state &get_state() { return this->state; } void notify_phase(exec_phase p) override { if (p == ISTART) owner->sync(this->reg.icount + cycle_offset); } 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.machine_state] << ";s:0x" << std::hex << std::setfill('0') << std::setw(sizeof(reg_t) * 2) << (reg_t)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 base_type::read_csr(addr, val); } } void wait_until(uint64_t flags) override { SCCDEBUG(owner->name()) << "Sleeping until interrupt"; do { wait(wfi_evt); } while (this->reg.pending_trap == 0); base_type::wait_until(flags); } void local_irq(short id, bool value) { base_type::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(); } 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; } core_complex::core_complex(sc_module_name name) : sc_module(name) , read_lut(tlm_dmi_ext()) , write_lut(tlm_dmi_ext()) , tgt_adapter(nullptr) #ifdef WITH_SCV , m_db(scv_tr_db::get_default_db()) , stream_handle(nullptr) , instr_tr_handle(nullptr) , fetch_tr_handle(nullptr) #endif { SC_HAS_PROCESS(core_complex);// NOLINT initiator.register_invalidate_direct_mem_ptr([=](uint64_t start, uint64_t end) -> void { auto lut_entry = read_lut.getEntry(start); if (lut_entry.get_granted_access() != tlm::tlm_dmi::DMI_ACCESS_NONE && end <= lut_entry.get_end_address() + 1) { read_lut.removeEntry(lut_entry); } lut_entry = write_lut.getEntry(start); if (lut_entry.get_granted_access() != tlm::tlm_dmi::DMI_ACCESS_NONE && end <= lut_entry.get_end_address() + 1) { write_lut.removeEntry(lut_entry); } }); SC_THREAD(run); SC_METHOD(clk_cb); sensitive << clk_i; SC_METHOD(rst_cb); sensitive << rst_i; SC_METHOD(sw_irq_cb); sensitive << sw_irq_i; SC_METHOD(timer_irq_cb); sensitive << timer_irq_i; SC_METHOD(global_irq_cb); sensitive << global_irq_i; } core_complex::~core_complex() = default; void core_complex::trace(sc_trace_file *trf) const {} using vm_ptr= std::unique_ptr; vm_ptr create_cpu(core_wrapper* cpu, std::string const& backend, unsigned gdb_port){ if(backend == "interp") return vm_ptr{iss::interp::create(cpu, gdb_port)}; #ifdef WITH_LLVM if(backend == "llvm") return vm_ptr{iss::llvm::create(lcpu, gdb_port)}; #endif if(backend == "tcc") return vm_ptr{iss::tcc::create(cpu, gdb_port)}; return {nullptr}; } void core_complex::before_end_of_elaboration() { SCCDEBUG(SCMOD)<<"instantiating iss::arch::tgf with "<(this); cpu->set_mhartid(mhartid.get_value()); vm = create_cpu(cpu.get(), backend.get_value(), gdb_server_port.get_value()); #ifdef WITH_SCV vm->setDisassEnabled(enable_disass.get_value() || m_db != nullptr); #else vm->setDisassEnabled(enable_disass.get_value()); #endif 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