make cpu type in core_complex configurable

This commit is contained in:
Eyck Jentzsch 2021-05-16 15:06:42 +02:00
parent 9c456ba8f2
commit a35974c9f5
7 changed files with 1746 additions and 543 deletions

View File

@ -67,7 +67,15 @@ set_target_properties(${PROJECT_NAME} PROPERTIES
if(SystemC_FOUND) if(SystemC_FOUND)
add_library(${PROJECT_NAME}_sc src/sysc/core_complex.cpp) add_library(${PROJECT_NAME}_sc src/sysc/core_complex.cpp)
target_compile_definitions(${PROJECT_NAME}_sc PUBLIC WITH_SYSTEMC) target_compile_definitions(${PROJECT_NAME}_sc PUBLIC WITH_SYSTEMC)
target_compile_definitions(${PROJECT_NAME}_sc PRIVATE CORE_${CORE_NAME}) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/incl/iss/arch/tgc_b.h)
target_compile_definitions(${PROJECT_NAME}_sc PRIVATE CORE_TGC_B)
endif()
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/incl/iss/arch/tgc_c.h)
target_compile_definitions(${PROJECT_NAME}_sc PRIVATE CORE_TGC_C)
endif()
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/incl/iss/arch/tgc_d.h)
target_compile_definitions(${PROJECT_NAME}_sc PRIVATE CORE_TGC_D)
endif()
target_include_directories(${PROJECT_NAME}_sc PUBLIC ../incl ${SystemC_INCLUDE_DIRS} ${CCI_INCLUDE_DIRS}) target_include_directories(${PROJECT_NAME}_sc PUBLIC ../incl ${SystemC_INCLUDE_DIRS} ${CCI_INCLUDE_DIRS})
if(SCV_FOUND) if(SCV_FOUND)
@ -110,17 +118,3 @@ install(TARGETS dbt-core-tgc tgc-sim
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} COMPONENT devel # headers for mac (note the different component -> different package) PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} COMPONENT devel # headers for mac (note the different component -> different package)
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # headers INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # headers
) )
#
# SYSTEM PACKAGING (RPM, TGZ, ...)
# _____________________________________________________________________________
#include(CPackConfig)
#
# CMAKE PACKAGING (for other CMake projects to use this one easily)
# _____________________________________________________________________________
#include(PackageConfigurator)

View File

@ -0,0 +1,235 @@
/*******************************************************************************
* Copyright (C) 2017, 2018, 2021 MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Contributors:
* eyck@minres.com - initial implementation
******************************************************************************/
#ifndef _RISCV_HART_COMMON
#define _RISCV_HART_COMMON
#include "iss/arch_if.h"
#include <cstdint>
namespace iss {
namespace arch {
enum { tohost_dflt = 0xF0001000, fromhost_dflt = 0xF0001040 };
enum riscv_csr {
/* user-level CSR */
// User Trap Setup
ustatus = 0x000,
uie = 0x004,
utvec = 0x005,
// User Trap Handling
uscratch = 0x040,
uepc = 0x041,
ucause = 0x042,
utval = 0x043,
uip = 0x044,
// User Floating-Point CSRs
fflags = 0x001,
frm = 0x002,
fcsr = 0x003,
// User Counter/Timers
cycle = 0xC00,
time = 0xC01,
instret = 0xC02,
hpmcounter3 = 0xC03,
hpmcounter4 = 0xC04,
/*...*/
hpmcounter31 = 0xC1F,
cycleh = 0xC80,
timeh = 0xC81,
instreth = 0xC82,
hpmcounter3h = 0xC83,
hpmcounter4h = 0xC84,
/*...*/
hpmcounter31h = 0xC9F,
/* supervisor-level CSR */
// Supervisor Trap Setup
sstatus = 0x100,
sedeleg = 0x102,
sideleg = 0x103,
sie = 0x104,
stvec = 0x105,
scounteren = 0x106,
// Supervisor Trap Handling
sscratch = 0x140,
sepc = 0x141,
scause = 0x142,
stval = 0x143,
sip = 0x144,
// Supervisor Protection and Translation
satp = 0x180,
/* machine-level CSR */
// Machine Information Registers
mvendorid = 0xF11,
marchid = 0xF12,
mimpid = 0xF13,
mhartid = 0xF14,
// Machine Trap Setup
mstatus = 0x300,
misa = 0x301,
medeleg = 0x302,
mideleg = 0x303,
mie = 0x304,
mtvec = 0x305,
mcounteren = 0x306,
// Machine Trap Handling
mscratch = 0x340,
mepc = 0x341,
mcause = 0x342,
mtval = 0x343,
mip = 0x344,
// Physical Memory Protection
pmpcfg0 = 0x3A0,
pmpcfg1 = 0x3A1,
pmpcfg2 = 0x3A2,
pmpcfg3 = 0x3A3,
pmpaddr0 = 0x3B0,
pmpaddr1 = 0x3B1,
pmpaddr2 = 0x3B2,
pmpaddr3 = 0x3B3,
pmpaddr4 = 0x3B4,
pmpaddr5 = 0x3B5,
pmpaddr6 = 0x3B6,
pmpaddr7 = 0x3B7,
pmpaddr8 = 0x3B8,
pmpaddr9 = 0x3B9,
pmpaddr10 = 0x3BA,
pmpaddr11 = 0x3BB,
pmpaddr12 = 0x3BC,
pmpaddr13 = 0x3BD,
pmpaddr14 = 0x3BE,
pmpaddr15 = 0x3BF,
// Machine Counter/Timers
mcycle = 0xB00,
minstret = 0xB02,
mhpmcounter3 = 0xB03,
mhpmcounter4 = 0xB04,
/*...*/
mhpmcounter31 = 0xB1F,
mcycleh = 0xB80,
minstreth = 0xB82,
mhpmcounter3h = 0xB83,
mhpmcounter4h = 0xB84,
/*...*/
mhpmcounter31h = 0xB9F,
// Machine Counter Setup
mhpmevent3 = 0x323,
mhpmevent4 = 0x324,
/*...*/
mhpmevent31 = 0x33F,
// Debug/Trace Registers (shared with Debug Mode)
tselect = 0x7A0,
tdata1 = 0x7A1,
tdata2 = 0x7A2,
tdata3 = 0x7A3,
// Debug Mode Registers
dcsr = 0x7B0,
dpc = 0x7B1,
dscratch = 0x7B2
};
enum {
PGSHIFT = 12,
PTE_PPN_SHIFT = 10,
// page table entry (PTE) fields
PTE_V = 0x001, // Valid
PTE_R = 0x002, // Read
PTE_W = 0x004, // Write
PTE_X = 0x008, // Execute
PTE_U = 0x010, // User
PTE_G = 0x020, // Global
PTE_A = 0x040, // Accessed
PTE_D = 0x080, // Dirty
PTE_SOFT = 0x300 // Reserved for Software
};
template <typename T> inline bool PTE_TABLE(T PTE) { return (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V); }
enum { PRIV_U = 0, PRIV_S = 1, PRIV_M = 3 };
enum {
ISA_A = 1,
ISA_B = 1 << 1,
ISA_C = 1 << 2,
ISA_D = 1 << 3,
ISA_E = 1 << 4,
ISA_F = 1 << 5,
ISA_G = 1 << 6,
ISA_I = 1 << 8,
ISA_M = 1 << 12,
ISA_N = 1 << 13,
ISA_Q = 1 << 16,
ISA_S = 1 << 18,
ISA_U = 1 << 20
};
struct vm_info {
int levels;
int idxbits;
int ptesize;
uint64_t ptbase;
bool is_active() { return levels; }
};
class trap_load_access_fault : public trap_access {
public:
trap_load_access_fault(uint64_t badaddr)
: trap_access(5 << 16, badaddr) {}
};
class illegal_instruction_fault : public trap_access {
public:
illegal_instruction_fault(uint64_t badaddr)
: trap_access(2 << 16, badaddr) {}
};
class trap_instruction_page_fault : public trap_access {
public:
trap_instruction_page_fault(uint64_t badaddr)
: trap_access(12 << 16, badaddr) {}
};
class trap_load_page_fault : public trap_access {
public:
trap_load_page_fault(uint64_t badaddr)
: trap_access(13 << 16, badaddr) {}
};
class trap_store_page_fault : public trap_access {
public:
trap_store_page_fault(uint64_t badaddr)
: trap_access(15 << 16, badaddr) {}
};
}
}
#endif

