factors clic & pmp into separate units
This commit is contained in:
parent
a13b7ac6d3
commit
23842742a6
@ -18,7 +18,7 @@ add_subdirectory(softfloat)
|
||||
set(LIB_SOURCES
|
||||
src/iss/plugin/instruction_count.cpp
|
||||
src/iss/arch/tgc5c.cpp
|
||||
src/iss/memory/memory_if.cpp
|
||||
src/iss/mmio/memory_if.cpp
|
||||
src/vm/interp/vm_tgc5c.cpp
|
||||
src/vm/fp_functions.cpp
|
||||
src/iss/debugger/csr_names.cpp
|
||||
|
233
src/iss/arch/mstatus.h
Normal file
233
src/iss/arch/mstatus.h
Normal file
@ -0,0 +1,233 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (C) 2025 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 _MSTATUS_TYPE
|
||||
#define _MSTATUS_TYPE
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <util/bit_field.h>
|
||||
#include <util/ities.h>
|
||||
|
||||
namespace iss {
|
||||
namespace arch {
|
||||
|
||||
template <class T, class Enable = void> struct status {};
|
||||
// specialization 32bit
|
||||
template <typename T> struct status<T, typename std::enable_if<std::is_same<T, uint32_t>::value>::type> {
|
||||
static inline unsigned SD(T v) { return bit_sub<63, 1>(v); }
|
||||
// value of XLEN for S-mode
|
||||
static inline unsigned SXL(T v) { return bit_sub<34, 2>(v); };
|
||||
// value of XLEN for U-mode
|
||||
static inline unsigned UXL(T v) { return bit_sub<32, 2>(v); };
|
||||
// Trap SRET
|
||||
static inline unsigned TSR(T v) { return bit_sub<22, 1>(v); };
|
||||
// Timeout Wait
|
||||
static inline unsigned TW(T v) { return bit_sub<21, 1>(v); };
|
||||
// Trap Virtual Memory
|
||||
static inline unsigned TVM(T v) { return bit_sub<20, 1>(v); };
|
||||
// Make eXecutable Readable
|
||||
static inline unsigned MXR(T v) { return bit_sub<19, 1>(v); };
|
||||
// permit Supervisor User Memory access
|
||||
static inline unsigned SUM(T v) { return bit_sub<18, 1>(v); };
|
||||
// Modify PRiVilege
|
||||
static inline unsigned MPRV(T v) { return bit_sub<17, 1>(v); };
|
||||
// status of additional user-mode extensions and associated state, All off/None dirty or clean, some on/None
|
||||
// dirty, some clean/Some dirty
|
||||
static inline unsigned XS(T v) { return bit_sub<15, 2>(v); };
|
||||
// floating-point unit status Off/Initial/Clean/Dirty
|
||||
static inline unsigned FS(T v) { return bit_sub<13, 2>(v); };
|
||||
// machine previous privilege
|
||||
static inline unsigned MPP(T v) { return bit_sub<11, 2>(v); };
|
||||
// supervisor previous privilege
|
||||
static inline unsigned SPP(T v) { return bit_sub<8, 1>(v); };
|
||||
// previous machine interrupt-enable
|
||||
static inline unsigned MPIE(T v) { return bit_sub<7, 1>(v); };
|
||||
// previous supervisor interrupt-enable
|
||||
static inline unsigned SPIE(T v) { return bit_sub<5, 1>(v); };
|
||||
// previous user interrupt-enable
|
||||
static inline unsigned UPIE(T v) { return bit_sub<4, 1>(v); };
|
||||
// machine interrupt-enable
|
||||
static inline unsigned MIE(T v) { return bit_sub<3, 1>(v); };
|
||||
// supervisor interrupt-enable
|
||||
static inline unsigned SIE(T v) { return bit_sub<1, 1>(v); };
|
||||
// user interrupt-enable
|
||||
static inline unsigned UIE(T v) { return bit_sub<0, 1>(v); };
|
||||
};
|
||||
|
||||
template <typename T> struct status<T, typename std::enable_if<std::is_same<T, uint64_t>::value>::type> {
|
||||
public:
|
||||
// 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)))
|
||||
static inline unsigned SD(T v) { return bit_sub<63, 1>(v); };
|
||||
// value of XLEN for S-mode
|
||||
static inline unsigned SXL(T v) { return bit_sub<34, 2>(v); };
|
||||
// value of XLEN for U-mode
|
||||
static inline unsigned UXL(T v) { return bit_sub<32, 2>(v); };
|
||||
// Trap SRET
|
||||
static inline unsigned TSR(T v) { return bit_sub<22, 1>(v); };
|
||||
// Timeout Wait
|
||||
static inline unsigned TW(T v) { return bit_sub<21, 1>(v); };
|
||||
// Trap Virtual Memory
|
||||
static inline unsigned TVM(T v) { return bit_sub<20, 1>(v); };
|
||||
// Make eXecutable Readable
|
||||
static inline unsigned MXR(T v) { return bit_sub<19, 1>(v); };
|
||||
// permit Supervisor User Memory access
|
||||
static inline unsigned SUM(T v) { return bit_sub<18, 1>(v); };
|
||||
// Modify PRiVilege
|
||||
static inline unsigned MPRV(T v) { return bit_sub<17, 1>(v); };
|
||||
// status of additional user-mode extensions and associated state, All off/None dirty or clean, some on/None
|
||||
// dirty, some clean/Some dirty
|
||||
static inline unsigned XS(T v) { return bit_sub<15, 2>(v); };
|
||||
// floating-point unit status Off/Initial/Clean/Dirty
|
||||
static inline unsigned FS(T v) { return bit_sub<13, 2>(v); };
|
||||
// machine previous privilege
|
||||
static inline unsigned MPP(T v) { return bit_sub<11, 2>(v); };
|
||||
// supervisor previous privilege
|
||||
static inline unsigned SPP(T v) { return bit_sub<8, 1>(v); };
|
||||
// previous machine interrupt-enable
|
||||
static inline unsigned MPIE(T v) { return bit_sub<7, 1>(v); };
|
||||
// previous supervisor interrupt-enable
|
||||
static inline unsigned SPIE(T v) { return bit_sub<5, 1>(v); };
|
||||
// previous user interrupt-enable
|
||||
static inline unsigned UPIE(T v) { return bit_sub<4, 1>(v); };
|
||||
// machine interrupt-enable
|
||||
static inline unsigned MIE(T v) { return bit_sub<3, 1>(v); };
|
||||
// supervisor interrupt-enable
|
||||
static inline unsigned SIE(T v) { return bit_sub<1, 1>(v); };
|
||||
// user interrupt-enable
|
||||
static inline unsigned UIE(T v) { return bit_sub<0, 1>(v); };
|
||||
};
|
||||
|
||||
// primary template
|
||||
template <class T, class Enable = void> struct hart_state {};
|
||||
// specialization 32bit
|
||||
template <typename T> class hart_state<T, typename std::enable_if<std::is_same<T, uint32_t>::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 T mstatus_reset_val = 0x1800;
|
||||
};
|
||||
|
||||
// specialization 64bit
|
||||
template <typename T> class hart_state<T, typename std::enable_if<std::is_same<T, uint64_t>::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, 63, 1);
|
||||
// value of XLEN for S-mode
|
||||
BF_FIELD(SXL, 34, 2);
|
||||
// value of XLEN for U-mode
|
||||
BF_FIELD(UXL, 32, 2);
|
||||
// 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 T mstatus_reset_val = 0x1800;
|
||||
};
|
||||
} // namespace arch
|
||||
} // namespace iss
|
||||
#endif // _MSTATUS_TYPE
|
@ -37,8 +37,9 @@
|
||||
|
||||
#include "iss/arch/traits.h"
|
||||
#include "iss/log_categories.h"
|
||||
#include "iss/memory/memory_if.h"
|
||||
#include "iss/mmio/memory_if.h"
|
||||
#include "iss/vm_types.h"
|
||||
#include "mstatus.h"
|
||||
#include "util/delegate.h"
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
@ -51,7 +52,6 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <util/ities.h>
|
||||
#include <util/logging.h>
|
||||
#include <util/sparse_array.h>
|
||||
|
||||
@ -66,7 +66,7 @@
|
||||
namespace iss {
|
||||
namespace arch {
|
||||
|
||||
enum features_e { FEAT_NONE, FEAT_PMP = 1, FEAT_EXT_N = 2, FEAT_CLIC = 4, FEAT_DEBUG = 8, FEAT_TCM = 16 };
|
||||
enum features_e { FEAT_NONE, FEAT_EXT_N = 1, FEAT_DEBUG = 2 };
|
||||
|
||||
enum riscv_csr {
|
||||
/* user-level CSR */
|
||||
@ -242,10 +242,6 @@ struct vm_info {
|
||||
};
|
||||
|
||||
struct feature_config {
|
||||
uint64_t clic_base{0xc0000000};
|
||||
unsigned clic_int_ctl_bits{4};
|
||||
unsigned clic_num_irq{16};
|
||||
unsigned clic_num_trigger{0};
|
||||
uint64_t tcm_base{0x10000000};
|
||||
uint64_t tcm_size{0x8000};
|
||||
uint64_t io_address{0xf0000000};
|
||||
@ -278,56 +274,22 @@ public:
|
||||
: trap_access(15 << 16, badaddr) {}
|
||||
};
|
||||
|
||||
inline void read_reg_uint32(uint64_t offs, uint32_t& reg, uint8_t* const data, unsigned length) {
|
||||
auto reg_ptr = reinterpret_cast<uint8_t*>(®);
|
||||
switch(offs & 0x3) {
|
||||
case 0:
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(data + i) = *(reg_ptr + i);
|
||||
break;
|
||||
case 1:
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(data + i) = *(reg_ptr + 1 + i);
|
||||
break;
|
||||
case 2:
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(data + i) = *(reg_ptr + 2 + i);
|
||||
break;
|
||||
case 3:
|
||||
*data = *(reg_ptr + 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline void write_reg_uint32(uint64_t offs, uint32_t& reg, const uint8_t* const data, unsigned length) {
|
||||
auto reg_ptr = reinterpret_cast<uint8_t*>(®);
|
||||
switch(offs & 0x3) {
|
||||
case 0:
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(reg_ptr + i) = *(data + i);
|
||||
break;
|
||||
case 1:
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(reg_ptr + 1 + i) = *(data + i);
|
||||
break;
|
||||
case 2:
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(reg_ptr + 2 + i) = *(data + i);
|
||||
break;
|
||||
case 3:
|
||||
*(reg_ptr + 3) = *data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
template <typename WORD_TYPE> struct priv_if {
|
||||
using rd_csr_f = std::function<iss::status(unsigned addr, WORD_TYPE&)>;
|
||||
using wr_csr_f = std::function<iss::status(unsigned addr, WORD_TYPE)>;
|
||||
|
||||
std::function<iss::status(unsigned, WORD_TYPE&)> read_csr;
|
||||
std::function<iss::status(unsigned, WORD_TYPE)> write_csr;
|
||||
std::function<iss::status(uint8_t const*)> exec_htif;
|
||||
std::unordered_map<unsigned, rd_csr_f>& csr_rd_cb;
|
||||
std::unordered_map<unsigned, wr_csr_f>& csr_wr_cb;
|
||||
hart_state<WORD_TYPE>& mstatus;
|
||||
uint64_t& tohost;
|
||||
uint64_t& fromhost;
|
||||
unsigned& mcause_max_irq;
|
||||
};
|
||||
|
||||
template <typename BASE, typename LOGCAT = logging::disass> struct riscv_hart_common : public BASE, public memory::memory_elem {
|
||||
template <typename BASE, typename LOGCAT = logging::disass> struct riscv_hart_common : public BASE, public mmio::memory_elem {
|
||||
const std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
|
||||
const std::array<const char*, 16> trap_str = {{""
|
||||
"Instruction address misaligned", // 0
|
||||
@ -365,7 +327,8 @@ template <typename BASE, typename LOGCAT = logging::disass> struct riscv_hart_co
|
||||
#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) {
|
||||
: state()
|
||||
, instr_if(*this) {
|
||||
// reset values
|
||||
csr[misa] = traits<BASE>::MISA_VAL;
|
||||
csr[mvendorid] = 0x669;
|
||||
@ -748,17 +711,16 @@ template <typename BASE, typename LOGCAT = logging::disass> struct riscv_hart_co
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status write_xtvt(unsigned addr, reg_t val) {
|
||||
csr[addr] = val & ~0x3fULL;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
priv_if<reg_t> get_priv_if() {
|
||||
return priv_if<reg_t>{.read_csr = [this](unsigned addr, reg_t& val) -> iss::status { return read_csr(addr, val); },
|
||||
.write_csr = [this](unsigned addr, reg_t val) -> iss::status { return write_csr(addr, val); },
|
||||
.exec_htif = [this](uint8_t const* data) -> iss::status { return execute_htif(data); },
|
||||
.csr_rd_cb{this->csr_rd_cb},
|
||||
.csr_wr_cb{csr_wr_cb},
|
||||
.mstatus{this->state},
|
||||
.tohost{this->tohost},
|
||||
.fromhost{this->fromhost}};
|
||||
.fromhost{this->fromhost},
|
||||
.mcause_max_irq{mcause_max_irq}};
|
||||
}
|
||||
|
||||
iss::status execute_htif(uint8_t const* data) {
|
||||
@ -798,17 +760,33 @@ template <typename BASE, typename LOGCAT = logging::disass> struct riscv_hart_co
|
||||
}
|
||||
}
|
||||
|
||||
memory::memory_hierarchy memories;
|
||||
mmio::memory_hierarchy memories;
|
||||
|
||||
virtual memory::memory_if get_mem_if() override {
|
||||
virtual mmio::memory_if get_mem_if() override {
|
||||
assert(false || "This function should nevver be called");
|
||||
return memory::memory_if{};
|
||||
return mmio::memory_if{};
|
||||
}
|
||||
|
||||
virtual void set_next(memory::memory_if mem_if) { memory = mem_if; };
|
||||
virtual void set_next(mmio::memory_if mem_if) { memory = mem_if; };
|
||||
|
||||
void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); }
|
||||
|
||||
protected:
|
||||
memory::memory_if memory;
|
||||
hart_state<reg_t> state;
|
||||
|
||||
static constexpr reg_t get_mstatus_mask_t(unsigned priv_lvl = PRIV_M) {
|
||||
if(sizeof(reg_t) == 4) {
|
||||
return priv_lvl == PRIV_U ? 0x80000011UL : // 0b1...0 0001 0001
|
||||
priv_lvl == PRIV_S ? 0x800de133UL // 0b0...0 0001 1000 1001 1001;
|
||||
: 0x807ff9ddUL;
|
||||
} else {
|
||||
return priv_lvl == PRIV_U ? 0x011ULL : // 0b1...0 0001 0001
|
||||
priv_lvl == PRIV_S ? 0x000de133ULL
|
||||
: 0x007ff9ddULL;
|
||||
}
|
||||
}
|
||||
|
||||
mmio::memory_if memory;
|
||||
struct riscv_instrumentation_if : public iss::instrumentation_if {
|
||||
|
||||
riscv_instrumentation_if(riscv_hart_common<BASE, LOGCAT>& arch)
|
||||
@ -864,6 +842,7 @@ protected:
|
||||
int64_t instret_offset{0};
|
||||
semihosting_cb_t<reg_t> semihosting_cb;
|
||||
std::array<vm_info, 2> vm;
|
||||
unsigned mcause_max_irq{16U};
|
||||
};
|
||||
|
||||
} // namespace arch
|
||||
|
@ -35,7 +35,6 @@
|
||||
#ifndef _RISCV_HART_M_P_H
|
||||
#define _RISCV_HART_M_P_H
|
||||
|
||||
#include "iss/instrumentation_if.h"
|
||||
#include "iss/vm_if.h"
|
||||
#include "iss/vm_types.h"
|
||||
#include "riscv_hart_common.h"
|
||||
@ -43,21 +42,15 @@
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <elfio/elf_types.hpp>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#ifndef FMT_HEADER_ONLY
|
||||
#define FMT_HEADER_ONLY
|
||||
#endif
|
||||
#include <array>
|
||||
#include <elfio/elfio.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <iss/memory/functional_memory.h>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
#include <iss/mmio/memory_with_htif.h>
|
||||
#include <unordered_map>
|
||||
#include <util/bit_field.h>
|
||||
|
||||
namespace iss {
|
||||
namespace arch {
|
||||
|
||||
@ -71,61 +64,8 @@ public:
|
||||
using reg_t = typename core::reg_t;
|
||||
using addr_t = typename core::addr_t;
|
||||
|
||||
// primary template
|
||||
template <class T, class Enable = void> struct hart_state {};
|
||||
// specialization 32bit
|
||||
template <typename T> class hart_state<T, typename std::enable_if<std::is_same<T, uint32_t>::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 = 0x1800;
|
||||
|
||||
void write_mstatus(T val) {
|
||||
auto mask = get_mask() & 0xff; // MPP is hardcode as 0x3
|
||||
auto new_val = (mstatus.backing.val & ~mask) | (val & mask);
|
||||
mstatus = new_val;
|
||||
}
|
||||
|
||||
static constexpr uint32_t get_mask() {
|
||||
static constexpr reg_t get_mstatus_mask() {
|
||||
if(sizeof(reg_t) == 4)
|
||||
// return 0x807ff988UL; // 0b1000 0000 0111 1111 1111 1000 1000 1000 // only machine mode is supported
|
||||
// +-SD
|
||||
// | +-TSR
|
||||
@ -141,66 +81,7 @@ public:
|
||||
// | |||||| | | | |+-MPIE
|
||||
// | ||||||/|/|/| || +-MIE
|
||||
return 0b00000000000000000001100010001000;
|
||||
}
|
||||
};
|
||||
|
||||
// specialization 64bit
|
||||
template <typename T> class hart_state<T, typename std::enable_if<std::is_same<T, uint64_t>::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, 63, 1);
|
||||
// value of XLEN for S-mode
|
||||
BF_FIELD(SXL, 34, 2);
|
||||
// value of XLEN for U-mode
|
||||
BF_FIELD(UXL, 32, 2);
|
||||
// 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 = 0x1800;
|
||||
|
||||
void write_mstatus(T val) {
|
||||
auto mask = get_mask() & 0xff; // MPP is hardcode as 0x3
|
||||
auto new_val = (mstatus.backing.val & ~mask) | (val & mask);
|
||||
mstatus = new_val;
|
||||
}
|
||||
|
||||
static constexpr T get_mask() {
|
||||
else if(sizeof(reg_t) == 8)
|
||||
// return 0x8000000f007ff9ddULL; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011
|
||||
//
|
||||
// +-TSR
|
||||
@ -216,10 +97,15 @@ public:
|
||||
// |||||| | | | |+-MPIE
|
||||
// ||||||/|/|/| || +-MIE
|
||||
return 0b00000000000000000001100010001000;
|
||||
}
|
||||
};
|
||||
else
|
||||
assert(false && "Unsupported XLEN value");
|
||||
}
|
||||
|
||||
using hart_state_type = hart_state<reg_t>;
|
||||
void write_mstatus(reg_t val) {
|
||||
auto mask = get_mstatus_mask() & 0xff; // MPP is hardcoded as 0x3
|
||||
auto new_val = (this->state.mstatus() & ~mask) | (val & mask);
|
||||
this->state.mstatus = new_val;
|
||||
}
|
||||
|
||||
constexpr reg_t get_irq_mask() {
|
||||
return 0b100010001000; // only machine mode is supported
|
||||
@ -242,11 +128,15 @@ public:
|
||||
|
||||
void set_csr(unsigned addr, reg_t val) { this->csr[addr & this->csr.page_addr_mask] = val; }
|
||||
|
||||
void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); }
|
||||
void set_num_of_irq(unsigned i) { this->mcause_max_irq = 1 << util::ilog2(i); }
|
||||
|
||||
protected:
|
||||
hart_state_type state;
|
||||
using mem_read_f = iss::status(phys_addr_t addr, unsigned, uint8_t* const);
|
||||
using mem_write_f = iss::status(phys_addr_t addr, unsigned, uint8_t const* const);
|
||||
|
||||
hart_state<reg_t> state;
|
||||
|
||||
std::unordered_map<reg_t, uint64_t> ptw;
|
||||
std::unordered_map<uint64_t, uint8_t> atomic_reservation;
|
||||
|
||||
iss::status read_status(unsigned addr, reg_t& val);
|
||||
@ -265,20 +155,13 @@ protected:
|
||||
|
||||
void check_interrupt();
|
||||
feature_config cfg;
|
||||
unsigned mcause_max_irq{(FEAT & features_e::FEAT_CLIC) ? std::max(16U, static_cast<unsigned>(traits<BASE>::CLIC_NUM_IRQ)) : 16U};
|
||||
memory::functional_memory<reg_t> default_mem;
|
||||
mmio::memory_with_htif<reg_t> default_mem;
|
||||
};
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
riscv_hart_m_p<BASE, FEAT, LOGCAT>::riscv_hart_m_p(feature_config cfg)
|
||||
: state()
|
||||
, cfg(cfg)
|
||||
: cfg(cfg)
|
||||
, default_mem(base::get_priv_if()) {
|
||||
this->rd_func = util::delegate<arch_if::rd_func_sig>::from<this_class, &this_class::read>(this);
|
||||
this->wr_func = util::delegate<arch_if::wr_func_sig>::from<this_class, &this_class::write>(this);
|
||||
this->memories.prepend(*this);
|
||||
this->memories.append(default_mem);
|
||||
|
||||
const std::array<unsigned, 4> rwaddrs{{mepc, mtvec, mscratch, mtval}};
|
||||
for(auto addr : rwaddrs) {
|
||||
this->csr_rd_cb[addr] = MK_CSR_RD_CB(read_plain);
|
||||
@ -310,6 +193,10 @@ riscv_hart_m_p<BASE, FEAT, LOGCAT>::riscv_hart_m_p(feature_config cfg)
|
||||
this->csr_wr_cb[dcsr] = MK_CSR_WR_CB(write_dcsr);
|
||||
this->csr_rd_cb[dcsr] = MK_CSR_RD_CB(read_debug);
|
||||
}
|
||||
this->rd_func = util::delegate<arch_if::rd_func_sig>::from<this_class, &this_class::read>(this);
|
||||
this->wr_func = util::delegate<arch_if::wr_func_sig>::from<this_class, &this_class::write>(this);
|
||||
this->memories.prepend(*this);
|
||||
this->memories.append(default_mem);
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
@ -318,7 +205,7 @@ iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::read(const address_type type, co
|
||||
#ifndef NDEBUG
|
||||
if(access && iss::access_type::DEBUG) {
|
||||
CPPLOG(TRACEALL) << "debug read of " << length << " bytes @addr 0x" << std::hex << addr;
|
||||
} else if(access && iss::access_type::FETCH) {
|
||||
} else if(is_fetch(access)) {
|
||||
CPPLOG(TRACEALL) << "fetch of " << length << " bytes @addr 0x" << std::hex << addr;
|
||||
} else {
|
||||
CPPLOG(TRACE) << "read of " << length << " bytes @addr 0x" << std::hex << addr;
|
||||
@ -464,39 +351,27 @@ iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::write(const address_type type, c
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::read_status(unsigned addr, reg_t& val) {
|
||||
val = state.mstatus & hart_state_type::get_mask();
|
||||
val = this->state.mstatus & get_mstatus_mask();
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::write_status(unsigned addr, reg_t val) {
|
||||
state.write_mstatus(val);
|
||||
write_mstatus(val);
|
||||
check_interrupt();
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::read_cause(unsigned addr, reg_t& val) {
|
||||
if((FEAT & features_e::FEAT_CLIC) && (this->csr[mtvec] & 0x3) == 3) {
|
||||
val = this->csr[addr] & ((1UL << (traits<BASE>::XLEN - 1)) | (mcause_max_irq - 1) | (0xfUL << 16));
|
||||
val |= state.mstatus.MPIE << 27;
|
||||
val |= state.mstatus.MPP << 28;
|
||||
} else
|
||||
val = this->csr[addr] & ((1UL << (traits<BASE>::XLEN - 1)) | (mcause_max_irq - 1));
|
||||
val = this->csr[addr] & ((1UL << (traits<BASE>::XLEN - 1)) | (this->mcause_max_irq - 1));
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::write_cause(unsigned addr, reg_t val) {
|
||||
if((FEAT & features_e::FEAT_CLIC) && (this->csr[mtvec] & 0x3) == 3) {
|
||||
auto mask = ((1UL << (traits<BASE>::XLEN - 1)) | (mcause_max_irq - 1) | (0xfUL << 16));
|
||||
this->csr[addr] = (val & mask) | (this->csr[addr] & ~mask);
|
||||
state.mstatus.MPIE = (val >> 27) & 0x1;
|
||||
state.mstatus.MPP = (val >> 28) & 0x3;
|
||||
} else {
|
||||
auto mask = ((1UL << (traits<BASE>::XLEN - 1)) | (mcause_max_irq - 1));
|
||||
this->csr[addr] = (val & mask) | (this->csr[addr] & ~mask);
|
||||
}
|
||||
auto mask = ((1UL << (traits<BASE>::XLEN - 1)) | (this->mcause_max_irq - 1));
|
||||
this->csr[addr] = (val & mask) | (this->csr[addr] & ~mask);
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
@ -524,7 +399,7 @@ iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::read_ip(unsigned addr, reg_t& va
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT> inline void riscv_hart_m_p<BASE, FEAT, LOGCAT>::reset(uint64_t address) {
|
||||
BASE::reset(address);
|
||||
state.mstatus = hart_state_type::mstatus_reset_val;
|
||||
this->state.mstatus = hart_state<reg_t>::mstatus_reset_val;
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT> void riscv_hart_m_p<BASE, FEAT, LOGCAT>::check_interrupt() {
|
||||
@ -536,7 +411,7 @@ template <typename BASE, features_e FEAT, typename LOGCAT> void riscv_hart_m_p<B
|
||||
// any synchronous traps.
|
||||
auto ena_irq = this->csr[mip] & this->csr[mie];
|
||||
|
||||
bool mstatus_mie = state.mstatus.MIE;
|
||||
bool mstatus_mie = this->state.mstatus.MIE;
|
||||
auto m_enabled = this->reg.PRIV < PRIV_M || mstatus_mie;
|
||||
auto enabled_interrupts = m_enabled ? ena_irq : 0;
|
||||
|
||||
@ -630,16 +505,17 @@ uint64_t riscv_hart_m_p<BASE, FEAT, LOGCAT>::enter_trap(uint64_t flags, uint64_t
|
||||
// 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;
|
||||
this->state.mstatus.MPP = PRIV_M;
|
||||
this->state.mstatus.MPIE = this->state.mstatus.MIE;
|
||||
this->state.mstatus.MIE = false;
|
||||
|
||||
// get trap vector
|
||||
auto xtvec = this->csr[mtvec];
|
||||
// calculate adds// set NEXT_PC to trap addressess to jump to based on MODE
|
||||
if((FEAT & features_e::FEAT_CLIC) && trap_id != 0 && (xtvec & 0x3UL) == 3UL) {
|
||||
if(trap_id != 0 && (xtvec & 0x3UL) == 3UL) {
|
||||
reg_t data;
|
||||
auto ret = read(address_type::LOGICAL, access_type::READ, 0, this->csr[mtvt], sizeof(reg_t), reinterpret_cast<uint8_t*>(&data));
|
||||
auto ret =
|
||||
this->memory.rd_mem(iss::access_type::DEBUG_READ, this->csr[arch::mtvt], sizeof(reg_t), reinterpret_cast<uint8_t*>(&data));
|
||||
if(ret == iss::Err)
|
||||
return this->reg.PC;
|
||||
this->reg.NEXT_PC = data;
|
||||
@ -666,8 +542,8 @@ uint64_t riscv_hart_m_p<BASE, FEAT, LOGCAT>::enter_trap(uint64_t flags, uint64_t
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT> uint64_t riscv_hart_m_p<BASE, FEAT, LOGCAT>::leave_trap(uint64_t flags) {
|
||||
state.mstatus.MIE = state.mstatus.MPIE;
|
||||
state.mstatus.MPIE = 1;
|
||||
this->state.mstatus.MIE = this->state.mstatus.MPIE;
|
||||
this->state.mstatus.MPIE = 1;
|
||||
// sets the pc to the value stored in the x epc register.
|
||||
this->reg.NEXT_PC = this->csr[mepc] & this->get_pc_mask();
|
||||
NSCLOG(INFO, LOGCAT) << "Executing xRET";
|
||||
|
@ -35,7 +35,6 @@
|
||||
#ifndef _RISCV_HART_MSU_VP_H
|
||||
#define _RISCV_HART_MSU_VP_H
|
||||
|
||||
#include "iss/instrumentation_if.h"
|
||||
#include "iss/vm_if.h"
|
||||
#include "iss/vm_types.h"
|
||||
#include "riscv_hart_common.h"
|
||||
@ -44,16 +43,12 @@
|
||||
#include <cstdint>
|
||||
#include <elfio/elf_types.hpp>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#ifndef FMT_HEADER_ONLY
|
||||
#define FMT_HEADER_ONLY
|
||||
#endif
|
||||
#include <array>
|
||||
#include <elfio/elfio.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <util/bit_field.h>
|
||||
@ -70,65 +65,12 @@ public:
|
||||
using reg_t = typename core::reg_t;
|
||||
using addr_t = typename core::addr_t;
|
||||
|
||||
// primary template
|
||||
template <class T, class Enable = void> struct hart_state {};
|
||||
// specialization 32bit
|
||||
template <typename T> class hart_state<T, typename std::enable_if<std::is_same<T, uint32_t>::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();
|
||||
static constexpr reg_t get_misa() {
|
||||
return (sizeof(reg_t) == 4 ? (1UL << 30) : (2ULL << 62)) | ISA_I | ISA_M | ISA_A | ISA_U | ISA_S | ISA_M;
|
||||
}
|
||||
|
||||
mstatus_t mstatus;
|
||||
|
||||
static const reg_t mstatus_reset_val = 0x1800;
|
||||
|
||||
void write_mstatus(T val, unsigned priv_lvl) {
|
||||
auto mask = get_mask(priv_lvl);
|
||||
auto new_val = (static_cast<T>(mstatus) & ~mask) | (val & mask);
|
||||
mstatus = new_val;
|
||||
}
|
||||
|
||||
T satp;
|
||||
|
||||
static constexpr T get_misa() { return (1UL << 30) | ISA_I | ISA_M | ISA_A | ISA_U | ISA_S | ISA_M; }
|
||||
|
||||
static constexpr uint32_t get_mask(unsigned priv_lvl) {
|
||||
static constexpr reg_t get_mstatus_mask(unsigned priv_lvl) {
|
||||
if(sizeof(reg_t) == 4) {
|
||||
#if __cplusplus < 201402L
|
||||
return priv_lvl == PRIV_U ? 0x80000011UL : priv_lvl == PRIV_S ? 0x800de133UL : 0x807ff9ddUL;
|
||||
#else
|
||||
@ -141,130 +83,79 @@ public:
|
||||
return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline vm_info decode_vm_info(uint32_t state, T sptbr) {
|
||||
if(state == PRIV_M)
|
||||
return {0, 0, 0, 0};
|
||||
if(state <= PRIV_S)
|
||||
switch(bit_sub<31, 1>(sptbr)) {
|
||||
case 0:
|
||||
return {0, 0, 0, 0}; // off
|
||||
case 1:
|
||||
return {2, 10, 4, bit_sub<0, 22>(sptbr) << PGSHIFT}; // SV32
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
abort();
|
||||
return {0, 0, 0, 0}; // dummy
|
||||
}
|
||||
};
|
||||
// specialization 64bit
|
||||
template <typename T> class hart_state<T, typename std::enable_if<std::is_same<T, uint64_t>::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, 63, 1);
|
||||
// value of XLEN for S-mode
|
||||
BF_FIELD(SXL, 34, 2);
|
||||
// value of XLEN for U-mode
|
||||
BF_FIELD(UXL, 32, 2);
|
||||
// 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 = 0xa00000000;
|
||||
|
||||
void write_mstatus(T val, unsigned priv_lvl) {
|
||||
T old_val = mstatus;
|
||||
auto mask = get_mask(priv_lvl);
|
||||
auto new_val = (old_val & ~mask) | (val & mask);
|
||||
if((new_val & mstatus.SXL.Mask) == 0) {
|
||||
new_val |= old_val & mstatus.SXL.Mask;
|
||||
}
|
||||
if((new_val & mstatus.UXL.Mask) == 0) {
|
||||
new_val |= old_val & mstatus.UXL.Mask;
|
||||
}
|
||||
mstatus = new_val;
|
||||
}
|
||||
|
||||
T satp;
|
||||
|
||||
static constexpr T get_misa() { return (2ULL << 62) | ISA_I | ISA_M | ISA_A | ISA_U | ISA_S | ISA_M; }
|
||||
|
||||
static constexpr T get_mask(unsigned priv_lvl) {
|
||||
uint64_t ret;
|
||||
} else if(sizeof(reg_t) == 8) {
|
||||
switch(priv_lvl) {
|
||||
case PRIV_U:
|
||||
ret = 0x8000000f00000011ULL;
|
||||
break; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011
|
||||
return 0x8000000f00000011ULL; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011
|
||||
case PRIV_S:
|
||||
ret = 0x8000000f000de133ULL;
|
||||
break; // 0b1...0 0011 0000 0000 0000 1101 1110 0001 0011 0011
|
||||
return 0x8000000f000de133ULL; // 0b1...0 0011 0000 0000 0000 1101 1110 0001 0011 0011
|
||||
default:
|
||||
ret = 0x8000000f007ff9ddULL;
|
||||
break; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011
|
||||
return 0x8000000f007ff9ddULL; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
} else
|
||||
assert(false && "Unsupported XLEN value");
|
||||
}
|
||||
|
||||
static inline vm_info decode_vm_info(uint32_t state, T sptbr) {
|
||||
if(state == PRIV_M)
|
||||
return {0, 0, 0, 0};
|
||||
if(state <= PRIV_S)
|
||||
switch(bit_sub<60, 4>(sptbr)) {
|
||||
case 0:
|
||||
return {0, 0, 0, 0}; // off
|
||||
case 8:
|
||||
return {3, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV39
|
||||
case 9:
|
||||
return {4, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV48
|
||||
case 10:
|
||||
return {5, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV57
|
||||
case 11:
|
||||
return {6, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV64
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
abort();
|
||||
return {0, 0, 0, 0}; // dummy
|
||||
template <typename T_ = reg_t, std::enable_if_t<std::is_same<T_, uint32_t>::value>* = nullptr>
|
||||
void write_mstatus(T_ val, unsigned priv_lvl) {
|
||||
reg_t old_val = mstatus;
|
||||
auto mask = get_mstatus_mask(priv_lvl);
|
||||
auto new_val = (old_val & ~mask) | (val & mask);
|
||||
this->state.mstatus = new_val;
|
||||
}
|
||||
|
||||
template <typename T_ = reg_t, std::enable_if_t<std::is_same<T_, uint64_t>::value>* = nullptr>
|
||||
void write_mstatus(T_ val, unsigned priv_lvl) {
|
||||
reg_t old_val = mstatus;
|
||||
auto mask = get_mstatus_mask(priv_lvl);
|
||||
auto new_val = (old_val & ~mask) | (val & mask);
|
||||
if((new_val & this->state.mstatus.SXL.Mask) == 0) {
|
||||
new_val |= old_val & this->state.mstatus.SXL.Mask;
|
||||
}
|
||||
};
|
||||
using hart_state_type = hart_state<reg_t>;
|
||||
if((new_val & this->state.mstatus.UXL.Mask) == 0) {
|
||||
new_val |= old_val & this->state.mstatus.UXL.Mask;
|
||||
}
|
||||
this->state.mstatus = new_val;
|
||||
}
|
||||
reg_t satp;
|
||||
|
||||
static inline vm_info decode_vm_info(uint32_t state, uint32_t sptbr) {
|
||||
if(state == PRIV_M)
|
||||
return {0, 0, 0, 0};
|
||||
if(state <= PRIV_S)
|
||||
switch(bit_sub<31, 1>(sptbr)) {
|
||||
case 0:
|
||||
return {0, 0, 0, 0}; // off
|
||||
case 1:
|
||||
return {2, 10, 4, bit_sub<0, 22>(sptbr) << PGSHIFT}; // SV32
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
abort();
|
||||
return {0, 0, 0, 0}; // dummy
|
||||
}
|
||||
|
||||
static inline vm_info decode_vm_info(uint32_t state, uint64_t sptbr) {
|
||||
if(state == PRIV_M)
|
||||
return {0, 0, 0, 0};
|
||||
if(state <= PRIV_S)
|
||||
switch(bit_sub<60, 4>(sptbr)) {
|
||||
case 0:
|
||||
return {0, 0, 0, 0}; // off
|
||||
case 8:
|
||||
return {3, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV39
|
||||
case 9:
|
||||
return {4, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV48
|
||||
case 10:
|
||||
return {5, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV57
|
||||
case 11:
|
||||
return {6, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV64
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
abort();
|
||||
return {0, 0, 0, 0}; // dummy
|
||||
}
|
||||
|
||||
const typename core::reg_t PGSIZE = 1 << PGSHIFT;
|
||||
const typename core::reg_t PGMASK = PGSIZE - 1;
|
||||
@ -305,7 +196,7 @@ protected:
|
||||
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);
|
||||
|
||||
hart_state_type state;
|
||||
hart_state<reg_t> state;
|
||||
|
||||
using mem_type = util::sparse_array<uint8_t, 1ULL << 32>;
|
||||
mem_type mem;
|
||||
@ -415,7 +306,7 @@ iss::status riscv_hart_msu_vp<BASE>::read(const address_type type, const access_
|
||||
return iss::Err;
|
||||
}
|
||||
if(unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary
|
||||
vm_info vm = hart_state_type::decode_vm_info(this->reg.PRIV, state.satp);
|
||||
vm_info vm = decode_vm_info(this->reg.PRIV, satp);
|
||||
if(vm.levels != 0) { // VM is active
|
||||
auto split_addr = (addr + length) & ~PGMASK;
|
||||
auto len1 = split_addr - addr;
|
||||
@ -521,7 +412,7 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access
|
||||
try {
|
||||
// TODO: There is no check for alignment
|
||||
if(unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary
|
||||
vm_info vm = hart_state_type::decode_vm_info(this->reg.PRIV, state.satp);
|
||||
vm_info vm = decode_vm_info(this->reg.PRIV, satp);
|
||||
if(vm.levels != 0) { // VM is active
|
||||
auto split_addr = (addr + length) & ~PGMASK;
|
||||
auto len1 = split_addr - addr;
|
||||
@ -583,13 +474,13 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access
|
||||
|
||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_status(unsigned addr, reg_t& val) {
|
||||
auto req_priv_lvl = (addr >> 8) & 0x3;
|
||||
val = state.mstatus & hart_state_type::get_mask(req_priv_lvl);
|
||||
val = state.mstatus & get_mstatus_mask(req_priv_lvl);
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_status(unsigned addr, reg_t val) {
|
||||
auto req_priv_lvl = (addr >> 8) & 0x3;
|
||||
state.write_mstatus(val, req_priv_lvl);
|
||||
write_mstatus(val, req_priv_lvl);
|
||||
check_interrupt();
|
||||
update_vm_info();
|
||||
return iss::Ok;
|
||||
@ -633,7 +524,7 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_satp(unsigned
|
||||
this->fault_data = this->reg.PC;
|
||||
return iss::Err;
|
||||
}
|
||||
val = state.satp;
|
||||
val = satp;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
@ -644,7 +535,7 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_satp(unsigne
|
||||
this->fault_data = this->reg.PC;
|
||||
return iss::Err;
|
||||
}
|
||||
state.satp = val;
|
||||
satp = val;
|
||||
update_vm_info();
|
||||
return iss::Ok;
|
||||
}
|
||||
@ -715,15 +606,15 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_mem(phys_add
|
||||
|
||||
template <typename BASE> inline void riscv_hart_msu_vp<BASE>::reset(uint64_t address) {
|
||||
BASE::reset(address);
|
||||
state.mstatus = hart_state_type::mstatus_reset_val;
|
||||
state.mstatus = hart_state<reg_t>::mstatus_reset_val;
|
||||
update_vm_info();
|
||||
}
|
||||
|
||||
template <typename BASE> inline void riscv_hart_msu_vp<BASE>::update_vm_info() {
|
||||
this->vm[1] = hart_state_type::decode_vm_info(this->reg.PRIV, state.satp);
|
||||
this->vm[1] = decode_vm_info(this->reg.PRIV, satp);
|
||||
BASE::addr_mode[3] = BASE::addr_mode[2] = this->vm[1].is_active() ? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL;
|
||||
if(state.mstatus.MPRV)
|
||||
this->vm[0] = hart_state_type::decode_vm_info(state.mstatus.MPP, state.satp);
|
||||
this->vm[0] = decode_vm_info(state.mstatus.MPP, satp);
|
||||
else
|
||||
this->vm[0] = this->vm[1];
|
||||
BASE::addr_mode[1] = BASE::addr_mode[0] = this->vm[0].is_active() ? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL;
|
||||
|
@ -35,7 +35,6 @@
|
||||
#ifndef _RISCV_HART_MU_P_H
|
||||
#define _RISCV_HART_MU_P_H
|
||||
|
||||
#include "iss/instrumentation_if.h"
|
||||
#include "iss/vm_if.h"
|
||||
#include "iss/vm_types.h"
|
||||
#include "riscv_hart_common.h"
|
||||
@ -43,20 +42,14 @@
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <elfio/elf_types.hpp>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#ifndef FMT_HEADER_ONLY
|
||||
#define FMT_HEADER_ONLY
|
||||
#endif
|
||||
#include <array>
|
||||
#include <elfio/elfio.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
#include <iss/mmio/memory_with_htif.h>
|
||||
#include <unordered_map>
|
||||
#include <util/bit_field.h>
|
||||
|
||||
namespace iss {
|
||||
namespace arch {
|
||||
@ -71,67 +64,14 @@ public:
|
||||
using reg_t = typename core::reg_t;
|
||||
using addr_t = typename core::addr_t;
|
||||
|
||||
// primary template
|
||||
template <class T, class Enable = void> struct hart_state {};
|
||||
// specialization 32bit
|
||||
template <typename T> class hart_state<T, typename std::enable_if<std::is_same<T, uint32_t>::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 = 0x1800; // MPP set to 1
|
||||
|
||||
void write_mstatus(T val, unsigned priv_lvl) {
|
||||
auto mask = get_mask(priv_lvl);
|
||||
auto new_val = (mstatus.backing.val & ~mask) | (val & mask);
|
||||
mstatus = new_val;
|
||||
}
|
||||
|
||||
static constexpr uint32_t get_mask(unsigned priv_lvl) {
|
||||
static constexpr reg_t get_mstatus_mask(unsigned priv_lvl) {
|
||||
if(sizeof(reg_t) == 4) {
|
||||
#if __cplusplus < 201402L
|
||||
return priv_lvl == PRIV_U ? 0x80000011UL : priv_lvl == PRIV_S ? 0x800de133UL : 0x807ff9ddUL;
|
||||
#else
|
||||
switch(priv_lvl) {
|
||||
case PRIV_U:
|
||||
return 0x00000011UL; // 0b1000 0000 0000 0000 0000 0000 0001 0001
|
||||
return 0x00000011UL; // 0b1...0 0001 0001
|
||||
default:
|
||||
// +-SD
|
||||
// | +-TSR
|
||||
@ -151,72 +91,13 @@ public:
|
||||
return 0b00000000000000000001100010011001;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
// specialization 64bit
|
||||
template <typename T> class hart_state<T, typename std::enable_if<std::is_same<T, uint64_t>::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, 63, 1);
|
||||
// value of XLEN for S-mode
|
||||
BF_FIELD(SXL, 34, 2);
|
||||
// value of XLEN for U-mode
|
||||
BF_FIELD(UXL, 32, 2);
|
||||
// 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 = 0x1800;
|
||||
|
||||
void write_mstatus(T val, unsigned priv_lvl) {
|
||||
auto mask = get_mask(priv_lvl);
|
||||
auto new_val = (mstatus.backing.val & ~mask) | (val & mask);
|
||||
mstatus = new_val;
|
||||
}
|
||||
|
||||
static constexpr uint64_t get_mask(unsigned priv_lvl) {
|
||||
} else if(sizeof(reg_t) == 8) {
|
||||
#if __cplusplus < 201402L
|
||||
return priv_lvl == PRIV_U ? 0x011ULL : priv_lvl == PRIV_S ? 0x000de133ULL : 0x007ff9ddULL;
|
||||
#else
|
||||
switch(priv_lvl) {
|
||||
case PRIV_U:
|
||||
return 0x00000011UL; // 0b1000 0000 0000 0000 0000 0000 0001 0001
|
||||
return 0x00000011UL; // 0b1...0 0001 0001
|
||||
default:
|
||||
// +-SD
|
||||
// | +-TSR
|
||||
@ -236,10 +117,15 @@ public:
|
||||
return 0b00000000000000000001100010011001;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
} else
|
||||
assert(false && "Unsupported XLEN value");
|
||||
}
|
||||
|
||||
using hart_state_type = hart_state<reg_t>;
|
||||
void write_mstatus(reg_t val, unsigned priv_lvl) {
|
||||
auto mask = get_mstatus_mask(priv_lvl);
|
||||
auto new_val = (this->state.mstatus() & ~mask) | (val & mask);
|
||||
this->state.mstatus = new_val;
|
||||
}
|
||||
|
||||
constexpr reg_t get_irq_mask(size_t mode) {
|
||||
std::array<const reg_t, 4> m = {{
|
||||
@ -268,40 +154,14 @@ public:
|
||||
|
||||
void set_csr(unsigned addr, reg_t val) { this->csr[addr & this->csr.page_addr_mask] = val; }
|
||||
|
||||
void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); }
|
||||
|
||||
protected:
|
||||
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);
|
||||
|
||||
iss::status read_clic(uint64_t addr, unsigned length, uint8_t* const data);
|
||||
iss::status write_clic(uint64_t addr, unsigned length, const uint8_t* const data);
|
||||
|
||||
using mem_read_f = iss::status(phys_addr_t addr, unsigned, uint8_t* const);
|
||||
using mem_write_f = iss::status(phys_addr_t addr, unsigned, uint8_t const* const);
|
||||
|
||||
hart_state_type state;
|
||||
hart_state<reg_t> state;
|
||||
|
||||
using mem_type = util::sparse_array<uint8_t, 1ULL << 32>;
|
||||
mem_type mem;
|
||||
std::unordered_map<reg_t, uint64_t> ptw;
|
||||
std::unordered_map<uint64_t, uint8_t> atomic_reservation;
|
||||
uint8_t clic_cfg_reg{0};
|
||||
std::array<uint32_t, 32> clic_inttrig_reg;
|
||||
union clic_int_reg_t {
|
||||
struct {
|
||||
uint8_t ip;
|
||||
uint8_t ie;
|
||||
uint8_t attr;
|
||||
uint8_t ctl;
|
||||
};
|
||||
uint32_t raw;
|
||||
};
|
||||
std::vector<clic_int_reg_t> clic_int_reg;
|
||||
uint8_t clic_mprev_lvl{0}, clic_uprev_lvl{0};
|
||||
uint8_t clic_mact_lvl{0}, clic_uact_lvl{0};
|
||||
|
||||
std::vector<uint8_t> tcm;
|
||||
|
||||
iss::status read_status(unsigned addr, reg_t& val);
|
||||
iss::status write_status(unsigned addr, reg_t val);
|
||||
@ -310,8 +170,6 @@ protected:
|
||||
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 read_intstatus(unsigned addr, reg_t& val);
|
||||
iss::status write_intthresh(unsigned addr, reg_t val);
|
||||
iss::status write_xtvt(unsigned addr, reg_t val);
|
||||
iss::status write_dcsr(unsigned addr, reg_t val);
|
||||
iss::status read_debug(unsigned addr, reg_t& val);
|
||||
@ -320,35 +178,17 @@ protected:
|
||||
iss::status write_dpc(unsigned addr, reg_t val);
|
||||
iss::status write_ideleg(unsigned addr, reg_t val);
|
||||
iss::status write_edeleg(unsigned addr, reg_t val);
|
||||
iss::status write_pmpcfg(unsigned addr, reg_t val);
|
||||
|
||||
void check_interrupt();
|
||||
bool pmp_check(const access_type type, const uint64_t addr, const unsigned len);
|
||||
std::vector<std::tuple<uint64_t, uint64_t>> memfn_range;
|
||||
std::vector<std::function<mem_read_f>> memfn_read;
|
||||
std::vector<std::function<mem_write_f>> memfn_write;
|
||||
void insert_mem_range(uint64_t, uint64_t, std::function<mem_read_f>, std::function<mem_write_f>);
|
||||
feature_config cfg;
|
||||
unsigned mcause_max_irq{(FEAT & features_e::FEAT_CLIC) ? std::max(16U, static_cast<unsigned>(traits<BASE>::CLIC_NUM_IRQ)) : 16U};
|
||||
|
||||
std::pair<std::function<mem_read_f>, std::function<mem_write_f>> replace_mem_access(std::function<mem_read_f> rd,
|
||||
std::function<mem_write_f> wr) {
|
||||
std::pair<std::function<mem_read_f>, std::function<mem_write_f>> ret{hart_mem_rd_delegate, hart_mem_wr_delegate};
|
||||
hart_mem_rd_delegate = rd;
|
||||
hart_mem_wr_delegate = wr;
|
||||
return ret;
|
||||
}
|
||||
std::function<mem_read_f> hart_mem_rd_delegate;
|
||||
std::function<mem_write_f> hart_mem_wr_delegate;
|
||||
mmio::memory_with_htif<reg_t> default_mem;
|
||||
};
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
riscv_hart_mu_p<BASE, FEAT, LOGCAT>::riscv_hart_mu_p(feature_config cfg)
|
||||
: state()
|
||||
, cfg(cfg) {
|
||||
this->rd_func = util::delegate<arch_if::rd_func_sig>::from<this_class, &this_class::read>(this);
|
||||
this->rd_func = util::delegate<arch_if::wr_func_sig>::from<this_class, &this_class::write>(this);
|
||||
|
||||
, cfg(cfg)
|
||||
, default_mem(base::get_priv_if()) {
|
||||
const std::array<unsigned, 8> rwaddrs{{
|
||||
mepc,
|
||||
mtvec,
|
||||
@ -380,16 +220,6 @@ riscv_hart_mu_p<BASE, FEAT, LOGCAT>::riscv_hart_mu_p(feature_config cfg)
|
||||
this->csr_wr_cb[marchid] = MK_CSR_WR_CB(write_null);
|
||||
this->csr_wr_cb[mimpid] = MK_CSR_WR_CB(write_null);
|
||||
|
||||
if(FEAT & FEAT_PMP) {
|
||||
for(size_t i = pmpaddr0; i <= pmpaddr15; ++i) {
|
||||
this->csr_rd_cb[i] = MK_CSR_RD_CB(read_plain);
|
||||
this->csr_wr_cb[i] = MK_CSR_WR_CB(write_plain);
|
||||
}
|
||||
for(size_t i = pmpcfg0; i < pmpcfg0 + 16 / sizeof(reg_t); ++i) {
|
||||
this->csr_rd_cb[i] = MK_CSR_RD_CB(read_plain);
|
||||
this->csr_wr_cb[i] = MK_CSR_WR_CB(write_pmpcfg);
|
||||
}
|
||||
}
|
||||
if(FEAT & FEAT_EXT_N) {
|
||||
this->csr_rd_cb[mideleg] = MK_CSR_RD_CB(read_plain);
|
||||
this->csr_wr_cb[mideleg] = MK_CSR_WR_CB(write_ideleg);
|
||||
@ -406,52 +236,6 @@ riscv_hart_mu_p<BASE, FEAT, LOGCAT>::riscv_hart_mu_p(feature_config cfg)
|
||||
this->csr_wr_cb[ucause] = MK_CSR_WR_CB(write_cause);
|
||||
this->csr_rd_cb[utvec] = MK_CSR_RD_CB(read_tvec);
|
||||
}
|
||||
if(FEAT & FEAT_CLIC) {
|
||||
this->csr_rd_cb[mtvt] = MK_CSR_RD_CB(read_plain);
|
||||
this->csr_wr_cb[mtvt] = MK_CSR_WR_CB(write_xtvt);
|
||||
// this->csr_rd_cb[mxnti] = MK_CSR_RD_CB(read_plain(a,r);};
|
||||
// this->csr_wr_cb[mxnti] = MK_CSR_WR_CB(write_plain(a,r);};
|
||||
this->csr_rd_cb[mintstatus] = MK_CSR_RD_CB(read_intstatus);
|
||||
this->csr_wr_cb[mintstatus] = MK_CSR_WR_CB(write_null);
|
||||
// this->csr_rd_cb[mscratchcsw] = MK_CSR_RD_CB(read_plain(a,r);};
|
||||
// this->csr_wr_cb[mscratchcsw] = MK_CSR_WR_CB(write_plain(a,r);};
|
||||
// this->csr_rd_cb[mscratchcswl] = MK_CSR_RD_CB(read_plain(a,r);};
|
||||
// this->csr_wr_cb[mscratchcswl] = MK_CSR_WR_CB(write_plain(a,r);};
|
||||
this->csr_rd_cb[mintthresh] = MK_CSR_RD_CB(read_plain);
|
||||
this->csr_wr_cb[mintthresh] = MK_CSR_WR_CB(write_intthresh);
|
||||
if(FEAT & FEAT_EXT_N) {
|
||||
this->csr_rd_cb[utvt] = MK_CSR_RD_CB(read_plain);
|
||||
this->csr_wr_cb[utvt] = MK_CSR_WR_CB(write_xtvt);
|
||||
this->csr_rd_cb[uintstatus] = MK_CSR_RD_CB(read_intstatus);
|
||||
this->csr_wr_cb[uintstatus] = MK_CSR_WR_CB(write_null);
|
||||
this->csr_rd_cb[uintthresh] = MK_CSR_RD_CB(read_plain);
|
||||
this->csr_wr_cb[uintthresh] = MK_CSR_WR_CB(write_intthresh);
|
||||
}
|
||||
clic_int_reg.resize(cfg.clic_num_irq, clic_int_reg_t{.raw = 0});
|
||||
clic_cfg_reg = 0x30;
|
||||
clic_mact_lvl = clic_mprev_lvl = (1 << (cfg.clic_int_ctl_bits)) - 1;
|
||||
clic_uact_lvl = clic_uprev_lvl = (1 << (cfg.clic_int_ctl_bits)) - 1;
|
||||
this->csr[mintthresh] = (1 << (cfg.clic_int_ctl_bits)) - 1;
|
||||
this->csr[uintthresh] = (1 << (cfg.clic_int_ctl_bits)) - 1;
|
||||
insert_mem_range(
|
||||
cfg.clic_base, 0x5000UL,
|
||||
[this](phys_addr_t addr, unsigned length, uint8_t* const data) { return read_clic(addr.val, length, data); },
|
||||
[this](phys_addr_t addr, unsigned length, uint8_t const* const data) { return write_clic(addr.val, length, data); });
|
||||
}
|
||||
if(FEAT & FEAT_TCM) {
|
||||
tcm.resize(cfg.tcm_size);
|
||||
std::function<mem_read_f> read_clic_cb = [this](phys_addr_t addr, unsigned length, uint8_t* const data) {
|
||||
auto offset = addr.val - this->cfg.tcm_base;
|
||||
std::copy(tcm.data() + offset, tcm.data() + offset + length, data);
|
||||
return iss::Ok;
|
||||
};
|
||||
std::function<mem_write_f> write_clic_cb = [this](phys_addr_t addr, unsigned length, uint8_t const* const data) {
|
||||
auto offset = addr.val - this->cfg.tcm_base;
|
||||
std::copy(data, data + length, tcm.data() + offset);
|
||||
return iss::Ok;
|
||||
};
|
||||
insert_mem_range(cfg.tcm_base, cfg.tcm_size, read_clic_cb, write_clic_cb);
|
||||
}
|
||||
if(FEAT & FEAT_DEBUG) {
|
||||
this->csr_wr_cb[dscratch0] = MK_CSR_WR_CB(write_dscratch);
|
||||
this->csr_rd_cb[dscratch0] = MK_CSR_RD_CB(read_debug);
|
||||
@ -462,108 +246,10 @@ riscv_hart_mu_p<BASE, FEAT, LOGCAT>::riscv_hart_mu_p(feature_config cfg)
|
||||
this->csr_wr_cb[dcsr] = MK_CSR_WR_CB(write_dcsr);
|
||||
this->csr_rd_cb[dcsr] = MK_CSR_RD_CB(read_debug);
|
||||
}
|
||||
hart_mem_rd_delegate = [this](phys_addr_t a, unsigned l, uint8_t* const d) -> iss::status { return this->read_mem(a, l, d); };
|
||||
hart_mem_wr_delegate = [this](phys_addr_t a, unsigned l, uint8_t const* const d) -> iss::status { return this->write_mem(a, l, d); };
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
inline void riscv_hart_mu_p<BASE, FEAT, LOGCAT>::insert_mem_range(uint64_t base, uint64_t size, std::function<mem_read_f> rd_f,
|
||||
std::function<mem_write_f> wr_fn) {
|
||||
std::tuple<uint64_t, uint64_t> entry{base, size};
|
||||
auto it = std::upper_bound(
|
||||
memfn_range.begin(), memfn_range.end(), entry,
|
||||
[](std::tuple<uint64_t, uint64_t> const& a, std::tuple<uint64_t, uint64_t> const& b) { return std::get<0>(a) < std::get<0>(b); });
|
||||
auto idx = std::distance(memfn_range.begin(), it);
|
||||
memfn_range.insert(it, entry);
|
||||
memfn_read.insert(std::begin(memfn_read) + idx, rd_f);
|
||||
memfn_write.insert(std::begin(memfn_write) + idx, wr_fn);
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
inline iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::write_pmpcfg(unsigned addr, reg_t val) {
|
||||
this->csr[addr] = val & 0x9f9f9f9f;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
bool riscv_hart_mu_p<BASE, FEAT, LOGCAT>::pmp_check(const access_type type, const uint64_t addr, const unsigned len) {
|
||||
constexpr auto PMP_SHIFT = 2U;
|
||||
constexpr auto PMP_R = 0x1U;
|
||||
constexpr auto PMP_W = 0x2U;
|
||||
constexpr auto PMP_X = 0x4U;
|
||||
constexpr auto PMP_A = 0x18U;
|
||||
constexpr auto PMP_L = 0x80U;
|
||||
constexpr auto PMP_TOR = 0x1U;
|
||||
constexpr auto PMP_NA4 = 0x2U;
|
||||
constexpr auto PMP_NAPOT = 0x3U;
|
||||
reg_t base = 0;
|
||||
auto any_active = false;
|
||||
auto const cfg_reg_size = sizeof(reg_t);
|
||||
for(size_t i = 0; i < 16; i++) {
|
||||
reg_t tor = this->csr[pmpaddr0 + i] << PMP_SHIFT;
|
||||
uint8_t cfg = this->csr[pmpcfg0 + (i / cfg_reg_size)] >> (i % cfg_reg_size);
|
||||
if(cfg & PMP_A) {
|
||||
any_active = true;
|
||||
auto pmp_a = (cfg & PMP_A) >> 3;
|
||||
auto is_tor = pmp_a == PMP_TOR;
|
||||
auto is_na4 = pmp_a == PMP_NA4;
|
||||
|
||||
reg_t mask = (this->csr[pmpaddr0 + i] << 1) | (!is_na4);
|
||||
mask = ~(mask & ~(mask + 1)) << PMP_SHIFT;
|
||||
|
||||
// Check each 4-byte sector of the access
|
||||
auto any_match = false;
|
||||
auto all_match = true;
|
||||
for(reg_t offset = 0; offset < len; offset += 1 << PMP_SHIFT) {
|
||||
reg_t cur_addr = addr + offset;
|
||||
auto napot_match = ((cur_addr ^ tor) & mask) == 0;
|
||||
auto tor_match = base <= (cur_addr + len - 1) && cur_addr < tor;
|
||||
auto match = is_tor ? tor_match : napot_match;
|
||||
any_match |= match;
|
||||
all_match &= match;
|
||||
}
|
||||
if(any_match) {
|
||||
// If the PMP matches only a strict subset of the access, fail it
|
||||
if(!all_match)
|
||||
return false;
|
||||
return (this->reg.PRIV == PRIV_M && !(cfg & PMP_L)) || (type == access_type::READ && (cfg & PMP_R)) ||
|
||||
(type == access_type::WRITE && (cfg & PMP_W)) || (type == access_type::FETCH && (cfg & PMP_X));
|
||||
}
|
||||
}
|
||||
base = tor;
|
||||
}
|
||||
// constexpr auto pmp_num_regs = 16;
|
||||
// reg_t tor_base = 0;
|
||||
// auto any_active = false;
|
||||
// auto lower_addr = addr >>2;
|
||||
// auto upper_addr = (addr+len-1)>>2;
|
||||
// for (size_t i = 0; i < pmp_num_regs; i++) {
|
||||
// uint8_t cfg = csr[pmpcfg0+(i/4)]>>(i%4);
|
||||
// uint8_t cfg_next = i==(pmp_num_regs-1)? 0 : csr[pmpcfg0+((i+1)/4)]>>((i+1)%4);
|
||||
// auto pmpaddr = csr[pmpaddr0+i];
|
||||
// if (cfg & PMP_A) {
|
||||
// any_active=true;
|
||||
// auto is_tor = bit_sub<3, 2>(cfg) == PMP_TOR;
|
||||
// auto is_napot = bit_sub<4, 1>(cfg) && bit_sub<3, 2>(cfg_next)!= PMP_TOR;
|
||||
// if(is_napot) {
|
||||
// reg_t mask = bit_sub<3, 1>(cfg)?~( pmpaddr & ~(pmpaddr + 1)): 0x3fffffff;
|
||||
// auto mpmpaddr = pmpaddr & mask;
|
||||
// if((lower_addr&mask) == mpmpaddr && (upper_addr&mask)==mpmpaddr)
|
||||
// return (this->reg.PRIV == PRIV_M && !(cfg & PMP_L)) ||
|
||||
// (type == access_type::READ && (cfg & PMP_R)) ||
|
||||
// (type == access_type::WRITE && (cfg & PMP_W)) ||
|
||||
// (type == access_type::FETCH && (cfg & PMP_X));
|
||||
// } else if(is_tor) {
|
||||
// if(lower_addr>=tor_base && upper_addr<=pmpaddr)
|
||||
// return (this->reg.PRIV == PRIV_M && !(cfg & PMP_L)) ||
|
||||
// (type == access_type::READ && (cfg & PMP_R)) ||
|
||||
// (type == access_type::WRITE && (cfg & PMP_W)) ||
|
||||
// (type == access_type::FETCH && (cfg & PMP_X));
|
||||
// }
|
||||
// }
|
||||
// tor_base = pmpaddr;
|
||||
// }
|
||||
return !any_active || this->reg.PRIV == PRIV_M;
|
||||
this->rd_func = util::delegate<arch_if::rd_func_sig>::from<this_class, &this_class::read>(this);
|
||||
this->wr_func = util::delegate<arch_if::wr_func_sig>::from<this_class, &this_class::write>(this);
|
||||
this->memories.prepend(*this);
|
||||
this->memories.append(default_mem);
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
@ -581,15 +267,6 @@ iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::read(const address_type type, c
|
||||
try {
|
||||
switch(space) {
|
||||
case traits<BASE>::MEM: {
|
||||
if(FEAT & FEAT_PMP) {
|
||||
if(!pmp_check(access, addr, length) && !is_debug(access)) {
|
||||
this->fault_data = addr;
|
||||
if(is_debug(access))
|
||||
throw trap_access(0, addr);
|
||||
this->reg.trap_state = (1UL << 31) | ((access == access_type::FETCH ? 1 : 5) << 16); // issue trap 1
|
||||
return iss::Err;
|
||||
}
|
||||
}
|
||||
auto alignment = is_fetch(access) ? (this->has_compressed() ? 2 : 4) : std::min<unsigned>(length, sizeof(reg_t));
|
||||
if(unlikely(is_fetch(access) && (addr & (alignment - 1)))) {
|
||||
this->fault_data = addr;
|
||||
@ -604,21 +281,7 @@ iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::read(const address_type type, c
|
||||
this->fault_data = addr;
|
||||
return iss::Err;
|
||||
}
|
||||
phys_addr_t phys_addr{access, space, addr};
|
||||
auto res = iss::Err;
|
||||
if(!is_fetch(access) && memfn_range.size()) {
|
||||
auto it =
|
||||
std::find_if(std::begin(memfn_range), std::end(memfn_range), [phys_addr](std::tuple<uint64_t, uint64_t> const& a) {
|
||||
return std::get<0>(a) <= phys_addr.val && (std::get<0>(a) + std::get<1>(a)) > phys_addr.val;
|
||||
});
|
||||
if(it != std::end(memfn_range)) {
|
||||
auto idx = std::distance(std::begin(memfn_range), it);
|
||||
res = memfn_read[idx](phys_addr, length, data);
|
||||
} else
|
||||
res = hart_mem_rd_delegate(phys_addr, length, data);
|
||||
} else {
|
||||
res = hart_mem_rd_delegate(phys_addr, length, data);
|
||||
}
|
||||
auto res = this->memory.rd_mem(access, addr, length, data);
|
||||
if(unlikely(res != iss::Ok && (access & access_type::DEBUG) == 0)) {
|
||||
this->reg.trap_state = (1UL << 31) | (5 << 16); // issue trap 5 (load access fault
|
||||
this->fault_data = addr;
|
||||
@ -638,8 +301,6 @@ iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::read(const address_type type, c
|
||||
return this->read_csr(addr, *reinterpret_cast<reg_t* const>(data));
|
||||
} break;
|
||||
case traits<BASE>::FENCE: {
|
||||
if((addr + length) > mem.size())
|
||||
return iss::Err;
|
||||
return iss::Ok;
|
||||
} break;
|
||||
case traits<BASE>::RES: {
|
||||
@ -692,15 +353,6 @@ iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::write(const address_type type,
|
||||
try {
|
||||
switch(space) {
|
||||
case traits<BASE>::MEM: {
|
||||
if(FEAT & FEAT_PMP) {
|
||||
if(!pmp_check(access, addr, length) && (access & access_type::DEBUG) != access_type::DEBUG) {
|
||||
this->fault_data = addr;
|
||||
if(access && iss::access_type::DEBUG)
|
||||
throw trap_access(0, addr);
|
||||
this->reg.trap_state = (1UL << 31) | (7 << 16); // issue trap 1
|
||||
return iss::Err;
|
||||
}
|
||||
}
|
||||
if(unlikely(is_fetch(access) && (addr & 0x1) == 1)) {
|
||||
this->fault_data = addr;
|
||||
if(access && iss::access_type::DEBUG)
|
||||
@ -715,22 +367,8 @@ iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::write(const address_type type,
|
||||
this->fault_data = addr;
|
||||
return iss::Err;
|
||||
}
|
||||
phys_addr_t phys_addr{access, space, addr};
|
||||
auto res = iss::Err;
|
||||
if(!is_fetch(access) && memfn_range.size()) {
|
||||
auto it =
|
||||
std::find_if(std::begin(memfn_range), std::end(memfn_range), [phys_addr](std::tuple<uint64_t, uint64_t> const& a) {
|
||||
return std::get<0>(a) <= phys_addr.val && (std::get<0>(a) + std::get<1>(a)) > phys_addr.val;
|
||||
});
|
||||
if(it != std::end(memfn_range)) {
|
||||
auto idx = std::distance(std::begin(memfn_range), it);
|
||||
res = memfn_write[idx](phys_addr, length, data);
|
||||
} else
|
||||
res = hart_mem_wr_delegate(phys_addr, length, data);
|
||||
} else {
|
||||
res = hart_mem_wr_delegate(phys_addr, length, data);
|
||||
}
|
||||
if(unlikely(res != iss::Ok && (access & access_type::DEBUG) == 0)) {
|
||||
auto res = this->memory.wr_mem(access, addr, length, data);
|
||||
if(unlikely(res != iss::Ok && !is_debug(access))) {
|
||||
this->reg.trap_state = (1UL << 31) | (7UL << 16); // issue trap 7 (Store/AMO access fault)
|
||||
this->fault_data = addr;
|
||||
}
|
||||
@ -747,8 +385,6 @@ iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::write(const address_type type,
|
||||
return this->write_csr(addr, *reinterpret_cast<const reg_t*>(data));
|
||||
} break;
|
||||
case traits<BASE>::FENCE: {
|
||||
if((addr + length) > mem.size())
|
||||
return iss::Err;
|
||||
switch(addr) {
|
||||
case 2:
|
||||
case 3: {
|
||||
@ -776,59 +412,27 @@ iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::write(const address_type type,
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::read_status(unsigned addr, reg_t& val) {
|
||||
val = state.mstatus & hart_state_type::get_mask((addr >> 8) & 0x3);
|
||||
val = state.mstatus & get_mstatus_mask((addr >> 8) & 0x3);
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::write_status(unsigned addr, reg_t val) {
|
||||
state.write_mstatus(val, (addr >> 8) & 0x3);
|
||||
write_mstatus(val, (addr >> 8) & 0x3);
|
||||
check_interrupt();
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::read_cause(unsigned addr, reg_t& val) {
|
||||
if((FEAT & features_e::FEAT_CLIC) && (this->csr[mtvec] & 0x3) == 3) {
|
||||
val = this->csr[addr] & ((1UL << (traits<BASE>::XLEN - 1)) | (mcause_max_irq - 1) | (0xfUL << 16));
|
||||
auto mode = (addr >> 8) & 0x3;
|
||||
switch(mode) {
|
||||
case 0:
|
||||
val |= clic_uprev_lvl << 16;
|
||||
val |= state.mstatus.UPIE << 27;
|
||||
break;
|
||||
default:
|
||||
val |= clic_mprev_lvl << 16;
|
||||
val |= state.mstatus.MPIE << 27;
|
||||
val |= state.mstatus.MPP << 28;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
val = this->csr[addr] & ((1UL << (traits<BASE>::XLEN - 1)) | (mcause_max_irq - 1));
|
||||
val = this->csr[addr] & ((1UL << (traits<BASE>::XLEN - 1)) | (this->mcause_max_irq - 1));
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::write_cause(unsigned addr, reg_t val) {
|
||||
if((FEAT & features_e::FEAT_CLIC) && (this->csr[mtvec] & 0x3) == 3) {
|
||||
auto mask = ((1UL << (traits<BASE>::XLEN - 1)) | (mcause_max_irq - 1) | (0xfUL << 16));
|
||||
this->csr[addr] = (val & mask) | (this->csr[addr] & ~mask);
|
||||
auto mode = (addr >> 8) & 0x3;
|
||||
switch(mode) {
|
||||
case 0:
|
||||
clic_uprev_lvl = ((val >> 16) & 0xff) | (1 << (8 - cfg.clic_int_ctl_bits)) - 1;
|
||||
state.mstatus.UPIE = (val >> 27) & 0x1;
|
||||
break;
|
||||
default:
|
||||
clic_mprev_lvl = ((val >> 16) & 0xff) | (1 << (8 - cfg.clic_int_ctl_bits)) - 1;
|
||||
state.mstatus.MPIE = (val >> 27) & 0x1;
|
||||
state.mstatus.MPP = (val >> 28) & 0x3;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
auto mask = ((1UL << (traits<BASE>::XLEN - 1)) | (mcause_max_irq - 1));
|
||||
this->csr[addr] = (val & mask) | (this->csr[addr] & ~mask);
|
||||
}
|
||||
auto mask = ((1UL << (traits<BASE>::XLEN - 1)) | (this->mcause_max_irq - 1));
|
||||
this->csr[addr] = (val & mask) | (this->csr[addr] & ~mask);
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
@ -872,126 +476,9 @@ iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::write_edeleg(unsigned addr, reg
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::read_intstatus(unsigned addr, reg_t& val) {
|
||||
auto mode = (addr >> 8) & 0x3;
|
||||
val = clic_uact_lvl & 0xff;
|
||||
if(mode == 0x3)
|
||||
val += (clic_mact_lvl & 0xff) << 24;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::write_intthresh(unsigned addr, reg_t val) {
|
||||
this->csr[addr] = (val & 0xff) | (1 << (cfg.clic_int_ctl_bits)) - 1;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::read_mem(phys_addr_t paddr, unsigned length, uint8_t* const data) {
|
||||
switch(paddr.val) {
|
||||
default: {
|
||||
for(auto offs = 0U; offs < length; ++offs) {
|
||||
*(data + offs) = mem[(paddr.val + offs) % mem.size()];
|
||||
}
|
||||
}
|
||||
}
|
||||
return iss::Ok;
|
||||
}
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t* const data) {
|
||||
mem_type::page_type& p = mem(paddr.val / mem.page_size);
|
||||
std::copy(data, data + length, p.data() + (paddr.val & mem.page_addr_mask));
|
||||
// tohost handling in case of riscv-test
|
||||
// according to https://github.com/riscv-software-src/riscv-isa-sim/issues/364#issuecomment-607657754:
|
||||
if(paddr.access && iss::access_type::FUNC) {
|
||||
if(paddr.val == this->tohost) {
|
||||
reg_t cur_data = *reinterpret_cast<const reg_t*>(data);
|
||||
// Extract Device (bits 63:56)
|
||||
uint8_t device = traits<BASE>::XLEN == 32
|
||||
? *reinterpret_cast<uint32_t*>(p.data() + ((this->tohost + 4) & mem.page_addr_mask)) >> 24
|
||||
: (cur_data >> 56) & 0xFF;
|
||||
// Extract Command (bits 55:48)
|
||||
uint8_t command = traits<BASE>::XLEN == 32
|
||||
? *reinterpret_cast<uint32_t*>(p.data() + ((this->tohost + 4) & mem.page_addr_mask)) >> 16
|
||||
: (cur_data >> 48) & 0xFF;
|
||||
// Extract payload (bits 47:0)
|
||||
uint64_t payload_addr = cur_data & 0xFFFFFFFFFFFFULL;
|
||||
if(payload_addr & 1) {
|
||||
CPPLOG(FATAL) << "tohost value is 0x" << std::hex << payload_addr << std::dec << " (" << payload_addr
|
||||
<< "), stopping simulation";
|
||||
this->reg.trap_state = std::numeric_limits<uint32_t>::max();
|
||||
this->interrupt_sim = payload_addr;
|
||||
return iss::Ok;
|
||||
} else if(device == 0 && command == 0) {
|
||||
std::array<uint64_t, 8> loaded_payload;
|
||||
if(read(address_type::PHYSICAL, access_type::DEBUG_READ, traits<BASE>::MEM, payload_addr, 8 * sizeof(uint64_t),
|
||||
reinterpret_cast<uint8_t*>(loaded_payload.data())) == iss::Err)
|
||||
CPPLOG(ERR) << "Syscall read went wrong";
|
||||
uint64_t syscall_num = loaded_payload.at(0);
|
||||
if(syscall_num == 64) { // SYS_WRITE
|
||||
return this->execute_sys_write(this, loaded_payload, traits<BASE>::MEM);
|
||||
} else {
|
||||
CPPLOG(ERR) << "tohost syscall with number 0x" << std::hex << syscall_num << std::dec << " (" << syscall_num
|
||||
<< ") not implemented";
|
||||
this->reg.trap_state = std::numeric_limits<uint32_t>::max();
|
||||
this->interrupt_sim = payload_addr;
|
||||
return iss::Ok;
|
||||
}
|
||||
} else {
|
||||
CPPLOG(ERR) << "tohost functionality not implemented for device " << device << " and command " << command;
|
||||
this->reg.trap_state = std::numeric_limits<uint32_t>::max();
|
||||
this->interrupt_sim = payload_addr;
|
||||
return iss::Ok;
|
||||
}
|
||||
}
|
||||
if((traits<BASE>::XLEN == 32 && paddr.val == this->fromhost + 4) || (traits<BASE>::XLEN == 64 && paddr.val == this->fromhost)) {
|
||||
uint64_t fhostvar = *reinterpret_cast<uint64_t*>(p.data() + (this->fromhost & mem.page_addr_mask));
|
||||
*reinterpret_cast<uint64_t*>(p.data() + (this->tohost & mem.page_addr_mask)) = fhostvar;
|
||||
}
|
||||
}
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::read_clic(uint64_t addr, unsigned length, uint8_t* const data) {
|
||||
if(addr == cfg.clic_base) { // cliccfg
|
||||
*data = clic_cfg_reg;
|
||||
for(auto i = 1; i < length; ++i)
|
||||
*(data + i) = 0;
|
||||
} else if(addr >= (cfg.clic_base + 0x40) && (addr + length) <= (cfg.clic_base + 0x40 + cfg.clic_num_trigger * 4)) { // clicinttrig
|
||||
auto offset = ((addr & 0x7fff) - 0x40) / 4;
|
||||
read_reg_uint32(addr, clic_inttrig_reg[offset], data, length);
|
||||
} else if(addr >= (cfg.clic_base + 0x1000) &&
|
||||
(addr + length) <= (cfg.clic_base + 0x1000 + cfg.clic_num_irq * 4)) { // clicintip/clicintie/clicintattr/clicintctl
|
||||
auto offset = ((addr & 0x7fff) - 0x1000) / 4;
|
||||
read_reg_uint32(addr, clic_int_reg[offset].raw, data, length);
|
||||
} else {
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(data + i) = 0;
|
||||
}
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||
iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::write_clic(uint64_t addr, unsigned length, const uint8_t* const data) {
|
||||
if(addr == cfg.clic_base) { // cliccfg
|
||||
clic_cfg_reg = (clic_cfg_reg & ~0x1e) | (*data & 0x1e);
|
||||
} else if(addr >= (cfg.clic_base + 0x40) && (addr + length) <= (cfg.clic_base + 0x40 + cfg.clic_num_trigger * 4)) { // clicinttrig
|
||||
auto offset = ((addr & 0x7fff) - 0x40) / 4;
|
||||
write_reg_uint32(addr, clic_inttrig_reg[offset], data, length);
|
||||
} else if(addr >= (cfg.clic_base + 0x1000) &&
|
||||
(addr + length) <= (cfg.clic_base + 0x1000 + cfg.clic_num_irq * 4)) { // clicintip/clicintie/clicintattr/clicintctl
|
||||
auto offset = ((addr & 0x7fff) - 0x1000) / 4;
|
||||
write_reg_uint32(addr, clic_int_reg[offset].raw, data, length);
|
||||
clic_int_reg[offset].raw &= 0xf0c70101; // clicIntCtlBits->0xf0, clicintattr->0xc7, clicintie->0x1, clicintip->0x1
|
||||
}
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT> inline void riscv_hart_mu_p<BASE, FEAT, LOGCAT>::reset(uint64_t address) {
|
||||
BASE::reset(address);
|
||||
state.mstatus = hart_state_type::mstatus_reset_val;
|
||||
state.mstatus = hart_state<reg_t>::mstatus_reset_val;
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT, typename LOGCAT> void riscv_hart_mu_p<BASE, FEAT, LOGCAT>::check_interrupt() {
|
||||
@ -1061,9 +548,9 @@ uint64_t riscv_hart_mu_p<BASE, FEAT, LOGCAT>::enter_trap(uint64_t flags, uint64_
|
||||
phys_addr_t p_addr(access_type::DEBUG_READ, traits<BASE>::MEM, addr - 4);
|
||||
std::array<uint8_t, 8> data;
|
||||
// check for SLLI_X0_X0_0X1F and SRAI_X0_X0_0X07
|
||||
this->read_mem(p_addr, 4, data.data());
|
||||
p_addr.val += 8;
|
||||
this->read_mem(p_addr, 4, data.data() + 4);
|
||||
this->memory.rd_mem(iss::access_type::DEBUG_READ, addr - 4, 4, data.data());
|
||||
addr += 8;
|
||||
this->memory.rd_mem(iss::access_type::DEBUG_READ, addr - 4, 4, data.data() + 4);
|
||||
|
||||
const std::array<uint8_t, 8> ref_data = {0x13, 0x10, 0xf0, 0x01, 0x13, 0x50, 0x70, 0x40};
|
||||
if(data == ref_data) {
|
||||
@ -1124,7 +611,7 @@ uint64_t riscv_hart_mu_p<BASE, FEAT, LOGCAT>::enter_trap(uint64_t flags, uint64_
|
||||
auto xtvec = this->csr[utvec | (new_priv << 8)];
|
||||
// calculate addr// set NEXT_PC to trap addressess to jump to based on MODE
|
||||
// bits in mtvec
|
||||
if((FEAT & features_e::FEAT_CLIC) && trap_id != 0 && (xtvec & 0x3UL) == 3UL) {
|
||||
if(trap_id != 0 && (xtvec & 0x3UL) == 3UL) {
|
||||
reg_t data;
|
||||
auto ret = read(address_type::LOGICAL, access_type::READ, 0, this->csr[mtvt], sizeof(reg_t), reinterpret_cast<uint8_t*>(&data));
|
||||
if(ret == iss::Err)
|
||||
|
252
src/iss/mmio/clic.h
Normal file
252
src/iss/mmio/clic.h
Normal file
@ -0,0 +1,252 @@
|
||||
|
||||
#include "iss/arch/riscv_hart_common.h"
|
||||
#include "iss/vm_types.h"
|
||||
#include "memory_if.h"
|
||||
#include <util/logging.h>
|
||||
|
||||
namespace iss {
|
||||
namespace mmio {
|
||||
struct clic_config {
|
||||
uint64_t clic_base{0xc0000000};
|
||||
unsigned clic_int_ctl_bits{4};
|
||||
unsigned clic_num_irq{16};
|
||||
unsigned clic_num_trigger{0};
|
||||
bool nmode{false};
|
||||
};
|
||||
|
||||
inline void read_reg_with_offset(uint32_t reg, uint8_t offs, uint8_t* const data, unsigned length) {
|
||||
auto reg_ptr = reinterpret_cast<uint8_t*>(®);
|
||||
switch(offs) {
|
||||
default:
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(data + i) = *(reg_ptr + i);
|
||||
break;
|
||||
case 1:
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(data + i) = *(reg_ptr + 1 + i);
|
||||
break;
|
||||
case 2:
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(data + i) = *(reg_ptr + 2 + i);
|
||||
break;
|
||||
case 3:
|
||||
*data = *(reg_ptr + 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline void write_reg_with_offset(uint32_t& reg, uint8_t offs, const uint8_t* const data, unsigned length) {
|
||||
auto reg_ptr = reinterpret_cast<uint8_t*>(®);
|
||||
switch(offs) {
|
||||
default:
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(reg_ptr + i) = *(data + i);
|
||||
break;
|
||||
case 1:
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(reg_ptr + 1 + i) = *(data + i);
|
||||
break;
|
||||
case 2:
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(reg_ptr + 2 + i) = *(data + i);
|
||||
break;
|
||||
case 3:
|
||||
*(reg_ptr + 3) = *data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename WORD_TYPE> struct clic : public memory_elem {
|
||||
using this_class = clic<WORD_TYPE>;
|
||||
using reg_t = WORD_TYPE;
|
||||
constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8;
|
||||
|
||||
clic(arch::priv_if<WORD_TYPE> hart_if, clic_config cfg)
|
||||
: hart_if(hart_if)
|
||||
, cfg(cfg) {
|
||||
clic_int_reg.resize(cfg.clic_num_irq, clic_int_reg_t{.raw = 0});
|
||||
clic_cfg_reg = 0x30;
|
||||
clic_mact_lvl = clic_mprev_lvl = (1 << (cfg.clic_int_ctl_bits)) - 1;
|
||||
clic_uact_lvl = clic_uprev_lvl = (1 << (cfg.clic_int_ctl_bits)) - 1;
|
||||
hart_if.csr_rd_cb[arch::mtvt] = MK_CSR_RD_CB(read_plain);
|
||||
hart_if.csr_wr_cb[arch::mtvt] = MK_CSR_WR_CB(write_xtvt);
|
||||
// hart_if.csr_rd_cb[mxnti] = MK_CSR_RD_CB(read_plain(a,r);};
|
||||
// hart_if.csr_wr_cb[mxnti] = MK_CSR_WR_CB(write_plain(a,r);};
|
||||
hart_if.csr_rd_cb[arch::mintstatus] = MK_CSR_RD_CB(read_intstatus);
|
||||
hart_if.csr_wr_cb[arch::mintstatus] = MK_CSR_WR_CB(write_null);
|
||||
// hart_if.csr_rd_cb[mscratchcsw] = MK_CSR_RD_CB(read_plain(a,r);};
|
||||
// hart_if.csr_wr_cb[mscratchcsw] = MK_CSR_WR_CB(write_plain(a,r);};
|
||||
// hart_if.csr_rd_cb[mscratchcswl] = MK_CSR_RD_CB(read_plain(a,r);};
|
||||
// hart_if.csr_wr_cb[mscratchcswl] = MK_CSR_WR_CB(write_plain(a,r);};
|
||||
hart_if.csr_rd_cb[arch::mintthresh] = MK_CSR_RD_CB(read_plain);
|
||||
hart_if.csr_wr_cb[arch::mintthresh] = MK_CSR_WR_CB(write_intthresh);
|
||||
if(cfg.nmode) {
|
||||
hart_if.csr_rd_cb[arch::utvt] = MK_CSR_RD_CB(read_plain);
|
||||
hart_if.csr_wr_cb[arch::utvt] = MK_CSR_WR_CB(write_xtvt);
|
||||
hart_if.csr_rd_cb[arch::uintstatus] = MK_CSR_RD_CB(read_intstatus);
|
||||
hart_if.csr_wr_cb[arch::uintstatus] = MK_CSR_WR_CB(write_null);
|
||||
hart_if.csr_rd_cb[arch::uintthresh] = MK_CSR_RD_CB(read_plain);
|
||||
hart_if.csr_wr_cb[arch::uintthresh] = MK_CSR_WR_CB(write_intthresh);
|
||||
}
|
||||
hart_if.csr[arch::mintthresh] = (1 << (cfg.clic_int_ctl_bits)) - 1;
|
||||
hart_if.csr[arch::uintthresh] = (1 << (cfg.clic_int_ctl_bits)) - 1;
|
||||
}
|
||||
|
||||
~clic() = default;
|
||||
|
||||
memory_if get_mem_if() override {
|
||||
return memory_if{.rd_mem{util::delegate<rd_mem_func_sig>::from<this_class, &this_class::read_mem>(this)},
|
||||
.wr_mem{util::delegate<wr_mem_func_sig>::from<this_class, &this_class::write_mem>(this)}};
|
||||
}
|
||||
|
||||
void set_next(memory_if mem) override { down_stream_mem = mem; }
|
||||
|
||||
std::tuple<uint64_t, uint64_t> get_range() override { return {cfg.clic_base, cfg.clic_base + 0x7fff}; }
|
||||
|
||||
private:
|
||||
iss::status read_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t* data) {
|
||||
if(addr >= cfg.clic_base && (addr + length) < (cfg.clic_base + 0x8000))
|
||||
return read_clic(addr, length, data);
|
||||
return down_stream_mem.rd_mem(access, addr, length, data);
|
||||
}
|
||||
|
||||
iss::status write_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t const* data) {
|
||||
if(addr >= cfg.clic_base && (addr + length) < (cfg.clic_base + 0x8000))
|
||||
return write_clic(addr, length, data);
|
||||
return down_stream_mem.wr_mem(access, addr, length, data);
|
||||
}
|
||||
|
||||
iss::status read_clic(uint64_t addr, unsigned length, uint8_t* data);
|
||||
|
||||
iss::status write_clic(uint64_t addr, unsigned length, uint8_t const* data);
|
||||
|
||||
iss::status write_null(unsigned addr, reg_t val) { return iss::status::Ok; }
|
||||
|
||||
iss::status read_plain(unsigned addr, reg_t& val) {
|
||||
val = hart_if.csr[addr];
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status write_xtvt(unsigned addr, reg_t val) {
|
||||
hart_if.csr[addr] = val & ~0x3fULL;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status read_cause(unsigned addr, reg_t& val);
|
||||
iss::status write_cause(unsigned addr, reg_t val);
|
||||
|
||||
iss::status read_intstatus(unsigned addr, reg_t& val);
|
||||
iss::status write_intthresh(unsigned addr, reg_t val);
|
||||
|
||||
protected:
|
||||
arch::priv_if<WORD_TYPE> hart_if;
|
||||
memory_if down_stream_mem;
|
||||
clic_config cfg;
|
||||
uint8_t clic_cfg_reg{0};
|
||||
std::array<uint32_t, 32> clic_inttrig_reg;
|
||||
union clic_int_reg_t {
|
||||
struct {
|
||||
uint8_t ip;
|
||||
uint8_t ie;
|
||||
uint8_t attr;
|
||||
uint8_t ctl;
|
||||
};
|
||||
uint32_t raw;
|
||||
};
|
||||
std::vector<clic_int_reg_t> clic_int_reg;
|
||||
uint8_t clic_mprev_lvl{0}, clic_uprev_lvl{0};
|
||||
uint8_t clic_mact_lvl{0}, clic_uact_lvl{0};
|
||||
};
|
||||
|
||||
template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::read_clic(uint64_t addr, unsigned length, uint8_t* const data) {
|
||||
if(addr == cfg.clic_base) { // cliccfg
|
||||
*data = clic_cfg_reg;
|
||||
for(auto i = 1; i < length; ++i)
|
||||
*(data + i) = 0;
|
||||
} else if(addr >= (cfg.clic_base + 0x40) && (addr + length) <= (cfg.clic_base + 0x40 + cfg.clic_num_trigger * 4)) { // clicinttrig
|
||||
auto offset = ((addr & 0x7fff) - 0x40) / 4;
|
||||
read_reg_with_offset(clic_inttrig_reg[offset], addr & 0x3, data, length);
|
||||
} else if(addr >= (cfg.clic_base + 0x1000) &&
|
||||
(addr + length) <= (cfg.clic_base + 0x1000 + cfg.clic_num_irq * 4)) { // clicintip/clicintie/clicintattr/clicintctl
|
||||
auto offset = ((addr & 0x7fff) - 0x1000) / 4;
|
||||
read_reg_with_offset(clic_int_reg[offset].raw, addr & 0x3, data, length);
|
||||
} else {
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(data + i) = 0;
|
||||
}
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::write_clic(uint64_t addr, unsigned length, const uint8_t* const data) {
|
||||
if(addr == cfg.clic_base) { // cliccfg
|
||||
clic_cfg_reg = (clic_cfg_reg & ~0x1e) | (*data & 0x1e);
|
||||
} else if(addr >= (cfg.clic_base + 0x40) && (addr + length) <= (cfg.clic_base + 0x40 + cfg.clic_num_trigger * 4)) { // clicinttrig
|
||||
auto offset = ((addr & 0x7fff) - 0x40) / 4;
|
||||
write_reg_with_offset(clic_inttrig_reg[offset], addr & 0x3, data, length);
|
||||
} else if(addr >= (cfg.clic_base + 0x1000) &&
|
||||
(addr + length) <= (cfg.clic_base + 0x1000 + cfg.clic_num_irq * 4)) { // clicintip/clicintie/clicintattr/clicintctl
|
||||
auto offset = ((addr & 0x7fff) - 0x1000) / 4;
|
||||
write_reg_with_offset(clic_int_reg[offset].raw, addr & 0x3, data, length);
|
||||
clic_int_reg[offset].raw &= 0xf0c70101; // clicIntCtlBits->0xf0, clicintattr->0xc7, clicintie->0x1, clicintip->0x1
|
||||
}
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::read_cause(unsigned addr, reg_t& val) {
|
||||
if((hart_if.csr[arch::mtvec] & 0x3) == 3) {
|
||||
val = hart_if.csr[addr] & (1UL << (sizeof(reg_t) * 8) | (hart_if.mcause_max_irq - 1) | (0xfUL << 16));
|
||||
auto mode = (addr >> 8) & 0x3;
|
||||
switch(mode) {
|
||||
case 0:
|
||||
val |= clic_uprev_lvl << 16;
|
||||
val |= hart_if.mstatus.UPIE << 27;
|
||||
break;
|
||||
default:
|
||||
val |= clic_mprev_lvl << 16;
|
||||
val |= hart_if.mstatus.MPIE << 27;
|
||||
val |= hart_if.mstatus.MPP << 28;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
val = hart_if.csr[addr] & ((1UL << (sizeof(WORD_TYPE) * 8 - 1)) | (hart_if.mcause_max_irq - 1));
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::write_cause(unsigned addr, reg_t val) {
|
||||
if((hart_if.csr[arch::mtvec] & 0x3) == 3) {
|
||||
auto mask = ((1UL << (sizeof(WORD_TYPE) * 8 - 1)) | (hart_if.mcause_max_irq - 1) | (0xfUL << 16));
|
||||
hart_if.csr[addr] = (val & mask) | (hart_if.csr[addr] & ~mask);
|
||||
auto mode = (addr >> 8) & 0x3;
|
||||
switch(mode) {
|
||||
case 0:
|
||||
clic_uprev_lvl = ((val >> 16) & 0xff) | (1 << (8 - cfg.clic_int_ctl_bits)) - 1;
|
||||
hart_if.mstatus.UPIE = (val >> 27) & 0x1;
|
||||
break;
|
||||
default:
|
||||
clic_mprev_lvl = ((val >> 16) & 0xff) | (1 << (8 - cfg.clic_int_ctl_bits)) - 1;
|
||||
hart_if.mstatus.MPIE = (val >> 27) & 0x1;
|
||||
hart_if.mstatus.MPP = (val >> 28) & 0x3;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
auto mask = ((1UL << (sizeof(WORD_TYPE) * 8 - 1)) | (hart_if.mcause_max_irq - 1));
|
||||
hart_if.csr[addr] = (val & mask) | (hart_if.csr[addr] & ~mask);
|
||||
}
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::read_intstatus(unsigned addr, reg_t& val) {
|
||||
auto mode = (addr >> 8) & 0x3;
|
||||
val = clic_uact_lvl & 0xff;
|
||||
if(mode == 0x3)
|
||||
val += (clic_mact_lvl & 0xff) << 24;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename WORD_TYPE> iss::status clic<WORD_TYPE>::write_intthresh(unsigned addr, reg_t val) {
|
||||
hart_if.csr[addr] = (val & 0xff) | (1 << (cfg.clic_int_ctl_bits)) - 1;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
} // namespace mmio
|
||||
} // namespace iss
|
@ -1,7 +1,7 @@
|
||||
#include "memory_if.h"
|
||||
|
||||
namespace iss {
|
||||
namespace memory {
|
||||
namespace mmio {
|
||||
void memory_hierarchy::prepend(memory_elem& e) {
|
||||
hierarchy.push_front(e);
|
||||
update_chain();
|
||||
@ -15,11 +15,12 @@ void memory_hierarchy::insert_after(memory_elem&) {}
|
||||
void memory_hierarchy::replace_last(memory_elem&) {}
|
||||
void memory_hierarchy::update_chain() {
|
||||
bool tail = false;
|
||||
if(hierarchy.size() > 1)
|
||||
for(size_t i = 1; i < hierarchy.size(); ++i) {
|
||||
for(size_t i = 0; i < hierarchy.size(); ++i) {
|
||||
hierarchy[i].get().register_csrs();
|
||||
if(i)
|
||||
hierarchy[i - 1].get().set_next(hierarchy[i].get().get_mem_if());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace memory
|
||||
} // namespace mmio
|
||||
} // namespace iss
|
@ -38,11 +38,11 @@
|
||||
#include "iss/vm_types.h"
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <limits>
|
||||
#include <util/delegate.h>
|
||||
|
||||
namespace iss {
|
||||
namespace memory {
|
||||
namespace mmio {
|
||||
|
||||
using rd_mem_func_sig = iss::status(iss::access_type, uint64_t, unsigned, uint8_t*);
|
||||
using wr_mem_func_sig = iss::status(iss::access_type, uint64_t, unsigned, uint8_t const*);
|
||||
@ -55,6 +55,8 @@ struct memory_if {
|
||||
struct memory_elem {
|
||||
virtual memory_if get_mem_if() = 0;
|
||||
virtual void set_next(memory_if) = 0;
|
||||
virtual void register_csrs() {}
|
||||
virtual std::tuple<uint64_t, uint64_t> get_range() { return {0, std::numeric_limits<uint64_t>::max()}; }
|
||||
};
|
||||
|
||||
struct memory_hierarchy {
|
||||
@ -69,6 +71,6 @@ protected:
|
||||
std::deque<std::reference_wrapper<memory_elem>> hierarchy;
|
||||
};
|
||||
|
||||
} // namespace memory
|
||||
} // namespace mmio
|
||||
} // namespace iss
|
||||
#endif
|
@ -1,3 +1,5 @@
|
||||
#ifndef _MEMORY_WITH_HTIF_
|
||||
#define _MEMORY_WITH_HTIF_
|
||||
|
||||
#include "iss/arch/riscv_hart_common.h"
|
||||
#include "iss/vm_types.h"
|
||||
@ -6,15 +8,15 @@
|
||||
#include <util/sparse_array.h>
|
||||
|
||||
namespace iss {
|
||||
namespace memory {
|
||||
template <typename WORD_TYPE> struct functional_memory : public memory_elem {
|
||||
using this_class = functional_memory<WORD_TYPE>;
|
||||
namespace mmio {
|
||||
template <typename WORD_TYPE> struct memory_with_htif : public memory_elem {
|
||||
using this_class = memory_with_htif<WORD_TYPE>;
|
||||
constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8;
|
||||
|
||||
functional_memory(arch::priv_if<WORD_TYPE> hart_if)
|
||||
memory_with_htif(arch::priv_if<WORD_TYPE> hart_if)
|
||||
: hart_if(hart_if) {}
|
||||
|
||||
~functional_memory() = default;
|
||||
~memory_with_htif() = default;
|
||||
|
||||
memory_if get_mem_if() override {
|
||||
return memory_if{.rd_mem{util::delegate<rd_mem_func_sig>::from<this_class, &this_class::read_mem>(this)},
|
||||
@ -55,5 +57,6 @@ protected:
|
||||
mem_type mem;
|
||||
arch::priv_if<WORD_TYPE> hart_if;
|
||||
};
|
||||
} // namespace memory
|
||||
} // namespace mmio
|
||||
} // namespace iss
|
||||
#endif // _MEMORY_WITH_HTIF_
|
212
src/iss/mmio/pmp.h
Normal file
212
src/iss/mmio/pmp.h
Normal file
@ -0,0 +1,212 @@
|
||||
|
||||
#include "iss/arch/riscv_hart_common.h"
|
||||
#include "iss/vm_types.h"
|
||||
#include "memory_if.h"
|
||||
#include <util/logging.h>
|
||||
|
||||
namespace iss {
|
||||
namespace mmio {
|
||||
struct clic_config {
|
||||
uint64_t clic_base{0xc0000000};
|
||||
unsigned clic_int_ctl_bits{4};
|
||||
unsigned clic_num_irq{16};
|
||||
unsigned clic_num_trigger{0};
|
||||
bool nmode{false};
|
||||
};
|
||||
|
||||
inline void read_reg_with_offset(uint32_t reg, uint8_t offs, uint8_t* const data, unsigned length) {
|
||||
auto reg_ptr = reinterpret_cast<uint8_t*>(®);
|
||||
switch(offs) {
|
||||
default:
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(data + i) = *(reg_ptr + i);
|
||||
break;
|
||||
case 1:
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(data + i) = *(reg_ptr + 1 + i);
|
||||
break;
|
||||
case 2:
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(data + i) = *(reg_ptr + 2 + i);
|
||||
break;
|
||||
case 3:
|
||||
*data = *(reg_ptr + 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline void write_reg_with_offset(uint32_t& reg, uint8_t offs, const uint8_t* const data, unsigned length) {
|
||||
auto reg_ptr = reinterpret_cast<uint8_t*>(®);
|
||||
switch(offs) {
|
||||
default:
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(reg_ptr + i) = *(data + i);
|
||||
break;
|
||||
case 1:
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(reg_ptr + 1 + i) = *(data + i);
|
||||
break;
|
||||
case 2:
|
||||
for(auto i = 0U; i < length; ++i)
|
||||
*(reg_ptr + 2 + i) = *(data + i);
|
||||
break;
|
||||
case 3:
|
||||
*(reg_ptr + 3) = *data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename WORD_TYPE> struct pmp : public memory_elem {
|
||||
using this_class = pmp<WORD_TYPE>;
|
||||
using reg_t = WORD_TYPE;
|
||||
constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8;
|
||||
|
||||
pmp(arch::priv_if<WORD_TYPE> hart_if, clic_config cfg)
|
||||
: hart_if(hart_if)
|
||||
, cfg(cfg) {
|
||||
for(size_t i = arch::pmpaddr0; i <= arch::pmpaddr15; ++i) {
|
||||
hart_if.csr_rd_cb[i] = MK_CSR_RD_CB(read_plain);
|
||||
hart_if.csr_wr_cb[i] = MK_CSR_WR_CB(write_plain);
|
||||
}
|
||||
for(size_t i = arch::pmpcfg0; i < arch::pmpcfg0 + 16 / sizeof(reg_t); ++i) {
|
||||
hart_if.csr_rd_cb[i] = MK_CSR_RD_CB(read_plain);
|
||||
hart_if.csr_wr_cb[i] = MK_CSR_WR_CB(write_pmpcfg);
|
||||
}
|
||||
}
|
||||
|
||||
~pmp() = default;
|
||||
|
||||
memory_if get_mem_if() override {
|
||||
return memory_if{.rd_mem{util::delegate<rd_mem_func_sig>::from<this_class, &this_class::read_mem>(this)},
|
||||
.wr_mem{util::delegate<wr_mem_func_sig>::from<this_class, &this_class::write_mem>(this)}};
|
||||
}
|
||||
|
||||
void set_next(memory_if mem) override { down_stream_mem = mem; }
|
||||
|
||||
private:
|
||||
iss::status read_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t* data) {
|
||||
if(!pmp_check(access, addr, length) && !is_debug(access)) {
|
||||
hart_if.fault_data = addr;
|
||||
if(is_debug(access))
|
||||
throw trap_access(0, addr);
|
||||
hart_if.reg.trap_state = (1UL << 31) | ((access == access_type::FETCH ? 1 : 5) << 16); // issue trap 1
|
||||
return iss::Err;
|
||||
}
|
||||
return down_stream_mem.rd_mem(access, addr, length, data);
|
||||
}
|
||||
|
||||
iss::status write_mem(iss::access_type access, uint64_t addr, unsigned length, uint8_t const* data) {
|
||||
if(!pmp_check(access, addr, length) && !is_debug(access)) {
|
||||
hart_if.fault_data = addr;
|
||||
if(is_debug(access))
|
||||
throw trap_access(0, addr);
|
||||
hart_if.reg.trap_state = (1UL << 31) | (7 << 16); // issue trap 1
|
||||
return iss::Err;
|
||||
}
|
||||
return down_stream_mem.wr_mem(access, addr, length, data);
|
||||
}
|
||||
|
||||
iss::status read_plain(unsigned addr, reg_t& val) {
|
||||
val = hart_if.csr[addr];
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status write_plain(unsigned addr, reg_t const& val) {
|
||||
hart_if.csr[addr] = val;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
iss::status write_pmpcfg(unsigned addr, reg_t val) {
|
||||
hart_if.csr[addr] = val & 0x9f9f9f9f;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
bool pmp_check(const access_type type, const uint64_t addr, const unsigned len);
|
||||
|
||||
protected:
|
||||
arch::priv_if<WORD_TYPE> hart_if;
|
||||
memory_if down_stream_mem;
|
||||
};
|
||||
|
||||
template <typename WORD_TYPE> bool pmp<WORD_TYPE>::pmp_check(const access_type type, const uint64_t addr, const unsigned len) {
|
||||
constexpr auto PMP_SHIFT = 2U;
|
||||
constexpr auto PMP_R = 0x1U;
|
||||
constexpr auto PMP_W = 0x2U;
|
||||
constexpr auto PMP_X = 0x4U;
|
||||
constexpr auto PMP_A = 0x18U;
|
||||
constexpr auto PMP_L = 0x80U;
|
||||
constexpr auto PMP_TOR = 0x1U;
|
||||
constexpr auto PMP_NA4 = 0x2U;
|
||||
constexpr auto PMP_NAPOT = 0x3U;
|
||||
reg_t base = 0;
|
||||
auto any_active = false;
|
||||
auto const cfg_reg_size = sizeof(reg_t);
|
||||
for(size_t i = 0; i < 16; i++) {
|
||||
reg_t tor = hart_if.csr[arch::pmpaddr0 + i] << PMP_SHIFT;
|
||||
uint8_t cfg = hart_if.csr[arch::pmpcfg0 + (i / cfg_reg_size)] >> (i % cfg_reg_size);
|
||||
if(cfg & PMP_A) {
|
||||
any_active = true;
|
||||
auto pmp_a = (cfg & PMP_A) >> 3;
|
||||
auto is_tor = pmp_a == PMP_TOR;
|
||||
auto is_na4 = pmp_a == PMP_NA4;
|
||||
|
||||
reg_t mask = (hart_if.csr[arch::pmpaddr0 + i] << 1) | (!is_na4);
|
||||
mask = ~(mask & ~(mask + 1)) << PMP_SHIFT;
|
||||
|
||||
// Check each 4-byte sector of the access
|
||||
auto any_match = false;
|
||||
auto all_match = true;
|
||||
for(reg_t offset = 0; offset < len; offset += 1 << PMP_SHIFT) {
|
||||
reg_t cur_addr = addr + offset;
|
||||
auto napot_match = ((cur_addr ^ tor) & mask) == 0;
|
||||
auto tor_match = base <= (cur_addr + len - 1) && cur_addr < tor;
|
||||
auto match = is_tor ? tor_match : napot_match;
|
||||
any_match |= match;
|
||||
all_match &= match;
|
||||
}
|
||||
if(any_match) {
|
||||
// If the PMP matches only a strict subset of the access, fail it
|
||||
if(!all_match)
|
||||
return false;
|
||||
return (hart_if.reg.PRIV == arch::PRIV_M && !(cfg & PMP_L)) || (type == access_type::READ && (cfg & PMP_R)) ||
|
||||
(type == access_type::WRITE && (cfg & PMP_W)) || (type == access_type::FETCH && (cfg & PMP_X));
|
||||
}
|
||||
}
|
||||
base = tor;
|
||||
}
|
||||
// constexpr auto pmp_num_regs = 16;
|
||||
// reg_t tor_base = 0;
|
||||
// auto any_active = false;
|
||||
// auto lower_addr = addr >>2;
|
||||
// auto upper_addr = (addr+len-1)>>2;
|
||||
// for (size_t i = 0; i < pmp_num_regs; i++) {
|
||||
// uint8_t cfg = csr[pmpcfg0+(i/4)]>>(i%4);
|
||||
// uint8_t cfg_next = i==(pmp_num_regs-1)? 0 : csr[pmpcfg0+((i+1)/4)]>>((i+1)%4);
|
||||
// auto pmpaddr = csr[pmpaddr0+i];
|
||||
// if (cfg & PMP_A) {
|
||||
// any_active=true;
|
||||
// auto is_tor = bit_sub<3, 2>(cfg) == PMP_TOR;
|
||||
// auto is_napot = bit_sub<4, 1>(cfg) && bit_sub<3, 2>(cfg_next)!= PMP_TOR;
|
||||
// if(is_napot) {
|
||||
// reg_t mask = bit_sub<3, 1>(cfg)?~( pmpaddr & ~(pmpaddr + 1)): 0x3fffffff;
|
||||
// auto mpmpaddr = pmpaddr & mask;
|
||||
// if((lower_addr&mask) == mpmpaddr && (upper_addr&mask)==mpmpaddr)
|
||||
// return (hart_if.reg.PRIV == PRIV_M && !(cfg & PMP_L)) ||
|
||||
// (type == access_type::READ && (cfg & PMP_R)) ||
|
||||
// (type == access_type::WRITE && (cfg & PMP_W)) ||
|
||||
// (type == access_type::FETCH && (cfg & PMP_X));
|
||||
// } else if(is_tor) {
|
||||
// if(lower_addr>=tor_base && upper_addr<=pmpaddr)
|
||||
// return (hart_if.reg.PRIV == PRIV_M && !(cfg & PMP_L)) ||
|
||||
// (type == access_type::READ && (cfg & PMP_R)) ||
|
||||
// (type == access_type::WRITE && (cfg & PMP_W)) ||
|
||||
// (type == access_type::FETCH && (cfg & PMP_X));
|
||||
// }
|
||||
// }
|
||||
// tor_base = pmpaddr;
|
||||
// }
|
||||
return !any_active || hart_if.reg.PRIV == arch::PRIV_M;
|
||||
}
|
||||
|
||||
} // namespace mmio
|
||||
} // namespace iss
|
Loading…
x
Reference in New Issue
Block a user