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
|
set(LIB_SOURCES
|
||||||
src/iss/plugin/instruction_count.cpp
|
src/iss/plugin/instruction_count.cpp
|
||||||
src/iss/arch/tgc5c.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/interp/vm_tgc5c.cpp
|
||||||
src/vm/fp_functions.cpp
|
src/vm/fp_functions.cpp
|
||||||
src/iss/debugger/csr_names.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/arch/traits.h"
|
||||||
#include "iss/log_categories.h"
|
#include "iss/log_categories.h"
|
||||||
#include "iss/memory/memory_if.h"
|
#include "iss/mmio/memory_if.h"
|
||||||
#include "iss/vm_types.h"
|
#include "iss/vm_types.h"
|
||||||
|
#include "mstatus.h"
|
||||||
#include "util/delegate.h"
|
#include "util/delegate.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@ -51,7 +52,6 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <util/ities.h>
|
|
||||||
#include <util/logging.h>
|
#include <util/logging.h>
|
||||||
#include <util/sparse_array.h>
|
#include <util/sparse_array.h>
|
||||||
|
|
||||||
@ -66,7 +66,7 @@
|
|||||||
namespace iss {
|
namespace iss {
|
||||||
namespace arch {
|
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 {
|
enum riscv_csr {
|
||||||
/* user-level CSR */
|
/* user-level CSR */
|
||||||
@ -242,10 +242,6 @@ struct vm_info {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct feature_config {
|
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_base{0x10000000};
|
||||||
uint64_t tcm_size{0x8000};
|
uint64_t tcm_size{0x8000};
|
||||||
uint64_t io_address{0xf0000000};
|
uint64_t io_address{0xf0000000};
|
||||||
@ -278,56 +274,22 @@ public:
|
|||||||
: trap_access(15 << 16, badaddr) {}
|
: 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 {
|
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&)> read_csr;
|
||||||
std::function<iss::status(unsigned, WORD_TYPE)> write_csr;
|
std::function<iss::status(unsigned, WORD_TYPE)> write_csr;
|
||||||
std::function<iss::status(uint8_t const*)> exec_htif;
|
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& tohost;
|
||||||
uint64_t& fromhost;
|
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, 4> lvl = {{'U', 'S', 'H', 'M'}};
|
||||||
const std::array<const char*, 16> trap_str = {{""
|
const std::array<const char*, 16> trap_str = {{""
|
||||||
"Instruction address misaligned", // 0
|
"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); };
|
#define MK_CSR_WR_CB(FCT) [this](unsigned a, reg_t r) -> iss::status { return this->FCT(a, r); };
|
||||||
|
|
||||||
riscv_hart_common()
|
riscv_hart_common()
|
||||||
: instr_if(*this) {
|
: state()
|
||||||
|
, instr_if(*this) {
|
||||||
// reset values
|
// reset values
|
||||||
csr[misa] = traits<BASE>::MISA_VAL;
|
csr[misa] = traits<BASE>::MISA_VAL;
|
||||||
csr[mvendorid] = 0x669;
|
csr[mvendorid] = 0x669;
|
||||||
@ -748,17 +711,16 @@ template <typename BASE, typename LOGCAT = logging::disass> struct riscv_hart_co
|
|||||||
return iss::Ok;
|
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() {
|
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); },
|
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); },
|
.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); },
|
.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},
|
.tohost{this->tohost},
|
||||||
.fromhost{this->fromhost}};
|
.fromhost{this->fromhost},
|
||||||
|
.mcause_max_irq{mcause_max_irq}};
|
||||||
}
|
}
|
||||||
|
|
||||||
iss::status execute_htif(uint8_t const* data) {
|
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");
|
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:
|
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 {
|
struct riscv_instrumentation_if : public iss::instrumentation_if {
|
||||||
|
|
||||||
riscv_instrumentation_if(riscv_hart_common<BASE, LOGCAT>& arch)
|
riscv_instrumentation_if(riscv_hart_common<BASE, LOGCAT>& arch)
|
||||||
@ -864,6 +842,7 @@ protected:
|
|||||||
int64_t instret_offset{0};
|
int64_t instret_offset{0};
|
||||||
semihosting_cb_t<reg_t> semihosting_cb;
|
semihosting_cb_t<reg_t> semihosting_cb;
|
||||||
std::array<vm_info, 2> vm;
|
std::array<vm_info, 2> vm;
|
||||||
|
unsigned mcause_max_irq{16U};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace arch
|
} // namespace arch
|
||||||
|
@ -35,7 +35,6 @@
|
|||||||
#ifndef _RISCV_HART_M_P_H
|
#ifndef _RISCV_HART_M_P_H
|
||||||
#define _RISCV_HART_M_P_H
|
#define _RISCV_HART_M_P_H
|
||||||
|
|
||||||
#include "iss/instrumentation_if.h"
|
|
||||||
#include "iss/vm_if.h"
|
#include "iss/vm_if.h"
|
||||||
#include "iss/vm_types.h"
|
#include "iss/vm_types.h"
|
||||||
#include "riscv_hart_common.h"
|
#include "riscv_hart_common.h"
|
||||||
@ -43,21 +42,15 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <elfio/elf_types.hpp>
|
#include <elfio/elf_types.hpp>
|
||||||
#include <limits>
|
|
||||||
#include <stdexcept>
|
|
||||||
#ifndef FMT_HEADER_ONLY
|
#ifndef FMT_HEADER_ONLY
|
||||||
#define FMT_HEADER_ONLY
|
#define FMT_HEADER_ONLY
|
||||||
#endif
|
#endif
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <elfio/elfio.hpp>
|
#include <elfio/elfio.hpp>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <functional>
|
#include <iss/mmio/memory_with_htif.h>
|
||||||
#include <iomanip>
|
|
||||||
#include <iss/memory/functional_memory.h>
|
|
||||||
#include <sstream>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <util/bit_field.h>
|
|
||||||
namespace iss {
|
namespace iss {
|
||||||
namespace arch {
|
namespace arch {
|
||||||
|
|
||||||
@ -71,61 +64,8 @@ public:
|
|||||||
using reg_t = typename core::reg_t;
|
using reg_t = typename core::reg_t;
|
||||||
using addr_t = typename core::addr_t;
|
using addr_t = typename core::addr_t;
|
||||||
|
|
||||||
// primary template
|
static constexpr reg_t get_mstatus_mask() {
|
||||||
template <class T, class Enable = void> struct hart_state {};
|
if(sizeof(reg_t) == 4)
|
||||||
// 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() {
|
|
||||||
// return 0x807ff988UL; // 0b1000 0000 0111 1111 1111 1000 1000 1000 // only machine mode is supported
|
// return 0x807ff988UL; // 0b1000 0000 0111 1111 1111 1000 1000 1000 // only machine mode is supported
|
||||||
// +-SD
|
// +-SD
|
||||||
// | +-TSR
|
// | +-TSR
|
||||||
@ -141,66 +81,7 @@ public:
|
|||||||
// | |||||| | | | |+-MPIE
|
// | |||||| | | | |+-MPIE
|
||||||
// | ||||||/|/|/| || +-MIE
|
// | ||||||/|/|/| || +-MIE
|
||||||
return 0b00000000000000000001100010001000;
|
return 0b00000000000000000001100010001000;
|
||||||
}
|
else if(sizeof(reg_t) == 8)
|
||||||
};
|
|
||||||
|
|
||||||
// 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() {
|
|
||||||
// return 0x8000000f007ff9ddULL; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011
|
// return 0x8000000f007ff9ddULL; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011
|
||||||
//
|
//
|
||||||
// +-TSR
|
// +-TSR
|
||||||
@ -216,10 +97,15 @@ public:
|
|||||||
// |||||| | | | |+-MPIE
|
// |||||| | | | |+-MPIE
|
||||||
// ||||||/|/|/| || +-MIE
|
// ||||||/|/|/| || +-MIE
|
||||||
return 0b00000000000000000001100010001000;
|
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() {
|
constexpr reg_t get_irq_mask() {
|
||||||
return 0b100010001000; // only machine mode is supported
|
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_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:
|
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;
|
std::unordered_map<uint64_t, uint8_t> atomic_reservation;
|
||||||
|
|
||||||
iss::status read_status(unsigned addr, reg_t& val);
|
iss::status read_status(unsigned addr, reg_t& val);
|
||||||
@ -265,20 +155,13 @@ protected:
|
|||||||
|
|
||||||
void check_interrupt();
|
void check_interrupt();
|
||||||
feature_config cfg;
|
feature_config cfg;
|
||||||
unsigned mcause_max_irq{(FEAT & features_e::FEAT_CLIC) ? std::max(16U, static_cast<unsigned>(traits<BASE>::CLIC_NUM_IRQ)) : 16U};
|
mmio::memory_with_htif<reg_t> default_mem;
|
||||||
memory::functional_memory<reg_t> default_mem;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||||
riscv_hart_m_p<BASE, FEAT, LOGCAT>::riscv_hart_m_p(feature_config cfg)
|
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()) {
|
, 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}};
|
const std::array<unsigned, 4> rwaddrs{{mepc, mtvec, mscratch, mtval}};
|
||||||
for(auto addr : rwaddrs) {
|
for(auto addr : rwaddrs) {
|
||||||
this->csr_rd_cb[addr] = MK_CSR_RD_CB(read_plain);
|
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_wr_cb[dcsr] = MK_CSR_WR_CB(write_dcsr);
|
||||||
this->csr_rd_cb[dcsr] = MK_CSR_RD_CB(read_debug);
|
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>
|
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
|
#ifndef NDEBUG
|
||||||
if(access && iss::access_type::DEBUG) {
|
if(access && iss::access_type::DEBUG) {
|
||||||
CPPLOG(TRACEALL) << "debug read of " << length << " bytes @addr 0x" << std::hex << addr;
|
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;
|
CPPLOG(TRACEALL) << "fetch of " << length << " bytes @addr 0x" << std::hex << addr;
|
||||||
} else {
|
} else {
|
||||||
CPPLOG(TRACE) << "read of " << length << " bytes @addr 0x" << std::hex << addr;
|
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>
|
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||||
iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::read_status(unsigned addr, reg_t& val) {
|
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;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||||
iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::write_status(unsigned addr, reg_t val) {
|
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();
|
check_interrupt();
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||||
iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::read_cause(unsigned addr, reg_t& val) {
|
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)) | (this->mcause_max_irq - 1));
|
||||||
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));
|
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||||
iss::status riscv_hart_m_p<BASE, FEAT, LOGCAT>::write_cause(unsigned addr, reg_t val) {
|
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)) | (this->mcause_max_irq - 1));
|
||||||
auto mask = ((1UL << (traits<BASE>::XLEN - 1)) | (mcause_max_irq - 1) | (0xfUL << 16));
|
this->csr[addr] = (val & mask) | (this->csr[addr] & ~mask);
|
||||||
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);
|
|
||||||
}
|
|
||||||
return iss::Ok;
|
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) {
|
template <typename BASE, features_e FEAT, typename LOGCAT> inline void riscv_hart_m_p<BASE, FEAT, LOGCAT>::reset(uint64_t address) {
|
||||||
BASE::reset(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() {
|
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.
|
// any synchronous traps.
|
||||||
auto ena_irq = this->csr[mip] & this->csr[mie];
|
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 m_enabled = this->reg.PRIV < PRIV_M || mstatus_mie;
|
||||||
auto enabled_interrupts = m_enabled ? ena_irq : 0;
|
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
|
// the trap; and the x IE field of mstatus
|
||||||
// is cleared
|
// is cleared
|
||||||
// store the actual privilege level in yPP and store interrupt enable flags
|
// store the actual privilege level in yPP and store interrupt enable flags
|
||||||
state.mstatus.MPP = PRIV_M;
|
this->state.mstatus.MPP = PRIV_M;
|
||||||
state.mstatus.MPIE = state.mstatus.MIE;
|
this->state.mstatus.MPIE = this->state.mstatus.MIE;
|
||||||
state.mstatus.MIE = false;
|
this->state.mstatus.MIE = false;
|
||||||
|
|
||||||
// get trap vector
|
// get trap vector
|
||||||
auto xtvec = this->csr[mtvec];
|
auto xtvec = this->csr[mtvec];
|
||||||
// calculate adds// set NEXT_PC to trap addressess to jump to based on MODE
|
// 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;
|
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)
|
if(ret == iss::Err)
|
||||||
return this->reg.PC;
|
return this->reg.PC;
|
||||||
this->reg.NEXT_PC = data;
|
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) {
|
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;
|
this->state.mstatus.MIE = this->state.mstatus.MPIE;
|
||||||
state.mstatus.MPIE = 1;
|
this->state.mstatus.MPIE = 1;
|
||||||
// sets the pc to the value stored in the x epc register.
|
// sets the pc to the value stored in the x epc register.
|
||||||
this->reg.NEXT_PC = this->csr[mepc] & this->get_pc_mask();
|
this->reg.NEXT_PC = this->csr[mepc] & this->get_pc_mask();
|
||||||
NSCLOG(INFO, LOGCAT) << "Executing xRET";
|
NSCLOG(INFO, LOGCAT) << "Executing xRET";
|
||||||
|
@ -35,7 +35,6 @@
|
|||||||
#ifndef _RISCV_HART_MSU_VP_H
|
#ifndef _RISCV_HART_MSU_VP_H
|
||||||
#define _RISCV_HART_MSU_VP_H
|
#define _RISCV_HART_MSU_VP_H
|
||||||
|
|
||||||
#include "iss/instrumentation_if.h"
|
|
||||||
#include "iss/vm_if.h"
|
#include "iss/vm_if.h"
|
||||||
#include "iss/vm_types.h"
|
#include "iss/vm_types.h"
|
||||||
#include "riscv_hart_common.h"
|
#include "riscv_hart_common.h"
|
||||||
@ -44,16 +43,12 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <elfio/elf_types.hpp>
|
#include <elfio/elf_types.hpp>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <stdexcept>
|
|
||||||
#ifndef FMT_HEADER_ONLY
|
#ifndef FMT_HEADER_ONLY
|
||||||
#define FMT_HEADER_ONLY
|
#define FMT_HEADER_ONLY
|
||||||
#endif
|
#endif
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <elfio/elfio.hpp>
|
#include <elfio/elfio.hpp>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <functional>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <sstream>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <util/bit_field.h>
|
#include <util/bit_field.h>
|
||||||
@ -70,65 +65,12 @@ public:
|
|||||||
using reg_t = typename core::reg_t;
|
using reg_t = typename core::reg_t;
|
||||||
using addr_t = typename core::addr_t;
|
using addr_t = typename core::addr_t;
|
||||||
|
|
||||||
// primary template
|
static constexpr reg_t get_misa() {
|
||||||
template <class T, class Enable = void> struct hart_state {};
|
return (sizeof(reg_t) == 4 ? (1UL << 30) : (2ULL << 62)) | ISA_I | ISA_M | ISA_A | ISA_U | ISA_S | ISA_M;
|
||||||
// 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 constexpr reg_t get_mstatus_mask(unsigned priv_lvl) {
|
||||||
|
if(sizeof(reg_t) == 4) {
|
||||||
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) {
|
|
||||||
#if __cplusplus < 201402L
|
#if __cplusplus < 201402L
|
||||||
return priv_lvl == PRIV_U ? 0x80000011UL : priv_lvl == PRIV_S ? 0x800de133UL : 0x807ff9ddUL;
|
return priv_lvl == PRIV_U ? 0x80000011UL : priv_lvl == PRIV_S ? 0x800de133UL : 0x807ff9ddUL;
|
||||||
#else
|
#else
|
||||||
@ -141,130 +83,79 @@ public:
|
|||||||
return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011
|
return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
} else if(sizeof(reg_t) == 8) {
|
||||||
|
|
||||||
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;
|
|
||||||
switch(priv_lvl) {
|
switch(priv_lvl) {
|
||||||
case PRIV_U:
|
case PRIV_U:
|
||||||
ret = 0x8000000f00000011ULL;
|
return 0x8000000f00000011ULL; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011
|
||||||
break; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011
|
|
||||||
case PRIV_S:
|
case PRIV_S:
|
||||||
ret = 0x8000000f000de133ULL;
|
return 0x8000000f000de133ULL; // 0b1...0 0011 0000 0000 0000 1101 1110 0001 0011 0011
|
||||||
break; // 0b1...0 0011 0000 0000 0000 1101 1110 0001 0011 0011
|
|
||||||
default:
|
default:
|
||||||
ret = 0x8000000f007ff9ddULL;
|
return 0x8000000f007ff9ddULL; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011
|
||||||
break; // 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) {
|
template <typename T_ = reg_t, std::enable_if_t<std::is_same<T_, uint32_t>::value>* = nullptr>
|
||||||
if(state == PRIV_M)
|
void write_mstatus(T_ val, unsigned priv_lvl) {
|
||||||
return {0, 0, 0, 0};
|
reg_t old_val = mstatus;
|
||||||
if(state <= PRIV_S)
|
auto mask = get_mstatus_mask(priv_lvl);
|
||||||
switch(bit_sub<60, 4>(sptbr)) {
|
auto new_val = (old_val & ~mask) | (val & mask);
|
||||||
case 0:
|
this->state.mstatus = new_val;
|
||||||
return {0, 0, 0, 0}; // off
|
}
|
||||||
case 8:
|
|
||||||
return {3, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV39
|
template <typename T_ = reg_t, std::enable_if_t<std::is_same<T_, uint64_t>::value>* = nullptr>
|
||||||
case 9:
|
void write_mstatus(T_ val, unsigned priv_lvl) {
|
||||||
return {4, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV48
|
reg_t old_val = mstatus;
|
||||||
case 10:
|
auto mask = get_mstatus_mask(priv_lvl);
|
||||||
return {5, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV57
|
auto new_val = (old_val & ~mask) | (val & mask);
|
||||||
case 11:
|
if((new_val & this->state.mstatus.SXL.Mask) == 0) {
|
||||||
return {6, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT}; // SV64
|
new_val |= old_val & this->state.mstatus.SXL.Mask;
|
||||||
default:
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
abort();
|
|
||||||
return {0, 0, 0, 0}; // dummy
|
|
||||||
}
|
}
|
||||||
};
|
if((new_val & this->state.mstatus.UXL.Mask) == 0) {
|
||||||
using hart_state_type = hart_state<reg_t>;
|
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 PGSIZE = 1 << PGSHIFT;
|
||||||
const typename core::reg_t PGMASK = PGSIZE - 1;
|
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 read_mem(phys_addr_t addr, unsigned length, uint8_t* const data);
|
||||||
virtual iss::status write_mem(phys_addr_t addr, unsigned length, const uint8_t* const data);
|
virtual iss::status 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>;
|
using mem_type = util::sparse_array<uint8_t, 1ULL << 32>;
|
||||||
mem_type mem;
|
mem_type mem;
|
||||||
@ -415,7 +306,7 @@ iss::status riscv_hart_msu_vp<BASE>::read(const address_type type, const access_
|
|||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
if(unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary
|
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
|
if(vm.levels != 0) { // VM is active
|
||||||
auto split_addr = (addr + length) & ~PGMASK;
|
auto split_addr = (addr + length) & ~PGMASK;
|
||||||
auto len1 = split_addr - addr;
|
auto len1 = split_addr - addr;
|
||||||
@ -521,7 +412,7 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access
|
|||||||
try {
|
try {
|
||||||
// TODO: There is no check for alignment
|
// TODO: There is no check for alignment
|
||||||
if(unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary
|
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
|
if(vm.levels != 0) { // VM is active
|
||||||
auto split_addr = (addr + length) & ~PGMASK;
|
auto split_addr = (addr + length) & ~PGMASK;
|
||||||
auto len1 = split_addr - addr;
|
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) {
|
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_status(unsigned addr, reg_t& val) {
|
||||||
auto req_priv_lvl = (addr >> 8) & 0x3;
|
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;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_status(unsigned addr, reg_t val) {
|
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_status(unsigned addr, reg_t val) {
|
||||||
auto req_priv_lvl = (addr >> 8) & 0x3;
|
auto req_priv_lvl = (addr >> 8) & 0x3;
|
||||||
state.write_mstatus(val, req_priv_lvl);
|
write_mstatus(val, req_priv_lvl);
|
||||||
check_interrupt();
|
check_interrupt();
|
||||||
update_vm_info();
|
update_vm_info();
|
||||||
return iss::Ok;
|
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;
|
this->fault_data = this->reg.PC;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
val = state.satp;
|
val = satp;
|
||||||
return iss::Ok;
|
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;
|
this->fault_data = this->reg.PC;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
state.satp = val;
|
satp = val;
|
||||||
update_vm_info();
|
update_vm_info();
|
||||||
return iss::Ok;
|
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) {
|
template <typename BASE> inline void riscv_hart_msu_vp<BASE>::reset(uint64_t address) {
|
||||||
BASE::reset(address);
|
BASE::reset(address);
|
||||||
state.mstatus = hart_state_type::mstatus_reset_val;
|
state.mstatus = hart_state<reg_t>::mstatus_reset_val;
|
||||||
update_vm_info();
|
update_vm_info();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE> inline void riscv_hart_msu_vp<BASE>::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;
|
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)
|
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
|
else
|
||||||
this->vm[0] = this->vm[1];
|
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;
|
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
|
#ifndef _RISCV_HART_MU_P_H
|
||||||
#define _RISCV_HART_MU_P_H
|
#define _RISCV_HART_MU_P_H
|
||||||
|
|
||||||
#include "iss/instrumentation_if.h"
|
|
||||||
#include "iss/vm_if.h"
|
#include "iss/vm_if.h"
|
||||||
#include "iss/vm_types.h"
|
#include "iss/vm_types.h"
|
||||||
#include "riscv_hart_common.h"
|
#include "riscv_hart_common.h"
|
||||||
@ -43,20 +42,14 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <elfio/elf_types.hpp>
|
#include <elfio/elf_types.hpp>
|
||||||
#include <limits>
|
|
||||||
#include <stdexcept>
|
|
||||||
#ifndef FMT_HEADER_ONLY
|
#ifndef FMT_HEADER_ONLY
|
||||||
#define FMT_HEADER_ONLY
|
#define FMT_HEADER_ONLY
|
||||||
#endif
|
#endif
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <elfio/elfio.hpp>
|
#include <elfio/elfio.hpp>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <functional>
|
#include <iss/mmio/memory_with_htif.h>
|
||||||
#include <iomanip>
|
|
||||||
#include <sstream>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <util/bit_field.h>
|
|
||||||
|
|
||||||
namespace iss {
|
namespace iss {
|
||||||
namespace arch {
|
namespace arch {
|
||||||
@ -71,67 +64,14 @@ public:
|
|||||||
using reg_t = typename core::reg_t;
|
using reg_t = typename core::reg_t;
|
||||||
using addr_t = typename core::addr_t;
|
using addr_t = typename core::addr_t;
|
||||||
|
|
||||||
// primary template
|
static constexpr reg_t get_mstatus_mask(unsigned priv_lvl) {
|
||||||
template <class T, class Enable = void> struct hart_state {};
|
if(sizeof(reg_t) == 4) {
|
||||||
// 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) {
|
|
||||||
#if __cplusplus < 201402L
|
#if __cplusplus < 201402L
|
||||||
return priv_lvl == PRIV_U ? 0x80000011UL : priv_lvl == PRIV_S ? 0x800de133UL : 0x807ff9ddUL;
|
return priv_lvl == PRIV_U ? 0x80000011UL : priv_lvl == PRIV_S ? 0x800de133UL : 0x807ff9ddUL;
|
||||||
#else
|
#else
|
||||||
switch(priv_lvl) {
|
switch(priv_lvl) {
|
||||||
case PRIV_U:
|
case PRIV_U:
|
||||||
return 0x00000011UL; // 0b1000 0000 0000 0000 0000 0000 0001 0001
|
return 0x00000011UL; // 0b1...0 0001 0001
|
||||||
default:
|
default:
|
||||||
// +-SD
|
// +-SD
|
||||||
// | +-TSR
|
// | +-TSR
|
||||||
@ -151,72 +91,13 @@ public:
|
|||||||
return 0b00000000000000000001100010011001;
|
return 0b00000000000000000001100010011001;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
} else if(sizeof(reg_t) == 8) {
|
||||||
};
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
#if __cplusplus < 201402L
|
#if __cplusplus < 201402L
|
||||||
return priv_lvl == PRIV_U ? 0x011ULL : priv_lvl == PRIV_S ? 0x000de133ULL : 0x007ff9ddULL;
|
return priv_lvl == PRIV_U ? 0x011ULL : priv_lvl == PRIV_S ? 0x000de133ULL : 0x007ff9ddULL;
|
||||||
#else
|
#else
|
||||||
switch(priv_lvl) {
|
switch(priv_lvl) {
|
||||||
case PRIV_U:
|
case PRIV_U:
|
||||||
return 0x00000011UL; // 0b1000 0000 0000 0000 0000 0000 0001 0001
|
return 0x00000011UL; // 0b1...0 0001 0001
|
||||||
default:
|
default:
|
||||||
// +-SD
|
// +-SD
|
||||||
// | +-TSR
|
// | +-TSR
|
||||||
@ -236,10 +117,15 @@ public:
|
|||||||
return 0b00000000000000000001100010011001;
|
return 0b00000000000000000001100010011001;
|
||||||
}
|
}
|
||||||
#endif
|
#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) {
|
constexpr reg_t get_irq_mask(size_t mode) {
|
||||||
std::array<const reg_t, 4> m = {{
|
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_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:
|
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_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);
|
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<reg_t, uint64_t> ptw;
|
||||||
std::unordered_map<uint64_t, uint8_t> atomic_reservation;
|
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 read_status(unsigned addr, reg_t& val);
|
||||||
iss::status write_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 read_ie(unsigned addr, reg_t& val);
|
||||||
iss::status write_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_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_xtvt(unsigned addr, reg_t val);
|
||||||
iss::status write_dcsr(unsigned addr, reg_t val);
|
iss::status write_dcsr(unsigned addr, reg_t val);
|
||||||
iss::status read_debug(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_dpc(unsigned addr, reg_t val);
|
||||||
iss::status write_ideleg(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_edeleg(unsigned addr, reg_t val);
|
||||||
iss::status write_pmpcfg(unsigned addr, reg_t val);
|
|
||||||
|
|
||||||
void check_interrupt();
|
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;
|
feature_config cfg;
|
||||||
unsigned mcause_max_irq{(FEAT & features_e::FEAT_CLIC) ? std::max(16U, static_cast<unsigned>(traits<BASE>::CLIC_NUM_IRQ)) : 16U};
|
mmio::memory_with_htif<reg_t> default_mem;
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||||
riscv_hart_mu_p<BASE, FEAT, LOGCAT>::riscv_hart_mu_p(feature_config cfg)
|
riscv_hart_mu_p<BASE, FEAT, LOGCAT>::riscv_hart_mu_p(feature_config cfg)
|
||||||
: state()
|
: state()
|
||||||
, cfg(cfg) {
|
, cfg(cfg)
|
||||||
this->rd_func = util::delegate<arch_if::rd_func_sig>::from<this_class, &this_class::read>(this);
|
, default_mem(base::get_priv_if()) {
|
||||||
this->rd_func = util::delegate<arch_if::wr_func_sig>::from<this_class, &this_class::write>(this);
|
|
||||||
|
|
||||||
const std::array<unsigned, 8> rwaddrs{{
|
const std::array<unsigned, 8> rwaddrs{{
|
||||||
mepc,
|
mepc,
|
||||||
mtvec,
|
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[marchid] = MK_CSR_WR_CB(write_null);
|
||||||
this->csr_wr_cb[mimpid] = 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) {
|
if(FEAT & FEAT_EXT_N) {
|
||||||
this->csr_rd_cb[mideleg] = MK_CSR_RD_CB(read_plain);
|
this->csr_rd_cb[mideleg] = MK_CSR_RD_CB(read_plain);
|
||||||
this->csr_wr_cb[mideleg] = MK_CSR_WR_CB(write_ideleg);
|
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_wr_cb[ucause] = MK_CSR_WR_CB(write_cause);
|
||||||
this->csr_rd_cb[utvec] = MK_CSR_RD_CB(read_tvec);
|
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) {
|
if(FEAT & FEAT_DEBUG) {
|
||||||
this->csr_wr_cb[dscratch0] = MK_CSR_WR_CB(write_dscratch);
|
this->csr_wr_cb[dscratch0] = MK_CSR_WR_CB(write_dscratch);
|
||||||
this->csr_rd_cb[dscratch0] = MK_CSR_RD_CB(read_debug);
|
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_wr_cb[dcsr] = MK_CSR_WR_CB(write_dcsr);
|
||||||
this->csr_rd_cb[dcsr] = MK_CSR_RD_CB(read_debug);
|
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); };
|
this->rd_func = util::delegate<arch_if::rd_func_sig>::from<this_class, &this_class::read>(this);
|
||||||
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); };
|
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>
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
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 {
|
try {
|
||||||
switch(space) {
|
switch(space) {
|
||||||
case traits<BASE>::MEM: {
|
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));
|
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)))) {
|
if(unlikely(is_fetch(access) && (addr & (alignment - 1)))) {
|
||||||
this->fault_data = addr;
|
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;
|
this->fault_data = addr;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
phys_addr_t phys_addr{access, space, addr};
|
auto res = this->memory.rd_mem(access, addr, length, data);
|
||||||
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);
|
|
||||||
}
|
|
||||||
if(unlikely(res != iss::Ok && (access & access_type::DEBUG) == 0)) {
|
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->reg.trap_state = (1UL << 31) | (5 << 16); // issue trap 5 (load access fault
|
||||||
this->fault_data = addr;
|
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));
|
return this->read_csr(addr, *reinterpret_cast<reg_t* const>(data));
|
||||||
} break;
|
} break;
|
||||||
case traits<BASE>::FENCE: {
|
case traits<BASE>::FENCE: {
|
||||||
if((addr + length) > mem.size())
|
|
||||||
return iss::Err;
|
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
} break;
|
} break;
|
||||||
case traits<BASE>::RES: {
|
case traits<BASE>::RES: {
|
||||||
@ -692,15 +353,6 @@ iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::write(const address_type type,
|
|||||||
try {
|
try {
|
||||||
switch(space) {
|
switch(space) {
|
||||||
case traits<BASE>::MEM: {
|
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)) {
|
if(unlikely(is_fetch(access) && (addr & 0x1) == 1)) {
|
||||||
this->fault_data = addr;
|
this->fault_data = addr;
|
||||||
if(access && iss::access_type::DEBUG)
|
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;
|
this->fault_data = addr;
|
||||||
return iss::Err;
|
return iss::Err;
|
||||||
}
|
}
|
||||||
phys_addr_t phys_addr{access, space, addr};
|
auto res = this->memory.wr_mem(access, addr, length, data);
|
||||||
auto res = iss::Err;
|
if(unlikely(res != iss::Ok && !is_debug(access))) {
|
||||||
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)) {
|
|
||||||
this->reg.trap_state = (1UL << 31) | (7UL << 16); // issue trap 7 (Store/AMO access fault)
|
this->reg.trap_state = (1UL << 31) | (7UL << 16); // issue trap 7 (Store/AMO access fault)
|
||||||
this->fault_data = addr;
|
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));
|
return this->write_csr(addr, *reinterpret_cast<const reg_t*>(data));
|
||||||
} break;
|
} break;
|
||||||
case traits<BASE>::FENCE: {
|
case traits<BASE>::FENCE: {
|
||||||
if((addr + length) > mem.size())
|
|
||||||
return iss::Err;
|
|
||||||
switch(addr) {
|
switch(addr) {
|
||||||
case 2:
|
case 2:
|
||||||
case 3: {
|
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>
|
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||||
iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::read_status(unsigned addr, reg_t& val) {
|
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;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||||
iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::write_status(unsigned addr, reg_t val) {
|
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();
|
check_interrupt();
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||||
iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::read_cause(unsigned addr, reg_t& val) {
|
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)) | (this->mcause_max_irq - 1));
|
||||||
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));
|
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||||
iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::write_cause(unsigned addr, reg_t val) {
|
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)) | (this->mcause_max_irq - 1));
|
||||||
auto mask = ((1UL << (traits<BASE>::XLEN - 1)) | (mcause_max_irq - 1) | (0xfUL << 16));
|
this->csr[addr] = (val & mask) | (this->csr[addr] & ~mask);
|
||||||
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);
|
|
||||||
}
|
|
||||||
return iss::Ok;
|
return iss::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -872,126 +476,9 @@ iss::status riscv_hart_mu_p<BASE, FEAT, LOGCAT>::write_edeleg(unsigned addr, reg
|
|||||||
return iss::Ok;
|
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) {
|
template <typename BASE, features_e FEAT, typename LOGCAT> inline void riscv_hart_mu_p<BASE, FEAT, LOGCAT>::reset(uint64_t address) {
|
||||||
BASE::reset(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() {
|
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);
|
phys_addr_t p_addr(access_type::DEBUG_READ, traits<BASE>::MEM, addr - 4);
|
||||||
std::array<uint8_t, 8> data;
|
std::array<uint8_t, 8> data;
|
||||||
// check for SLLI_X0_X0_0X1F and SRAI_X0_X0_0X07
|
// check for SLLI_X0_X0_0X1F and SRAI_X0_X0_0X07
|
||||||
this->read_mem(p_addr, 4, data.data());
|
this->memory.rd_mem(iss::access_type::DEBUG_READ, addr - 4, 4, data.data());
|
||||||
p_addr.val += 8;
|
addr += 8;
|
||||||
this->read_mem(p_addr, 4, data.data() + 4);
|
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};
|
const std::array<uint8_t, 8> ref_data = {0x13, 0x10, 0xf0, 0x01, 0x13, 0x50, 0x70, 0x40};
|
||||||
if(data == ref_data) {
|
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)];
|
auto xtvec = this->csr[utvec | (new_priv << 8)];
|
||||||
// calculate addr// set NEXT_PC to trap addressess to jump to based on MODE
|
// calculate addr// set NEXT_PC to trap addressess to jump to based on MODE
|
||||||
// bits in mtvec
|
// 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;
|
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 = read(address_type::LOGICAL, access_type::READ, 0, this->csr[mtvt], sizeof(reg_t), reinterpret_cast<uint8_t*>(&data));
|
||||||
if(ret == iss::Err)
|
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"
|
#include "memory_if.h"
|
||||||
|
|
||||||
namespace iss {
|
namespace iss {
|
||||||
namespace memory {
|
namespace mmio {
|
||||||
void memory_hierarchy::prepend(memory_elem& e) {
|
void memory_hierarchy::prepend(memory_elem& e) {
|
||||||
hierarchy.push_front(e);
|
hierarchy.push_front(e);
|
||||||
update_chain();
|
update_chain();
|
||||||
@ -15,11 +15,12 @@ void memory_hierarchy::insert_after(memory_elem&) {}
|
|||||||
void memory_hierarchy::replace_last(memory_elem&) {}
|
void memory_hierarchy::replace_last(memory_elem&) {}
|
||||||
void memory_hierarchy::update_chain() {
|
void memory_hierarchy::update_chain() {
|
||||||
bool tail = false;
|
bool tail = false;
|
||||||
if(hierarchy.size() > 1)
|
for(size_t i = 0; i < hierarchy.size(); ++i) {
|
||||||
for(size_t i = 1; i < hierarchy.size(); ++i) {
|
hierarchy[i].get().register_csrs();
|
||||||
|
if(i)
|
||||||
hierarchy[i - 1].get().set_next(hierarchy[i].get().get_mem_if());
|
hierarchy[i - 1].get().set_next(hierarchy[i].get().get_mem_if());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace memory
|
} // namespace mmio
|
||||||
} // namespace iss
|
} // namespace iss
|
@ -38,11 +38,11 @@
|
|||||||
#include "iss/vm_types.h"
|
#include "iss/vm_types.h"
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <limits>
|
||||||
#include <util/delegate.h>
|
#include <util/delegate.h>
|
||||||
|
|
||||||
namespace iss {
|
namespace iss {
|
||||||
namespace memory {
|
namespace mmio {
|
||||||
|
|
||||||
using rd_mem_func_sig = iss::status(iss::access_type, uint64_t, unsigned, uint8_t*);
|
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*);
|
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 {
|
struct memory_elem {
|
||||||
virtual memory_if get_mem_if() = 0;
|
virtual memory_if get_mem_if() = 0;
|
||||||
virtual void set_next(memory_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 {
|
struct memory_hierarchy {
|
||||||
@ -69,6 +71,6 @@ protected:
|
|||||||
std::deque<std::reference_wrapper<memory_elem>> hierarchy;
|
std::deque<std::reference_wrapper<memory_elem>> hierarchy;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace memory
|
} // namespace mmio
|
||||||
} // namespace iss
|
} // namespace iss
|
||||||
#endif
|
#endif
|
@ -1,3 +1,5 @@
|
|||||||
|
#ifndef _MEMORY_WITH_HTIF_
|
||||||
|
#define _MEMORY_WITH_HTIF_
|
||||||
|
|
||||||
#include "iss/arch/riscv_hart_common.h"
|
#include "iss/arch/riscv_hart_common.h"
|
||||||
#include "iss/vm_types.h"
|
#include "iss/vm_types.h"
|
||||||
@ -6,15 +8,15 @@
|
|||||||
#include <util/sparse_array.h>
|
#include <util/sparse_array.h>
|
||||||
|
|
||||||
namespace iss {
|
namespace iss {
|
||||||
namespace memory {
|
namespace mmio {
|
||||||
template <typename WORD_TYPE> struct functional_memory : public memory_elem {
|
template <typename WORD_TYPE> struct memory_with_htif : public memory_elem {
|
||||||
using this_class = functional_memory<WORD_TYPE>;
|
using this_class = memory_with_htif<WORD_TYPE>;
|
||||||
constexpr static unsigned WORD_LEN = sizeof(WORD_TYPE) * 8;
|
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) {}
|
: hart_if(hart_if) {}
|
||||||
|
|
||||||
~functional_memory() = default;
|
~memory_with_htif() = default;
|
||||||
|
|
||||||
memory_if get_mem_if() override {
|
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)},
|
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;
|
mem_type mem;
|
||||||
arch::priv_if<WORD_TYPE> hart_if;
|
arch::priv_if<WORD_TYPE> hart_if;
|
||||||
};
|
};
|
||||||
} // namespace memory
|
} // namespace mmio
|
||||||
} // namespace iss
|
} // 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