| /* | |
| * 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); | |
| } | |
| 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(); | |
| DPRINTF(LLSC, "[cid:%d]: Reserved address %x.\n", | |
| req->contextId(), req->getPaddr()); | |
| } | |
| 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() & cacheBlockMask); | |
| DPRINTF(LLSC, "[cid:%d]: last locked addr = %x.\n", req->contextId(), | |
| load_reservation_addr & cacheBlockMask); | |
| } | |
| if (lr_addr_empty || | |
| (load_reservation_addr & cacheBlockMask) | |
| != ((req->getPaddr() & cacheBlockMask))) { | |
| 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); | |
| } | |
| // Must clear any reservations | |
| load_reservation_addr = INVALID_RESERVATION_ADDR; | |
| return false; | |
| } | |
| if (req->isUncacheable()) { | |
| req->setExtraData(2); | |
| } | |
| // Must clear any reservations | |
| load_reservation_addr = INVALID_RESERVATION_ADDR; | |
| DPRINTF(LLSC, "[cid:%d]: SC success! Current locked addr = %x.\n", | |
| req->contextId(), load_reservation_addr & cacheBlockMask); | |
| return true; | |
| } | |
| void | |
| ISA::globalClearExclusive() | |
| { | |
| tc->getCpuPtr()->wakeup(tc->threadId()); | |
| Addr& load_reservation_addr = load_reservation_addrs[tc->contextId()]; | |
| load_reservation_addr = INVALID_RESERVATION_ADDR; | |
| } | |
| 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>"; | |
| } |