From de06ab35efefe162a2e78f046ddbd7b27b99b016 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 4 Feb 2021 13:02:19 +0800 Subject: [PATCH] arch-riscv: PLIC Implementation This patch contains the implementation for the RISC-V PLIC. The PLIC Memory Map is based on the SiFive U54MC datasheet. The PLIC models a 3-cycle latency as stated in the RISC-V specs. Change-Id: I571c7bd3bd2918c92e4f207a1b57cf9d06e9c72f Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/40598 Reviewed-by: Ayaz Akram Reviewed-by: Jason Lowe-Power Tested-by: kokoro Maintainer: Jason Lowe-Power --- src/dev/riscv/Plic.py | 52 ++++ src/dev/riscv/SConscript | 3 + src/dev/riscv/plic.cc | 544 +++++++++++++++++++++++++++++++++++++++ src/dev/riscv/plic.hh | 283 ++++++++++++++++++++ 4 files changed, 882 insertions(+) create mode 100644 src/dev/riscv/Plic.py create mode 100644 src/dev/riscv/plic.cc create mode 100644 src/dev/riscv/plic.hh diff --git a/src/dev/riscv/Plic.py b/src/dev/riscv/Plic.py new file mode 100644 index 0000000000..0e2f386e2e --- /dev/null +++ b/src/dev/riscv/Plic.py @@ -0,0 +1,52 @@ +# Copyright (c) 2021 Huawei International +# All rights reserved. +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# +# 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. + +from m5.objects.Device import BasicPioDevice +from m5.params import * +from m5.proxy import * + +class Plic(BasicPioDevice): + """ + This implementation of PLIC is based on + the SiFive U54MC datasheet: + https://sifive.cdn.prismic.io/sifive/fab000f6- + 0e07-48d0-9602-e437d5367806_sifive_U54MC_rtl_ + full_20G1.03.00_manual.pdf + """ + type = 'Plic' + cxx_header = 'dev/riscv/plic.hh' + intrctrl = Param.IntrControl(Parent.any, "interrupt controller") + pio_size = Param.Addr(0x4000000, "PIO Size") + n_src = Param.Int("Number of interrupt sources") diff --git a/src/dev/riscv/SConscript b/src/dev/riscv/SConscript index 43946c2bfa..08d462b030 100755 --- a/src/dev/riscv/SConscript +++ b/src/dev/riscv/SConscript @@ -31,9 +31,12 @@ Import('*') if env['TARGET_ISA'] == 'riscv': SimObject('Clint.py') + SimObject('Plic.py') SimObject('RTC.py') DebugFlag('Clint') + DebugFlag('Plic') Source('clint.cc') + Source('plic.cc') Source('rtc.cc') diff --git a/src/dev/riscv/plic.cc b/src/dev/riscv/plic.cc new file mode 100644 index 0000000000..60ac192201 --- /dev/null +++ b/src/dev/riscv/plic.cc @@ -0,0 +1,544 @@ +/* + * Copyright (c) 2021 Huawei International + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * 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 "dev/riscv/plic.hh" + +#include + +#include "arch/riscv/registers.hh" +#include "debug/Plic.hh" +#include "mem/packet.hh" +#include "mem/packet_access.hh" +#include "params/Plic.hh" +#include "sim/system.hh" + +using namespace RiscvISA; + +Plic::Plic(const Params ¶ms) : + BasicPioDevice(params, params.pio_size), + system(params.system), + intrctrl(params.intrctrl), + nSrc(params.n_src), + registers(params.name, pioAddr, this), + update([this]{updateOutput();}, name() + ".update") +{ +} + +void +Plic::post(int src_id) +{ + // Sanity check + assert(src_id < nSrc && src_id >= 0); + + // Update pending bit + int src_index = src_id >> 5; + int src_offset = src_id & 0x1F; + + uint32_t& pending = registers.pending[src_index].get(); + std::bitset<32> pending_bits(pending); + pending_bits[src_offset] = 1; + pending = (uint32_t) pending_bits.to_ulong(); + + // Update states + pendingPriority[src_id] = registers.priority[src_id].get(); + for (int i = 0; i < nContext; i++) { + bool enabled = bits(registers.enable[i][src_index].get(), src_offset); + effPriority[i][src_id] = enabled ? pendingPriority[src_id] : 0; + } + DPRINTF(Plic, + "Int post request - source: %#x, current priority: %#x\n", + src_id, pendingPriority[src_id]); + + // Propagate output changes + propagateOutput(); +} + +void +Plic::clear(int src_id) +{ + // Sanity check + assert(src_id < nSrc); + assert(src_id >= 0); + + // Update pending bit + int src_index = src_id >> 5; + int src_offset = src_id & 0x1F; + uint32_t& pending = registers.pending[src_index].get(); + std::bitset<32> pending_bits(pending); + pending_bits[src_offset] = 0; + pending = (uint32_t) pending_bits.to_ulong(); + + // Update states + pendingPriority[src_id] = 0; + for (int i = 0; i < nContext; i++) { + effPriority[i][src_id] = 0; + } + DPRINTF(Plic, + "Int clear request - source: %#x, current priority: %#x\n", + src_id, pendingPriority[src_id]); + + // Propagate output changes + propagateOutput(); +} + +Tick +Plic::read(PacketPtr pkt) +{ + // Check for atomic operation + bool is_atomic = pkt->isAtomicOp() && pkt->cmd == MemCmd::SwapReq; + DPRINTF(Plic, + "Read request - addr: %#x, size: %#x, atomic:%d\n", + pkt->getAddr(), pkt->getSize(), is_atomic); + + // Perform register read + registers.read(pkt->getAddr(), pkt->getPtr(), pkt->getSize()); + + if (is_atomic) { + // Perform atomic operation + (*(pkt->getAtomicOp()))(pkt->getPtr()); + return write(pkt); + } else { + pkt->makeResponse(); + return pioDelay; + } +} + +Tick +Plic::write(PacketPtr pkt) +{ + DPRINTF(Plic, + "Write request - addr: %#x, size: %#x\n", + pkt->getAddr(), pkt->getSize()); + + // Perform register write + registers.write(pkt->getAddr(), pkt->getPtr(), pkt->getSize()); + + // Propagate output changes + propagateOutput(); + + // Apply threshold changes + updateInt(); + + pkt->makeResponse(); + return pioDelay; +} + +void +Plic::init() +{ + // Number of contexts + nContext = system->threads.size() * 2; + // Number of 32-bit pending registesrs where + // each bit correspondings to one interrupt source + nSrc32 = divCeil(nSrc, 32); + + // Setup register bank + registers.init(); + + // Setup internal states + pendingPriority.resize(nSrc, 0x0); + for (int i = 0; i < nContext; i++) { + std::vector context_priority(nSrc, 0x0); + effPriority.push_back(context_priority); + } + lastID.resize(nContext, 0x0); + + // Setup outputs + output = PlicOutput{ + std::vector(nContext, 0x0), + std::vector(nContext, 0x0)}; + + DPRINTF(Plic, + "Device init - %d contexts, %d sources, %d pending registers\n", + nContext, nSrc, nSrc32); + + BasicPioDevice::init(); +} + +void +Plic::PlicRegisters::init() +{ + using namespace std::placeholders; + + // Calculate reserved space size + const size_t reserve0_size = pendingStart - plic->nSrc * 4; + reserved.emplace_back("reserved0", reserve0_size); + const size_t reserve1_size = enableStart - pendingStart + - plic->nSrc32 * 4; + reserved.emplace_back("reserved1", reserve1_size); + const size_t reserve2_size = thresholdStart - enableStart + - plic->nSrc32 * plic->nContext * enablePadding; + reserved.emplace_back("reserved2", reserve2_size); + const size_t reserve3_size = plic->pioSize - thresholdStart + - plic->nContext * thresholdPadding; + reserved.emplace_back("reserved3", reserve3_size); + + // Sanity check + assert(plic->pioSize >= thresholdStart + + plic->nContext * thresholdPadding); + assert((int) plic->pioSize <= maxBankSize); + + // Calculate hole sizes + const size_t enable_hole_size = enablePadding - plic->nSrc32 * 4; + const size_t claim_hole_size = thresholdPadding - 0x8; + + // Initialize registers + for (int i = 0; i < plic->nSrc; i++) { + priority.emplace_back( + std::string("priority") + std::to_string(i), 0); + } + for (int i = 0; i < plic->nSrc32; i++) { + pending.emplace_back( + std::string("pending") + std::to_string(i), 0); + } + for (int i = 0; i < plic->nContext; i++) { + + enable.push_back(std::vector()); + for (int j = 0; j < plic->nSrc32; j++) { + enable[i].emplace_back( + std::string("enable") + std::to_string(i) + + "_" + std::to_string(j), 0); + } + enable_holes.emplace_back( + std::string("enable_hole") + std::to_string(i), enable_hole_size); + + threshold.emplace_back( + std::string("threshold") + std::to_string(i), 0); + claim.emplace_back( + std::string("claim") + std::to_string(i), 0); + claim_holes.emplace_back( + std::string("claim_hole") + std::to_string(i), claim_hole_size); + } + + // Add registers to bank + // Priority + for (int i = 0; i < plic->nSrc; i++) { + auto write_cb = std::bind(&Plic::writePriority, plic, _1, _2, i); + priority[i].writer(write_cb); + addRegister(priority[i]); + } + addRegister(reserved[0]); + + // Pending + for (int i = 0; i < plic->nSrc32; i++) { + pending[i].readonly(); + addRegister(pending[i]); + } + addRegister(reserved[1]); + + // Enable + for (int i = 0; i < plic->nContext; i++) { + for (int j = 0; j < plic->nSrc32; j++) { + auto write_cb = std::bind(&Plic::writeEnable, plic, _1, _2, j, i); + enable[i][j].writer(write_cb); + addRegister(enable[i][j]); + } + addRegister(enable_holes[i]); + } + addRegister(reserved[2]); + + // Threshold and claim + for (int i = 0; i < plic->nContext; i++) { + auto threshold_cb = std::bind(&Plic::writeThreshold, plic, _1, _2, i); + threshold[i].writer(threshold_cb); + auto read_cb = std::bind(&Plic::readClaim, plic, _1, i); + auto write_cb = std::bind(&Plic::writeClaim, plic, _1, _2, i); + claim[i].reader(read_cb) + .writer(write_cb); + addRegister(threshold[i]); + addRegister(claim[i]); + addRegister(claim_holes[i]); + } + addRegister(reserved[3]); +} + +void +Plic::writePriority(Register32& reg, const uint32_t& data, const int src_id) +{ + reg.update(data); + + // Calculate indices + int src_index = src_id >> 5; + int src_offset = src_id & 0x1F; + + // Update states + bool pending = bits(registers.pending[src_index].get(), src_offset); + pendingPriority[src_id] = pending ? reg.get() : 0; + for (int i = 0; i < nContext; i++) { + bool enabled = bits( + registers.enable[i][src_index].get(), src_offset); + effPriority[i][src_id] = enabled ? pendingPriority[src_id] : 0; + } + + DPRINTF(Plic, + "Priority updated - src: %d, val: %d\n", + src_id, reg.get()); +} + +void +Plic::writeEnable(Register32& reg, const uint32_t& data, + const int src32_id, const int context_id) +{ + reg.update(data); + + for (int i = 0; i < 32; i ++) { + int src_id = (src32_id << 5) + i; + if (src_id < nSrc) { + effPriority[context_id][src_id] = + bits(reg.get(), i) ? pendingPriority[src_id] : 0; + } + } + DPRINTF(Plic, + "Enable updated - context: %d, src32: %d, val: %#x\n", + context_id, src32_id, reg.get()); +} + +void +Plic::writeThreshold(Register32& reg, const uint32_t& data, + const int context_id) +{ + DPRINTF(Plic, + "Threshold updated - context: %d, val: %d\n", + context_id, reg.get()); +} + +uint32_t +Plic::readClaim(Register32& reg, const int context_id) +{ + if (lastID[context_id] == 0) { + // Calculate indices + uint32_t max_int_id = output.maxID[context_id]; + int src_index = max_int_id >> 5; + int src_offset = max_int_id & 0x1F; + + // Check pending bits + if (bits(registers.pending[src_index].get(), src_offset)) { + lastID[context_id] = max_int_id; + DPRINTF(Plic, + "Claim success - context: %d, interrupt ID: %d\n", + context_id, max_int_id); + clear(max_int_id); + reg.update(max_int_id); + } else { + DPRINTF(Plic, + "Claim already cleared - context: %d, interrupt ID: %d\n", + context_id, max_int_id); + reg.update(0); + } + } else { + warn("PLIC claim failed (not completed) - context: %d", context_id); + reg.update(0); + } + return reg.get(); +} + +void +Plic::writeClaim(Register32& reg, const uint32_t& data, const int context_id) +{ + reg.update(data); + + /** + * Plic spec states that this error should be silently ignored. + * However, this is not supposed to happen. + */ + assert(lastID[context_id] == reg.get()); + lastID[context_id] = 0; + DPRINTF(Plic, + "Complete - context: %d, interrupt ID: %d\n", + context_id, reg.get()); +} + +void +Plic::propagateOutput() +{ + // Calculate new output + PlicOutput new_output{ + std::vector(nContext, 0x0), + std::vector(nContext, 0x0)}; + uint32_t max_id; + uint32_t max_priority; + for (int i = 0; i < nContext; i++) { + max_id = max_element(effPriority[i].begin(), + effPriority[i].end()) - effPriority[i].begin(); + max_priority = effPriority[i][max_id]; + new_output.maxID[i] = max_id; + new_output.maxPriority[i] = max_priority; + } + + // Add new output to outputQueue + Tick next_update = curTick() + cyclesToTicks(Cycles(3)); + if (outputQueue.find(next_update) != outputQueue.end()) { + outputQueue[next_update] = new_output; + } else { + outputQueue.insert({next_update, new_output}); + } + + // Schedule next update event + if (!update.scheduled()) { + DPRINTF(Plic, "Update scheduled - tick: %d\n", next_update); + schedule(update, next_update); + } +} + +void +Plic::updateOutput() +{ + DPRINTF(Plic, "Update triggered\n"); + // Set current output to new output + output = outputQueue.begin()->second; + outputQueue.erase(outputQueue.begin()->first); + + // Schedule next update event (if any) + if (!outputQueue.empty()) { + DPRINTF(Plic, "Update scheduled - tick: %d\n", + outputQueue.begin()->first); + schedule(update, outputQueue.begin()->first); + } + + updateInt(); +} + +void +Plic::updateInt() +{ + // Update xEIP lines + for (int i = 0; i < nContext; i++) { + int thread_id = i >> 1; + int int_id = (i & 1) ? + ExceptionCode::INT_EXT_SUPER : ExceptionCode::INT_EXT_MACHINE; + + uint32_t max_id = output.maxID[i]; + uint32_t priority = output.maxPriority[i]; + uint32_t threshold = registers.threshold[i].get(); + if (priority > threshold && max_id > 0) { + DPRINTF(Plic, + "Int posted - thread: %d, int id: %d, ", + thread_id, int_id); + DPRINTF(Plic, + "pri: %d, thres: %d\n", priority, threshold); + intrctrl->post(thread_id, int_id, 0); + } else { + if (priority > 0) { + DPRINTF(Plic, + "Int filtered - thread: %d, int id: %d, ", + thread_id, int_id); + DPRINTF(Plic, + "pri: %d, thres: %d\n", priority, threshold); + } + intrctrl->clear(thread_id, int_id, 0); + } + } +} + +void +Plic::serialize(CheckpointOut &cp) const +{ + int n_outputs = 0; + + for (auto const ®: registers.pending) { + paramOut(cp, reg.name(), reg); + } + for (auto const ®: registers.priority) { + paramOut(cp, reg.name(), reg); + } + for (auto const ®: registers.enable) { + for (auto const ®_inner: reg) { + paramOut(cp, reg_inner.name(), reg_inner); + } + } + for (auto const ®: registers.threshold) { + paramOut(cp, reg.name(), reg); + } + for (auto const ®: registers.claim) { + paramOut(cp, reg.name(), reg); + } + for (auto const & it : outputQueue) { + paramOut(cp, std::string("output_tick") + + std::to_string(n_outputs), it.first); + arrayParamOut(cp, std::string("output_id") + + std::to_string(n_outputs), it.second.maxID); + arrayParamOut(cp, std::string("output_pri") + + std::to_string(n_outputs), it.second.maxPriority); + n_outputs++; + } + SERIALIZE_SCALAR(n_outputs); + SERIALIZE_CONTAINER(output.maxID); + SERIALIZE_CONTAINER(output.maxPriority); +} + +void +Plic::unserialize(CheckpointIn &cp) +{ + int n_outputs; + UNSERIALIZE_SCALAR(n_outputs); + + for (auto ®: registers.pending) { + paramIn(cp, reg.name(), reg); + } + for (auto ®: registers.priority) { + paramIn(cp, reg.name(), reg); + } + for (auto ®: registers.enable) { + for (auto ®_inner: reg) { + paramIn(cp, reg_inner.name(), reg_inner); + } + } + for (auto ®: registers.threshold) { + paramIn(cp, reg.name(), reg); + } + for (auto ®: registers.claim) { + paramIn(cp, reg.name(), reg); + } + for (int i = 0; i < n_outputs; i++) { + Tick output_tick; + std::vector output_id; + std::vector output_pri; + paramIn(cp, std::string("output_tick") + + std::to_string(i), output_tick); + arrayParamIn(cp, std::string("output_id") + + std::to_string(i), output_id); + arrayParamIn(cp, std::string("output_pri") + + std::to_string(i), output_pri); + outputQueue[output_tick] = PlicOutput{output_id, output_pri}; + } + if (!outputQueue.empty()) { + schedule(update, outputQueue.begin()->first); + } + UNSERIALIZE_CONTAINER(output.maxID); + UNSERIALIZE_CONTAINER(output.maxPriority); +} diff --git a/src/dev/riscv/plic.hh b/src/dev/riscv/plic.hh new file mode 100644 index 0000000000..a0dfbbfbf5 --- /dev/null +++ b/src/dev/riscv/plic.hh @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2021 Huawei International + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * 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. + */ + +#ifndef __DEV_RISCV_PLIC_HH__ +#define __DEV_RISCV_PLIC_HH__ + +#include +#include + +#include "arch/riscv/interrupts.hh" +#include "cpu/intr_control.hh" +#include "dev/io_device.hh" +#include "dev/reg_bank.hh" +#include "mem/packet.hh" +#include "mem/packet_access.hh" +#include "params/Plic.hh" +#include "sim/system.hh" + +using namespace RiscvISA; +/** + * NOTE: + * This implementation of CLINT is based on + * the SiFive U54MC datasheet: + * https://sifive.cdn.prismic.io/sifive/fab000f6- + * 0e07-48d0-9602-e437d5367806_sifive_U54MC_rtl_ + * full_20G1.03.00_manual.pdf + */ + +/** + * PLIC Latency Model + * MMIO changed (aside from threshold) + * => update internal states + * => calculate new output + * => schedule update (3 cycles delay) + * => update output & schedule next update + * => update xEIP lines + * + * threshold changed + * => update xEIP lines + * + * This ensures cycle-accurate values for + * MMIO accesses and xEIP lines + * + * NOTE: + * check pending bit when returning maxID + * to avoid claiming by multiple contexts. + * Note that pending bits are not propagated + * through the 3-cycle delay. + * + * TODO: + * - Enforce access control (e.g. avoid S mode + * writing to M mode registers) + */ + +struct PlicOutput { + std::vector maxID; + std::vector maxPriority; +}; + +class Plic : public BasicPioDevice +{ + // Params + protected: + System *system; + IntrControl *intrctrl; + + // Number of interrupt sources + int nSrc; + /** + * Number of 32-bit pending registers needed + * = ceil(nSrc / 32) + */ + int nSrc32; + /** + * Number of interrupt contexts + * = nThread * 2 + * e.g. context 0 => thread 0 M mode + * context 1 => thread 0 S mode + * This is based on SiFive U54MC datasheet + */ + int nContext; + + public: + typedef PlicParams Params; + Plic(const Params ¶ms); + + // External API + public: + /** + * Interrupt interface + */ + void post(int src_id); + void clear(int src_id); + + /** + * SimObject functions + */ + void init() override; + void serialize(CheckpointOut &cp) const override; + void unserialize(CheckpointIn &cp) override; + + protected: + /** + * PioDevice funcitons + */ + Tick read(PacketPtr pkt) override; + Tick write(PacketPtr pkt) override; + + // Register bank + private: + + /** + * MMIO Registers + * + * Priority (0-7): + * - memory map: 0x0000000 - 0x0000FFC + * (1024 sources, 32 bits each) + * - gem5: vector + * (index: source_id, size: n_src) + * + * ... reserved[0] + * + * Pending: + * - memory map: 0x0001000 - 0x0001080 + * (1024 sources, 1 bit each) + * - gem5: vector> + * (index: addr_offset, size: ceil(n_src/32)) + * + * ... reserved[1] + * + * Enable: + * - memory map: 0x0002000 - 0x01F2000 + * (15872 contexts, 1024 sources, 1 bit each) + * - gem5: vector>> + * (index: [context_id addr_offset], size: [n_context, ceil(n_src/32)]) + * ... reserved[2] + * + * Threshold: + * - memory map: 0x0200000 - 0x3FFFFFC + * (15872 contexts, 32-bit each, 0x1000 byte spacing) + * - gem5: vector + * (index: context_id, size: n_context) + * + * Claim / Complete: + * - memory map: 0x0200004 - 0x3FFFFFC + * (15872 contexts, 32-bit each, 0x1000 byte spacing) + * - gem5: getter / setter functions + * + * ... reserved[3] + */ + class PlicRegisters: public RegisterBankLE { + public: + const Addr pendingStart = 0x1000; + const Addr enableStart = 0x2000; + const Addr thresholdStart = 0x0200000; + const Addr enablePadding = 0x80; + const Addr thresholdPadding = 0x1000; + const Addr maxBankSize = 0x4000000; + + + std::vector priority; + std::vector pending; + std::vector> enable; + std::vector threshold; + std::vector claim; + std::vector enable_holes; + std::vector claim_holes; + std::vector reserved; + + PlicRegisters(const std::string &name, Addr base, Plic* plic) : + RegisterBankLE(name, base), + plic(plic) {} + + Plic* plic; + + void init(); + + } registers; + + using Register32 = PlicRegisters::Register32; + + /** + * Register read / write callbacks + */ + void writePriority(Register32& reg, const uint32_t& data, + const int src_id); + + void writeEnable(Register32& reg, const uint32_t& data, + const int src32_id, const int context_id); + + void writeThreshold(Register32& reg, const uint32_t& data, + const int context_id); + + uint32_t readClaim(Register32& reg, const int context_id); + + void writeClaim(Register32& reg, const uint32_t& data, + const int context_id); + + // Latency Model + private: + + // Internal states + // per-source pending * priority + std::vector pendingPriority; + // per-context, per-source pendingPriority * enable + std::vector> effPriority; + // per-context last-claimed id + std::vector lastID; + PlicOutput output; + + /** + * Trigger: + * - Plic::post + * - Plic::clear + * - Plic::write + * + * Task: + * - calculate new output + * - schedule next update event and/or + * add new output to outputQueue + */ + void propagateOutput(); + std::map outputQueue; + EventFunctionWrapper update; + + /** + * Trigger: + * - Plic::update event + * + * Task: + * - set current output to new output + * - schedule next update event (if any) + */ + void updateOutput(); + + /** + * Trigger: + * - Plic::update event + * - Plic::write + * + * Task: + * - update xEIP lines based on new + * output and threshold + */ + void updateInt(); +}; + + +#endif // __DEV_RISCV_PLIC_HH__