moves common functionality to base class
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (C) 2017, 2018, 2021 MINRES Technologies GmbH
|
||||
* Copyright (C) 2017 - 2025 MINRES Technologies GmbH
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -35,6 +35,8 @@
|
||||
#ifndef _RISCV_HART_COMMON
|
||||
#define _RISCV_HART_COMMON
|
||||
|
||||
#include "iss/arch/traits.h"
|
||||
#include "iss/log_categories.h"
|
||||
#include "iss/vm_types.h"
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
@@ -42,11 +44,14 @@
|
||||
#include <fmt/format.h>
|
||||
#include <iss/arch_if.h>
|
||||
#include <iss/log_categories.h>
|
||||
#include <iss/semihosting/semihosting.h>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <util/ities.h>
|
||||
#include <util/logging.h>
|
||||
#include <util/sparse_array.h>
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define likely(x) ::__builtin_expect(!!(x), 1)
|
||||
@@ -312,16 +317,120 @@ inline void write_reg_uint32(uint64_t offs, uint32_t& reg, const uint8_t* const
|
||||
break;
|
||||
}
|
||||
}
|
||||
struct riscv_hart_common {
|
||||
riscv_hart_common(){};
|
||||
~riscv_hart_common(){};
|
||||
template <typename BASE, typename LOGCAT = logging::disass> struct riscv_hart_common : public BASE {
|
||||
const std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
|
||||
const std::array<const char*, 16> 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"}};
|
||||
const std::array<const char*, 12> 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"}};
|
||||
constexpr static unsigned MEM = traits<BASE>::MEM;
|
||||
|
||||
using core = BASE;
|
||||
using this_class = riscv_hart_common<BASE, LOGCAT>;
|
||||
using phys_addr_t = typename core::phys_addr_t;
|
||||
using reg_t = typename core::reg_t;
|
||||
using addr_t = typename core::addr_t;
|
||||
|
||||
#define MK_CSR_RD_CB(FCT) [this](unsigned a, reg_t& r) -> iss::status { return this->FCT(a, r); };
|
||||
#define MK_CSR_WR_CB(FCT) [this](unsigned a, reg_t r) -> iss::status { return this->FCT(a, r); };
|
||||
|
||||
riscv_hart_common()
|
||||
: instr_if(*this) {
|
||||
// reset values
|
||||
csr[misa] = traits<BASE>::MISA_VAL;
|
||||
csr[mvendorid] = 0x669;
|
||||
csr[marchid] = traits<BASE>::MARCHID_VAL;
|
||||
csr[mimpid] = 1;
|
||||
|
||||
if(traits<BASE>::FLEN > 0) {
|
||||
csr_rd_cb[fcsr] = MK_CSR_RD_CB(read_fcsr);
|
||||
csr_wr_cb[fcsr] = MK_CSR_WR_CB(write_fcsr);
|
||||
csr_rd_cb[fflags] = MK_CSR_RD_CB(read_fcsr);
|
||||
csr_wr_cb[fflags] = MK_CSR_WR_CB(write_fcsr);
|
||||
csr_rd_cb[frm] = MK_CSR_RD_CB(read_fcsr);
|
||||
csr_wr_cb[frm] = MK_CSR_WR_CB(write_fcsr);
|
||||
}
|
||||
for(unsigned addr = mhpmcounter3; addr <= mhpmcounter31; ++addr) {
|
||||
csr_rd_cb[addr] = MK_CSR_RD_CB(read_null);
|
||||
csr_wr_cb[addr] = MK_CSR_WR_CB(write_plain);
|
||||
}
|
||||
if(traits<BASE>::XLEN == 32)
|
||||
for(unsigned addr = mhpmcounter3h; addr <= mhpmcounter31h; ++addr) {
|
||||
csr_rd_cb[addr] = MK_CSR_RD_CB(read_null);
|
||||
csr_wr_cb[addr] = MK_CSR_WR_CB(write_plain);
|
||||
}
|
||||
for(unsigned addr = mhpmevent3; addr <= mhpmevent31; ++addr) {
|
||||
csr_rd_cb[addr] = MK_CSR_RD_CB(read_null);
|
||||
csr_wr_cb[addr] = MK_CSR_WR_CB(write_plain);
|
||||
}
|
||||
for(unsigned addr = hpmcounter3; addr <= hpmcounter31; ++addr) {
|
||||
csr_rd_cb[addr] = MK_CSR_RD_CB(read_null);
|
||||
}
|
||||
if(traits<BASE>::XLEN == 32)
|
||||
for(unsigned addr = hpmcounter3h; addr <= hpmcounter31h; ++addr) {
|
||||
csr_rd_cb[addr] = MK_CSR_RD_CB(read_null);
|
||||
}
|
||||
// common regs
|
||||
const std::array<unsigned, 4> roaddrs{{misa, mvendorid, marchid, mimpid}};
|
||||
for(auto addr : roaddrs) {
|
||||
csr_rd_cb[addr] = MK_CSR_RD_CB(read_plain);
|
||||
csr_wr_cb[addr] = MK_CSR_WR_CB(write_null);
|
||||
}
|
||||
// special handling & overrides
|
||||
csr_rd_cb[time] = MK_CSR_RD_CB(read_time);
|
||||
if(traits<BASE>::XLEN == 32)
|
||||
csr_rd_cb[timeh] = MK_CSR_RD_CB(read_time);
|
||||
csr_rd_cb[cycle] = MK_CSR_RD_CB(read_cycle);
|
||||
if(traits<BASE>::XLEN == 32)
|
||||
csr_rd_cb[cycleh] = MK_CSR_RD_CB(read_cycle);
|
||||
csr_rd_cb[instret] = MK_CSR_RD_CB(read_instret);
|
||||
if(traits<BASE>::XLEN == 32)
|
||||
csr_rd_cb[instreth] = MK_CSR_RD_CB(read_instret);
|
||||
|
||||
csr_rd_cb[mcycle] = MK_CSR_RD_CB(read_cycle);
|
||||
csr_wr_cb[mcycle] = MK_CSR_WR_CB(write_cycle);
|
||||
if(traits<BASE>::XLEN == 32)
|
||||
csr_rd_cb[mcycleh] = MK_CSR_RD_CB(read_cycle);
|
||||
if(traits<BASE>::XLEN == 32)
|
||||
csr_wr_cb[mcycleh] = MK_CSR_WR_CB(write_cycle);
|
||||
csr_rd_cb[minstret] = MK_CSR_RD_CB(read_instret);
|
||||
csr_wr_cb[minstret] = MK_CSR_WR_CB(write_instret);
|
||||
if(traits<BASE>::XLEN == 32)
|
||||
csr_rd_cb[minstreth] = MK_CSR_RD_CB(read_instret);
|
||||
if(traits<BASE>::XLEN == 32)
|
||||
csr_wr_cb[minstreth] = MK_CSR_WR_CB(write_instret);
|
||||
csr_rd_cb[mhartid] = MK_CSR_RD_CB(read_hartid);
|
||||
};
|
||||
~riscv_hart_common() {};
|
||||
std::unordered_map<std::string, uint64_t> symbol_table;
|
||||
uint64_t entry_address{0};
|
||||
uint64_t tohost = std::numeric_limits<uint64_t>::max();
|
||||
uint64_t fromhost = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
bool read_elf_file(std::string name, uint8_t expected_elf_class,
|
||||
std::function<iss::status(uint64_t, uint64_t, const uint8_t* const)> cb) {
|
||||
void set_semihosting_callback(semihosting_cb_t<reg_t> cb) { semihosting_cb = cb; };
|
||||
|
||||
std::pair<uint64_t, bool> load_file(std::string name, int type) {
|
||||
return std::make_pair(entry_address, read_elf_file(name, sizeof(reg_t) == 4 ? ELFIO::ELFCLASS32 : ELFIO::ELFCLASS64));
|
||||
}
|
||||
|
||||
bool read_elf_file(std::string name, uint8_t expected_elf_class) {
|
||||
// Create elfio reader
|
||||
ELFIO::elfio reader;
|
||||
// Load ELF data
|
||||
@@ -339,7 +448,8 @@ struct riscv_hart_common {
|
||||
const auto seg_data = pseg->get_data();
|
||||
const auto type = pseg->get_type();
|
||||
if(type == ELFIO::PT_LOAD && fsize > 0) {
|
||||
auto res = cb(pseg->get_physical_address(), fsize, reinterpret_cast<const uint8_t* const>(seg_data));
|
||||
auto res = this->write(iss::address_type::PHYSICAL, iss::access_type::DEBUG_WRITE, traits<BASE>::MEM,
|
||||
pseg->get_physical_address(), fsize, reinterpret_cast<const uint8_t* const>(seg_data));
|
||||
if(res != iss::Ok)
|
||||
CPPLOG(ERR) << "problem writing " << fsize << "bytes to 0x" << std::hex << pseg->get_physical_address();
|
||||
}
|
||||
@@ -407,6 +517,280 @@ struct riscv_hart_common {
|
||||
}
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
constexpr bool has_compressed() { return traits<BASE>::MISA_VAL & 0b0100; }
|
||||
|
||||
constexpr reg_t get_pc_mask() { return has_compressed() ? (reg_t)~1 : (reg_t)~3; }
|
||||
|
||||
void disass_output(uint64_t pc, const std::string instr) override {
|
||||
// NSCLOG(INFO, LOGCAT) << fmt::format("0x{:016x} {:40} [p:{};s:0x{:x};c:{}]", pc, instr, lvl[this->reg.PRIV],
|
||||
// (reg_t)state.mstatus,
|
||||
// this->reg.cycle + cycle_offset);
|
||||
NSCLOG(INFO, LOGCAT) << fmt::format("0x{:016x} {:40} [p:{};c:{}]", pc, instr, lvl[this->reg.PRIV],
|
||||
this->reg.cycle + cycle_offset);
|
||||
};
|
||||
|
||||
void register_custom_csr_rd(unsigned addr) { csr_rd_cb[addr] = &this_class::read_custom_csr; }
|
||||
void register_custom_csr_wr(unsigned addr) { csr_wr_cb[addr] = &this_class::write_custom_csr; }
|
||||
|
||||
bool debug_mode_active() { return this->reg.PRIV & 0x4; }
|
||||
|
||||
const reg_t& get_mhartid() const { return mhartid_reg; }
|
||||
void set_mhartid(reg_t mhartid) { mhartid_reg = mhartid; };
|
||||
|
||||
iss::status 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 it->second(addr, val);
|
||||
}
|
||||
|
||||
iss::status 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 it->second(addr, val);
|
||||
}
|
||||
|
||||
iss::status read_null(unsigned addr, reg_t& val) {
|
||||
val = 0;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status write_null(unsigned addr, reg_t val) { return iss::status::Ok; }
|
||||
|
||||
iss::status read_plain(unsigned addr, reg_t& val) {
|
||||
val = csr[addr];
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status write_plain(unsigned addr, reg_t val) {
|
||||
csr[addr] = val;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status read_cycle(unsigned addr, reg_t& val) {
|
||||
auto cycle_val = this->reg.cycle + cycle_offset;
|
||||
if(addr == mcycle) {
|
||||
val = static_cast<reg_t>(cycle_val);
|
||||
} else if(addr == mcycleh) {
|
||||
val = static_cast<reg_t>(cycle_val >> 32);
|
||||
}
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status write_cycle(unsigned addr, reg_t val) {
|
||||
if(sizeof(typename traits<BASE>::reg_t) != 4) {
|
||||
mcycle_csr = static_cast<uint64_t>(val);
|
||||
} else {
|
||||
if(addr == mcycle) {
|
||||
mcycle_csr = (mcycle_csr & 0xffffffff00000000) + val;
|
||||
} else {
|
||||
mcycle_csr = (static_cast<uint64_t>(val) << 32) + (mcycle_csr & 0xffffffff);
|
||||
}
|
||||
}
|
||||
cycle_offset = mcycle_csr - this->reg.cycle; // TODO: relying on wrap-around
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status read_instret(unsigned addr, reg_t& val) {
|
||||
if((addr & 0xff) == (minstret & 0xff)) {
|
||||
val = static_cast<reg_t>(this->reg.instret);
|
||||
} else if((addr & 0xff) == (minstreth & 0xff)) {
|
||||
val = static_cast<reg_t>(this->reg.instret >> 32);
|
||||
}
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status write_instret(unsigned addr, reg_t val) {
|
||||
if(sizeof(typename traits<BASE>::reg_t) != 4) {
|
||||
this->reg.instret = static_cast<uint64_t>(val);
|
||||
} else {
|
||||
if((addr & 0xff) == (minstret & 0xff)) {
|
||||
this->reg.instret = (this->reg.instret & 0xffffffff00000000) + val;
|
||||
} else {
|
||||
this->reg.instret = (static_cast<uint64_t>(val) << 32) + (this->reg.instret & 0xffffffff);
|
||||
}
|
||||
}
|
||||
this->reg.instret--;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status read_time(unsigned addr, reg_t& val) {
|
||||
uint64_t time_val = this->reg.cycle / (100000000 / 32768 - 1); //-> ~3052;
|
||||
if(addr == time) {
|
||||
val = static_cast<reg_t>(time_val);
|
||||
} else if(addr == timeh) {
|
||||
if(sizeof(typename traits<BASE>::reg_t) != 4)
|
||||
return iss::Err;
|
||||
val = static_cast<reg_t>(time_val >> 32);
|
||||
}
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status read_tvec(unsigned addr, reg_t& val) {
|
||||
val = csr[addr] & ~2;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status read_hartid(unsigned addr, reg_t& val) {
|
||||
val = mhartid_reg;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status write_epc(unsigned addr, reg_t val) {
|
||||
csr[addr] = val & get_pc_mask();
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status write_dcsr(unsigned addr, reg_t val) {
|
||||
if(!debug_mode_active())
|
||||
throw illegal_instruction_fault(this->fault_data);
|
||||
// +-------------- ebreakm
|
||||
// | +---------- stepi
|
||||
// | | +++----- cause
|
||||
// | | ||| +- step
|
||||
csr[addr] = val & 0b1000100111000100U;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status read_debug(unsigned addr, reg_t& val) {
|
||||
if(!debug_mode_active())
|
||||
throw illegal_instruction_fault(this->fault_data);
|
||||
val = csr[addr];
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status write_dscratch(unsigned addr, reg_t val) {
|
||||
if(!debug_mode_active())
|
||||
throw illegal_instruction_fault(this->fault_data);
|
||||
csr[addr] = val;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status read_dpc(unsigned addr, reg_t& val) {
|
||||
if(!debug_mode_active())
|
||||
throw illegal_instruction_fault(this->fault_data);
|
||||
val = this->reg.DPC;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status write_dpc(unsigned addr, reg_t val) {
|
||||
if(!debug_mode_active())
|
||||
throw illegal_instruction_fault(this->fault_data);
|
||||
this->reg.DPC = val;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status read_fcsr(unsigned addr, reg_t& val) {
|
||||
switch(addr) {
|
||||
case 1: // fflags, 4:0
|
||||
val = bit_sub<0, 5>(this->get_fcsr());
|
||||
break;
|
||||
case 2: // frm, 7:5
|
||||
val = bit_sub<5, 3>(this->get_fcsr());
|
||||
break;
|
||||
case 3: // fcsr
|
||||
val = this->get_fcsr();
|
||||
break;
|
||||
default:
|
||||
return iss::Err;
|
||||
}
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status write_fcsr(unsigned addr, reg_t val) {
|
||||
switch(addr) {
|
||||
case 1: // fflags, 4:0
|
||||
this->set_fcsr((this->get_fcsr() & 0xffffffe0) | (val & 0x1f));
|
||||
break;
|
||||
case 2: // frm, 7:5
|
||||
this->set_fcsr((this->get_fcsr() & 0xffffff1f) | ((val & 0x7) << 5));
|
||||
break;
|
||||
case 3: // fcsr
|
||||
this->set_fcsr(val & 0xff);
|
||||
break;
|
||||
default:
|
||||
return iss::Err;
|
||||
}
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status write_xtvt(unsigned addr, reg_t val) {
|
||||
csr[addr] = val & ~0x3fULL;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
protected:
|
||||
struct riscv_instrumentation_if : public iss::instrumentation_if {
|
||||
|
||||
riscv_instrumentation_if(riscv_hart_common<BASE, LOGCAT>& 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<BASE>::core_type; }
|
||||
|
||||
uint64_t get_pc() override { return arch.reg.PC; }
|
||||
|
||||
uint64_t get_next_pc() override { return arch.reg.NEXT_PC; }
|
||||
|
||||
uint64_t get_instr_word() override { return arch.reg.instruction; }
|
||||
|
||||
uint64_t get_instr_count() override { return arch.reg.icount; }
|
||||
|
||||
uint64_t get_pendig_traps() override { return arch.reg.trap_state; }
|
||||
|
||||
uint64_t get_total_cycles() override { return arch.reg.cycle + arch.cycle_offset; }
|
||||
|
||||
void update_last_instr_cycles(unsigned cycles) override { arch.cycle_offset += cycles - 1; }
|
||||
|
||||
bool is_branch_taken() override { return arch.reg.last_branch; }
|
||||
|
||||
unsigned get_reg_num() override { return traits<BASE>::NUM_REGS; }
|
||||
|
||||
unsigned get_reg_size(unsigned num) override { return traits<BASE>::reg_bit_widths[num]; }
|
||||
|
||||
std::unordered_map<std::string, uint64_t> const& get_symbol_table(std::string name) override { return arch.symbol_table; }
|
||||
|
||||
riscv_hart_common<BASE, LOGCAT>& arch;
|
||||
};
|
||||
|
||||
friend struct riscv_instrumentation_if;
|
||||
riscv_instrumentation_if instr_if;
|
||||
|
||||
using csr_type = util::sparse_array<typename traits<BASE>::reg_t, 1ULL << 12, 12>;
|
||||
using csr_page_type = typename csr_type::page_type;
|
||||
csr_type csr;
|
||||
|
||||
using rd_csr_f = std::function<iss::status(unsigned addr, reg_t&)>;
|
||||
using wr_csr_f = std::function<iss::status(unsigned addr, reg_t)>;
|
||||
std::unordered_map<unsigned, rd_csr_f> csr_rd_cb;
|
||||
std::unordered_map<unsigned, wr_csr_f> csr_wr_cb;
|
||||
|
||||
reg_t mhartid_reg{0x0};
|
||||
uint64_t mcycle_csr{0};
|
||||
uint64_t minstret_csr{0};
|
||||
reg_t fault_data;
|
||||
|
||||
int64_t cycle_offset{0};
|
||||
int64_t instret_offset{0};
|
||||
semihosting_cb_t<reg_t> semihosting_cb;
|
||||
std::array<vm_info, 2> vm;
|
||||
};
|
||||
|
||||
} // namespace arch
|
||||
|
Reference in New Issue
Block a user