diff --git a/incl/iss/arch/riscv_hart_mu_p.h b/incl/iss/arch/riscv_hart_mu_p.h new file mode 100644 index 0000000..bb4f2c1 --- /dev/null +++ b/incl/iss/arch/riscv_hart_mu_p.h @@ -0,0 +1,961 @@ +/******************************************************************************* + * 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. + * + * Contributors: + * eyck@minres.com - initial implementation + ******************************************************************************/ + +#ifndef _RISCV_CORE_H_ +#define _RISCV_CORE_H_ + +#include "iss/arch/traits.h" +#include "iss/arch_if.h" +#include "iss/instrumentation_if.h" +#include "iss/log_categories.h" +#include "iss/vm_if.h" +#ifndef FMT_HEADER_ONLY +#define FMT_HEADER_ONLY +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__GNUC__) +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#else +#define likely(x) x +#define unlikely(x) x +#endif + +namespace iss { +namespace arch { + +enum { tohost_dflt = 0xF0001000, fromhost_dflt = 0xF0001040 }; + +enum riscv_csr { + /* user-level CSR */ + // User Trap Setup + ustatus = 0x000, + uie = 0x004, + utvec = 0x005, + // User Trap Handling + uscratch = 0x040, + uepc = 0x041, + ucause = 0x042, + utval = 0x043, + uip = 0x044, + // User Floating-Point CSRs + fflags = 0x001, + frm = 0x002, + fcsr = 0x003, + // User Counter/Timers + cycle = 0xC00, + time = 0xC01, + instret = 0xC02, + hpmcounter3 = 0xC03, + hpmcounter4 = 0xC04, + /*...*/ + hpmcounter31 = 0xC1F, + cycleh = 0xC80, + timeh = 0xC81, + instreth = 0xC82, + hpmcounter3h = 0xC83, + hpmcounter4h = 0xC84, + /*...*/ + hpmcounter31h = 0xC9F, + /* supervisor-level CSR */ + // Supervisor Trap Setup + sstatus = 0x100, + sedeleg = 0x102, + sideleg = 0x103, + sie = 0x104, + stvec = 0x105, + scounteren = 0x106, + // Supervisor Trap Handling + sscratch = 0x140, + sepc = 0x141, + scause = 0x142, + stval = 0x143, + sip = 0x144, + // Supervisor Protection and Translation + satp = 0x180, + /* machine-level CSR */ + // Machine Information Registers + mvendorid = 0xF11, + marchid = 0xF12, + mimpid = 0xF13, + mhartid = 0xF14, + // Machine Trap Setup + mstatus = 0x300, + misa = 0x301, + medeleg = 0x302, + mideleg = 0x303, + mie = 0x304, + mtvec = 0x305, + mcounteren = 0x306, + // Machine Trap Handling + mscratch = 0x340, + mepc = 0x341, + mcause = 0x342, + mtval = 0x343, + mip = 0x344, + // Machine Protection and Translation + pmpcfg0 = 0x3A0, + pmpcfg1 = 0x3A1, + pmpcfg2 = 0x3A2, + pmpcfg3 = 0x3A3, + pmpaddr0 = 0x3B0, + pmpaddr1 = 0x3B1, + /*...*/ + pmpaddr15 = 0x3BF, + // Machine Counter/Timers + mcycle = 0xB00, + minstret = 0xB02, + mhpmcounter3 = 0xB03, + mhpmcounter4 = 0xB04, + /*...*/ + mhpmcounter31 = 0xB1F, + mcycleh = 0xB80, + minstreth = 0xB82, + mhpmcounter3h = 0xB83, + mhpmcounter4h = 0xB84, + /*...*/ + mhpmcounter31h = 0xB9F, + // Machine Counter Setup + mhpmevent3 = 0x323, + mhpmevent4 = 0x324, + /*...*/ + mhpmevent31 = 0x33F, + // Debug/Trace Registers (shared with Debug Mode) + tselect = 0x7A0, + tdata1 = 0x7A1, + tdata2 = 0x7A2, + tdata3 = 0x7A3, + // Debug Mode Registers + dcsr = 0x7B0, + dpc = 0x7B1, + dscratch = 0x7B2 +}; + +namespace { + +std::array trap_str = {{"" + "Instruction address misaligned", // 0 + "Instruction access fault", // 1 + "Illegal instruction", // 2 + "Breakpoint", // 3 + "Load address misaligned", // 4 + "Load access fault", // 5 + "Store/AMO address misaligned", // 6 + "Store/AMO access fault", // 7 + "Environment call from U-mode", // 8 + "Environment call from S-mode", // 9 + "Reserved", // a + "Environment call from M-mode", // b + "Instruction page fault", // c + "Load page fault", // d + "Reserved", // e + "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"}}; + +enum { + PGSHIFT = 12, + PTE_PPN_SHIFT = 10, + // page table entry (PTE) fields + PTE_V = 0x001, // Valid + PTE_R = 0x002, // Read + PTE_W = 0x004, // Write + PTE_X = 0x008, // Execute + PTE_U = 0x010, // User + PTE_G = 0x020, // Global + PTE_A = 0x040, // Accessed + PTE_D = 0x080, // Dirty + PTE_SOFT = 0x300 // Reserved for Software +}; + +template inline bool PTE_TABLE(T PTE) { return (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V); } + +enum { PRIV_U = 0, PRIV_S = 1, PRIV_M = 3 }; + +enum { + ISA_A = 1, + ISA_B = 1 << 1, + ISA_C = 1 << 2, + ISA_D = 1 << 3, + ISA_E = 1 << 4, + ISA_F = 1 << 5, + ISA_G = 1 << 6, + ISA_I = 1 << 8, + ISA_M = 1 << 12, + ISA_N = 1 << 13, + ISA_Q = 1 << 16, + ISA_S = 1 << 18, + ISA_U = 1 << 20 +}; + +class trap_load_access_fault : public trap_access { +public: + trap_load_access_fault(uint64_t badaddr) + : trap_access(5 << 16, badaddr) {} +}; +class illegal_instruction_fault : public trap_access { +public: + illegal_instruction_fault(uint64_t badaddr) + : trap_access(2 << 16, badaddr) {} +}; +} // namespace + +template class riscv_hart_mu_p : public BASE { +public: + using super = BASE; + using this_class = riscv_hart_mu_p; + using phys_addr_t = typename super::phys_addr_t; + using reg_t = typename super::reg_t; + using addr_t = typename super::addr_t; + + using rd_csr_f = iss::status (this_class::*)(unsigned addr, reg_t &); + using wr_csr_f = iss::status (this_class::*)(unsigned addr, reg_t); + + // primary template + template struct hart_state {}; + // specialization 32bit + template class hart_state::value>::type> { + public: + BEGIN_BF_DECL(mstatus_t, T); + // SD bit is read-only and is set when either the FS or XS bits encode a Dirty state (i.e., SD=((FS==11) OR XS==11))) + BF_FIELD(SD, 31, 1); + // Trap SRET + BF_FIELD(TSR, 22, 1); + // Timeout Wait + BF_FIELD(TW, 21, 1); + // Trap Virtual Memory + BF_FIELD(TVM, 20, 1); + // Make eXecutable Readable + BF_FIELD(MXR, 19, 1); + // permit Supervisor User Memory access + BF_FIELD(SUM, 18, 1); + // Modify PRiVilege + BF_FIELD(MPRV, 17, 1); + // status of additional user-mode extensions and associated state, All off/None dirty or clean, some on/None dirty, some clean/Some dirty + BF_FIELD(XS, 15, 2); + // floating-point unit status Off/Initial/Clean/Dirty + BF_FIELD(FS, 13, 2); + // machine previous privilege + BF_FIELD(MPP, 11, 2); + // supervisor previous privilege + BF_FIELD(SPP, 8, 1); + // previous machine interrupt-enable + BF_FIELD(MPIE, 7, 1); + // previous supervisor interrupt-enable + BF_FIELD(SPIE, 5, 1); + // previous user interrupt-enable + BF_FIELD(UPIE, 4, 1); + // machine interrupt-enable + BF_FIELD(MIE, 3, 1); + // supervisor interrupt-enable + BF_FIELD(SIE, 1, 1); + // user interrupt-enable + BF_FIELD(UIE, 0, 1); + END_BF_DECL(); + + mstatus_t mstatus; + + static const reg_t mstatus_reset_val = 0; + + void write_mstatus(T val) { + auto mask = get_mask(); + auto new_val = (mstatus.backing.val & ~mask) | (val & mask); + mstatus = new_val; + } + + T satp; + + static constexpr uint32_t get_mask() { + return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011 // only machine mode is supported + } + }; + + constexpr reg_t get_irq_mask() { + return 0b101110111011; // only machine mode is supported + } + + riscv_hart_mu_p(); + virtual ~riscv_hart_mu_p() = default; + + void reset(uint64_t address) override; + + std::pair load_file(std::string name, int type = -1) override; + + iss::status read(const address_type type, const access_type access, const uint32_t space, + const uint64_t addr, const unsigned length, uint8_t *const data) override; + iss::status write(const address_type type, const access_type access, const uint32_t space, + const uint64_t addr, const unsigned length, const uint8_t *const data) override; + + virtual uint64_t enter_trap(uint64_t flags) override { return riscv_hart_mu_p::enter_trap(flags, fault_data); } + virtual uint64_t enter_trap(uint64_t flags, uint64_t addr) override; + virtual uint64_t leave_trap(uint64_t flags) override; + + const reg_t& get_mhartid() const { return mhartid_reg; } + void set_mhartid(reg_t mhartid) { mhartid_reg = mhartid; }; + + void disass_output(uint64_t pc, const std::string instr) override { + CLOG(INFO, disass) << fmt::format("0x{:016x} {:40} [s:0x{:x};c:{}]", + pc, instr, (reg_t)state.mstatus, this->reg.icount); + }; + + iss::instrumentation_if *get_instrumentation_if() override { return &instr_if; } + + void setMemReadCb(std::function const& memReadCb) { + mem_read_cb = memReadCb; + } + + void setMemWriteCb(std::function const& memWriteCb) { + mem_write_cb = memWriteCb; + } + + void set_csr(unsigned addr, reg_t val){ + csr[addr & csr.page_addr_mask] = val; + } + +protected: + struct riscv_instrumentation_if : public iss::instrumentation_if { + + riscv_instrumentation_if(riscv_hart_mu_p &arch) + : arch(arch) {} + /** + * get the name of this architecture + * + * @return the name of this architecture + */ + const std::string core_type_name() const override { return traits::core_type; } + + virtual uint64_t get_pc() { return arch.get_pc(); }; + + virtual uint64_t get_next_pc() { return arch.get_next_pc(); }; + + virtual void set_curr_instr_cycles(unsigned cycles) { arch.cycle_offset += cycles - 1; }; + + riscv_hart_mu_p &arch; + }; + + friend struct riscv_instrumentation_if; + addr_t get_pc() { return this->reg.PC; } + addr_t get_next_pc() { return this->reg.NEXT_PC; } + + virtual iss::status read_mem(phys_addr_t addr, unsigned length, uint8_t *const data); + virtual iss::status write_mem(phys_addr_t addr, unsigned length, const uint8_t *const data); + + virtual iss::status read_csr(unsigned addr, reg_t &val); + virtual iss::status write_csr(unsigned addr, reg_t val); + + hart_state state; + uint64_t cycle_offset; + reg_t fault_data; + uint64_t tohost = tohost_dflt; + uint64_t fromhost = fromhost_dflt; + unsigned to_host_wr_cnt = 0; + riscv_instrumentation_if instr_if; + + using mem_type = util::sparse_array; + using csr_type = util::sparse_array::reg_t, 1ULL << 12, 12>; + using csr_page_type = typename csr_type::page_type; + mem_type mem; + csr_type csr; + std::stringstream uart_buf; + std::unordered_map ptw; + std::unordered_map atomic_reservation; + std::unordered_map csr_rd_cb; + std::unordered_map csr_wr_cb; + +private: + iss::status read_reg(unsigned addr, reg_t &val); + iss::status write_reg(unsigned addr, reg_t val); + iss::status read_cycle(unsigned addr, reg_t &val); + iss::status read_time(unsigned addr, reg_t &val); + iss::status read_status(unsigned addr, reg_t &val); + iss::status write_status(unsigned addr, reg_t val); + iss::status read_ie(unsigned addr, reg_t &val); + iss::status write_ie(unsigned addr, reg_t val); + iss::status read_ip(unsigned addr, reg_t &val); + iss::status write_ip(unsigned addr, reg_t val); + iss::status read_hartid(unsigned addr, reg_t &val); + + reg_t mhartid_reg{0x0}; + std::functionmem_read_cb; + std::function mem_write_cb; + +protected: + void check_interrupt(); +}; + +template +riscv_hart_mu_p::riscv_hart_mu_p() +: state() +, cycle_offset(0) +, instr_if(*this) { + csr[misa] = traits::MISA_VAL; + uart_buf.str(""); + for (unsigned addr = mcycle; addr <= hpmcounter31; ++addr) csr_wr_cb[addr] = nullptr; + for (unsigned addr = mcycleh; addr <= hpmcounter31h; ++addr) csr_wr_cb[addr] = nullptr; + // special handling + csr_rd_cb[time] = &riscv_hart_mu_p::read_time; + csr_wr_cb[time] = nullptr; + csr_rd_cb[timeh] = &riscv_hart_mu_p::read_time; + csr_wr_cb[timeh] = nullptr; + csr_rd_cb[mcycle] = &riscv_hart_mu_p::read_cycle; + csr_rd_cb[mcycleh] = &riscv_hart_mu_p::read_cycle; + csr_rd_cb[minstret] = &riscv_hart_mu_p::read_cycle; + csr_rd_cb[minstreth] = &riscv_hart_mu_p::read_cycle; + csr_rd_cb[mstatus] = &riscv_hart_mu_p::read_status; + csr_wr_cb[mstatus] = &riscv_hart_mu_p::write_status; + csr_rd_cb[mip] = &riscv_hart_mu_p::read_ip; + csr_wr_cb[mip] = &riscv_hart_mu_p::write_ip; + csr_rd_cb[mie] = &riscv_hart_mu_p::read_ie; + csr_wr_cb[mie] = &riscv_hart_mu_p::write_ie; + csr_rd_cb[mhartid] = &riscv_hart_mu_p::read_hartid; + // common regs + const std::array addrs{{mepc, mtvec, mscratch, mcause, mtval, mscratch}}; + for(auto addr: addrs) { + csr_rd_cb[addr] = &riscv_hart_mu_p::read_reg; + csr_wr_cb[addr] = &riscv_hart_mu_p::write_reg; + } + // read-only registers + csr_rd_cb[misa] = &riscv_hart_mu_p::read_reg; + csr_wr_cb[misa] = nullptr; +} + +template std::pair riscv_hart_mu_p::load_file(std::string name, int type) { + FILE *fp = fopen(name.c_str(), "r"); + if (fp) { + std::array buf; + auto n = fread(buf.data(), 1, 4, fp); + if (n != 4) throw std::runtime_error("input file has insufficient size"); + buf[4] = 0; + if (strcmp(buf.data() + 1, "ELF") == 0) { + fclose(fp); + // Create elfio reader + ELFIO::elfio reader; + // Load ELF data + if (!reader.load(name)) throw std::runtime_error("could not process elf file"); + // check elf properties + if (reader.get_class() != ELFCLASS32) + if (sizeof(reg_t) == 4) throw std::runtime_error("wrong elf class in file"); + if (reader.get_type() != ET_EXEC) throw std::runtime_error("wrong elf type in file"); + if (reader.get_machine() != EM_RISCV) throw std::runtime_error("wrong elf machine in file"); + for (const auto pseg : reader.segments) { + const auto fsize = pseg->get_file_size(); // 0x42c/0x0 + const auto seg_data = pseg->get_data(); + if (fsize > 0) { + auto res = this->write(iss::address_type::PHYSICAL, iss::access_type::DEBUG_WRITE, + traits::MEM, pseg->get_physical_address(), + fsize, reinterpret_cast(seg_data)); + if (res != iss::Ok) + LOG(ERROR) << "problem writing " << fsize << "bytes to 0x" << std::hex + << pseg->get_physical_address(); + } + } + for (const auto sec : reader.sections) { + if (sec->get_name() == ".tohost") { + tohost = sec->get_address(); + fromhost = tohost + 0x40; + } + } + + return std::make_pair(reader.get_entry(), true); + } + throw std::runtime_error("memory load file is not a valid elf file"); + } + throw std::runtime_error("memory load file not found"); +} + +template +iss::status riscv_hart_mu_p::read(const address_type type, const access_type access, const uint32_t space, + const uint64_t addr, const unsigned length, uint8_t *const data) { +#ifndef NDEBUG + if (access && iss::access_type::DEBUG) { + LOG(TRACEALL) << "debug read of " << length << " bytes @addr 0x" << std::hex << addr; + } else if(access && iss::access_type::FETCH){ + LOG(TRACEALL) << "fetch of " << length << " bytes @addr 0x" << std::hex << addr; + } else { + LOG(TRACE) << "read of " << length << " bytes @addr 0x" << std::hex << addr; + } +#endif + try { + switch (space) { + case traits::MEM: { + if (unlikely((access == iss::access_type::FETCH || access == iss::access_type::DEBUG_FETCH) && (addr & 0x1) == 1)) { + fault_data = addr; + if (access && iss::access_type::DEBUG) throw trap_access(0, addr); + this->reg.trap_state = (1 << 31); // issue trap 0 + return iss::Err; + } + try { + auto res = type==iss::address_type::PHYSICAL? + read_mem( BASE::v2p(phys_addr_t{access, space, addr}), length, data): + read_mem( BASE::v2p(iss::addr_t{access, type, space, addr}), length, data); + if (unlikely(res != iss::Ok)) this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 5 (load access fault + return res; + } catch (trap_access &ta) { + this->reg.trap_state = (1 << 31) | ta.id; + return iss::Err; + } + } break; + case traits::CSR: { + if (length != sizeof(reg_t)) return iss::Err; + return read_csr(addr, *reinterpret_cast(data)); + } break; + case traits::FENCE: { + if ((addr + length) > mem.size()) return iss::Err; + return iss::Ok; + } break; + case traits::RES: { + auto it = atomic_reservation.find(addr); + if (it != atomic_reservation.end() && it->second != 0) { + memset(data, 0xff, length); + atomic_reservation.erase(addr); + } else + memset(data, 0, length); + } break; + default: + return iss::Err; // assert("Not supported"); + } + return iss::Ok; + } catch (trap_access &ta) { + this->reg.trap_state = (1 << 31) | ta.id; + return iss::Err; + } +} + +template +iss::status riscv_hart_mu_p::write(const address_type type, const access_type access, const uint32_t space, + const uint64_t addr, const unsigned length, const uint8_t *const data) { +#ifndef NDEBUG + const char *prefix = (access && iss::access_type::DEBUG) ? "debug " : ""; + switch (length) { + case 8: + LOG(TRACE) << prefix << "write of " << length << " bytes (0x" << std::hex << *(uint64_t *)&data[0] << std::dec + << ") @addr 0x" << std::hex << addr; + break; + case 4: + LOG(TRACE) << prefix << "write of " << length << " bytes (0x" << std::hex << *(uint32_t *)&data[0] << std::dec + << ") @addr 0x" << std::hex << addr; + break; + case 2: + LOG(TRACE) << prefix << "write of " << length << " bytes (0x" << std::hex << *(uint16_t *)&data[0] << std::dec + << ") @addr 0x" << std::hex << addr; + break; + case 1: + LOG(TRACE) << prefix << "write of " << length << " bytes (0x" << std::hex << (uint16_t)data[0] << std::dec + << ") @addr 0x" << std::hex << addr; + break; + default: + LOG(TRACE) << prefix << "write of " << length << " bytes @addr " << addr; + } +#endif + try { + switch (space) { + case traits::MEM: { + if (unlikely((access && iss::access_type::FETCH) && (addr & 0x1) == 1)) { + fault_data = addr; + if (access && iss::access_type::DEBUG) throw trap_access(0, addr); + this->reg.trap_state = (1 << 31); // issue trap 0 + return iss::Err; + } + try { + auto res = type==iss::address_type::PHYSICAL? + write_mem(phys_addr_t{access, space, addr}, length, data): + write_mem(BASE::v2p(iss::addr_t{access, type, space, addr}), length, data); + if (unlikely(res != iss::Ok)) + this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 7 (Store/AMO access fault) + return res; + } catch (trap_access &ta) { + this->reg.trap_state = (1 << 31) | ta.id; + return iss::Err; + } + + phys_addr_t paddr = BASE::v2p(iss::addr_t{access, type, space, addr}); + if ((paddr.val + length) > mem.size()) return iss::Err; + switch (paddr.val) { + case 0x10013000: // UART0 base, TXFIFO reg + case 0x10023000: // UART1 base, TXFIFO reg + uart_buf << (char)data[0]; + if (((char)data[0]) == '\n' || data[0] == 0) { + // LOG(INFO)<<"UART"<<((paddr.val>>16)&0x3)<<" send + // '"<::CSR: { + if (length != sizeof(reg_t)) return iss::Err; + return write_csr(addr, *reinterpret_cast(data)); + } break; + case traits::FENCE: { + if ((addr + length) > mem.size()) return iss::Err; + switch (addr) { + case 2: + case 3: { + ptw.clear(); + auto tvm = state.mstatus.TVM; + return iss::Ok; + } + } + } break; + case traits::RES: { + atomic_reservation[addr] = data[0]; + } break; + default: + return iss::Err; + } + return iss::Ok; + } catch (trap_access &ta) { + this->reg.trap_state = (1 << 31) | ta.id; + return iss::Err; + } +} + +template iss::status riscv_hart_mu_p::read_csr(unsigned addr, reg_t &val) { + if (addr >= csr.size()) return iss::Err; + auto req_priv_lvl = (addr >> 8) & 0x3; + if (this->reg.PRIV < req_priv_lvl) // not having required privileges + throw illegal_instruction_fault(this->fault_data); + auto it = csr_rd_cb.find(addr); + if (it == csr_rd_cb.end() || !it->second) // non existent register + throw illegal_instruction_fault(this->fault_data); + return (this->*(it->second))(addr, val); +} + +template iss::status riscv_hart_mu_p::write_csr(unsigned addr, reg_t val) { + if (addr >= csr.size()) return iss::Err; + auto req_priv_lvl = (addr >> 8) & 0x3; + if (this->reg.PRIV < req_priv_lvl) // not having required privileges + throw illegal_instruction_fault(this->fault_data); + if((addr&0xc00)==0xc00) // writing to read-only region + throw illegal_instruction_fault(this->fault_data); + auto it = csr_wr_cb.find(addr); + if (it == csr_wr_cb.end() || !it->second) // non existent register + throw illegal_instruction_fault(this->fault_data); + return (this->*(it->second))(addr, val); +} + +template iss::status riscv_hart_mu_p::read_reg(unsigned addr, reg_t &val) { + val = csr[addr]; + return iss::Ok; +} + +template iss::status riscv_hart_mu_p::write_reg(unsigned addr, reg_t val) { + csr[addr] = val; + return iss::Ok; +} + +template iss::status riscv_hart_mu_p::read_cycle(unsigned addr, reg_t &val) { + auto cycle_val = this->reg.icount + cycle_offset; + if (addr == mcycle) { + val = static_cast(cycle_val); + } else if (addr == mcycleh) { + if (sizeof(typename traits::reg_t) != 4) return iss::Err; + val = static_cast(cycle_val >> 32); + } + return iss::Ok; +} + +template iss::status riscv_hart_mu_p::read_time(unsigned addr, reg_t &val) { + uint64_t time_val = (this->reg.icount + cycle_offset) / (100000000 / 32768 - 1); //-> ~3052; + if (addr == time) { + val = static_cast(time_val); + } else if (addr == timeh) { + if (sizeof(typename traits::reg_t) != 4) return iss::Err; + val = static_cast(time_val >> 32); + } + return iss::Ok; +} + +template iss::status riscv_hart_mu_p::read_status(unsigned addr, reg_t &val) { + val = state.mstatus & hart_state::get_mask(); + return iss::Ok; +} + +template iss::status riscv_hart_mu_p::write_status(unsigned addr, reg_t val) { + state.write_mstatus(val); + check_interrupt(); + return iss::Ok; +} + +template iss::status riscv_hart_mu_p::read_ie(unsigned addr, reg_t &val) { + val = csr[mie]; + val &= csr[mideleg]; + return iss::Ok; +} + +template iss::status riscv_hart_mu_p::read_hartid(unsigned addr, reg_t &val) { + val = mhartid_reg; + return iss::Ok; +} + +template iss::status riscv_hart_mu_p::write_ie(unsigned addr, reg_t val) { + auto mask = get_irq_mask(); + csr[mie] = (csr[mie] & ~mask) | (val & mask); + check_interrupt(); + return iss::Ok; +} + +template iss::status riscv_hart_mu_p::read_ip(unsigned addr, reg_t &val) { + val = csr[mip]; + val &= csr[mideleg]; + return iss::Ok; +} + +template iss::status riscv_hart_mu_p::write_ip(unsigned addr, reg_t val) { + auto mask = get_irq_mask(); + mask &= ~(1 << 7); // MTIP is read only + csr[mip] = (csr[mip] & ~mask) | (val & mask); + check_interrupt(); + return iss::Ok; +} + +template +iss::status riscv_hart_mu_p::read_mem(phys_addr_t paddr, unsigned length, uint8_t *const data) { + if ((paddr.val + length) > mem.size()) return iss::Err; + if(mem_read_cb) return mem_read_cb(paddr, length, data); + switch (paddr.val) { + case 0x0200BFF8: { // CLINT base, mtime reg + if (sizeof(reg_t) < length) return iss::Err; + reg_t time_val; + this->read_csr(time, time_val); + std::copy((uint8_t *)&time_val, ((uint8_t *)&time_val) + length, data); + } break; + case 0x10008000: { + const mem_type::page_type &p = mem(paddr.val / mem.page_size); + uint64_t offs = paddr.val & mem.page_addr_mask; + std::copy(p.data() + offs, p.data() + offs + length, data); + if (this->reg.icount > 30000) data[3] |= 0x80; + } break; + default: { + const auto &p = mem(paddr.val / mem.page_size); + auto offs = paddr.val & mem.page_addr_mask; + std::copy(p.data() + offs, p.data() + offs + length, data); + } + } + return iss::Ok; +} + +template +iss::status riscv_hart_mu_p::write_mem(phys_addr_t paddr, unsigned length, const uint8_t *const data) { + if ((paddr.val + length) > mem.size()) return iss::Err; + if(mem_write_cb) return mem_write_cb(paddr, length, data); + switch (paddr.val) { + case 0x10013000: // UART0 base, TXFIFO reg + case 0x10023000: // UART1 base, TXFIFO reg + uart_buf << (char)data[0]; + if (((char)data[0]) == '\n' || data[0] == 0) { + // LOG(INFO)<<"UART"<<((paddr.val>>16)&0x3)<<" send + // '"<::XLEN == 32 && paddr.val == (tohost + 4)); + auto tohost_lower = + (traits::XLEN == 32 && paddr.val == tohost); + if (tohost_lower || tohost_upper) { + uint64_t hostvar = *reinterpret_cast(p.data() + (tohost & mem.page_addr_mask)); + if (tohost_upper || (tohost_lower && to_host_wr_cnt > 0)) { + switch (hostvar >> 48) { + case 0: + if (hostvar != 0x1) { + LOG(FATAL) << "tohost value is 0x" << std::hex << hostvar << std::dec << " (" << hostvar + << "), stopping simulation"; + } else { + LOG(INFO) << "tohost value is 0x" << std::hex << hostvar << std::dec << " (" << hostvar + << "), stopping simulation"; + } + this->reg.trap_state=std::numeric_limits::max(); + this->interrupt_sim=hostvar; + break; + //throw(iss::simulation_stopped(hostvar)); + case 0x0101: { + char c = static_cast(hostvar & 0xff); + if (c == '\n' || c == 0) { + LOG(INFO) << "tohost send '" << uart_buf.str() << "'"; + uart_buf.str(""); + } else + uart_buf << c; + to_host_wr_cnt = 0; + } break; + default: + break; + } + } else if (tohost_lower) + to_host_wr_cnt++; + } else if (traits::XLEN == 32 && paddr.val == fromhost + 4) { + uint64_t fhostvar = *reinterpret_cast(p.data() + (fromhost & mem.page_addr_mask)); + *reinterpret_cast(p.data() + (tohost & mem.page_addr_mask)) = fhostvar; + } + } + } + } + return iss::Ok; +} + +template inline void riscv_hart_mu_p::reset(uint64_t address) { + BASE::reset(address); + state.mstatus = hart_state::mstatus_reset_val; +} + +template void riscv_hart_mu_p::check_interrupt() { + auto ideleg = csr[mideleg]; + // Multiple simultaneous interrupts and traps at the same privilege level are + // handled in the following decreasing priority order: + // external interrupts, software interrupts, timer interrupts, then finally + // any synchronous traps. + auto ena_irq = csr[mip] & csr[mie]; + + bool mie = state.mstatus.MIE; + auto m_enabled = this->reg.PRIV < PRIV_M || (this->reg.PRIV == PRIV_M && mie); + auto enabled_interrupts = m_enabled ? ena_irq & ~ideleg : 0; + + if (enabled_interrupts != 0) { + int res = 0; + while ((enabled_interrupts & 1) == 0) { + enabled_interrupts >>= 1; + res++; + } + this->reg.pending_trap = res << 16 | 1; // 0x80 << 24 | (cause << 16) | trap_id + } +} + +template uint64_t riscv_hart_mu_p::enter_trap(uint64_t flags, uint64_t addr) { + // flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0] + // calculate and write mcause val + auto trap_id = bit_sub<0, 16>(flags); + auto cause = bit_sub<16, 15>(flags); + if (trap_id == 0 && cause == 11) cause = 0x8 + PRIV_M; // adjust environment call cause + // calculate effective privilege level + if (trap_id == 0) { // exception + // store ret addr in xepc register + csr[mepc] = static_cast(addr); // store actual address instruction of exception + csr[mtval] = fault_data; + fault_data = 0; + } else { + csr[mepc] = this->reg.NEXT_PC; // store next address if interrupt + this->reg.pending_trap = 0; + } + csr[mcause] = (trap_id << 31) + cause; + // update mstatus + // xPP field of mstatus is written with the active privilege mode at the time + // of the trap; the x PIE field of mstatus + // is written with the value of the active interrupt-enable bit at the time of + // the trap; and the x IE field of mstatus + // is cleared + // store the actual privilege level in yPP and store interrupt enable flags + state.mstatus.MPP = PRIV_M; + state.mstatus.MPIE = state.mstatus.MIE; + state.mstatus.MIE = false; + + // get trap vector + auto ivec = csr[mtvec]; + // calculate addr// set NEXT_PC to trap addressess to jump to based on MODE + // bits in mtvec + this->reg.NEXT_PC = ivec & ~0x1UL; + if ((ivec & 0x1) == 1 && trap_id != 0) this->reg.NEXT_PC += 4 * cause; + // reset trap state + this->reg.PRIV = PRIV_M; + this->reg.trap_state = 0; + std::array buffer; + sprintf(buffer.data(), "0x%016lx", addr); + if((flags&0xffffffff) != 0xffffffff) + CLOG(INFO, disass) << (trap_id ? "Interrupt" : "Trap") << " with cause '" + << (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" << cause << ")" + << " at address " << buffer.data() << " occurred"; + return this->reg.NEXT_PC; +} + +template uint64_t riscv_hart_mu_p::leave_trap(uint64_t flags) { + state.mstatus.MIE = state.mstatus.MPIE; + // sets the pc to the value stored in the x epc register. + this->reg.NEXT_PC = csr[mepc]; + CLOG(INFO, disass) << "Executing xRET"; + return this->reg.NEXT_PC; +} + +} // namespace arch +} // namespace iss + +#endif /* _RISCV_CORE_H_ */ diff --git a/src/sysc/core_complex.cpp b/src/sysc/core_complex.cpp index 7685445..ae61de0 100644 --- a/src/sysc/core_complex.cpp +++ b/src/sysc/core_complex.cpp @@ -31,14 +31,17 @@ *******************************************************************************/ #include "sysc/core_complex.h" -#include "iss/arch/riscv_hart_m_p.h" #ifdef CORE_TGC_C +#include "iss/arch/riscv_hart_m_p.h" #include "iss/arch/tgc_c.h" using core_type = iss::arch::tgc_c; +using plat_type = iss::arch::riscv_hart_m_p; #endif #ifdef CORE_TGC_D +#include "iss/arch/riscv_hart_mu_p.h" #include "iss/arch/tgc_d.h" using core_type = iss::arch::tgc_d; +using plat_type = iss::arch::riscv_hart_mu_p; #endif #include "iss/debugger/encoderdecoder.h" #include "iss/debugger/gdb_session.h" @@ -95,9 +98,8 @@ std::array irq_str = { { "User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt" } }; } -class core_wrapper : public iss::arch::riscv_hart_m_p { +class core_wrapper : public plat_type { 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) { } @@ -108,7 +110,7 @@ public: inline bool get_interrupt_execution() { return this->interrupt_sim; } - base_type::hart_state &get_state() { return this->state; } + plat_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); @@ -163,7 +165,7 @@ public: } return ret?Ok:Err; } else { - return base_type::read_csr(addr, val); + return plat_type::read_csr(addr, val); } } @@ -172,11 +174,11 @@ public: do { wait(wfi_evt); } while (this->reg.pending_trap == 0); - base_type::wait_until(flags); + plat_type::wait_until(flags); } void local_irq(short id, bool value) { - base_type::reg_t mask = 0; + plat_type::reg_t mask = 0; switch (id) { case 16: // SW mask = 1 << 3;