//////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2017, 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. // // Contributors: // eyck@minres.com - initial implementation // // //////////////////////////////////////////////////////////////////////////////// #include "scc/report.h" #include "iss/arch/riscv_hart_msu_vp.h" #include "iss/arch/rv32imac.h" #include "iss/iss.h" #include "iss/vm_types.h" #include "iss/debugger/server.h" #include "iss/debugger/gdb_session.h" #include "iss/debugger/target_adapter_if.h" #include "iss/debugger/encoderdecoder.h" #include "sysc/SiFive/core_complex.h" #ifdef WITH_SCV #include #include #endif namespace sysc { namespace SiFive { using namespace std; using namespace iss; namespace { iss::debugger::encoder_decoder encdec; } 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_msu_vp { public: using core_type = arch::rv32imac; using base_type = arch::riscv_hart_msu_vp; 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; } base_type::hart_state& get_state() { return this->state; } void notify_phase(exec_phase) override; sync_type needed_sync() const override { return PRE_SYNC; } void disass_output(uint64_t pc, const std::string instr) override { if (logging::INFO <= logging::Log>::reporting_level() && logging::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 << "]"; scc::Log>().get(logging::INFO, "disass") << "0x"<disass_output(pc,instr); }; status read_mem(phys_addr_t addr, unsigned length, uint8_t *const data) { 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) { 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; // TODO: this is an ugly hack (clear MTIP on mtimecmp write), needs to be fixed if(addr.val==0x2004000) this->csr[arch::mip] &= ~(1ULL<<7); return res; } } void wait_until(uint64_t flags) { do{ wait(wfi_evt); this->check_interrupt(); } while(this->reg.pending_trap==0); base_type::wait_until(flags); } void local_irq(short id){ switch(id){ case 16: // SW this->csr[arch::mip] |= 1<<3; break; case 17: // timer this->csr[arch::mip] |= 1<<7; break; case 18: //external this->csr[arch::mip] |= 1<<11; break; default: /* do nothing*/ break; } wfi_evt.notify(); } 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_core::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_core::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{ LOG(TRACE)<<"Checking condition at "<=t?std::numeric_limits::max():0; }); return Ok; } return Err; } return Err; } void core_wrapper::notify_phase(exec_phase p) { if(p == ISTART) owner->sync(); } core_complex::core_complex(sc_core::sc_module_name name) : sc_core::sc_module(name) , NAMED(initiator) , NAMED(clk_i) , NAMED(rst_i) , NAMED(global_irq_i) , NAMED(timer_irq_i) , NAMED(local_irq_i, 16) , NAMED(elf_file, "") , NAMED(enable_disass, true) , NAMED(reset_address, 0ULL) , NAMED(gdb_server_port, 0) , NAMED(dump_ir, false) , 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 { 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(sw_irq_cb); sensitive<(this); vm = create(cpu.get(), gdb_server_port.get_value(), dump_ir.get_value()); vm->setDisassEnabled(enable_disass.get_value()); 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