View File

@ -1,5 +1,5 @@
/******************************************************************************* /*******************************************************************************
* Copyright (C) 2017, 2018, MINRES Technologies GmbH * Copyright (C) 2021, MINRES Technologies GmbH
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -32,11 +32,11 @@
* eyck@minres.com - initial implementation * eyck@minres.com - initial implementation
******************************************************************************/ ******************************************************************************/
#ifndef _RISCV_CORE_H_ #ifndef _RISCV_HART_M_P_H
#define _RISCV_CORE_H_ #define _RISCV_HART_M_P_H
#include "riscv_hart_common.h"
#include "iss/arch/traits.h" #include "iss/arch/traits.h"
#include "iss/arch_if.h"
#include "iss/instrumentation_if.h" #include "iss/instrumentation_if.h"
#include "iss/log_categories.h" #include "iss/log_categories.h"
#include "iss/vm_if.h" #include "iss/vm_if.h"
@ -66,116 +66,10 @@
namespace iss { namespace iss {
namespace arch { namespace arch {
enum { tohost_dflt = 0xF0001000, fromhost_dflt = 0xF0001040 }; template <typename BASE> class riscv_hart_m_p : public BASE {
protected:
enum riscv_csr { const std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
/* user-level CSR */ const std::array<const char *, 16> trap_str = {{""
// User Trap Setup
ustatus = 0x000,
uie = 0x004,
utvec = 0x005,
// User Trap Handling
uscratch = 0x040,
uepc = 0x041,
ucause = 0x042,
utval = 0x043,
uip = 0x044,
// User Floating-Point CSRs
fflags = 0x001,
frm = 0x002,
fcsr = 0x003,
// User Counter/Timers
cycle = 0xC00,
time = 0xC01,
instret = 0xC02,
hpmcounter3 = 0xC03,
hpmcounter4 = 0xC04,
/*...*/
hpmcounter31 = 0xC1F,
cycleh = 0xC80,
timeh = 0xC81,
instreth = 0xC82,
hpmcounter3h = 0xC83,
hpmcounter4h = 0xC84,
/*...*/
hpmcounter31h = 0xC9F,
/* supervisor-level CSR */
// Supervisor Trap Setup
sstatus = 0x100,
sedeleg = 0x102,
sideleg = 0x103,
sie = 0x104,
stvec = 0x105,
scounteren = 0x106,
// Supervisor Trap Handling
sscratch = 0x140,
sepc = 0x141,
scause = 0x142,
stval = 0x143,
sip = 0x144,
// Supervisor Protection and Translation
satp = 0x180,
/* machine-level CSR */
// Machine Information Registers
mvendorid = 0xF11,
marchid = 0xF12,
mimpid = 0xF13,
mhartid = 0xF14,
// Machine Trap Setup
mstatus = 0x300,
misa = 0x301,
medeleg = 0x302,
mideleg = 0x303,
mie = 0x304,
mtvec = 0x305,
mcounteren = 0x306,
// Machine Trap Handling
mscratch = 0x340,
mepc = 0x341,
mcause = 0x342,
mtval = 0x343,
mip = 0x344,
// Machine Protection and Translation
pmpcfg0 = 0x3A0,
pmpcfg1 = 0x3A1,
pmpcfg2 = 0x3A2,
pmpcfg3 = 0x3A3,
pmpaddr0 = 0x3B0,
pmpaddr1 = 0x3B1,
/*...*/
pmpaddr15 = 0x3BF,
// Machine Counter/Timers
mcycle = 0xB00,
minstret = 0xB02,
mhpmcounter3 = 0xB03,
mhpmcounter4 = 0xB04,
/*...*/
mhpmcounter31 = 0xB1F,
mcycleh = 0xB80,
minstreth = 0xB82,
mhpmcounter3h = 0xB83,
mhpmcounter4h = 0xB84,
/*...*/
mhpmcounter31h = 0xB9F,
// Machine Counter Setup
mhpmevent3 = 0x323,
mhpmevent4 = 0x324,
/*...*/
mhpmevent31 = 0x33F,
// Debug/Trace Registers (shared with Debug Mode)
tselect = 0x7A0,
tdata1 = 0x7A1,
tdata2 = 0x7A2,
tdata3 = 0x7A3,
// Debug Mode Registers
dcsr = 0x7B0,
dpc = 0x7B1,
dscratch = 0x7B2
};
namespace {
std::array<const char *, 16> trap_str = {{""
"Instruction address misaligned", // 0 "Instruction address misaligned", // 0
"Instruction access fault", // 1 "Instruction access fault", // 1
"Illegal instruction", // 2 "Illegal instruction", // 2
@ -192,59 +86,10 @@ std::array<const char *, 16> trap_str = {{""
"Load page fault", // d "Load page fault", // d
"Reserved", // e "Reserved", // e
"Store/AMO page fault"}}; "Store/AMO page fault"}};
std::array<const char *, 12> irq_str = { const std::array<const char *, 12> irq_str = {
{"User software interrupt", "Supervisor software interrupt", "Reserved", "Machine software interrupt", {"User software interrupt", "Supervisor software interrupt", "Reserved", "Machine software interrupt",
"User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt", "User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt",
"User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt"}}; "User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt"}};
enum {
PGSHIFT = 12,
PTE_PPN_SHIFT = 10,
// page table entry (PTE) fields
PTE_V = 0x001, // Valid
PTE_R = 0x002, // Read
PTE_W = 0x004, // Write
PTE_X = 0x008, // Execute
PTE_U = 0x010, // User
PTE_G = 0x020, // Global
PTE_A = 0x040, // Accessed
PTE_D = 0x080, // Dirty
PTE_SOFT = 0x300 // Reserved for Software
};
template <typename T> inline bool PTE_TABLE(T PTE) { return (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V); }
enum { PRIV_U = 0, PRIV_S = 1, PRIV_M = 3 };
enum {
ISA_A = 1,
ISA_B = 1 << 1,
ISA_C = 1 << 2,
ISA_D = 1 << 3,
ISA_E = 1 << 4,
ISA_F = 1 << 5,
ISA_G = 1 << 6,
ISA_I = 1 << 8,
ISA_M = 1 << 12,
ISA_N = 1 << 13,
ISA_Q = 1 << 16,
ISA_S = 1 << 18,
ISA_U = 1 << 20
};
class trap_load_access_fault : public trap_access {
public:
trap_load_access_fault(uint64_t badaddr)
: trap_access(5 << 16, badaddr) {}
};
class illegal_instruction_fault : public trap_access {
public:
illegal_instruction_fault(uint64_t badaddr)
: trap_access(2 << 16, badaddr) {}
};
} // namespace
template <typename BASE> class riscv_hart_m_p : public BASE {
public: public:
using super = BASE; using super = BASE;
using this_class = riscv_hart_m_p<BASE>; using this_class = riscv_hart_m_p<BASE>;
@ -313,6 +158,7 @@ public:
return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011 // only machine mode is supported return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011 // only machine mode is supported
} }
}; };
using hart_state_type = hart_state<reg_t>;
constexpr reg_t get_irq_mask() { constexpr reg_t get_irq_mask() {
return 0b101110111011; // only machine mode is supported return 0b101110111011; // only machine mode is supported
@ -387,7 +233,7 @@ protected:
virtual iss::status read_csr(unsigned addr, reg_t &val); virtual iss::status read_csr(unsigned addr, reg_t &val);
virtual iss::status write_csr(unsigned addr, reg_t val); virtual iss::status write_csr(unsigned addr, reg_t val);
hart_state<reg_t> state; hart_state_type state;
uint64_t cycle_offset; uint64_t cycle_offset;
reg_t fault_data; reg_t fault_data;
uint64_t tohost = tohost_dflt; uint64_t tohost = tohost_dflt;
@ -729,7 +575,7 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_time(unsigned ad
} }
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_status(unsigned addr, reg_t &val) { template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_status(unsigned addr, reg_t &val) {
val = state.mstatus & hart_state<reg_t>::get_mask(); val = state.mstatus & hart_state_type::get_mask();
return iss::Ok; return iss::Ok;
} }
@ -876,7 +722,7 @@ iss::status riscv_hart_m_p<BASE>::write_mem(phys_addr_t paddr, unsigned length,
template <typename BASE> inline void riscv_hart_m_p<BASE>::reset(uint64_t address) { template <typename BASE> inline void riscv_hart_m_p<BASE>::reset(uint64_t address) {
BASE::reset(address); BASE::reset(address);
state.mstatus = hart_state<reg_t>::mstatus_reset_val; state.mstatus = hart_state_type::mstatus_reset_val;
} }
template <typename BASE> void riscv_hart_m_p<BASE>::check_interrupt() { template <typename BASE> void riscv_hart_m_p<BASE>::check_interrupt() {
@ -958,4 +804,4 @@ template <typename BASE> uint64_t riscv_hart_m_p<BASE>::leave_trap(uint64_t flag
} // namespace arch } // namespace arch
} // namespace iss } // namespace iss
#endif /* _RISCV_CORE_H_ */ #endif /* _RISCV_HART_M_P_H */

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/******************************************************************************* /*******************************************************************************
* Copyright (C) 2017, 2018, MINRES Technologies GmbH * Copyright (C) 2021 MINRES Technologies GmbH
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -32,11 +32,11 @@
* eyck@minres.com - initial implementation * eyck@minres.com - initial implementation
******************************************************************************/ ******************************************************************************/
#ifndef _RISCV_CORE_H_ #ifndef _RISCV_HART_MU_P_H
#define _RISCV_CORE_H_ #define _RISCV_HART_MU_P_H
#include "riscv_hart_common.h"
#include "iss/arch/traits.h" #include "iss/arch/traits.h"
#include "iss/arch_if.h"
#include "iss/instrumentation_if.h" #include "iss/instrumentation_if.h"
#include "iss/log_categories.h" #include "iss/log_categories.h"
#include "iss/vm_if.h" #include "iss/vm_if.h"
@ -66,116 +66,10 @@
namespace iss { namespace iss {
namespace arch { namespace arch {
enum { tohost_dflt = 0xF0001000, fromhost_dflt = 0xF0001040 }; template <typename BASE, bool PMP=false> class riscv_hart_mu_p : public BASE {
protected:
enum riscv_csr { const std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
/* user-level CSR */ const std::array<const char *, 16> trap_str = {{""
// User Trap Setup
ustatus = 0x000,
uie = 0x004,
utvec = 0x005,
// User Trap Handling
uscratch = 0x040,
uepc = 0x041,
ucause = 0x042,
utval = 0x043,
uip = 0x044,
// User Floating-Point CSRs
fflags = 0x001,
frm = 0x002,
fcsr = 0x003,
// User Counter/Timers
cycle = 0xC00,
time = 0xC01,
instret = 0xC02,
hpmcounter3 = 0xC03,
hpmcounter4 = 0xC04,
/*...*/
hpmcounter31 = 0xC1F,
cycleh = 0xC80,
timeh = 0xC81,
instreth = 0xC82,
hpmcounter3h = 0xC83,
hpmcounter4h = 0xC84,
/*...*/
hpmcounter31h = 0xC9F,
/* supervisor-level CSR */
// Supervisor Trap Setup
sstatus = 0x100,
sedeleg = 0x102,
sideleg = 0x103,
sie = 0x104,
stvec = 0x105,
scounteren = 0x106,
// Supervisor Trap Handling
sscratch = 0x140,
sepc = 0x141,
scause = 0x142,
stval = 0x143,
sip = 0x144,
// Supervisor Protection and Translation
satp = 0x180,
/* machine-level CSR */
// Machine Information Registers
mvendorid = 0xF11,
marchid = 0xF12,
mimpid = 0xF13,
mhartid = 0xF14,
// Machine Trap Setup
mstatus = 0x300,
misa = 0x301,
medeleg = 0x302,
mideleg = 0x303,
mie = 0x304,
mtvec = 0x305,
mcounteren = 0x306,
// Machine Trap Handling
mscratch = 0x340,
mepc = 0x341,
mcause = 0x342,
mtval = 0x343,
mip = 0x344,
// Machine Protection and Translation
pmpcfg0 = 0x3A0,
pmpcfg1 = 0x3A1,
pmpcfg2 = 0x3A2,
pmpcfg3 = 0x3A3,
pmpaddr0 = 0x3B0,
pmpaddr1 = 0x3B1,
/*...*/
pmpaddr15 = 0x3BF,
// Machine Counter/Timers
mcycle = 0xB00,
minstret = 0xB02,
mhpmcounter3 = 0xB03,
mhpmcounter4 = 0xB04,
/*...*/
mhpmcounter31 = 0xB1F,
mcycleh = 0xB80,
minstreth = 0xB82,
mhpmcounter3h = 0xB83,
mhpmcounter4h = 0xB84,
/*...*/
mhpmcounter31h = 0xB9F,
// Machine Counter Setup
mhpmevent3 = 0x323,
mhpmevent4 = 0x324,
/*...*/
mhpmevent31 = 0x33F,
// Debug/Trace Registers (shared with Debug Mode)
tselect = 0x7A0,
tdata1 = 0x7A1,
tdata2 = 0x7A2,
tdata3 = 0x7A3,
// Debug Mode Registers
dcsr = 0x7B0,
dpc = 0x7B1,
dscratch = 0x7B2
};
namespace {
std::array<const char *, 16> trap_str = {{""
"Instruction address misaligned", // 0 "Instruction address misaligned", // 0
"Instruction access fault", // 1 "Instruction access fault", // 1
"Illegal instruction", // 2 "Illegal instruction", // 2
@ -192,62 +86,13 @@ std::array<const char *, 16> trap_str = {{""
"Load page fault", // d "Load page fault", // d
"Reserved", // e "Reserved", // e
"Store/AMO page fault"}}; "Store/AMO page fault"}};
std::array<const char *, 12> irq_str = { const std::array<const char *, 12> irq_str = {
{"User software interrupt", "Supervisor software interrupt", "Reserved", "Machine software interrupt", {"User software interrupt", "Supervisor software interrupt", "Reserved", "Machine software interrupt",
"User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt", "User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt",
"User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt"}}; "User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt"}};
enum {
PGSHIFT = 12,
PTE_PPN_SHIFT = 10,
// page table entry (PTE) fields
PTE_V = 0x001, // Valid
PTE_R = 0x002, // Read
PTE_W = 0x004, // Write
PTE_X = 0x008, // Execute
PTE_U = 0x010, // User
PTE_G = 0x020, // Global
PTE_A = 0x040, // Accessed
PTE_D = 0x080, // Dirty
PTE_SOFT = 0x300 // Reserved for Software
};
template <typename T> inline bool PTE_TABLE(T PTE) { return (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V); }
enum { PRIV_U = 0, PRIV_S = 1, PRIV_M = 3 };
enum {
ISA_A = 1,
ISA_B = 1 << 1,
ISA_C = 1 << 2,
ISA_D = 1 << 3,
ISA_E = 1 << 4,
ISA_F = 1 << 5,
ISA_G = 1 << 6,
ISA_I = 1 << 8,
ISA_M = 1 << 12,
ISA_N = 1 << 13,
ISA_Q = 1 << 16,
ISA_S = 1 << 18,
ISA_U = 1 << 20
};
class trap_load_access_fault : public trap_access {
public:
trap_load_access_fault(uint64_t badaddr)
: trap_access(5 << 16, badaddr) {}
};
class illegal_instruction_fault : public trap_access {
public:
illegal_instruction_fault(uint64_t badaddr)
: trap_access(2 << 16, badaddr) {}
};
} // namespace
template <typename BASE> class riscv_hart_mu_p : public BASE {
public: public:
using super = BASE; using super = BASE;
using this_class = riscv_hart_mu_p<BASE>; using this_class = riscv_hart_mu_p<BASE, PMP>;
using phys_addr_t = typename super::phys_addr_t; using phys_addr_t = typename super::phys_addr_t;
using reg_t = typename super::reg_t; using reg_t = typename super::reg_t;
using addr_t = typename super::addr_t; using addr_t = typename super::addr_t;
@ -301,21 +146,35 @@ public:
static const reg_t mstatus_reset_val = 0; static const reg_t mstatus_reset_val = 0;
void write_mstatus(T val) { void write_mstatus(T val, unsigned priv_lvl) {
auto mask = get_mask(); auto mask = get_mask(priv_lvl);
auto new_val = (mstatus.backing.val & ~mask) | (val & mask); auto new_val = (mstatus.backing.val & ~mask) | (val & mask);
mstatus = new_val; mstatus = new_val;
} }
T satp; T satp;
static constexpr uint32_t get_mask() { static constexpr uint32_t get_mask(unsigned priv_lvl) {
return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011 // only machine mode is supported #if __cplusplus < 201402L
return priv_lvl == PRIV_U ? 0x80000011UL : priv_lvl == PRIV_S ? 0x800de133UL : 0x807ff9ddUL;
#else
switch (priv_lvl) {
case PRIV_U: return 0x80000011UL; // 0b1000 0000 0000 0000 0000 0000 0001 0001
default: return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011
}
#endif
} }
}; };
using hart_state_type = hart_state<reg_t>;
constexpr reg_t get_irq_mask() { constexpr reg_t get_irq_mask(size_t mode) {
return 0b101110111011; // only machine mode is supported std::array<const reg_t, 4> m = {{
0b000100010001, // U mode
0b001100110011, // S mode
0,
0b101110111011 // M mode
}};
return m[mode];
} }
riscv_hart_mu_p(); riscv_hart_mu_p();
@ -338,8 +197,8 @@ public:
void set_mhartid(reg_t mhartid) { mhartid_reg = mhartid; }; void set_mhartid(reg_t mhartid) { mhartid_reg = mhartid; };
void disass_output(uint64_t pc, const std::string instr) override { void disass_output(uint64_t pc, const std::string instr) override {
CLOG(INFO, disass) << fmt::format("0x{:016x} {:40} [s:0x{:x};c:{}]", CLOG(INFO, disass) << fmt::format("0x{:016x} {:40} [p:{};s:0x{:x};c:{}]",
pc, instr, (reg_t)state.mstatus, this->reg.icount); pc, instr, lvl[this->reg.PRIV], (reg_t)state.mstatus, this->reg.icount);
}; };
iss::instrumentation_if *get_instrumentation_if() override { return &instr_if; } iss::instrumentation_if *get_instrumentation_if() override { return &instr_if; }
@ -359,7 +218,7 @@ public:
protected: protected:
struct riscv_instrumentation_if : public iss::instrumentation_if { struct riscv_instrumentation_if : public iss::instrumentation_if {
riscv_instrumentation_if(riscv_hart_mu_p<BASE> &arch) riscv_instrumentation_if(riscv_hart_mu_p<BASE, PMP> &arch)
: arch(arch) {} : arch(arch) {}
/** /**
* get the name of this architecture * get the name of this architecture
@ -374,7 +233,7 @@ protected:
virtual void set_curr_instr_cycles(unsigned cycles) { arch.cycle_offset += cycles - 1; }; virtual void set_curr_instr_cycles(unsigned cycles) { arch.cycle_offset += cycles - 1; };
riscv_hart_mu_p<BASE> &arch; riscv_hart_mu_p<BASE, PMP> &arch;
}; };
friend struct riscv_instrumentation_if; friend struct riscv_instrumentation_if;
@ -387,7 +246,7 @@ protected:
virtual iss::status read_csr(unsigned addr, reg_t &val); virtual iss::status read_csr(unsigned addr, reg_t &val);
virtual iss::status write_csr(unsigned addr, reg_t val); virtual iss::status write_csr(unsigned addr, reg_t val);
hart_state<reg_t> state; hart_state_type state;
uint64_t cycle_offset; uint64_t cycle_offset;
reg_t fault_data; reg_t fault_data;
uint64_t tohost = tohost_dflt; uint64_t tohost = tohost_dflt;
@ -427,8 +286,8 @@ protected:
void check_interrupt(); void check_interrupt();
}; };
template <typename BASE> template <typename BASE, bool PMP>
riscv_hart_mu_p<BASE>::riscv_hart_mu_p() riscv_hart_mu_p<BASE, PMP>::riscv_hart_mu_p()
: state() : state()
, cycle_offset(0) , cycle_offset(0)
, instr_if(*this) { , instr_if(*this) {
@ -437,33 +296,39 @@ riscv_hart_mu_p<BASE>::riscv_hart_mu_p()
for (unsigned addr = mcycle; addr <= hpmcounter31; ++addr) csr_wr_cb[addr] = nullptr; for (unsigned addr = mcycle; addr <= hpmcounter31; ++addr) csr_wr_cb[addr] = nullptr;
for (unsigned addr = mcycleh; addr <= hpmcounter31h; ++addr) csr_wr_cb[addr] = nullptr; for (unsigned addr = mcycleh; addr <= hpmcounter31h; ++addr) csr_wr_cb[addr] = nullptr;
// special handling // special handling
csr_rd_cb[time] = &riscv_hart_mu_p<BASE>::read_time; csr_rd_cb[time] = &this_class::read_time;
csr_wr_cb[time] = nullptr; csr_wr_cb[time] = nullptr;
csr_rd_cb[timeh] = &riscv_hart_mu_p<BASE>::read_time; csr_rd_cb[timeh] = &this_class::read_time;
csr_wr_cb[timeh] = nullptr; csr_wr_cb[timeh] = nullptr;
csr_rd_cb[mcycle] = &riscv_hart_mu_p<BASE>::read_cycle; csr_rd_cb[mcycle] = &this_class::read_cycle;
csr_rd_cb[mcycleh] = &riscv_hart_mu_p<BASE>::read_cycle; csr_rd_cb[mcycleh] = &this_class::read_cycle;
csr_rd_cb[minstret] = &riscv_hart_mu_p<BASE>::read_cycle; csr_rd_cb[minstret] = &this_class::read_cycle;
csr_rd_cb[minstreth] = &riscv_hart_mu_p<BASE>::read_cycle; csr_rd_cb[minstreth] = &this_class::read_cycle;
csr_rd_cb[mstatus] = &riscv_hart_mu_p<BASE>::read_status; csr_rd_cb[mstatus] = &this_class::read_status;
csr_wr_cb[mstatus] = &riscv_hart_mu_p<BASE>::write_status; csr_wr_cb[mstatus] = &this_class::write_status;
csr_rd_cb[mip] = &riscv_hart_mu_p<BASE>::read_ip; csr_rd_cb[ustatus] = &this_class::read_status;
csr_wr_cb[mip] = &riscv_hart_mu_p<BASE>::write_ip; csr_wr_cb[ustatus] = &this_class::write_status;
csr_rd_cb[mie] = &riscv_hart_mu_p<BASE>::read_ie; csr_rd_cb[mip] = &this_class::read_ip;
csr_wr_cb[mie] = &riscv_hart_mu_p<BASE>::write_ie; csr_wr_cb[mip] = &this_class::write_ip;
csr_rd_cb[mhartid] = &riscv_hart_mu_p<BASE>::read_hartid; csr_rd_cb[uip] = &this_class::read_ip;
csr_wr_cb[uip] = &this_class::write_ip;
csr_rd_cb[mie] = &this_class::read_ie;
csr_wr_cb[mie] = &this_class::write_ie;
csr_rd_cb[uie] = &this_class::read_ie;
csr_wr_cb[uie] = &this_class::write_ie;
csr_rd_cb[mhartid] = &this_class::read_hartid;
// common regs // common regs
const std::array<unsigned, 6> addrs{{mepc, mtvec, mscratch, mcause, mtval, mscratch}}; const std::array<unsigned, 6> addrs{{mepc, mtvec, mscratch, mcause, mtval, mscratch}};
for(auto addr: addrs) { for(auto addr: addrs) {
csr_rd_cb[addr] = &riscv_hart_mu_p<BASE>::read_reg; csr_rd_cb[addr] = &this_class::read_reg;
csr_wr_cb[addr] = &riscv_hart_mu_p<BASE>::write_reg; csr_wr_cb[addr] = &this_class::write_reg;
} }
// read-only registers // read-only registers
csr_rd_cb[misa] = &riscv_hart_mu_p<BASE>::read_reg; csr_rd_cb[misa] = &this_class::read_reg;
csr_wr_cb[misa] = nullptr; csr_wr_cb[misa] = nullptr;
} }
template <typename BASE> std::pair<uint64_t, bool> riscv_hart_mu_p<BASE>::load_file(std::string name, int type) { template <typename BASE, bool PMP> std::pair<uint64_t, bool> riscv_hart_mu_p<BASE, PMP>::load_file(std::string name, int type) {
FILE *fp = fopen(name.c_str(), "r"); FILE *fp = fopen(name.c_str(), "r");
if (fp) { if (fp) {
std::array<char, 5> buf; std::array<char, 5> buf;
@ -507,8 +372,8 @@ template <typename BASE> std::pair<uint64_t, bool> riscv_hart_mu_p<BASE>::load_f
throw std::runtime_error("memory load file not found"); throw std::runtime_error("memory load file not found");
} }
template <typename BASE> template <typename BASE, bool PMP>
iss::status riscv_hart_mu_p<BASE>::read(const address_type type, const access_type access, const uint32_t space, iss::status riscv_hart_mu_p<BASE, PMP>::read(const address_type type, const access_type access, const uint32_t space,
const uint64_t addr, const unsigned length, uint8_t *const data) { const uint64_t addr, const unsigned length, uint8_t *const data) {
#ifndef NDEBUG #ifndef NDEBUG
if (access && iss::access_type::DEBUG) { if (access && iss::access_type::DEBUG) {
@ -565,8 +430,8 @@ iss::status riscv_hart_mu_p<BASE>::read(const address_type type, const access_ty
} }
} }
template <typename BASE> template <typename BASE, bool PMP>
iss::status riscv_hart_mu_p<BASE>::write(const address_type type, const access_type access, const uint32_t space, iss::status riscv_hart_mu_p<BASE, PMP>::write(const address_type type, const access_type access, const uint32_t space,
const uint64_t addr, const unsigned length, const uint8_t *const data) { const uint64_t addr, const unsigned length, const uint8_t *const data) {
#ifndef NDEBUG #ifndef NDEBUG
const char *prefix = (access && iss::access_type::DEBUG) ? "debug " : ""; const char *prefix = (access && iss::access_type::DEBUG) ? "debug " : "";
@ -672,7 +537,7 @@ iss::status riscv_hart_mu_p<BASE>::write(const address_type type, const access_t
} }
} }
template <typename BASE> iss::status riscv_hart_mu_p<BASE>::read_csr(unsigned addr, reg_t &val) { template <typename BASE, bool PMP> iss::status riscv_hart_mu_p<BASE, PMP>::read_csr(unsigned addr, reg_t &val) {
if (addr >= csr.size()) return iss::Err; if (addr >= csr.size()) return iss::Err;
auto req_priv_lvl = (addr >> 8) & 0x3; auto req_priv_lvl = (addr >> 8) & 0x3;
if (this->reg.PRIV < req_priv_lvl) // not having required privileges if (this->reg.PRIV < req_priv_lvl) // not having required privileges
@ -683,7 +548,7 @@ template <typename BASE> iss::status riscv_hart_mu_p<BASE>::read_csr(unsigned ad
return (this->*(it->second))(addr, val); return (this->*(it->second))(addr, val);
} }
template <typename BASE> iss::status riscv_hart_mu_p<BASE>::write_csr(unsigned addr, reg_t val) { template <typename BASE, bool PMP> iss::status riscv_hart_mu_p<BASE, PMP>::write_csr(unsigned addr, reg_t val) {
if (addr >= csr.size()) return iss::Err; if (addr >= csr.size()) return iss::Err;
auto req_priv_lvl = (addr >> 8) & 0x3; auto req_priv_lvl = (addr >> 8) & 0x3;
if (this->reg.PRIV < req_priv_lvl) // not having required privileges if (this->reg.PRIV < req_priv_lvl) // not having required privileges
@ -696,17 +561,17 @@ template <typename BASE> iss::status riscv_hart_mu_p<BASE>::write_csr(unsigned a
return (this->*(it->second))(addr, val); return (this->*(it->second))(addr, val);
} }
template <typename BASE> iss::status riscv_hart_mu_p<BASE>::read_reg(unsigned addr, reg_t &val) { template <typename BASE, bool PMP> iss::status riscv_hart_mu_p<BASE, PMP>::read_reg(unsigned addr, reg_t &val) {
val = csr[addr]; val = csr[addr];
return iss::Ok; return iss::Ok;
} }
template <typename BASE> iss::status riscv_hart_mu_p<BASE>::write_reg(unsigned addr, reg_t val) { template <typename BASE, bool PMP> iss::status riscv_hart_mu_p<BASE, PMP>::write_reg(unsigned addr, reg_t val) {
csr[addr] = val; csr[addr] = val;
return iss::Ok; return iss::Ok;
} }
template <typename BASE> iss::status riscv_hart_mu_p<BASE>::read_cycle(unsigned addr, reg_t &val) { template <typename BASE, bool PMP> iss::status riscv_hart_mu_p<BASE, PMP>::read_cycle(unsigned addr, reg_t &val) {
auto cycle_val = this->reg.icount + cycle_offset; auto cycle_val = this->reg.icount + cycle_offset;
if (addr == mcycle) { if (addr == mcycle) {
val = static_cast<reg_t>(cycle_val); val = static_cast<reg_t>(cycle_val);
@ -717,7 +582,7 @@ template <typename BASE> iss::status riscv_hart_mu_p<BASE>::read_cycle(unsigned
return iss::Ok; return iss::Ok;
} }
template <typename BASE> iss::status riscv_hart_mu_p<BASE>::read_time(unsigned addr, reg_t &val) { template <typename BASE, bool PMP> iss::status riscv_hart_mu_p<BASE, PMP>::read_time(unsigned addr, reg_t &val) {
uint64_t time_val = (this->reg.icount + cycle_offset) / (100000000 / 32768 - 1); //-> ~3052; uint64_t time_val = (this->reg.icount + cycle_offset) / (100000000 / 32768 - 1); //-> ~3052;
if (addr == time) { if (addr == time) {
val = static_cast<reg_t>(time_val); val = static_cast<reg_t>(time_val);
@ -728,51 +593,55 @@ template <typename BASE> iss::status riscv_hart_mu_p<BASE>::read_time(unsigned a
return iss::Ok; return iss::Ok;
} }
template <typename BASE> iss::status riscv_hart_mu_p<BASE>::read_status(unsigned addr, reg_t &val) { template <typename BASE, bool PMP> iss::status riscv_hart_mu_p<BASE, PMP>::read_status(unsigned addr, reg_t &val) {
val = state.mstatus & hart_state<reg_t>::get_mask(); auto req_priv_lvl = (addr >> 8) & 0x3;
val = state.mstatus & hart_state_type::get_mask(req_priv_lvl);
return iss::Ok; return iss::Ok;
} }
template <typename BASE> iss::status riscv_hart_mu_p<BASE>::write_status(unsigned addr, reg_t val) { template <typename BASE, bool PMP> iss::status riscv_hart_mu_p<BASE, PMP>::write_status(unsigned addr, reg_t val) {
state.write_mstatus(val); auto req_priv_lvl = (addr >> 8) & 0x3;
state.write_mstatus(val, req_priv_lvl);
check_interrupt(); check_interrupt();
return iss::Ok; return iss::Ok;
} }
template <typename BASE> iss::status riscv_hart_mu_p<BASE>::read_ie(unsigned addr, reg_t &val) { template <typename BASE, bool PMP> iss::status riscv_hart_mu_p<BASE, PMP>::read_ie(unsigned addr, reg_t &val) {
val = csr[mie]; val = csr[mie];
val &= csr[mideleg]; val &= csr[mideleg];
return iss::Ok; return iss::Ok;
} }
template <typename BASE> iss::status riscv_hart_mu_p<BASE>::read_hartid(unsigned addr, reg_t &val) { template <typename BASE, bool PMP> iss::status riscv_hart_mu_p<BASE, PMP>::read_hartid(unsigned addr, reg_t &val) {
val = mhartid_reg; val = mhartid_reg;
return iss::Ok; return iss::Ok;
} }
template <typename BASE> iss::status riscv_hart_mu_p<BASE>::write_ie(unsigned addr, reg_t val) { template <typename BASE, bool PMP> iss::status riscv_hart_mu_p<BASE, PMP>::write_ie(unsigned addr, reg_t val) {
auto mask = get_irq_mask(); auto req_priv_lvl = (addr >> 8) & 0x3;
auto mask = get_irq_mask(req_priv_lvl);
csr[mie] = (csr[mie] & ~mask) | (val & mask); csr[mie] = (csr[mie] & ~mask) | (val & mask);
check_interrupt(); check_interrupt();
return iss::Ok; return iss::Ok;
} }
template <typename BASE> iss::status riscv_hart_mu_p<BASE>::read_ip(unsigned addr, reg_t &val) { template <typename BASE, bool PMP> iss::status riscv_hart_mu_p<BASE, PMP>::read_ip(unsigned addr, reg_t &val) {
val = csr[mip]; val = csr[mip];
val &= csr[mideleg]; val &= csr[mideleg];
return iss::Ok; return iss::Ok;
} }
template <typename BASE> iss::status riscv_hart_mu_p<BASE>::write_ip(unsigned addr, reg_t val) { template <typename BASE, bool PMP> iss::status riscv_hart_mu_p<BASE, PMP>::write_ip(unsigned addr, reg_t val) {
auto mask = get_irq_mask(); auto req_priv_lvl = (addr >> 8) & 0x3;
auto mask = get_irq_mask(req_priv_lvl);
mask &= ~(1 << 7); // MTIP is read only mask &= ~(1 << 7); // MTIP is read only
csr[mip] = (csr[mip] & ~mask) | (val & mask); csr[mip] = (csr[mip] & ~mask) | (val & mask);
check_interrupt(); check_interrupt();
return iss::Ok; return iss::Ok;
} }
template <typename BASE> template <typename BASE, bool PMP>
iss::status riscv_hart_mu_p<BASE>::read_mem(phys_addr_t paddr, unsigned length, uint8_t *const data) { iss::status riscv_hart_mu_p<BASE, PMP>::read_mem(phys_addr_t paddr, unsigned length, uint8_t *const data) {
if ((paddr.val + length) > mem.size()) return iss::Err; if ((paddr.val + length) > mem.size()) return iss::Err;
if(mem_read_cb) return mem_read_cb(paddr, length, data); if(mem_read_cb) return mem_read_cb(paddr, length, data);
switch (paddr.val) { switch (paddr.val) {
@ -797,8 +666,8 @@ iss::status riscv_hart_mu_p<BASE>::read_mem(phys_addr_t paddr, unsigned length,
return iss::Ok; return iss::Ok;
} }
template <typename BASE> template <typename BASE, bool PMP>
iss::status riscv_hart_mu_p<BASE>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t *const data) { iss::status riscv_hart_mu_p<BASE, PMP>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t *const data) {
if ((paddr.val + length) > mem.size()) return iss::Err; if ((paddr.val + length) > mem.size()) return iss::Err;
if(mem_write_cb) return mem_write_cb(paddr, length, data); if(mem_write_cb) return mem_write_cb(paddr, length, data);
switch (paddr.val) { switch (paddr.val) {
@ -874,12 +743,12 @@ iss::status riscv_hart_mu_p<BASE>::write_mem(phys_addr_t paddr, unsigned length,
return iss::Ok; return iss::Ok;
} }
template <typename BASE> inline void riscv_hart_mu_p<BASE>::reset(uint64_t address) { template <typename BASE, bool PMP> inline void riscv_hart_mu_p<BASE, PMP>::reset(uint64_t address) {
BASE::reset(address); BASE::reset(address);
state.mstatus = hart_state<reg_t>::mstatus_reset_val; state.mstatus = hart_state_type::mstatus_reset_val;
} }
template <typename BASE> void riscv_hart_mu_p<BASE>::check_interrupt() { template <typename BASE, bool PMP> void riscv_hart_mu_p<BASE, PMP>::check_interrupt() {
auto ideleg = csr[mideleg]; auto ideleg = csr[mideleg];
// Multiple simultaneous interrupts and traps at the same privilege level are // Multiple simultaneous interrupts and traps at the same privilege level are
// handled in the following decreasing priority order: // handled in the following decreasing priority order:
@ -901,23 +770,36 @@ template <typename BASE> void riscv_hart_mu_p<BASE>::check_interrupt() {
} }
} }
template <typename BASE> uint64_t riscv_hart_mu_p<BASE>::enter_trap(uint64_t flags, uint64_t addr) { template <typename BASE, bool PMP> uint64_t riscv_hart_mu_p<BASE, PMP>::enter_trap(uint64_t flags, uint64_t addr) {
// flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0] // flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0]
// calculate and write mcause val // calculate and write mcause val
auto trap_id = bit_sub<0, 16>(flags); auto trap_id = bit_sub<0, 16>(flags);
auto cause = bit_sub<16, 15>(flags); auto cause = bit_sub<16, 15>(flags);
if (trap_id == 0 && cause == 11) cause = 0x8 + PRIV_M; // adjust environment call cause if (trap_id == 0 && cause == 11) cause = 0x8 + this->reg.PRIV; // adjust environment call cause
// calculate effective privilege level // calculate effective privilege level
auto new_priv = PRIV_M;
if (trap_id == 0) { // exception if (trap_id == 0) { // exception
if (this->reg.PRIV != PRIV_M && ((csr[medeleg] >> cause) & 0x1) != 0)
new_priv = PRIV_U;
// store ret addr in xepc register // store ret addr in xepc register
csr[mepc] = static_cast<reg_t>(addr); // store actual address instruction of exception csr[uepc | (new_priv << 8)] = static_cast<reg_t>(addr); // store actual address instruction of exception
csr[mtval] = fault_data; /*
* write mtval if new_priv=M_MODE, spec says:
* When a hardware breakpoint is triggered, or an instruction-fetch, load,
* or store address-misaligned,
* access, or page-fault exception occurs, mtval is written with the
* faulting effective address.
*/
csr[utval | (new_priv << 8)] = fault_data;
fault_data = 0; fault_data = 0;
} else { } else {
csr[mepc] = this->reg.NEXT_PC; // store next address if interrupt if (this->reg.PRIV != PRIV_M && ((csr[mideleg] >> cause) & 0x1) != 0)
new_priv = PRIV_U;
csr[uepc | (new_priv << 8)] = this->reg.NEXT_PC; // store next address if interrupt
this->reg.pending_trap = 0; this->reg.pending_trap = 0;
} }
csr[mcause] = (trap_id << 31) + cause; size_t adr = ucause | (new_priv << 8);
csr[adr] = (trap_id << 31) + cause;
// update mstatus // update mstatus
// xPP field of mstatus is written with the active privilege mode at the time // xPP field of mstatus is written with the active privilege mode at the time
// of the trap; the x PIE field of mstatus // of the trap; the x PIE field of mstatus
@ -925,37 +807,64 @@ template <typename BASE> uint64_t riscv_hart_mu_p<BASE>::enter_trap(uint64_t fla
// 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; switch (new_priv) {
case PRIV_M:
state.mstatus.MPP = this->reg.PRIV;
state.mstatus.MPIE = state.mstatus.MIE; state.mstatus.MPIE = state.mstatus.MIE;
state.mstatus.MIE = false; state.mstatus.MIE = false;
break;
case PRIV_U:
state.mstatus.UPIE = state.mstatus.UIE;
state.mstatus.UIE = false;
break;
default:
break;
}
// get trap vector // get trap vector
auto ivec = csr[mtvec]; auto ivec = 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
this->reg.NEXT_PC = ivec & ~0x1UL; this->reg.NEXT_PC = ivec & ~0x1UL;
if ((ivec & 0x1) == 1 && trap_id != 0) this->reg.NEXT_PC += 4 * cause; if ((ivec & 0x1) == 1 && trap_id != 0) this->reg.NEXT_PC += 4 * cause;
// reset trap state // reset trap state
this->reg.PRIV = PRIV_M; this->reg.PRIV = new_priv;
this->reg.trap_state = 0; this->reg.trap_state = 0;
std::array<char, 32> buffer; std::array<char, 32> buffer;
sprintf(buffer.data(), "0x%016lx", addr); sprintf(buffer.data(), "0x%016lx", addr);
if((flags&0xffffffff) != 0xffffffff) if((flags&0xffffffff) != 0xffffffff)
CLOG(INFO, disass) << (trap_id ? "Interrupt" : "Trap") << " with cause '" CLOG(INFO, disass) << (trap_id ? "Interrupt" : "Trap") << " with cause '"
<< (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" << cause << ")" << (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" << cause << ")"
<< " at address " << buffer.data() << " occurred"; << " at address " << buffer.data() << " occurred, changing privilege level from "
<< lvl[this->reg.PRIV] << " to " << lvl[new_priv];
return this->reg.NEXT_PC; return this->reg.NEXT_PC;
} }
template <typename BASE> uint64_t riscv_hart_mu_p<BASE>::leave_trap(uint64_t flags) { template <typename BASE, bool PMP> uint64_t riscv_hart_mu_p<BASE, PMP>::leave_trap(uint64_t flags) {
auto inst_priv = (flags & 0x3)? 3:0;
auto status = state.mstatus;
// pop the relevant lower-privilege interrupt enable and privilege mode stack
// clear respective yIE
switch (inst_priv) {
case PRIV_M:
this->reg.PRIV = state.mstatus.MPP;
state.mstatus.MPP = 0; // clear mpp to U mode
state.mstatus.MIE = state.mstatus.MPIE; state.mstatus.MIE = state.mstatus.MPIE;
break;
case PRIV_U:
this->reg.PRIV = 0;
state.mstatus.UIE = state.mstatus.UPIE;
break;
}
// 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 = csr[mepc]; this->reg.NEXT_PC = csr[uepc | inst_priv << 8];
CLOG(INFO, disass) << "Executing xRET"; CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << lvl[this->reg.PRIV] << " to "
<< lvl[this->reg.PRIV];
return this->reg.NEXT_PC; return this->reg.NEXT_PC;
} }
} // namespace arch } // namespace arch
} // namespace iss } // namespace iss
#endif /* _RISCV_CORE_H_ */ #endif /* _RISCV_HART_MU_P_H */

View File

@ -48,16 +48,6 @@ class scv_tr_stream;
struct _scv_tr_generator_default_data; struct _scv_tr_generator_default_data;
template <class T_begin, class T_end> class scv_tr_generator; template <class T_begin, class T_end> class scv_tr_generator;
namespace iss {
class vm_if;
namespace arch {
template <typename BASE> class riscv_hart_m_p;
}
namespace debugger {
class target_adapter_if;
}
} // namespace iss
namespace sysc { namespace sysc {
class tlm_dmi_ext : public tlm::tlm_dmi { class tlm_dmi_ext : public tlm::tlm_dmi {
@ -97,6 +87,8 @@ public:
cci::cci_param<uint64_t> reset_address{"reset_address", 0ULL}; cci::cci_param<uint64_t> reset_address{"reset_address", 0ULL};
cci::cci_param<std::string> core_type{"core_type", "tgc_c"};
cci::cci_param<std::string> backend{"backend", "interp"}; cci::cci_param<std::string> backend{"backend", "interp"};
cci::cci_param<unsigned short> gdb_server_port{"gdb_server_port", 0}; cci::cci_param<unsigned short> gdb_server_port{"gdb_server_port", 0};
@ -145,9 +137,7 @@ protected:
tlm_utils::tlm_quantumkeeper quantum_keeper; tlm_utils::tlm_quantumkeeper quantum_keeper;
std::vector<uint8_t> write_buf; std::vector<uint8_t> write_buf;
std::unique_ptr<core_wrapper> cpu; std::unique_ptr<core_wrapper> cpu;
std::unique_ptr<iss::vm_if> vm;
sc_core::sc_time curr_clk; sc_core::sc_time curr_clk;
iss::debugger::target_adapter_if *tgt_adapter;
#ifdef WITH_SCV #ifdef WITH_SCV
//! transaction recording database //! transaction recording database
scv_tr_db *m_db; scv_tr_db *m_db;

View File

@ -31,17 +31,20 @@
*******************************************************************************/ *******************************************************************************/
#include "sysc/core_complex.h" #include "sysc/core_complex.h"
#ifdef CORE_TGC_B
#include "iss/arch/riscv_hart_m_p.h"
#include "iss/arch/tgc_b.h"
using tgc_b_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc_b>;
#endif
#ifdef CORE_TGC_C #ifdef CORE_TGC_C
#include "iss/arch/riscv_hart_m_p.h" #include "iss/arch/riscv_hart_m_p.h"
#include "iss/arch/tgc_c.h" #include "iss/arch/tgc_c.h"
using core_type = iss::arch::tgc_c; using tgc_c_plat_type = iss::arch::riscv_hart_m_p<iss::arch::tgc_c>;
using plat_type = iss::arch::riscv_hart_m_p<core_type>;
#endif #endif
#ifdef CORE_TGC_D #ifdef CORE_TGC_D
#include "iss/arch/riscv_hart_mu_p.h" #include "iss/arch/riscv_hart_mu_p.h"
#include "iss/arch/tgc_d.h" #include "iss/arch/tgc_d.h"
using core_type = iss::arch::tgc_d; using tgc_d_plat_type = iss::arch::riscv_hart_mu_p<iss::arch::tgc_d>;
using plat_type = iss::arch::riscv_hart_mu_p<core_type>;
#endif #endif
#include "iss/debugger/encoderdecoder.h" #include "iss/debugger/encoderdecoder.h"
#include "iss/debugger/gdb_session.h" #include "iss/debugger/gdb_session.h"
@ -53,6 +56,10 @@ using plat_type = iss::arch::riscv_hart_mu_p<core_type>;
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#define STR(X) #X
#define CREATE_CORE(CN) \
else if (type == STR(CN)) { std::tie(cpu, vm) = create_core<CN ## _plat_type>(backend, gdb_port, hart_id); }
#ifdef WITH_SCV #ifdef WITH_SCV
#include <array> #include <array>
#include <scv.h> #include <scv.h>
@ -67,41 +74,17 @@ using namespace sc_core;
namespace { namespace {
iss::debugger::encoder_decoder encdec; iss::debugger::encoder_decoder encdec;
}
namespace {
std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}}; std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
std::array<const char*, 16> trap_str = { {
"Instruction address misaligned",
"Instruction access fault",
"Illegal instruction",
"Breakpoint",
"Load address misaligned",
"Load access fault",
"Store/AMO address misaligned",
"Store/AMO access fault",
"Environment call from U-mode",
"Environment call from S-mode",
"Reserved",
"Environment call from M-mode",
"Instruction page fault",
"Load page fault",
"Reserved",
"Store/AMO page fault"
} };
std::array<const char*, 12> irq_str = { {
"User software interrupt", "Supervisor software interrupt", "Reserved", "Machine software interrupt",
"User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt",
"User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt" } };
} }
class core_wrapper : public plat_type { template<typename PLAT>
class core_wrapper_t : public PLAT {
public: public:
using phys_addr_t = typename arch::traits<core_type>::phys_addr_t; using reg_t = typename arch::traits<typename PLAT::super>::reg_t;
core_wrapper(core_complex *owner) using phys_addr_t = typename arch::traits<typename PLAT::super>::phys_addr_t;
using heart_state_t = typename PLAT::hart_state_type;
core_wrapper_t(core_complex *owner)
: owner(owner) { } : owner(owner) { }
uint32_t get_mode() { return this->reg.PRIV; } uint32_t get_mode() { return this->reg.PRIV; }
@ -110,10 +93,10 @@ public:
inline bool get_interrupt_execution() { return this->interrupt_sim; } inline bool get_interrupt_execution() { return this->interrupt_sim; }
plat_type::hart_state<plat_type::reg_t> &get_state() { return this->state; } heart_state_t &get_state() { return this->state; }
void notify_phase(exec_phase p) override { void notify_phase(iss::arch_if::exec_phase p) override {
if (p == ISTART) owner->sync(this->reg.icount + cycle_offset); if (p == iss::arch_if::ISTART) owner->sync(this->reg.icount);
} }
sync_type needed_sync() const override { return PRE_SYNC; } sync_type needed_sync() const override { return PRE_SYNC; }
@ -122,7 +105,7 @@ public:
if (INFO <= Log<Output2FILE<disass>>::reporting_level() && Output2FILE<disass>::stream()) { if (INFO <= Log<Output2FILE<disass>>::reporting_level() && Output2FILE<disass>::stream()) {
std::stringstream s; std::stringstream s;
s << "[p:" << lvl[this->reg.PRIV] << ";s:0x" << std::hex << std::setfill('0') s << "[p:" << lvl[this->reg.PRIV] << ";s:0x" << std::hex << std::setfill('0')
<< std::setw(sizeof(reg_t) * 2) << (reg_t)state.mstatus << std::dec << ";c:" << this->reg.icount << "]"; << std::setw(sizeof(reg_t) * 2) << (reg_t)this->state.mstatus << std::dec << ";c:" << this->reg.icount << "]";
Log<Output2FILE<disass>>().get(INFO, "disass") Log<Output2FILE<disass>>().get(INFO, "disass")
<< "0x" << std::setw(16) << std::right << std::setfill('0') << std::hex << pc << "\t\t" << std::setw(40) << "0x" << std::setw(16) << std::right << std::setfill('0') << std::hex << pc << "\t\t" << std::setw(40)
<< std::setfill(' ') << std::left << instr << s.str(); << std::setfill(' ') << std::left << instr << s.str();
@ -165,7 +148,7 @@ public:
} }
return ret?Ok:Err; return ret?Ok:Err;
} else { } else {
return plat_type::read_csr(addr, val); return PLAT::read_csr(addr, val);
} }
} }
@ -174,11 +157,11 @@ public:
do { do {
wait(wfi_evt); wait(wfi_evt);
} while (this->reg.pending_trap == 0); } while (this->reg.pending_trap == 0);
plat_type::wait_until(flags); PLAT::wait_until(flags);
} }
void local_irq(short id, bool value) { void local_irq(short id, bool value) {
plat_type::reg_t mask = 0; reg_t mask = 0;
switch (id) { switch (id) {
case 16: // SW case 16: // SW
mask = 1 << 3; mask = 1 << 3;
@ -238,11 +221,82 @@ int cmd_sysc(int argc, char *argv[], debugger::out_func of, debugger::data_func
return Err; return Err;
} }
using cpu_ptr = std::unique_ptr<iss::arch_if>;
using vm_ptr= std::unique_ptr<iss::vm_if>;
class core_wrapper {
public:
core_wrapper(core_complex *owner) : owner(owner) { }
void reset(uint64_t addr){vm->reset(addr);}
inline void start(){vm->start();}
inline std::pair<uint64_t, bool> load_file(std::string const& name){ return cpu->load_file(name);};
std::function<unsigned(void)> get_mode;
std::function<uint64_t(void)> get_state;
std::function<bool(void)> get_interrupt_execution;
std::function<void(bool)> set_interrupt_execution;
std::function<void(short, bool)> local_irq;
template<typename PLAT>
std::tuple<cpu_ptr, vm_ptr> create_core(std::string const& backend, unsigned gdb_port, uint32_t hart_id){
auto* lcpu = new core_wrapper_t<PLAT>(owner);
lcpu->set_mhartid(hart_id);
get_mode = [lcpu]() { return lcpu->get_mode(); };
get_state = [lcpu]() { return lcpu->get_state().mstatus.backing.val; };
get_interrupt_execution = [lcpu]() { return lcpu->get_interrupt_execution(); };
set_interrupt_execution = [lcpu](bool b) { return lcpu->set_interrupt_execution(b); };
local_irq = [lcpu](short s, bool b) { return lcpu->local_irq(s, b); };
if(backend == "interp")
return {cpu_ptr{lcpu}, vm_ptr{iss::interp::create(lcpu, gdb_port)}};
#ifdef WITH_LLVM
if(backend == "llvm")
return {cpu_ptr{lcpu}, vm_ptr{iss::llvm::create(lcpu, gdb_port)}};
#endif
#ifdef WITH_TCC
if(backend == "tcc")
s return {cpu_ptr{lcpu}, vm_ptr{iss::tcc::create(lcpu, gdb_port)}};
#endif
return {nullptr, nullptr};
}
void create_cpu(std::string const& type, std::string const& backend, unsigned gdb_port, uint32_t hart_id){
if (type == "") {
LOG(ERROR) << "Illegal argument value for core type: " << type << std::endl;
}
#ifdef CORE_TGC_B
CREATE_CORE(tgc_c)
#endif
#ifdef CORE_TGC_C
CREATE_CORE(tgc_c)
#endif
#ifdef CORE_TGC_D
CREATE_CORE(tgc_d)
#endif
else {
LOG(ERROR) << "Illegal argument value for core type: " << type << std::endl;
}
auto *srv = debugger::server<debugger::gdb_session>::get();
if (srv) tgt_adapter = srv->get_target();
if (tgt_adapter)
tgt_adapter->add_custom_command(
{"sysc", [this](int argc, char *argv[], debugger::out_func of,
debugger::data_func df) -> int { return cmd_sysc(argc, argv, of, df, tgt_adapter); },
"SystemC sub-commands: break <time>, print_time"});
}
core_complex * const owner;
vm_ptr vm{nullptr};
cpu_ptr cpu{nullptr};
iss::debugger::target_adapter_if *tgt_adapter{nullptr};
};
core_complex::core_complex(sc_module_name name) core_complex::core_complex(sc_module_name name)
: sc_module(name) : sc_module(name)
, read_lut(tlm_dmi_ext()) , read_lut(tlm_dmi_ext())
, write_lut(tlm_dmi_ext()) , write_lut(tlm_dmi_ext())
, tgt_adapter(nullptr)
#ifdef WITH_SCV #ifdef WITH_SCV
, m_db(scv_tr_db::get_default_db()) , m_db(scv_tr_db::get_default_db())
, stream_handle(nullptr) , stream_handle(nullptr)
@ -279,40 +333,16 @@ core_complex::~core_complex() = default;
void core_complex::trace(sc_trace_file *trf) const {} void core_complex::trace(sc_trace_file *trf) const {}
using vm_ptr= std::unique_ptr<iss::vm_if>;
vm_ptr create_cpu(core_wrapper* cpu, std::string const& backend, unsigned gdb_port){
if(backend == "interp")
return vm_ptr{iss::interp::create<core_type>(cpu, gdb_port)};
#ifdef WITH_LLVM
if(backend == "llvm")
return vm_ptr{iss::llvm::create(lcpu, gdb_port)};
#endif
#ifdef WITH_TCC
if(backend == "tcc")
return vm_ptr{iss::tcc::create<core_type>(cpu, gdb_port)};
#endif
return {nullptr};
}
void core_complex::before_end_of_elaboration() { void core_complex::before_end_of_elaboration() {
SCCDEBUG(SCMOD)<<"instantiating iss::arch::tgf with "<<backend.get_value()<<" backend"; SCCDEBUG(SCMOD)<<"instantiating iss::arch::tgf with "<<backend.get_value()<<" backend";
cpu = scc::make_unique<core_wrapper>(this); cpu = scc::make_unique<core_wrapper>(this);
cpu->set_mhartid(mhartid.get_value()); cpu->create_cpu(core_type.get_value(), backend.get_value(), gdb_server_port.get_value(), mhartid.get_value());
sc_assert(cpu->vm!=nullptr);
vm = create_cpu(cpu.get(), backend.get_value(), gdb_server_port.get_value());
sc_assert(vm!=nullptr);
#ifdef WITH_SCV #ifdef WITH_SCV
vm->setDisassEnabled(enable_disass.get_value() || m_db != nullptr); cpu->vm->setDisassEnabled(enable_disass.get_value() || m_db != nullptr);
#else #else
vm->setDisassEnabled(enable_disass.get_value()); vm->setDisassEnabled(enable_disass.get_value());
#endif #endif
auto *srv = debugger::server<debugger::gdb_session>::get();
if (srv) tgt_adapter = srv->get_target();
if (tgt_adapter)
tgt_adapter->add_custom_command(
{"sysc", [this](int argc, char *argv[], debugger::out_func of,
debugger::data_func df) -> int { return cmd_sysc(argc, argv, of, df, tgt_adapter); },
"SystemC sub-commands: break <time>, print_time"});
} }
void core_complex::start_of_simulation() { void core_complex::start_of_simulation() {
@ -344,7 +374,7 @@ void core_complex::disass_output(uint64_t pc, const std::string instr_str) {
tr_handle.record_attribute("PC", pc); tr_handle.record_attribute("PC", pc);
tr_handle.record_attribute("INSTR", instr_str); tr_handle.record_attribute("INSTR", instr_str);
tr_handle.record_attribute("MODE", lvl[cpu->get_mode()]); tr_handle.record_attribute("MODE", lvl[cpu->get_mode()]);
tr_handle.record_attribute("MSTATUS", cpu->get_state().mstatus.backing.val); tr_handle.record_attribute("MSTATUS", cpu->get_state());
tr_handle.record_attribute("LTIME_START", quantum_keeper.get_current_time().value() / 1000); tr_handle.record_attribute("LTIME_START", quantum_keeper.get_current_time().value() / 1000);
#endif #endif
} }
@ -375,7 +405,7 @@ void core_complex::run() {
wait(clk_i.value_changed_event()); wait(clk_i.value_changed_event());
} }
cpu->set_interrupt_execution(false); cpu->set_interrupt_execution(false);
vm->start(); cpu->start();
} while (cpu->get_interrupt_execution()); } while (cpu->get_interrupt_execution());
sc_stop(); sc_stop();
} }