blob: d744fe369b4ecd2dbf5bc19a8f0d9cebf288694f [file] [log] [blame]
/*
* Copyright (c) 2016 RISC-V Foundation
* Copyright (c) 2016 The University of Virginia
* Copyright (c) 2020 Barkhausen Institut
* Copyright (c) 2022 Google LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* 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;
* neither the name of the copyright holders 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
* OWNER 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.
*/
#include "arch/riscv/isa.hh"
#include <ctime>
#include <set>
#include <sstream>
#include "arch/riscv/faults.hh"
#include "arch/riscv/interrupts.hh"
#include "arch/riscv/mmu.hh"
#include "arch/riscv/pagetable.hh"
#include "arch/riscv/pmp.hh"
#include "arch/riscv/regs/float.hh"
#include "arch/riscv/regs/int.hh"
#include "arch/riscv/regs/misc.hh"
#include "base/bitfield.hh"
#include "base/compiler.hh"
#include "base/logging.hh"
#include "base/trace.hh"
#include "cpu/base.hh"
#include "debug/Checkpoint.hh"
#include "debug/LLSC.hh"
#include "debug/MatRegs.hh"
#include "debug/RiscvMisc.hh"
#include "mem/packet.hh"
#include "mem/request.hh"
#include "params/RiscvISA.hh"
#include "sim/pseudo_inst.hh"
namespace gem5
{
namespace RiscvISA
{
[[maybe_unused]] const std::array<const char *, NUM_MISCREGS> MiscRegNames = {{
[MISCREG_PRV] = "PRV",
[MISCREG_ISA] = "ISA",
[MISCREG_VENDORID] = "VENDORID",
[MISCREG_ARCHID] = "ARCHID",
[MISCREG_IMPID] = "IMPID",
[MISCREG_HARTID] = "HARTID",
[MISCREG_STATUS] = "STATUS",
[MISCREG_IP] = "IP",
[MISCREG_IE] = "IE",
[MISCREG_CYCLE] = "CYCLE",
[MISCREG_TIME] = "TIME",
[MISCREG_INSTRET] = "INSTRET",
[MISCREG_HPMCOUNTER03] = "HPMCOUNTER03",
[MISCREG_HPMCOUNTER04] = "HPMCOUNTER04",
[MISCREG_HPMCOUNTER05] = "HPMCOUNTER05",
[MISCREG_HPMCOUNTER06] = "HPMCOUNTER06",
[MISCREG_HPMCOUNTER07] = "HPMCOUNTER07",
[MISCREG_HPMCOUNTER08] = "HPMCOUNTER08",
[MISCREG_HPMCOUNTER09] = "HPMCOUNTER09",
[MISCREG_HPMCOUNTER10] = "HPMCOUNTER10",
[MISCREG_HPMCOUNTER11] = "HPMCOUNTER11",
[MISCREG_HPMCOUNTER12] = "HPMCOUNTER12",
[MISCREG_HPMCOUNTER13] = "HPMCOUNTER13",
[MISCREG_HPMCOUNTER14] = "HPMCOUNTER14",
[MISCREG_HPMCOUNTER15] = "HPMCOUNTER15",
[MISCREG_HPMCOUNTER16] = "HPMCOUNTER16",
[MISCREG_HPMCOUNTER17] = "HPMCOUNTER17",
[MISCREG_HPMCOUNTER18] = "HPMCOUNTER18",
[MISCREG_HPMCOUNTER19] = "HPMCOUNTER19",
[MISCREG_HPMCOUNTER20] = "HPMCOUNTER20",
[MISCREG_HPMCOUNTER21] = "HPMCOUNTER21",
[MISCREG_HPMCOUNTER22] = "HPMCOUNTER22",
[MISCREG_HPMCOUNTER23] = "HPMCOUNTER23",
[MISCREG_HPMCOUNTER24] = "HPMCOUNTER24",
[MISCREG_HPMCOUNTER25] = "HPMCOUNTER25",
[MISCREG_HPMCOUNTER26] = "HPMCOUNTER26",
[MISCREG_HPMCOUNTER27] = "HPMCOUNTER27",
[MISCREG_HPMCOUNTER28] = "HPMCOUNTER28",
[MISCREG_HPMCOUNTER29] = "HPMCOUNTER29",
[MISCREG_HPMCOUNTER30] = "HPMCOUNTER30",
[MISCREG_HPMCOUNTER31] = "HPMCOUNTER31",
[MISCREG_HPMEVENT03] = "HPMEVENT03",
[MISCREG_HPMEVENT04] = "HPMEVENT04",
[MISCREG_HPMEVENT05] = "HPMEVENT05",
[MISCREG_HPMEVENT06] = "HPMEVENT06",
[MISCREG_HPMEVENT07] = "HPMEVENT07",
[MISCREG_HPMEVENT08] = "HPMEVENT08",
[MISCREG_HPMEVENT09] = "HPMEVENT09",
[MISCREG_HPMEVENT10] = "HPMEVENT10",
[MISCREG_HPMEVENT11] = "HPMEVENT11",
[MISCREG_HPMEVENT12] = "HPMEVENT12",
[MISCREG_HPMEVENT13] = "HPMEVENT13",
[MISCREG_HPMEVENT14] = "HPMEVENT14",
[MISCREG_HPMEVENT15] = "HPMEVENT15",
[MISCREG_HPMEVENT16] = "HPMEVENT16",
[MISCREG_HPMEVENT17] = "HPMEVENT17",
[MISCREG_HPMEVENT18] = "HPMEVENT18",
[MISCREG_HPMEVENT19] = "HPMEVENT19",
[MISCREG_HPMEVENT20] = "HPMEVENT20",
[MISCREG_HPMEVENT21] = "HPMEVENT21",
[MISCREG_HPMEVENT22] = "HPMEVENT22",
[MISCREG_HPMEVENT23] = "HPMEVENT23",
[MISCREG_HPMEVENT24] = "HPMEVENT24",
[MISCREG_HPMEVENT25] = "HPMEVENT25",
[MISCREG_HPMEVENT26] = "HPMEVENT26",
[MISCREG_HPMEVENT27] = "HPMEVENT27",
[MISCREG_HPMEVENT28] = "HPMEVENT28",
[MISCREG_HPMEVENT29] = "HPMEVENT29",
[MISCREG_HPMEVENT30] = "HPMEVENT30",
[MISCREG_HPMEVENT31] = "HPMEVENT31",
[MISCREG_TSELECT] = "TSELECT",
[MISCREG_TDATA1] = "TDATA1",
[MISCREG_TDATA2] = "TDATA2",
[MISCREG_TDATA3] = "TDATA3",
[MISCREG_DCSR] = "DCSR",
[MISCREG_DPC] = "DPC",
[MISCREG_DSCRATCH] = "DSCRATCH",
[MISCREG_MEDELEG] = "MEDELEG",
[MISCREG_MIDELEG] = "MIDELEG",
[MISCREG_MTVEC] = "MTVEC",
[MISCREG_MCOUNTEREN] = "MCOUNTEREN",
[MISCREG_MSCRATCH] = "MSCRATCH",
[MISCREG_MEPC] = "MEPC",
[MISCREG_MCAUSE] = "MCAUSE",
[MISCREG_MTVAL] = "MTVAL",
[MISCREG_PMPCFG0] = "PMPCFG0",
[MISCREG_PMPCFG1] = "PMPCFG1", // pmpcfg1 is rv32 only
[MISCREG_PMPCFG2] = "PMPCFG2",
[MISCREG_PMPCFG3] = "PMPCFG3", // pmpcfg3 is rv32 only
[MISCREG_PMPADDR00] = "PMPADDR00",
[MISCREG_PMPADDR01] = "PMPADDR01",
[MISCREG_PMPADDR02] = "PMPADDR02",
[MISCREG_PMPADDR03] = "PMPADDR03",
[MISCREG_PMPADDR04] = "PMPADDR04",
[MISCREG_PMPADDR05] = "PMPADDR05",
[MISCREG_PMPADDR06] = "PMPADDR06",
[MISCREG_PMPADDR07] = "PMPADDR07",
[MISCREG_PMPADDR08] = "PMPADDR08",
[MISCREG_PMPADDR09] = "PMPADDR09",
[MISCREG_PMPADDR10] = "PMPADDR10",
[MISCREG_PMPADDR11] = "PMPADDR11",
[MISCREG_PMPADDR12] = "PMPADDR12",
[MISCREG_PMPADDR13] = "PMPADDR13",
[MISCREG_PMPADDR14] = "PMPADDR14",
[MISCREG_PMPADDR15] = "PMPADDR15",
[MISCREG_SEDELEG] = "SEDELEG",
[MISCREG_SIDELEG] = "SIDELEG",
[MISCREG_STVEC] = "STVEC",
[MISCREG_SCOUNTEREN] = "SCOUNTEREN",
[MISCREG_SSCRATCH] = "SSCRATCH",
[MISCREG_SEPC] = "SEPC",
[MISCREG_SCAUSE] = "SCAUSE",
[MISCREG_STVAL] = "STVAL",
[MISCREG_SATP] = "SATP",
[MISCREG_UTVEC] = "UTVEC",
[MISCREG_USCRATCH] = "USCRATCH",
[MISCREG_UEPC] = "UEPC",
[MISCREG_UCAUSE] = "UCAUSE",
[MISCREG_UTVAL] = "UTVAL",
[MISCREG_FFLAGS] = "FFLAGS",
[MISCREG_FRM] = "FRM",
[MISCREG_NMIVEC] = "NMIVEC",
[MISCREG_NMIE] = "NMIE",
[MISCREG_NMIP] = "NMIP",
// following are rv32 only registers
[MISCREG_MSTATUSH] = "MSTATUSH",
[MISCREG_CYCLEH] = "CYCLEH",
[MISCREG_TIMEH] = "TIMEH",
[MISCREG_INSTRETH] = "INSTRETH",
[MISCREG_HPMCOUNTER03H] = "HPMCOUNTER03H",
[MISCREG_HPMCOUNTER04H] = "HPMCOUNTER04H",
[MISCREG_HPMCOUNTER05H] = "HPMCOUNTER05H",
[MISCREG_HPMCOUNTER06H] = "HPMCOUNTER06H",
[MISCREG_HPMCOUNTER07H] = "HPMCOUNTER07H",
[MISCREG_HPMCOUNTER08H] = "HPMCOUNTER08H",
[MISCREG_HPMCOUNTER09H] = "HPMCOUNTER09H",
[MISCREG_HPMCOUNTER10H] = "HPMCOUNTER10H",
[MISCREG_HPMCOUNTER11H] = "HPMCOUNTER11H",
[MISCREG_HPMCOUNTER12H] = "HPMCOUNTER12H",
[MISCREG_HPMCOUNTER13H] = "HPMCOUNTER13H",
[MISCREG_HPMCOUNTER14H] = "HPMCOUNTER14H",
[MISCREG_HPMCOUNTER15H] = "HPMCOUNTER15H",
[MISCREG_HPMCOUNTER16H] = "HPMCOUNTER16H",
[MISCREG_HPMCOUNTER17H] = "HPMCOUNTER17H",
[MISCREG_HPMCOUNTER18H] = "HPMCOUNTER18H",
[MISCREG_HPMCOUNTER19H] = "HPMCOUNTER19H",
[MISCREG_HPMCOUNTER20H] = "HPMCOUNTER20H",
[MISCREG_HPMCOUNTER21H] = "HPMCOUNTER21H",
[MISCREG_HPMCOUNTER22H] = "HPMCOUNTER22H",
[MISCREG_HPMCOUNTER23H] = "HPMCOUNTER23H",
[MISCREG_HPMCOUNTER24H] = "HPMCOUNTER24H",
[MISCREG_HPMCOUNTER25H] = "HPMCOUNTER25H",
[MISCREG_HPMCOUNTER26H] = "HPMCOUNTER26H",
[MISCREG_HPMCOUNTER27H] = "HPMCOUNTER27H",
[MISCREG_HPMCOUNTER28H] = "HPMCOUNTER28H",
[MISCREG_HPMCOUNTER29H] = "HPMCOUNTER29H",
[MISCREG_HPMCOUNTER30H] = "HPMCOUNTER30H",
[MISCREG_HPMCOUNTER31H] = "HPMCOUNTER31H",
}};
namespace
{
/* Not applicable to RISCV */
RegClass vecRegClass(VecRegClass, VecRegClassName, 1, debug::IntRegs);
RegClass vecElemClass(VecElemClass, VecElemClassName, 2, debug::IntRegs);
RegClass vecPredRegClass(VecPredRegClass, VecPredRegClassName, 1,
debug::IntRegs);
RegClass matRegClass(MatRegClass, MatRegClassName, 1, debug::MatRegs);
RegClass ccRegClass(CCRegClass, CCRegClassName, 0, debug::IntRegs);
} // anonymous namespace
ISA::ISA(const Params &p) :
BaseISA(p), rv_type(p.riscv_type), checkAlignment(p.check_alignment)
{
_regClasses.push_back(&intRegClass);
_regClasses.push_back(&floatRegClass);
_regClasses.push_back(&vecRegClass);
_regClasses.push_back(&vecElemClass);
_regClasses.push_back(&vecPredRegClass);
_regClasses.push_back(&matRegClass);
_regClasses.push_back(&ccRegClass);
_regClasses.push_back(&miscRegClass);
miscRegFile.resize(NUM_MISCREGS);
clear();
}
bool ISA::inUserMode() const
{
return miscRegFile[MISCREG_PRV] == PRV_U;
}
void
ISA::copyRegsFrom(ThreadContext *src)
{
// First loop through the integer registers.
for (auto &id: intRegClass)
tc->setReg(id, src->getReg(id));
// Second loop through the float registers.
for (auto &id: floatRegClass)
tc->setReg(id, src->getReg(id));
// Lastly copy PC/NPC
tc->pcState(src->pcState());
}
void ISA::clear()
{
std::fill(miscRegFile.begin(), miscRegFile.end(), 0);
miscRegFile[MISCREG_PRV] = PRV_M;
miscRegFile[MISCREG_VENDORID] = 0;
miscRegFile[MISCREG_ARCHID] = 0;
miscRegFile[MISCREG_IMPID] = 0;
MISA misa = 0;
STATUS status = 0;
// default config arch isa string is rv64(32)imafdc
misa.rvi = misa.rvm = misa.rva = misa.rvf = misa.rvd = misa.rvc = 1;
// default privlege modes if MSU
misa.rvs = misa.rvu = 1;
// mark FS is initial
status.fs = INITIAL;
// rv_type dependent init.
switch (rv_type) {
case RV32:
misa.rv32_mxl = 1;
break;
case RV64:
misa.rv64_mxl = 2;
status.uxl = status.sxl = 2;
break;
default:
panic("%s: Unknown rv_type: %d", name(), (int)rv_type);
}
miscRegFile[MISCREG_ISA] = misa;
miscRegFile[MISCREG_STATUS] = status;
miscRegFile[MISCREG_MCOUNTEREN] = 0x7;
miscRegFile[MISCREG_SCOUNTEREN] = 0x7;
// don't set it to zero; software may try to determine the supported
// triggers, starting at zero. simply set a different value here.
miscRegFile[MISCREG_TSELECT] = 1;
// NMI is always enabled.
miscRegFile[MISCREG_NMIE] = 1;
}
bool
ISA::hpmCounterEnabled(int misc_reg) const
{
int hpmcounter = 0;
if (misc_reg >= MISCREG_CYCLEH) {
hpmcounter = misc_reg - MISCREG_CYCLEH;
} else {
hpmcounter = misc_reg - MISCREG_CYCLE;
}
if (hpmcounter < 0 || hpmcounter > 31)
panic("Illegal HPM counter %d\n", hpmcounter);
int counteren;
switch (readMiscRegNoEffect(MISCREG_PRV)) {
case PRV_M:
return true;
case PRV_S:
counteren = MISCREG_MCOUNTEREN;
break;
case PRV_U:
counteren = MISCREG_SCOUNTEREN;
break;
default:
panic("Unknown privilege level %d\n", miscRegFile[MISCREG_PRV]);
return false;
}
return (miscRegFile[counteren] & (1ULL << (hpmcounter))) > 0;
}
RegVal
ISA::readMiscRegNoEffect(RegIndex idx) const
{
// Illegal CSR
panic_if(idx > NUM_MISCREGS, "Illegal CSR index %#x\n", idx);
DPRINTF(RiscvMisc, "Reading MiscReg %s (%d): %#x.\n",
MiscRegNames[idx], idx, miscRegFile[idx]);
return miscRegFile[idx];
}
RegVal
ISA::readMiscReg(RegIndex idx)
{
switch (idx) {
case MISCREG_HARTID:
return tc->contextId();
case MISCREG_CYCLE:
if (hpmCounterEnabled(MISCREG_CYCLE)) {
DPRINTF(RiscvMisc, "Cycle counter at: %llu.\n",
tc->getCpuPtr()->curCycle());
return static_cast<RegVal>(tc->getCpuPtr()->curCycle());
} else {
warn("Cycle counter disabled.\n");
return 0;
}
case MISCREG_CYCLEH:
if (hpmCounterEnabled(MISCREG_CYCLEH)) {
DPRINTF(RiscvMisc, "Cycle counter at: %llu.\n",
tc->getCpuPtr()->curCycle());
return bits<RegVal>(tc->getCpuPtr()->curCycle(), 63, 32);
} else {
warn("Cycle counter disabled.\n");
return 0;
}
case MISCREG_TIME:
if (hpmCounterEnabled(MISCREG_TIME)) {
DPRINTF(RiscvMisc, "Wall-clock counter at: %llu.\n",
std::time(nullptr));
return readMiscRegNoEffect(MISCREG_TIME);
} else {
warn("Wall clock disabled.\n");
return 0;
}
case MISCREG_TIMEH:
if (hpmCounterEnabled(MISCREG_TIMEH)) {
DPRINTF(RiscvMisc, "Wall-clock counter at: %llu.\n",
std::time(nullptr));
return readMiscRegNoEffect(MISCREG_TIMEH);
} else {
warn("Wall clock disabled.\n");
return 0;
}
case MISCREG_INSTRET:
if (hpmCounterEnabled(MISCREG_INSTRET)) {
DPRINTF(RiscvMisc, "Instruction counter at: %llu.\n",
tc->getCpuPtr()->totalInsts());
return static_cast<RegVal>(tc->getCpuPtr()->totalInsts());
} else {
warn("Instruction counter disabled.\n");
return 0;
}
case MISCREG_INSTRETH:
if (hpmCounterEnabled(MISCREG_INSTRETH)) {
DPRINTF(RiscvMisc, "Instruction counter at: %llu.\n",
tc->getCpuPtr()->totalInsts());
return bits<RegVal>(tc->getCpuPtr()->totalInsts(), 63, 32);
} else {
warn("Instruction counter disabled.\n");
return 0;
}
case MISCREG_IP:
{
auto ic = dynamic_cast<RiscvISA::Interrupts *>(
tc->getCpuPtr()->getInterruptController(tc->threadId()));
return ic->readIP();
}
case MISCREG_IE:
{
auto ic = dynamic_cast<RiscvISA::Interrupts *>(
tc->getCpuPtr()->getInterruptController(tc->threadId()));
return ic->readIE();
}
case MISCREG_SEPC:
case MISCREG_MEPC:
{
MISA misa = readMiscRegNoEffect(MISCREG_ISA);
auto val = readMiscRegNoEffect(idx);
// if compressed instructions are disabled, epc[1] is set to 0
if (misa.rvc == 0)
return mbits(val, 63, 2);
// epc[0] is always 0
else
return mbits(val, 63, 1);
}
case MISCREG_STATUS:
{
// Updating the SD bit.
// . Per RISC-V ISA Manual, vol II, section 3.1.6.6, page 26,
// the SD bit is a read-only bit indicating whether any of
// FS, VS, and XS fields being in the respective dirty state.
// . Per section 3.1.6, page 20, the SD bit is the most
// significant bit of the MSTATUS CSR for both RV32 and RV64.
// . Per section 3.1.6.6, page 29, the explicit formula for
// updating the SD is,
// SD = ((FS==DIRTY) | (XS==DIRTY) | (VS==DIRTY))
// . Ideally, we want to update the SD after every relevant
// instruction, however, lazily updating the Status register
// upon its read produces the same effect as well.
STATUS status = readMiscRegNoEffect(idx);
uint64_t sd_bit = \
(status.xs == 3) || (status.fs == 3) || (status.vs == 3);
// For RV32, the SD bit is at index 31
// For RV64, the SD bit is at index 63.
switch (rv_type) {
case RV32:
status.rv32_sd = sd_bit;
break;
case RV64:
status.rv64_sd = sd_bit;
break;
default:
panic("%s: Unknown rv_type: %d", name(), (int)rv_type);
}
setMiscRegNoEffect(idx, status);
return readMiscRegNoEffect(idx);
}
default:
// Try reading HPM counters
// As a placeholder, all HPM counters are just cycle counters
if (idx >= MISCREG_HPMCOUNTER03 &&
idx <= MISCREG_HPMCOUNTER31) {
if (hpmCounterEnabled(idx)) {
DPRINTF(RiscvMisc, "HPM counter %d: %llu.\n",
idx - MISCREG_CYCLE, tc->getCpuPtr()->curCycle());
return tc->getCpuPtr()->curCycle();
} else {
warn("HPM counter %d disabled.\n", idx - MISCREG_CYCLE);
return 0;
}
} else if (idx >= MISCREG_HPMCOUNTER03H &&
idx <= MISCREG_HPMCOUNTER31H) {
if (hpmCounterEnabled(idx)) {
DPRINTF(RiscvMisc, "HPM counter %d: %llu.\n",
idx - MISCREG_CYCLE, tc->getCpuPtr()->curCycle());
return bits<RegVal>(tc->getCpuPtr()->curCycle(), 63, 32);
} else {
warn("HPM counter %d disabled.\n", idx - MISCREG_CYCLE);
return 0;
}
}
return readMiscRegNoEffect(idx);
}
}
void
ISA::setMiscRegNoEffect(RegIndex idx, RegVal val)
{
// Illegal CSR
panic_if(idx > NUM_MISCREGS, "Illegal CSR index %#x\n", idx);
DPRINTF(RiscvMisc, "Setting MiscReg %s (%d) to %#x.\n",
MiscRegNames[idx], idx, val);
miscRegFile[idx] = val;
}
void
ISA::setMiscReg(RegIndex idx, RegVal val)
{
if (idx >= MISCREG_CYCLE && idx <= MISCREG_HPMCOUNTER31) {
// Ignore writes to HPM counters for now
warn("Ignoring write to miscreg %s.\n", MiscRegNames[idx]);
} else {
switch (idx) {
// From section 3.7.1 of RISCV priv. specs
// V1.12, the odd-numbered configuration
// registers are illegal for RV64 and
// each 64 bit CFG register hold configurations
// for 8 PMP entries.
case MISCREG_PMPCFG0:
case MISCREG_PMPCFG1:
case MISCREG_PMPCFG2:
case MISCREG_PMPCFG3:
{
// PMP registers should only be modified in M mode
assert(readMiscRegNoEffect(MISCREG_PRV) == PRV_M);
int regSize = 0;
switch (rv_type) {
case RV32:
regSize = 4;
break;
case RV64:
regSize = 8;
break;
default:
panic("%s: Unknown rv_type: %d", name(), (int)rv_type);
}
// Specs do not seem to mention what should be
// configured first, cfg or address regs!
// qemu seems to update the tables when
// pmp addr regs are written (with the assumption
// that cfg regs are already written)
RegVal res = 0;
RegVal old_val = readMiscRegNoEffect(idx);
for (int i=0; i < regSize; i++) {
uint8_t cfg_val = (val >> (8*i)) & 0xff;
auto mmu = dynamic_cast<RiscvISA::MMU *>
(tc->getMMUPtr());
// Form pmp_index using the index i and
// PMPCFG register number
uint32_t pmp_index = i+(4*(idx-MISCREG_PMPCFG0));
bool result = mmu->getPMP()->pmpUpdateCfg(pmp_index,cfg_val);
if (result) {
res |= ((RegVal)cfg_val << (8*i));
} else {
res |= (old_val & (0xFF << (8*i)));
}
}
setMiscRegNoEffect(idx, res);
}
break;
case MISCREG_PMPADDR00 ... MISCREG_PMPADDR15:
{
// PMP registers should only be modified in M mode
assert(readMiscRegNoEffect(MISCREG_PRV) == PRV_M);
auto mmu = dynamic_cast<RiscvISA::MMU *>
(tc->getMMUPtr());
uint32_t pmp_index = idx-MISCREG_PMPADDR00;
if (mmu->getPMP()->pmpUpdateAddr(pmp_index, val)) {
setMiscRegNoEffect(idx, val);
}
}
break;
case MISCREG_IP:
{
auto ic = dynamic_cast<RiscvISA::Interrupts *>(
tc->getCpuPtr()->getInterruptController(tc->threadId()));
ic->setIP(val);
}
break;
case MISCREG_IE:
{
auto ic = dynamic_cast<RiscvISA::Interrupts *>(
tc->getCpuPtr()->getInterruptController(tc->threadId()));
ic->setIE(val);
}
break;
case MISCREG_SATP:
{
// we only support bare and Sv39 mode; setting a different mode
// shall have no effect (see 4.1.12 in priv ISA manual)
SATP cur_val = readMiscRegNoEffect(idx);
SATP new_val = val;
if (new_val.mode != AddrXlateMode::BARE &&
new_val.mode != AddrXlateMode::SV39)
new_val.mode = cur_val.mode;
setMiscRegNoEffect(idx, new_val);
}
break;
case MISCREG_TSELECT:
{
// we don't support debugging, so always set a different value
// than written
setMiscRegNoEffect(idx, val + 1);
}
break;
case MISCREG_ISA:
{
MISA cur_misa = (MISA)readMiscRegNoEffect(MISCREG_ISA);
MISA new_misa = (MISA)val;
// only allow to disable compressed instructions
// if the following instruction is 4-byte aligned
if (new_misa.rvc == 0 &&
bits(tc->pcState().as<RiscvISA::PCState>().npc(),
2, 0) != 0) {
new_misa.rvc = new_misa.rvc | cur_misa.rvc;
}
setMiscRegNoEffect(idx, new_misa);
}
break;
case MISCREG_STATUS:
{
if (rv_type != RV32) {
// SXL and UXL are hard-wired to 64 bit
auto cur = readMiscRegNoEffect(idx);
val &= ~(STATUS_SXL_MASK | STATUS_UXL_MASK);
val |= cur & (STATUS_SXL_MASK | STATUS_UXL_MASK);
}
setMiscRegNoEffect(idx, val);
}
break;
default:
setMiscRegNoEffect(idx, val);
}
}
}
void
ISA::serialize(CheckpointOut &cp) const
{
DPRINTF(Checkpoint, "Serializing Riscv Misc Registers\n");
SERIALIZE_CONTAINER(miscRegFile);
}
void
ISA::unserialize(CheckpointIn &cp)
{
DPRINTF(Checkpoint, "Unserializing Riscv Misc Registers\n");
UNSERIALIZE_CONTAINER(miscRegFile);
}
const int WARN_FAILURE = 10000;
const Addr INVALID_RESERVATION_ADDR = (Addr) -1;
std::unordered_map<int, Addr> load_reservation_addrs;
void
ISA::handleLockedSnoop(PacketPtr pkt, Addr cacheBlockMask)
{
Addr& load_reservation_addr = load_reservation_addrs[tc->contextId()];
if (load_reservation_addr == INVALID_RESERVATION_ADDR)
return;
Addr snoop_addr = pkt->getAddr() & cacheBlockMask;
DPRINTF(LLSC, "Locked snoop on address %x.\n", snoop_addr);
if ((load_reservation_addr & cacheBlockMask) == snoop_addr)
load_reservation_addr = INVALID_RESERVATION_ADDR;
}
void
ISA::handleLockedRead(const RequestPtr &req)
{
Addr& load_reservation_addr = load_reservation_addrs[tc->contextId()];
load_reservation_addr = req->getPaddr() & ~0xF;
DPRINTF(LLSC, "[cid:%d]: Reserved address %x.\n",
req->contextId(), req->getPaddr() & ~0xF);
}
bool
ISA::handleLockedWrite(const RequestPtr &req, Addr cacheBlockMask)
{
Addr& load_reservation_addr = load_reservation_addrs[tc->contextId()];
bool lr_addr_empty = (load_reservation_addr == INVALID_RESERVATION_ADDR);
// Normally RISC-V uses zero to indicate success and nonzero to indicate
// failure (right now only 1 is reserved), but in gem5 zero indicates
// failure and one indicates success, so here we conform to that (it should
// be switched in the instruction's implementation)
DPRINTF(LLSC, "[cid:%d]: load_reservation_addrs empty? %s.\n",
req->contextId(),
lr_addr_empty ? "yes" : "no");
if (!lr_addr_empty) {
DPRINTF(LLSC, "[cid:%d]: addr = %x.\n", req->contextId(),
req->getPaddr() & ~0xF);
DPRINTF(LLSC, "[cid:%d]: last locked addr = %x.\n", req->contextId(),
load_reservation_addr);
}
if (lr_addr_empty
|| load_reservation_addr != ((req->getPaddr() & ~0xF))) {
req->setExtraData(0);
int stCondFailures = tc->readStCondFailures();
tc->setStCondFailures(++stCondFailures);
if (stCondFailures % WARN_FAILURE == 0) {
warn("%i: context %d: %d consecutive SC failures.\n",
curTick(), tc->contextId(), stCondFailures);
}
return false;
}
if (req->isUncacheable()) {
req->setExtraData(2);
}
return true;
}
void
ISA::globalClearExclusive()
{
tc->getCpuPtr()->wakeup(tc->threadId());
}
void
ISA::resetThread()
{
Reset().invoke(tc);
}
} // namespace RiscvISA
} // namespace gem5
std::ostream &
operator<<(std::ostream &os, gem5::RiscvISA::PrivilegeMode pm)
{
switch (pm) {
case gem5::RiscvISA::PRV_U:
return os << "PRV_U";
case gem5::RiscvISA::PRV_S:
return os << "PRV_S";
case gem5::RiscvISA::PRV_M:
return os << "PRV_M";
}
return os << "PRV_<invalid>";
}