You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1272 lines
41 KiB
1272 lines
41 KiB
// See LICENSE for license details.
|
|
|
|
#include "arith.h"
|
|
#include "processor.h"
|
|
#include "extension.h"
|
|
#include "common.h"
|
|
#include "config.h"
|
|
#include "simif.h"
|
|
#include "mmu.h"
|
|
#include "disasm.h"
|
|
#include "platform.h"
|
|
#include <cinttypes>
|
|
#include <cmath>
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
#include <assert.h>
|
|
#include <limits.h>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <algorithm>
|
|
|
|
#undef STATE
|
|
#define STATE state
|
|
|
|
processor_t::processor_t(const char* isa, const char* priv, const char* varch,
|
|
simif_t* sim, uint32_t id, bool halt_on_reset,
|
|
FILE* log_file, std::ostream& sout_)
|
|
: debug(false), halt_request(HR_NONE), sim(sim), id(id), xlen(0),
|
|
histogram_enabled(false), log_commits_enabled(false),
|
|
log_file(log_file), sout_(sout_.rdbuf()), halt_on_reset(halt_on_reset),
|
|
extension_table(256, false), impl_table(256, false), last_pc(1), executions(1)
|
|
{
|
|
VU.p = this;
|
|
|
|
parse_isa_string(isa);
|
|
parse_priv_string(priv);
|
|
parse_varch_string(varch);
|
|
|
|
register_base_instructions();
|
|
mmu = new mmu_t(sim, this);
|
|
|
|
disassembler = new disassembler_t(max_xlen);
|
|
for (auto e : custom_extensions)
|
|
for (auto disasm_insn : e.second->get_disasms())
|
|
disassembler->add_insn(disasm_insn);
|
|
|
|
set_pmp_granularity(1 << PMP_SHIFT);
|
|
set_pmp_num(state.max_pmp);
|
|
|
|
if (max_xlen == 32)
|
|
set_mmu_capability(IMPL_MMU_SV32);
|
|
else if (max_xlen == 64)
|
|
set_mmu_capability(IMPL_MMU_SV48);
|
|
|
|
reset();
|
|
}
|
|
|
|
processor_t::~processor_t()
|
|
{
|
|
#ifdef RISCV_ENABLE_HISTOGRAM
|
|
if (histogram_enabled)
|
|
{
|
|
fprintf(stderr, "PC Histogram size:%zu\n", pc_histogram.size());
|
|
for (auto it : pc_histogram)
|
|
fprintf(stderr, "%0" PRIx64 " %" PRIu64 "\n", it.first, it.second);
|
|
}
|
|
#endif
|
|
|
|
delete mmu;
|
|
delete disassembler;
|
|
}
|
|
|
|
static void bad_option_string(const char *option, const char *value,
|
|
const char *msg)
|
|
{
|
|
fprintf(stderr, "error: bad %s option '%s'. %s\n", option, value, msg);
|
|
abort();
|
|
}
|
|
|
|
static void bad_isa_string(const char* isa, const char* msg)
|
|
{
|
|
bad_option_string("--isa", isa, msg);
|
|
}
|
|
|
|
static void bad_priv_string(const char* priv)
|
|
{
|
|
fprintf(stderr, "error: bad --priv option %s\n", priv);
|
|
abort();
|
|
}
|
|
|
|
static void bad_varch_string(const char* varch, const char *msg)
|
|
{
|
|
bad_option_string("--varch", varch, msg);
|
|
}
|
|
|
|
static std::string get_string_token(std::string str, const char delimiter, size_t& pos)
|
|
{
|
|
size_t _pos = pos;
|
|
while (pos < str.length() && str[pos] != delimiter) ++pos;
|
|
return str.substr(_pos, pos - _pos);
|
|
}
|
|
|
|
static int get_int_token(std::string str, const char delimiter, size_t& pos)
|
|
{
|
|
size_t _pos = pos;
|
|
while (pos < str.length() && str[pos] != delimiter) {
|
|
if (!isdigit(str[pos]))
|
|
bad_varch_string(str.c_str(), "Unsupported value"); // An integer is expected
|
|
++pos;
|
|
}
|
|
return (pos == _pos) ? 0 : stoi(str.substr(_pos, pos - _pos));
|
|
}
|
|
|
|
static bool check_pow2(int val)
|
|
{
|
|
return ((val & (val - 1))) == 0;
|
|
}
|
|
|
|
void processor_t::parse_varch_string(const char* s)
|
|
{
|
|
std::string str, tmp;
|
|
for (const char *r = s; *r; r++)
|
|
str += std::tolower(*r);
|
|
|
|
size_t pos = 0;
|
|
size_t len = str.length();
|
|
int vlen = 0;
|
|
int elen = 0;
|
|
int slen = 0;
|
|
int vstart_alu = 1;
|
|
|
|
while (pos < len) {
|
|
std::string attr = get_string_token(str, ':', pos);
|
|
|
|
++pos;
|
|
|
|
if (attr == "vlen")
|
|
vlen = get_int_token(str, ',', pos);
|
|
else if (attr == "slen")
|
|
slen = get_int_token(str, ',', pos);
|
|
else if (attr == "elen")
|
|
elen = get_int_token(str, ',', pos);
|
|
else if (attr == "vstartalu")
|
|
vstart_alu = get_int_token(str, ',', pos);
|
|
else
|
|
bad_varch_string(s, "Unsupported token");
|
|
|
|
++pos;
|
|
}
|
|
|
|
// The integer should be the power of 2
|
|
if (!check_pow2(vlen) || !check_pow2(elen) || !check_pow2(slen)){
|
|
bad_varch_string(s, "The integer value should be the power of 2");
|
|
}
|
|
|
|
if (slen == 0)
|
|
slen = vlen;
|
|
|
|
/* Vector spec requirements. */
|
|
if (vlen < elen)
|
|
bad_varch_string(s, "vlen must be >= elen");
|
|
if (vlen != slen)
|
|
bad_varch_string(s, "vlen must be == slen for current limitation");
|
|
|
|
/* spike requirements. */
|
|
if (vlen > 4096)
|
|
bad_varch_string(s, "vlen must be <= 4096");
|
|
|
|
VU.VLEN = vlen;
|
|
VU.ELEN = elen;
|
|
VU.vlenb = vlen / 8;
|
|
VU.vstart_alu = vstart_alu;
|
|
}
|
|
|
|
static std::string strtolower(const char* str)
|
|
{
|
|
std::string res;
|
|
for (const char *r = str; *r; r++)
|
|
res += std::tolower(*r);
|
|
return res;
|
|
}
|
|
|
|
void processor_t::parse_priv_string(const char* str)
|
|
{
|
|
std::string lowercase = strtolower(str);
|
|
bool user = false, supervisor = false;
|
|
|
|
if (lowercase == "m")
|
|
;
|
|
else if (lowercase == "mu")
|
|
user = true;
|
|
else if (lowercase == "msu")
|
|
user = supervisor = true;
|
|
else
|
|
bad_priv_string(str);
|
|
|
|
if (user) {
|
|
max_isa |= reg_t(user) << ('u' - 'a');
|
|
extension_table['U'] = true;
|
|
}
|
|
|
|
if (supervisor) {
|
|
max_isa |= reg_t(supervisor) << ('s' - 'a');
|
|
extension_table['S'] = true;
|
|
}
|
|
}
|
|
|
|
void processor_t::parse_isa_string(const char* str)
|
|
{
|
|
isa_string = strtolower(str);
|
|
const char* all_subsets = "imafdqchp"
|
|
#ifdef __SIZEOF_INT128__
|
|
"v"
|
|
#endif
|
|
"";
|
|
|
|
max_isa = reg_t(2) << 62;
|
|
if (isa_string.compare(0, 4, "rv32") == 0)
|
|
max_xlen = 32, max_isa = reg_t(1) << 30;
|
|
else if (isa_string.compare(0, 4, "rv64") == 0)
|
|
max_xlen = 64;
|
|
else
|
|
bad_isa_string(str, "Spike supports either RV32I or RV64I");
|
|
if (isa_string[4] == 'g')
|
|
isa_string = isa_string.substr(0, 4) + "imafd" + isa_string.substr(5);
|
|
if (isa_string[4] != 'i')
|
|
bad_isa_string(str, "'I' extension is required");
|
|
|
|
auto p = isa_string.begin();
|
|
for (p += 4; islower(*p) && !strchr("zsx", *p); ++p) {
|
|
while (*all_subsets && (*p != *all_subsets))
|
|
++all_subsets;
|
|
if (!*all_subsets)
|
|
bad_isa_string(str, "Wrong order");
|
|
switch (*p) {
|
|
case 'q': max_isa |= 1L << ('d' - 'a');
|
|
case 'd': max_isa |= 1L << ('f' - 'a');
|
|
}
|
|
max_isa |= 1L << (*p - 'a');
|
|
extension_table[toupper(*p)] = true;
|
|
while (isdigit(*(p + 1))) {
|
|
++p; // skip major version, point, and minor version if presented
|
|
if (*(p + 1) == 'p') ++p;
|
|
}
|
|
p += *(p + 1) == '_'; // underscores may be used to improve readability
|
|
}
|
|
|
|
while (islower(*p) || (*p == '_')) {
|
|
p += *p == '_'; // first underscore is optional
|
|
auto end = p;
|
|
do ++end; while (*end && *end != '_');
|
|
auto ext_str = std::string(p, end);
|
|
if (ext_str == "zfh") {
|
|
if (!((max_isa >> ('f' - 'a')) & 1))
|
|
bad_isa_string(str, "'Zfh' extension requires 'F'");
|
|
extension_table[EXT_ZFH] = true;
|
|
} else if (ext_str == "zicsr") {
|
|
// Spike necessarily has Zicsr, because
|
|
// Zicsr is implied by the privileged architecture
|
|
} else if (ext_str == "zba") {
|
|
extension_table[EXT_ZBA] = true;
|
|
} else if (ext_str == "zbb") {
|
|
extension_table[EXT_ZBB] = true;
|
|
} else if (ext_str == "zbc") {
|
|
extension_table[EXT_ZBC] = true;
|
|
} else if (ext_str == "zbs") {
|
|
extension_table[EXT_ZBS] = true;
|
|
} else if (ext_str == "zbkb") {
|
|
extension_table[EXT_ZBKB] = true;
|
|
} else if (ext_str == "zbkc") {
|
|
extension_table[EXT_ZBKC] = true;
|
|
} else if (ext_str == "zbkx") {
|
|
extension_table[EXT_ZBKX] = true;
|
|
} else if (ext_str == "zk") {
|
|
extension_table[EXT_ZBKB] = true;
|
|
extension_table[EXT_ZBKC] = true;
|
|
extension_table[EXT_ZBKX] = true;
|
|
extension_table[EXT_ZKND] = true;
|
|
extension_table[EXT_ZKNE] = true;
|
|
extension_table[EXT_ZKNH] = true;
|
|
extension_table[EXT_ZKR] = true;
|
|
} else if (ext_str == "zkn") {
|
|
extension_table[EXT_ZBKB] = true;
|
|
extension_table[EXT_ZBKC] = true;
|
|
extension_table[EXT_ZBKX] = true;
|
|
extension_table[EXT_ZKND] = true;
|
|
extension_table[EXT_ZKNE] = true;
|
|
extension_table[EXT_ZKNH] = true;
|
|
} else if (ext_str == "zknd") {
|
|
extension_table[EXT_ZKND] = true;
|
|
} else if (ext_str == "zkne") {
|
|
extension_table[EXT_ZKNE] = true;
|
|
} else if (ext_str == "zknh") {
|
|
extension_table[EXT_ZKNH] = true;
|
|
} else if (ext_str == "zks") {
|
|
extension_table[EXT_ZBKB] = true;
|
|
extension_table[EXT_ZBKC] = true;
|
|
extension_table[EXT_ZBKX] = true;
|
|
extension_table[EXT_ZKSED] = true;
|
|
extension_table[EXT_ZKSH] = true;
|
|
} else if (ext_str == "zksed") {
|
|
extension_table[EXT_ZKSED] = true;
|
|
} else if (ext_str == "zksh") {
|
|
extension_table[EXT_ZKSH] = true;
|
|
} else if (ext_str == "zkr") {
|
|
extension_table[EXT_ZKR] = true;
|
|
} else if (ext_str == "zkt") {
|
|
} else if (ext_str == "svnapot") {
|
|
extension_table[EXT_SVNAPOT] = true;
|
|
} else if (ext_str == "svpbmt") {
|
|
extension_table[EXT_SVPBMT] = true;
|
|
} else if (ext_str == "svinval") {
|
|
extension_table[EXT_SVINVAL] = true;
|
|
} else if (ext_str[0] == 'x') {
|
|
max_isa |= 1L << ('x' - 'a');
|
|
extension_table[toupper('x')] = true;
|
|
if (ext_str == "xbitmanip") {
|
|
extension_table[EXT_XBITMANIP] = true;
|
|
} else if (ext_str.size() == 1) {
|
|
bad_isa_string(str, "single 'X' is not a proper name");
|
|
} else if (ext_str != "xdummy") {
|
|
register_extension(find_extension(ext_str.substr(1).c_str())());
|
|
}
|
|
} else {
|
|
bad_isa_string(str, ("unsupported extension: " + ext_str).c_str());
|
|
}
|
|
p = end;
|
|
}
|
|
if (*p) {
|
|
bad_isa_string(str, ("can't parse: " + std::string(p, isa_string.end())).c_str());
|
|
}
|
|
}
|
|
|
|
static int xlen_to_uxl(int xlen)
|
|
{
|
|
if (xlen == 32)
|
|
return 1;
|
|
if (xlen == 64)
|
|
return 2;
|
|
abort();
|
|
}
|
|
|
|
void state_t::reset(processor_t* const proc, reg_t max_isa)
|
|
{
|
|
pc = DEFAULT_RSTVEC;
|
|
XPR.reset();
|
|
FPR.reset();
|
|
|
|
// This assumes xlen is always max_xlen, which is true today (see
|
|
// mstatus_csr_t::unlogged_write()):
|
|
auto xlen = proc->get_max_xlen();
|
|
|
|
prv = PRV_M;
|
|
v = false;
|
|
csrmap[CSR_MISA] = misa = std::make_shared<misa_csr_t>(proc, CSR_MISA, max_isa);
|
|
csrmap[CSR_MSTATUS] = mstatus = std::make_shared<mstatus_csr_t>(proc, CSR_MSTATUS);
|
|
if (xlen == 32) csrmap[CSR_MSTATUSH] = std::make_shared<mstatush_csr_t>(proc, CSR_MSTATUSH, mstatus);
|
|
csrmap[CSR_MEPC] = mepc = std::make_shared<epc_csr_t>(proc, CSR_MEPC);
|
|
csrmap[CSR_MTVAL] = mtval = std::make_shared<basic_csr_t>(proc, CSR_MTVAL, 0);
|
|
csrmap[CSR_MSCRATCH] = std::make_shared<basic_csr_t>(proc, CSR_MSCRATCH, 0);
|
|
csrmap[CSR_MTVEC] = mtvec = std::make_shared<tvec_csr_t>(proc, CSR_MTVEC);
|
|
csrmap[CSR_MCAUSE] = mcause = std::make_shared<cause_csr_t>(proc, CSR_MCAUSE);
|
|
csrmap[CSR_MINSTRET] = minstret = std::make_shared<minstret_csr_t>(proc, CSR_MINSTRET);
|
|
csrmap[CSR_MCYCLE] = std::make_shared<proxy_csr_t>(proc, CSR_MCYCLE, minstret);
|
|
csrmap[CSR_INSTRET] = std::make_shared<counter_proxy_csr_t>(proc, CSR_INSTRET, minstret);
|
|
csrmap[CSR_CYCLE] = std::make_shared<counter_proxy_csr_t>(proc, CSR_CYCLE, minstret);
|
|
if (xlen == 32) {
|
|
minstreth_csr_t_p minstreth;
|
|
csrmap[CSR_MINSTRETH] = minstreth = std::make_shared<minstreth_csr_t>(proc, CSR_MINSTRETH, minstret);
|
|
csrmap[CSR_MCYCLEH] = std::make_shared<proxy_csr_t>(proc, CSR_MCYCLEH, minstreth);
|
|
csrmap[CSR_INSTRETH] = std::make_shared<counter_proxy_csr_t>(proc, CSR_INSTRETH, minstreth);
|
|
csrmap[CSR_CYCLEH] = std::make_shared<counter_proxy_csr_t>(proc, CSR_CYCLEH, minstreth);
|
|
}
|
|
for (reg_t i=3; i<=31; ++i) {
|
|
const reg_t which_mevent = CSR_MHPMEVENT3 + i - 3;
|
|
const reg_t which_mcounter = CSR_MHPMCOUNTER3 + i - 3;
|
|
const reg_t which_mcounterh = CSR_MHPMCOUNTER3H + i - 3;
|
|
const reg_t which_counter = CSR_HPMCOUNTER3 + i - 3;
|
|
const reg_t which_counterh = CSR_HPMCOUNTER3H + i - 3;
|
|
auto mevent = std::make_shared<const_csr_t>(proc, which_mevent, 0);
|
|
auto mcounter = std::make_shared<const_csr_t>(proc, which_mcounter, 0);
|
|
auto counter = std::make_shared<counter_proxy_csr_t>(proc, which_counter, mcounter);
|
|
csrmap[which_mevent] = mevent;
|
|
csrmap[which_mcounter] = mcounter;
|
|
csrmap[which_counter] = counter;
|
|
if (xlen == 32) {
|
|
auto mcounterh = std::make_shared<const_csr_t>(proc, which_mcounterh, 0);
|
|
auto counterh = std::make_shared<counter_proxy_csr_t>(proc, which_counterh, mcounterh);
|
|
csrmap[which_mcounterh] = mcounterh;
|
|
csrmap[which_counterh] = counterh;
|
|
}
|
|
}
|
|
csrmap[CSR_MCOUNTINHIBIT] = std::make_shared<const_csr_t>(proc, CSR_MCOUNTINHIBIT, 0);
|
|
csrmap[CSR_MIE] = mie = std::make_shared<mie_csr_t>(proc, CSR_MIE);
|
|
csrmap[CSR_MIP] = mip = std::make_shared<mip_csr_t>(proc, CSR_MIP);
|
|
auto sip_sie_accr = std::make_shared<generic_int_accessor_t>(this,
|
|
~MIP_HS_MASK, // read_mask
|
|
MIP_SSIP, // ip_write_mask
|
|
~MIP_HS_MASK, // ie_write_mask
|
|
true, // mask_mideleg
|
|
false, // mask_hideleg
|
|
0); // shiftamt
|
|
|
|
auto hip_hie_accr = std::make_shared<generic_int_accessor_t>(this,
|
|
MIP_HS_MASK, // read_mask
|
|
MIP_VSSIP, // ip_write_mask
|
|
MIP_HS_MASK, // ie_write_mask
|
|
false, // mask_mideleg
|
|
false, // mask_hideleg
|
|
0);
|
|
|
|
auto hvip_accr = std::make_shared<generic_int_accessor_t>(this,
|
|
MIP_VS_MASK, // read_mask
|
|
MIP_VS_MASK, // ip_write_mask
|
|
MIP_VS_MASK, // ie_write_mask
|
|
false, // mask_mideleg
|
|
false, // mask_hideleg
|
|
0); // shiftamt
|
|
|
|
auto vsip_vsie_accr = std::make_shared<generic_int_accessor_t>(this,
|
|
MIP_VS_MASK, // read_mask
|
|
MIP_VSSIP, // ip_write_mask
|
|
MIP_VSSIP, // ie_write_mask
|
|
false, // mask_mideleg
|
|
true, // mask_hideleg
|
|
1); // shiftamt
|
|
|
|
auto nonvirtual_sip = std::make_shared<mip_proxy_csr_t>(proc, CSR_SIP, sip_sie_accr);
|
|
auto vsip = std::make_shared<mip_proxy_csr_t>(proc, CSR_VSIP, vsip_vsie_accr);
|
|
csrmap[CSR_VSIP] = vsip;
|
|
csrmap[CSR_SIP] = std::make_shared<virtualized_csr_t>(proc, nonvirtual_sip, vsip);
|
|
csrmap[CSR_HIP] = std::make_shared<mip_proxy_csr_t>(proc, CSR_HIP, hip_hie_accr);
|
|
csrmap[CSR_HVIP] = std::make_shared<mip_proxy_csr_t>(proc, CSR_HVIP, hvip_accr);
|
|
|
|
auto nonvirtual_sie = std::make_shared<mie_proxy_csr_t>(proc, CSR_SIE, sip_sie_accr);
|
|
auto vsie = std::make_shared<mie_proxy_csr_t>(proc, CSR_VSIE, vsip_vsie_accr);
|
|
csrmap[CSR_VSIE] = vsie;
|
|
csrmap[CSR_SIE] = std::make_shared<virtualized_csr_t>(proc, nonvirtual_sie, vsie);
|
|
csrmap[CSR_HIE] = std::make_shared<mie_proxy_csr_t>(proc, CSR_HIE, hip_hie_accr);
|
|
|
|
csrmap[CSR_MEDELEG] = medeleg = std::make_shared<medeleg_csr_t>(proc, CSR_MEDELEG);
|
|
csrmap[CSR_MIDELEG] = mideleg = std::make_shared<mideleg_csr_t>(proc, CSR_MIDELEG);
|
|
const reg_t counteren_mask = 0xffffffffULL;
|
|
mcounteren = std::make_shared<masked_csr_t>(proc, CSR_MCOUNTEREN, counteren_mask, 0);
|
|
if (proc->extension_enabled_const('U')) csrmap[CSR_MCOUNTEREN] = mcounteren;
|
|
csrmap[CSR_SCOUNTEREN] = scounteren = std::make_shared<masked_csr_t>(proc, CSR_SCOUNTEREN, counteren_mask, 0);
|
|
auto nonvirtual_sepc = std::make_shared<epc_csr_t>(proc, CSR_SEPC);
|
|
csrmap[CSR_VSEPC] = vsepc = std::make_shared<epc_csr_t>(proc, CSR_VSEPC);
|
|
csrmap[CSR_SEPC] = sepc = std::make_shared<virtualized_csr_t>(proc, nonvirtual_sepc, vsepc);
|
|
auto nonvirtual_stval = std::make_shared<basic_csr_t>(proc, CSR_STVAL, 0);
|
|
csrmap[CSR_VSTVAL] = vstval = std::make_shared<basic_csr_t>(proc, CSR_VSTVAL, 0);
|
|
csrmap[CSR_STVAL] = stval = std::make_shared<virtualized_csr_t>(proc, nonvirtual_stval, vstval);
|
|
auto sscratch = std::make_shared<basic_csr_t>(proc, CSR_SSCRATCH, 0);
|
|
auto vsscratch = std::make_shared<basic_csr_t>(proc, CSR_VSSCRATCH, 0);
|
|
// Note: if max_isa does not include H, we don't really need this virtualized_csr_t at all (though it doesn't hurt):
|
|
csrmap[CSR_SSCRATCH] = std::make_shared<virtualized_csr_t>(proc, sscratch, vsscratch);
|
|
csrmap[CSR_VSSCRATCH] = vsscratch;
|
|
auto nonvirtual_stvec = std::make_shared<tvec_csr_t>(proc, CSR_STVEC);
|
|
csrmap[CSR_VSTVEC] = vstvec = std::make_shared<tvec_csr_t>(proc, CSR_VSTVEC);
|
|
csrmap[CSR_STVEC] = stvec = std::make_shared<virtualized_csr_t>(proc, nonvirtual_stvec, vstvec);
|
|
auto nonvirtual_satp = std::make_shared<satp_csr_t>(proc, CSR_SATP);
|
|
csrmap[CSR_VSATP] = vsatp = std::make_shared<base_atp_csr_t>(proc, CSR_VSATP);
|
|
csrmap[CSR_SATP] = satp = std::make_shared<virtualized_satp_csr_t>(proc, nonvirtual_satp, vsatp);
|
|
auto nonvirtual_scause = std::make_shared<cause_csr_t>(proc, CSR_SCAUSE);
|
|
csrmap[CSR_VSCAUSE] = vscause = std::make_shared<cause_csr_t>(proc, CSR_VSCAUSE);
|
|
csrmap[CSR_SCAUSE] = scause = std::make_shared<virtualized_csr_t>(proc, nonvirtual_scause, vscause);
|
|
csrmap[CSR_MTVAL2] = mtval2 = std::make_shared<hypervisor_csr_t>(proc, CSR_MTVAL2);
|
|
csrmap[CSR_MTINST] = mtinst = std::make_shared<hypervisor_csr_t>(proc, CSR_MTINST);
|
|
const reg_t hstatus_init = set_field((reg_t)0, HSTATUS_VSXL, xlen_to_uxl(proc->get_const_xlen()));
|
|
const reg_t hstatus_mask = HSTATUS_VTSR | HSTATUS_VTW
|
|
| (proc->supports_impl(IMPL_MMU) ? HSTATUS_VTVM : 0)
|
|
| HSTATUS_HU | HSTATUS_SPVP | HSTATUS_SPV | HSTATUS_GVA;
|
|
csrmap[CSR_HSTATUS] = hstatus = std::make_shared<masked_csr_t>(proc, CSR_HSTATUS, hstatus_mask, hstatus_init);
|
|
csrmap[CSR_HGEIE] = std::make_shared<const_csr_t>(proc, CSR_HGEIE, 0);
|
|
csrmap[CSR_HGEIP] = std::make_shared<const_csr_t>(proc, CSR_HGEIP, 0);
|
|
csrmap[CSR_HIDELEG] = hideleg = std::make_shared<masked_csr_t>(proc, CSR_HIDELEG, MIP_VS_MASK, 0);
|
|
const reg_t hedeleg_mask =
|
|
(1 << CAUSE_MISALIGNED_FETCH) |
|
|
(1 << CAUSE_FETCH_ACCESS) |
|
|
(1 << CAUSE_ILLEGAL_INSTRUCTION) |
|
|
(1 << CAUSE_BREAKPOINT) |
|
|
(1 << CAUSE_MISALIGNED_LOAD) |
|
|
(1 << CAUSE_LOAD_ACCESS) |
|
|
(1 << CAUSE_MISALIGNED_STORE) |
|
|
(1 << CAUSE_STORE_ACCESS) |
|
|
(1 << CAUSE_USER_ECALL) |
|
|
(1 << CAUSE_FETCH_PAGE_FAULT) |
|
|
(1 << CAUSE_LOAD_PAGE_FAULT) |
|
|
(1 << CAUSE_STORE_PAGE_FAULT);
|
|
csrmap[CSR_HEDELEG] = hedeleg = std::make_shared<masked_csr_t>(proc, CSR_HEDELEG, hedeleg_mask, 0);
|
|
csrmap[CSR_HCOUNTEREN] = hcounteren = std::make_shared<masked_csr_t>(proc, CSR_HCOUNTEREN, counteren_mask, 0);
|
|
csrmap[CSR_HTVAL] = htval = std::make_shared<basic_csr_t>(proc, CSR_HTVAL, 0);
|
|
csrmap[CSR_HTINST] = htinst = std::make_shared<basic_csr_t>(proc, CSR_HTINST, 0);
|
|
csrmap[CSR_HGATP] = hgatp = std::make_shared<hgatp_csr_t>(proc, CSR_HGATP);
|
|
auto nonvirtual_sstatus = std::make_shared<sstatus_proxy_csr_t>(proc, CSR_SSTATUS, mstatus);
|
|
csrmap[CSR_VSSTATUS] = vsstatus = std::make_shared<vsstatus_csr_t>(proc, CSR_VSSTATUS);
|
|
csrmap[CSR_SSTATUS] = sstatus = std::make_shared<sstatus_csr_t>(proc, nonvirtual_sstatus, vsstatus);
|
|
|
|
csrmap[CSR_DPC] = dpc = std::make_shared<dpc_csr_t>(proc, CSR_DPC);
|
|
csrmap[CSR_DSCRATCH0] = std::make_shared<debug_mode_csr_t>(proc, CSR_DSCRATCH0);
|
|
csrmap[CSR_DSCRATCH1] = std::make_shared<debug_mode_csr_t>(proc, CSR_DSCRATCH1);
|
|
csrmap[CSR_DCSR] = dcsr = std::make_shared<dcsr_csr_t>(proc, CSR_DCSR);
|
|
|
|
csrmap[CSR_TSELECT] = tselect = std::make_shared<tselect_csr_t>(proc, CSR_TSELECT);
|
|
memset(this->mcontrol, 0, sizeof(this->mcontrol));
|
|
for (auto &item : mcontrol)
|
|
item.type = 2;
|
|
|
|
csrmap[CSR_TDATA1] = std::make_shared<tdata1_csr_t>(proc, CSR_TDATA1);
|
|
csrmap[CSR_TDATA2] = tdata2 = std::make_shared<tdata2_csr_t>(proc, CSR_TDATA2, num_triggers);
|
|
csrmap[CSR_TDATA3] = std::make_shared<const_csr_t>(proc, CSR_TDATA3, 0);
|
|
debug_mode = false;
|
|
single_step = STEP_NONE;
|
|
|
|
for (int i=0; i < max_pmp; ++i) {
|
|
csrmap[CSR_PMPADDR0 + i] = pmpaddr[i] = std::make_shared<pmpaddr_csr_t>(proc, CSR_PMPADDR0 + i);
|
|
}
|
|
for (int i=0; i < max_pmp; i += xlen/8) {
|
|
reg_t addr = CSR_PMPCFG0 + i/4;
|
|
csrmap[addr] = std::make_shared<pmpcfg_csr_t>(proc, addr);
|
|
}
|
|
|
|
csrmap[CSR_FFLAGS] = fflags = std::make_shared<float_csr_t>(proc, CSR_FFLAGS, FSR_AEXC >> FSR_AEXC_SHIFT, 0);
|
|
csrmap[CSR_FRM] = frm = std::make_shared<float_csr_t>(proc, CSR_FRM, FSR_RD >> FSR_RD_SHIFT, 0);
|
|
assert(FSR_AEXC_SHIFT == 0); // composite_csr_t assumes fflags begins at bit 0
|
|
csrmap[CSR_FCSR] = std::make_shared<composite_csr_t>(proc, CSR_FFLAGS, frm, fflags, FSR_RD_SHIFT);
|
|
serialized = false;
|
|
|
|
#ifdef RISCV_ENABLE_COMMITLOG
|
|
log_reg_write.clear();
|
|
log_mem_read.clear();
|
|
log_mem_write.clear();
|
|
last_inst_priv = 0;
|
|
last_inst_xlen = 0;
|
|
last_inst_flen = 0;
|
|
#endif
|
|
}
|
|
|
|
void processor_t::vectorUnit_t::reset(){
|
|
free(reg_file);
|
|
VLEN = get_vlen();
|
|
ELEN = get_elen();
|
|
reg_file = malloc(NVPR * vlenb);
|
|
memset(reg_file, 0, NVPR * vlenb);
|
|
|
|
vtype = 0;
|
|
set_vl(0, 0, 0, -1); // default to illegal configuration
|
|
}
|
|
|
|
reg_t processor_t::vectorUnit_t::set_vl(int rd, int rs1, reg_t reqVL, reg_t newType){
|
|
int new_vlmul = 0;
|
|
if (vtype != newType){
|
|
vtype = newType;
|
|
vsew = 1 << (extract64(newType, 3, 3) + 3);
|
|
new_vlmul = int8_t(extract64(newType, 0, 3) << 5) >> 5;
|
|
vflmul = new_vlmul >= 0 ? 1 << new_vlmul : 1.0 / (1 << -new_vlmul);
|
|
vlmax = (VLEN/vsew) * vflmul;
|
|
vta = extract64(newType, 6, 1);
|
|
vma = extract64(newType, 7, 1);
|
|
|
|
vill = !(vflmul >= 0.125 && vflmul <= 8)
|
|
|| vsew > std::min(vflmul, 1.0f) * ELEN
|
|
|| (newType >> 8) != 0;
|
|
|
|
if (vill) {
|
|
vlmax = 0;
|
|
vtype = UINT64_MAX << (p->get_xlen() - 1);
|
|
}
|
|
}
|
|
|
|
// set vl
|
|
if (vlmax == 0) {
|
|
vl = 0;
|
|
} else if (rd == 0 && rs1 == 0) {
|
|
vl = vl > vlmax ? vlmax : vl;
|
|
} else if (rd != 0 && rs1 == 0) {
|
|
vl = vlmax;
|
|
} else if (rs1 != 0) {
|
|
vl = reqVL > vlmax ? vlmax : reqVL;
|
|
}
|
|
|
|
vstart = 0;
|
|
setvl_count++;
|
|
return vl;
|
|
}
|
|
|
|
void processor_t::set_debug(bool value)
|
|
{
|
|
debug = value;
|
|
|
|
for (auto e : custom_extensions)
|
|
e.second->set_debug(value);
|
|
}
|
|
|
|
void processor_t::set_histogram(bool value)
|
|
{
|
|
histogram_enabled = value;
|
|
#ifndef RISCV_ENABLE_HISTOGRAM
|
|
if (value) {
|
|
fprintf(stderr, "PC Histogram support has not been properly enabled;");
|
|
fprintf(stderr, " please re-build the riscv-isa-sim project using \"configure --enable-histogram\".\n");
|
|
abort();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef RISCV_ENABLE_COMMITLOG
|
|
void processor_t::enable_log_commits()
|
|
{
|
|
log_commits_enabled = true;
|
|
}
|
|
#endif
|
|
|
|
void processor_t::reset()
|
|
{
|
|
xlen = max_xlen;
|
|
state.reset(this, max_isa);
|
|
state.dcsr->halt = halt_on_reset;
|
|
halt_on_reset = false;
|
|
VU.reset();
|
|
|
|
if (n_pmp > 0) {
|
|
// For backwards compatibility with software that is unaware of PMP,
|
|
// initialize PMP to permit unprivileged access to all of memory.
|
|
set_csr(CSR_PMPADDR0, ~reg_t(0));
|
|
set_csr(CSR_PMPCFG0, PMP_R | PMP_W | PMP_X | PMP_NAPOT);
|
|
}
|
|
|
|
for (auto e : custom_extensions) // reset any extensions
|
|
e.second->reset();
|
|
|
|
if (sim)
|
|
sim->proc_reset(id);
|
|
}
|
|
|
|
extension_t* processor_t::get_extension()
|
|
{
|
|
switch (custom_extensions.size()) {
|
|
case 0: return NULL;
|
|
case 1: return custom_extensions.begin()->second;
|
|
default:
|
|
fprintf(stderr, "processor_t::get_extension() is ambiguous when multiple extensions\n");
|
|
fprintf(stderr, "are present!\n");
|
|
abort();
|
|
}
|
|
}
|
|
|
|
extension_t* processor_t::get_extension(const char* name)
|
|
{
|
|
auto it = custom_extensions.find(name);
|
|
if (it == custom_extensions.end())
|
|
abort();
|
|
return it->second;
|
|
}
|
|
|
|
void processor_t::set_pmp_num(reg_t n)
|
|
{
|
|
// check the number of pmp is in a reasonable range
|
|
if (n > state.max_pmp) {
|
|
fprintf(stderr, "error: bad number of pmp regions: '%ld' from the dtb\n", (unsigned long)n);
|
|
abort();
|
|
}
|
|
n_pmp = n;
|
|
}
|
|
|
|
void processor_t::set_pmp_granularity(reg_t gran) {
|
|
// check the pmp granularity is set from dtb(!=0) and is power of 2
|
|
if (gran < (1 << PMP_SHIFT) || (gran & (gran - 1)) != 0) {
|
|
fprintf(stderr, "error: bad pmp granularity '%ld' from the dtb\n", (unsigned long)gran);
|
|
abort();
|
|
}
|
|
|
|
lg_pmp_granularity = ctz(gran);
|
|
}
|
|
|
|
void processor_t::set_mmu_capability(int cap)
|
|
{
|
|
switch (cap) {
|
|
case IMPL_MMU_SV32:
|
|
set_impl(cap, true);
|
|
set_impl(IMPL_MMU, true);
|
|
break;
|
|
case IMPL_MMU_SV39:
|
|
set_impl(cap, true);
|
|
set_impl(IMPL_MMU, true);
|
|
break;
|
|
case IMPL_MMU_SV48:
|
|
set_impl(cap, true);
|
|
set_impl(IMPL_MMU_SV39, true);
|
|
set_impl(IMPL_MMU, true);
|
|
break;
|
|
default:
|
|
set_impl(IMPL_MMU_SV32, false);
|
|
set_impl(IMPL_MMU_SV39, false);
|
|
set_impl(IMPL_MMU_SV48, false);
|
|
set_impl(IMPL_MMU, false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void processor_t::take_interrupt(reg_t pending_interrupts)
|
|
{
|
|
// Do nothing if no pending interrupts
|
|
if (!pending_interrupts) {
|
|
return;
|
|
}
|
|
|
|
// M-ints have higher priority over HS-ints and VS-ints
|
|
const reg_t mie = get_field(state.mstatus->read(), MSTATUS_MIE);
|
|
const reg_t m_enabled = state.prv < PRV_M || (state.prv == PRV_M && mie);
|
|
reg_t enabled_interrupts = pending_interrupts & ~state.mideleg->read() & -m_enabled;
|
|
if (enabled_interrupts == 0) {
|
|
// HS-ints have higher priority over VS-ints
|
|
const reg_t deleg_to_hs = state.mideleg->read() & ~state.hideleg->read();
|
|
const reg_t sie = get_field(state.sstatus->read(), MSTATUS_SIE);
|
|
const reg_t hs_enabled = state.v || state.prv < PRV_S || (state.prv == PRV_S && sie);
|
|
enabled_interrupts = pending_interrupts & deleg_to_hs & -hs_enabled;
|
|
if (state.v && enabled_interrupts == 0) {
|
|
// VS-ints have least priority and can only be taken with virt enabled
|
|
const reg_t deleg_to_vs = state.mideleg->read() & state.hideleg->read();
|
|
const reg_t vs_enabled = state.prv < PRV_S || (state.prv == PRV_S && sie);
|
|
enabled_interrupts = pending_interrupts & deleg_to_vs & -vs_enabled;
|
|
}
|
|
}
|
|
|
|
if (!state.debug_mode && enabled_interrupts) {
|
|
// nonstandard interrupts have highest priority
|
|
if (enabled_interrupts >> (IRQ_M_EXT + 1))
|
|
enabled_interrupts = enabled_interrupts >> (IRQ_M_EXT + 1) << (IRQ_M_EXT + 1);
|
|
// standard interrupt priority is MEI, MSI, MTI, SEI, SSI, STI
|
|
else if (enabled_interrupts & MIP_MEIP)
|
|
enabled_interrupts = MIP_MEIP;
|
|
else if (enabled_interrupts & MIP_MSIP)
|
|
enabled_interrupts = MIP_MSIP;
|
|
else if (enabled_interrupts & MIP_MTIP)
|
|
enabled_interrupts = MIP_MTIP;
|
|
else if (enabled_interrupts & MIP_SEIP)
|
|
enabled_interrupts = MIP_SEIP;
|
|
else if (enabled_interrupts & MIP_SSIP)
|
|
enabled_interrupts = MIP_SSIP;
|
|
else if (enabled_interrupts & MIP_STIP)
|
|
enabled_interrupts = MIP_STIP;
|
|
else if (enabled_interrupts & MIP_VSEIP)
|
|
enabled_interrupts = MIP_VSEIP;
|
|
else if (enabled_interrupts & MIP_VSSIP)
|
|
enabled_interrupts = MIP_VSSIP;
|
|
else if (enabled_interrupts & MIP_VSTIP)
|
|
enabled_interrupts = MIP_VSTIP;
|
|
else
|
|
abort();
|
|
|
|
throw trap_t(((reg_t)1 << (max_xlen-1)) | ctz(enabled_interrupts));
|
|
}
|
|
}
|
|
|
|
reg_t processor_t::legalize_privilege(reg_t prv)
|
|
{
|
|
assert(prv <= PRV_M);
|
|
|
|
if (!extension_enabled('U'))
|
|
return PRV_M;
|
|
|
|
if (prv == PRV_HS || (prv == PRV_S && !extension_enabled('S')))
|
|
return PRV_U;
|
|
|
|
return prv;
|
|
}
|
|
|
|
void processor_t::set_privilege(reg_t prv)
|
|
{
|
|
mmu->flush_tlb();
|
|
state.prv = legalize_privilege(prv);
|
|
}
|
|
|
|
void processor_t::set_virt(bool virt)
|
|
{
|
|
reg_t tmp, mask;
|
|
|
|
if (state.prv == PRV_M)
|
|
return;
|
|
|
|
if (state.v != virt) {
|
|
/*
|
|
* Ideally, we should flush TLB here but we don't need it because
|
|
* set_virt() is always used in conjucter with set_privilege() and
|
|
* set_privilege() will flush TLB unconditionally.
|
|
*
|
|
* The virtualized sstatus register also relies on this TLB flush,
|
|
* since changing V might change sstatus.MXR and sstatus.SUM.
|
|
*/
|
|
state.v = virt;
|
|
}
|
|
}
|
|
|
|
void processor_t::enter_debug_mode(uint8_t cause)
|
|
{
|
|
state.debug_mode = true;
|
|
state.dcsr->write_cause_and_prv(cause, state.prv);
|
|
set_privilege(PRV_M);
|
|
state.dpc->write(state.pc);
|
|
state.pc = DEBUG_ROM_ENTRY;
|
|
}
|
|
|
|
void processor_t::debug_output_log(std::stringstream *s)
|
|
{
|
|
if (log_file==stderr) {
|
|
std::ostream out(sout_.rdbuf());
|
|
out << s->str(); // handles command line options -d -s -l
|
|
} else {
|
|
fputs(s->str().c_str(), log_file); // handles command line option --log
|
|
}
|
|
}
|
|
|
|
void processor_t::take_trap(trap_t& t, reg_t epc)
|
|
{
|
|
if (debug) {
|
|
std::stringstream s; // first put everything in a string, later send it to output
|
|
s << "core " << std::dec << std::setfill(' ') << std::setw(3) << id
|
|
<< ": exception " << t.name() << ", epc 0x"
|
|
<< std::hex << std::setfill('0') << std::setw(max_xlen/4) << zext(epc, max_xlen) << std::endl;
|
|
if (t.has_tval())
|
|
s << "core " << std::dec << std::setfill(' ') << std::setw(3) << id
|
|
<< ": tval 0x" << std::hex << std::setfill('0') << std::setw(max_xlen/4)
|
|
<< zext(t.get_tval(), max_xlen) << std::endl;
|
|
debug_output_log(&s);
|
|
}
|
|
|
|
if (state.debug_mode) {
|
|
if (t.cause() == CAUSE_BREAKPOINT) {
|
|
state.pc = DEBUG_ROM_ENTRY;
|
|
} else {
|
|
state.pc = DEBUG_ROM_TVEC;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (t.cause() == CAUSE_BREAKPOINT && (
|
|
(state.prv == PRV_M && state.dcsr->ebreakm) ||
|
|
(state.prv == PRV_S && state.dcsr->ebreaks) ||
|
|
(state.prv == PRV_U && state.dcsr->ebreaku))) {
|
|
enter_debug_mode(DCSR_CAUSE_SWBP);
|
|
return;
|
|
}
|
|
|
|
// By default, trap to M-mode, unless delegated to HS-mode or VS-mode
|
|
reg_t vsdeleg, hsdeleg;
|
|
reg_t bit = t.cause();
|
|
bool curr_virt = state.v;
|
|
bool interrupt = (bit & ((reg_t)1 << (max_xlen-1))) != 0;
|
|
if (interrupt) {
|
|
vsdeleg = (curr_virt && state.prv <= PRV_S) ? (state.mideleg->read() & state.hideleg->read()) : 0;
|
|
hsdeleg = (state.prv <= PRV_S) ? state.mideleg->read() : 0;
|
|
bit &= ~((reg_t)1 << (max_xlen-1));
|
|
} else {
|
|
vsdeleg = (curr_virt && state.prv <= PRV_S) ? (state.medeleg->read() & state.hedeleg->read()) : 0;
|
|
hsdeleg = (state.prv <= PRV_S) ? state.medeleg->read() : 0;
|
|
}
|
|
if (state.prv <= PRV_S && bit < max_xlen && ((vsdeleg >> bit) & 1)) {
|
|
// Handle the trap in VS-mode
|
|
reg_t vector = (state.vstvec->read() & 1) && interrupt ? 4*bit : 0;
|
|
state.pc = (state.vstvec->read() & ~(reg_t)1) + vector;
|
|
state.vscause->write((interrupt) ? (t.cause() - 1) : t.cause());
|
|
state.vsepc->write(epc);
|
|
state.vstval->write(t.get_tval());
|
|
|
|
reg_t s = state.sstatus->read();
|
|
s = set_field(s, MSTATUS_SPIE, get_field(s, MSTATUS_SIE));
|
|
s = set_field(s, MSTATUS_SPP, state.prv);
|
|
s = set_field(s, MSTATUS_SIE, 0);
|
|
state.sstatus->write(s);
|
|
set_privilege(PRV_S);
|
|
} else if (state.prv <= PRV_S && bit < max_xlen && ((hsdeleg >> bit) & 1)) {
|
|
// Handle the trap in HS-mode
|
|
set_virt(false);
|
|
reg_t vector = (state.stvec->read() & 1) && interrupt ? 4*bit : 0;
|
|
state.pc = (state.stvec->read() & ~(reg_t)1) + vector;
|
|
state.scause->write(t.cause());
|
|
state.sepc->write(epc);
|
|
state.stval->write(t.get_tval());
|
|
state.htval->write(t.get_tval2());
|
|
state.htinst->write(t.get_tinst());
|
|
|
|
reg_t s = state.sstatus->read();
|
|
s = set_field(s, MSTATUS_SPIE, get_field(s, MSTATUS_SIE));
|
|
s = set_field(s, MSTATUS_SPP, state.prv);
|
|
s = set_field(s, MSTATUS_SIE, 0);
|
|
state.sstatus->write(s);
|
|
if (extension_enabled('H')) {
|
|
s = state.hstatus->read();
|
|
if (curr_virt)
|
|
s = set_field(s, HSTATUS_SPVP, state.prv);
|
|
s = set_field(s, HSTATUS_SPV, curr_virt);
|
|
s = set_field(s, HSTATUS_GVA, t.has_gva());
|
|
state.hstatus->write(s);
|
|
}
|
|
set_privilege(PRV_S);
|
|
} else {
|
|
// Handle the trap in M-mode
|
|
set_virt(false);
|
|
reg_t vector = (state.mtvec->read() & 1) && interrupt ? 4*bit : 0;
|
|
state.pc = (state.mtvec->read() & ~(reg_t)1) + vector;
|
|
state.mepc->write(epc);
|
|
state.mcause->write(t.cause());
|
|
state.mtval->write(t.get_tval());
|
|
state.mtval2->write(t.get_tval2());
|
|
state.mtinst->write(t.get_tinst());
|
|
|
|
reg_t s = state.mstatus->read();
|
|
s = set_field(s, MSTATUS_MPIE, get_field(s, MSTATUS_MIE));
|
|
s = set_field(s, MSTATUS_MPP, state.prv);
|
|
s = set_field(s, MSTATUS_MIE, 0);
|
|
s = set_field(s, MSTATUS_MPV, curr_virt);
|
|
s = set_field(s, MSTATUS_GVA, t.has_gva());
|
|
state.mstatus->write(s);
|
|
set_privilege(PRV_M);
|
|
}
|
|
}
|
|
|
|
void processor_t::disasm(insn_t insn)
|
|
{
|
|
uint64_t bits = insn.bits() & ((1ULL << (8 * insn_length(insn.bits()))) - 1);
|
|
if (last_pc != state.pc || last_bits != bits) {
|
|
std::stringstream s; // first put everything in a string, later send it to output
|
|
|
|
#ifdef RISCV_ENABLE_COMMITLOG
|
|
const char* sym = get_symbol(state.pc);
|
|
if (sym != nullptr)
|
|
{
|
|
s << "core " << std::dec << std::setfill(' ') << std::setw(3) << id
|
|
<< ": >>>> " << sym << std::endl;
|
|
}
|
|
#endif
|
|
|
|
if (executions != 1) {
|
|
s << "core " << std::dec << std::setfill(' ') << std::setw(3) << id
|
|
<< ": Executed " << executions << " times" << std::endl;
|
|
}
|
|
|
|
s << "core " << std::dec << std::setfill(' ') << std::setw(3) << id
|
|
<< std::hex << ": 0x" << std::setfill('0') << std::setw(max_xlen/4)
|
|
<< zext(state.pc, max_xlen) << " (0x" << std::setw(8) << bits << ") "
|
|
<< disassembler->disassemble(insn) << std::endl;
|
|
|
|
debug_output_log(&s);
|
|
|
|
last_pc = state.pc;
|
|
last_bits = bits;
|
|
executions = 1;
|
|
} else {
|
|
executions++;
|
|
}
|
|
}
|
|
|
|
int processor_t::paddr_bits()
|
|
{
|
|
assert(xlen == max_xlen);
|
|
return max_xlen == 64 ? 50 : 34;
|
|
}
|
|
|
|
void processor_t::set_csr(int which, reg_t val)
|
|
{
|
|
#if defined(RISCV_ENABLE_COMMITLOG)
|
|
#define LOG_CSR(rd) \
|
|
STATE.log_reg_write[((rd) << 4) | 4] = {get_csr(rd), 0};
|
|
#else
|
|
#define LOG_CSR(rd)
|
|
#endif
|
|
|
|
val = zext_xlen(val);
|
|
auto search = state.csrmap.find(which);
|
|
if (search != state.csrmap.end()) {
|
|
search->second->write(val);
|
|
return;
|
|
}
|
|
|
|
switch (which)
|
|
{
|
|
case CSR_SENTROPY:
|
|
es.set_sentropy(val);
|
|
break;
|
|
case CSR_VCSR:
|
|
dirty_vs_state;
|
|
VU.vxsat = (val & VCSR_VXSAT) >> VCSR_VXSAT_SHIFT;
|
|
VU.vxrm = (val & VCSR_VXRM) >> VCSR_VXRM_SHIFT;
|
|
break;
|
|
case CSR_VSTART:
|
|
dirty_vs_state;
|
|
VU.vstart = val & (VU.get_vlen() - 1);
|
|
break;
|
|
case CSR_VXSAT:
|
|
dirty_vs_state;
|
|
VU.vxsat = val & 0x1ul;
|
|
break;
|
|
case CSR_VXRM:
|
|
dirty_vs_state;
|
|
VU.vxrm = val & 0x3ul;
|
|
break;
|
|
}
|
|
|
|
#if defined(RISCV_ENABLE_COMMITLOG)
|
|
switch (which)
|
|
{
|
|
case CSR_VCSR:
|
|
LOG_CSR(CSR_VXSAT);
|
|
LOG_CSR(CSR_VXRM);
|
|
break;
|
|
|
|
case CSR_VSTART:
|
|
LOG_CSR(CSR_VSTART);
|
|
break;
|
|
case CSR_VXSAT:
|
|
LOG_CSR(CSR_VXSAT);
|
|
break;
|
|
case CSR_VXRM:
|
|
LOG_CSR(CSR_VXRM);
|
|
break;
|
|
|
|
case CSR_SENTROPY:
|
|
LOG_CSR(which);
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Note that get_csr is sometimes called when read side-effects should not
|
|
// be actioned. In other words, Spike cannot currently support CSRs with
|
|
// side effects on reads.
|
|
reg_t processor_t::get_csr(int which, insn_t insn, bool write, bool peek)
|
|
{
|
|
reg_t res = 0;
|
|
#define ret(n) do { \
|
|
res = (n); \
|
|
goto out; \
|
|
} while (false)
|
|
|
|
auto search = state.csrmap.find(which);
|
|
if (search != state.csrmap.end()) {
|
|
if (!peek)
|
|
search->second->verify_permissions(insn, write);
|
|
return search->second->read();
|
|
}
|
|
|
|
switch (which)
|
|
{
|
|
case CSR_SENTROPY:
|
|
if (!extension_enabled(EXT_ZKR))
|
|
break;
|
|
/* Read-only access disallowed due to wipe-on-read side effect */
|
|
if (!write && !peek)
|
|
break;
|
|
ret(es.get_sentropy());
|
|
case CSR_VCSR:
|
|
require_vector_vs;
|
|
if (!extension_enabled('V'))
|
|
break;
|
|
ret((VU.vxsat << VCSR_VXSAT_SHIFT) | (VU.vxrm << VCSR_VXRM_SHIFT));
|
|
case CSR_MARCHID: ret(5);
|
|
case CSR_MIMPID: ret(0);
|
|
case CSR_MVENDORID: ret(0);
|
|
case CSR_MHARTID: ret(id);
|
|
case CSR_VSTART:
|
|
require_vector_vs;
|
|
if (!extension_enabled('V'))
|
|
break;
|
|
ret(VU.vstart);
|
|
case CSR_VXSAT:
|
|
require_vector_vs;
|
|
if (!extension_enabled('V'))
|
|
break;
|
|
ret(VU.vxsat);
|
|
case CSR_VXRM:
|
|
require_vector_vs;
|
|
if (!extension_enabled('V'))
|
|
break;
|
|
ret(VU.vxrm);
|
|
case CSR_VL:
|
|
require_vector_vs;
|
|
if (!extension_enabled('V'))
|
|
break;
|
|
ret(VU.vl);
|
|
case CSR_VTYPE:
|
|
require_vector_vs;
|
|
if (!extension_enabled('V'))
|
|
break;
|
|
ret(VU.vtype);
|
|
case CSR_VLENB:
|
|
require_vector_vs;
|
|
if (!extension_enabled('V'))
|
|
break;
|
|
ret(VU.vlenb);
|
|
}
|
|
|
|
#undef ret
|
|
|
|
// If we get here, the CSR doesn't exist. Unimplemented CSRs always throw
|
|
// illegal-instruction exceptions, not virtual-instruction exceptions.
|
|
throw_illegal:
|
|
throw trap_illegal_instruction(insn.bits());
|
|
|
|
throw_virtual:
|
|
throw trap_virtual_instruction(insn.bits());
|
|
|
|
out:
|
|
// Check permissions. Raise virtual-instruction exception if V=1,
|
|
// privileges are insufficient, and the CSR belongs to supervisor or
|
|
// hypervisor. Raise illegal-instruction exception otherwise.
|
|
|
|
if (peek)
|
|
return res;
|
|
|
|
unsigned csr_priv = get_field(which, 0x300);
|
|
unsigned priv = state.prv == PRV_S && !state.v ? PRV_HS : state.prv;
|
|
|
|
if ((csr_priv == PRV_S && !extension_enabled('S')) ||
|
|
(csr_priv == PRV_HS && !extension_enabled('H')))
|
|
goto throw_illegal;
|
|
|
|
if (priv < csr_priv) {
|
|
if (state.v && csr_priv <= PRV_HS)
|
|
goto throw_virtual;
|
|
goto throw_illegal;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
reg_t illegal_instruction(processor_t* p, insn_t insn, reg_t pc)
|
|
{
|
|
throw trap_illegal_instruction(insn.bits());
|
|
}
|
|
|
|
insn_func_t processor_t::decode_insn(insn_t insn)
|
|
{
|
|
// look up opcode in hash table
|
|
size_t idx = insn.bits() % OPCODE_CACHE_SIZE;
|
|
insn_desc_t desc = opcode_cache[idx];
|
|
|
|
if (unlikely(insn.bits() != desc.match || !(xlen == 64 ? desc.rv64 : desc.rv32))) {
|
|
// fall back to linear search
|
|
int cnt = 0;
|
|
insn_desc_t* p = &instructions[0];
|
|
while ((insn.bits() & p->mask) != p->match || !(xlen == 64 ? p->rv64 : p->rv32))
|
|
p++, cnt++;
|
|
desc = *p;
|
|
|
|
if (p->mask != 0 && p > &instructions[0]) {
|
|
if (p->match != (p-1)->match && p->match != (p+1)->match) {
|
|
// move to front of opcode list to reduce miss penalty
|
|
while (--p >= &instructions[0])
|
|
*(p+1) = *p;
|
|
instructions[0] = desc;
|
|
}
|
|
}
|
|
|
|
opcode_cache[idx] = desc;
|
|
opcode_cache[idx].match = insn.bits();
|
|
}
|
|
|
|
return xlen == 64 ? desc.rv64 : desc.rv32;
|
|
}
|
|
|
|
void processor_t::register_insn(insn_desc_t desc)
|
|
{
|
|
instructions.push_back(desc);
|
|
}
|
|
|
|
void processor_t::build_opcode_map()
|
|
{
|
|
struct cmp {
|
|
bool operator()(const insn_desc_t& lhs, const insn_desc_t& rhs) {
|
|
if (lhs.match == rhs.match)
|
|
return lhs.mask > rhs.mask;
|
|
return lhs.match > rhs.match;
|
|
}
|
|
};
|
|
std::sort(instructions.begin(), instructions.end(), cmp());
|
|
|
|
for (size_t i = 0; i < OPCODE_CACHE_SIZE; i++)
|
|
opcode_cache[i] = {0, 0, &illegal_instruction, &illegal_instruction};
|
|
}
|
|
|
|
void processor_t::register_extension(extension_t* x)
|
|
{
|
|
for (auto insn : x->get_instructions())
|
|
register_insn(insn);
|
|
build_opcode_map();
|
|
|
|
if (disassembler)
|
|
for (auto disasm_insn : x->get_disasms())
|
|
disassembler->add_insn(disasm_insn);
|
|
|
|
if (!custom_extensions.insert(std::make_pair(x->name(), x)).second) {
|
|
fprintf(stderr, "extensions must have unique names (got two named \"%s\"!)\n", x->name());
|
|
abort();
|
|
}
|
|
|
|
x->set_processor(this);
|
|
}
|
|
|
|
void processor_t::register_base_instructions()
|
|
{
|
|
#define DECLARE_INSN(name, match, mask) \
|
|
insn_bits_t name##_match = (match), name##_mask = (mask);
|
|
#include "encoding.h"
|
|
#undef DECLARE_INSN
|
|
|
|
#define DEFINE_INSN(name) \
|
|
extern reg_t rv32_##name(processor_t*, insn_t, reg_t); \
|
|
extern reg_t rv64_##name(processor_t*, insn_t, reg_t); \
|
|
register_insn((insn_desc_t){ \
|
|
name##_match, \
|
|
name##_mask, \
|
|
rv32_##name, \
|
|
rv64_##name});
|
|
#include "insn_list.h"
|
|
#undef DEFINE_INSN
|
|
|
|
register_insn({0, 0, &illegal_instruction, &illegal_instruction});
|
|
build_opcode_map();
|
|
}
|
|
|
|
bool processor_t::load(reg_t addr, size_t len, uint8_t* bytes)
|
|
{
|
|
switch (addr)
|
|
{
|
|
case 0:
|
|
if (len <= 4) {
|
|
memset(bytes, 0, len);
|
|
bytes[0] = get_field(state.mip->read(), MIP_MSIP);
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool processor_t::store(reg_t addr, size_t len, const uint8_t* bytes)
|
|
{
|
|
switch (addr)
|
|
{
|
|
case 0:
|
|
if (len <= 4) {
|
|
state.mip->write_with_mask(MIP_MSIP, bytes[0] << IRQ_M_SOFT);
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void processor_t::trigger_updated()
|
|
{
|
|
mmu->flush_tlb();
|
|
mmu->check_triggers_fetch = false;
|
|
mmu->check_triggers_load = false;
|
|
mmu->check_triggers_store = false;
|
|
|
|
for (unsigned i = 0; i < state.num_triggers; i++) {
|
|
if (state.mcontrol[i].execute) {
|
|
mmu->check_triggers_fetch = true;
|
|
}
|
|
if (state.mcontrol[i].load) {
|
|
mmu->check_triggers_load = true;
|
|
}
|
|
if (state.mcontrol[i].store) {
|
|
mmu->check_triggers_store = true;
|
|
}
|
|
}
|
|
}
|
|
|