Peter Yuen has uploaded this change for review. ( https://gem5-review.googlesource.com/c/public/gem5/+/40598 )

Change subject: arch-riscv: PLIC Implementation
......................................................................

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
---
A src/dev/riscv/Plic.py
M src/dev/riscv/SConscript
A src/dev/riscv/plic.cc
A src/dev/riscv/plic.hh
4 files changed, 884 insertions(+), 0 deletions(-)



diff --git a/src/dev/riscv/Plic.py b/src/dev/riscv/Plic.py
new file mode 100644
index 0000000..9650ff3
--- /dev/null
+++ b/src/dev/riscv/Plic.py
@@ -0,0 +1,47 @@
+# -*- mode:python -*-
+
+# 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):
+    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 b2092ca..300c099 100755
--- a/src/dev/riscv/SConscript
+++ b/src/dev/riscv/SConscript
@@ -30,7 +30,10 @@

 if env['TARGET_ISA'] == 'riscv':
     SimObject('Clint.py')
+    SimObject('Plic.py')

     DebugFlag('RiscvClint')
+    DebugFlag('RiscvPlic')

     Source('clint.cc')
+    Source('plic.cc')
diff --git a/src/dev/riscv/plic.cc b/src/dev/riscv/plic.cc
new file mode 100644
index 0000000..fbea925
--- /dev/null
+++ b/src/dev/riscv/plic.cc
@@ -0,0 +1,535 @@
+/*
+ * 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 <algorithm>
+
+#include "arch/riscv/registers.hh"
+#include "debug/RiscvPlic.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 &params) :
+    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(RiscvPlic,
+        "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(RiscvPlic,
+        "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(RiscvPlic,
+        "Read request - addr: %#x, size: %#x, atomic:%d\n",
+        pkt->getAddr(), pkt->getSize(), is_atomic);
+
+    // Perform register read
+    registers.read(pkt->getAddr(), pkt->getPtr<void>(), pkt->getSize());
+
+    if (is_atomic) {
+        // Perform atomic operation
+        (*(pkt->getAtomicOp()))(pkt->getPtr<uint8_t>());
+        return write(pkt);
+    } else {
+        pkt->makeResponse();
+        return pioDelay;
+    }
+}
+
+Tick
+Plic::write(PacketPtr pkt)
+{
+    DPRINTF(RiscvPlic,
+        "Write request - addr: %#x, size: %#x\n",
+        pkt->getAddr(), pkt->getSize());
+
+    // Perform register write
+    registers.write(pkt->getAddr(), pkt->getPtr<void>(), pkt->getSize());
+
+    // Propagate output changes
+    propagateOutput();
+
+    // Apply threshold changes
+    updateInt();
+
+    pkt->makeResponse();
+    return pioDelay;
+}
+
+void
+Plic::init()
+{
+    // Calculate number of contexts and 32-bit pending registesrs
+    nContext = system->threads.size() * 2;
+    nSrc32 = ((nSrc - 1) >> 5) + 1;
+
+    // Setup register bank
+    registers.init();
+
+    // Setup internal states
+    pendingPriority.resize(nSrc, 0x0);
+    for (int i = 0; i < nContext; i++) {
+        std::vector<uint32_t> context_priority(nSrc, 0x0);
+        effPriority.push_back(context_priority);
+    }
+    lastID.resize(nContext, 0x0);
+
+    // Setup outputs
+    output = PlicOutput{
+        std::vector<uint32_t>(nContext, 0x0),
+        std::vector<uint32_t>(nContext, 0x0)};
+
+    DPRINTF(RiscvPlic,
+        "Device init - %d contexts, %d sources, %d pending registers\n",
+        nContext, nSrc, nSrc32);
+
+    BasicPioDevice::init();
+}
+
+void
+Plic::PlicRegisters::init()
+{
+    // Calculate reserved space size
+    const size_t reserve0_size = 0x0001000 - plic->nSrc * 4;
+    reserved.emplace_back("reserved0", reserve0_size);
+    const size_t reserve1_size = 0x0002000 - 0x0001000
+        - plic->nSrc32 * 4;
+    reserved.emplace_back("reserved1", reserve1_size);
+    const size_t reserve2_size = 0x0200000 - 0x0002000
+        - plic->nSrc32 * plic->nContext * 0x80;
+    reserved.emplace_back("reserved2", reserve2_size);
+    const size_t reserve3_size = plic->pioSize - 0x0200000
+        - plic->nContext * 0x1000;
+    reserved.emplace_back("reserved3", reserve3_size);
+
+    // Sanity check
+    assert((int) plic->pioSize - 0x0200000 - plic->nContext * 0x1000 >= 0);
+
+    // Calculate hole sizes
+    const size_t enable_hole_size = 0x80 - plic->nSrc32 * 4;
+    const size_t claim_hole_size = 0x1000 - 0x8;
+
+    // Initialize registers
+    for (int i = 0; i < plic->nSrc; i++) {
+        SrcID src_id = {i};
+        priority.emplace_back(
+            std::string("priority") + std::to_string(i), src_id, 0);
+    }
+    for (int i = 0; i < plic->nSrc32; i++) {
+        Src32ID src32_id = {i};
+        pending.emplace_back(
+            std::string("pending") + std::to_string(i), src32_id, 0);
+    }
+    for (int i = 0; i < plic->nContext; i++) {
+
+        enable.push_back(std::vector<EnableRegister>());
+        for (int j = 0; j < plic->nSrc32; j++) {
+            Src32CtxID src32_ctx_id = {j, i};
+            enable[i].emplace_back(
+                std::string("enable") + std::to_string(i)
+                + "_" + std::to_string(j), src32_ctx_id, 0);
+        }
+        enable_holes.emplace_back(
+ std::string("enable_hole") + std::to_string(i), enable_hole_size);
+
+        CtxID ctx_id = {i};
+        threshold.emplace_back(
+            std::string("threshold") + std::to_string(i), ctx_id, 0);
+        claim.emplace_back(
+            std::string("claim") + std::to_string(i), ctx_id, 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++) {
+        priority[i].afterWrite(plic, &Plic::writePriority);
+        addRegister(priority[i]);
+    }
+    addRegister(reserved[0]);
+
+    // Pending
+    for (int i = 0; i < plic->nSrc32; i++) {
+        pending[i].reg.readonly();
+        addRegister(pending[i]);
+    }
+    addRegister(reserved[1]);
+
+    // Enable
+    for (int i = 0; i < plic->nContext; i++) {
+        for (int j = 0; j < plic->nSrc32; j++) {
+            enable[i][j].afterWrite(plic, &Plic::writeEnable);
+            addRegister(enable[i][j]);
+        }
+        addRegister(enable_holes[i]);
+    }
+    addRegister(reserved[2]);
+
+    // Threshold and claim
+    for (int i = 0; i < plic->nContext; i++) {
+        threshold[i].afterWrite(plic, &Plic::writeThreshold);
+        claim[i].beforeRead(plic, &Plic::readClaim)
+                .afterWrite(plic, &Plic::writeClaim);
+        addRegister(threshold[i]);
+        addRegister(claim[i]);
+        addRegister(claim_holes[i]);
+    }
+    addRegister(reserved[3]);
+}
+
+void
+Plic::writePriority(PriorityRegister& reg)
+{
+    // Calculate indices
+    int src_id = reg.getProp().src_id;
+    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(RiscvPlic,
+        "Priority updated - src: %d, val: %d\n",
+        src_id, reg.get());
+}
+
+void
+Plic::writeEnable(EnableRegister& reg)
+{
+    int src32_id = reg.getProp().src32_id;
+    int context_id = reg.getProp().context_id;
+    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(RiscvPlic,
+        "Enable updated - context: %d, src32: %d, val: %#x\n",
+        context_id, src32_id, reg.get());
+}
+
+void
+Plic::writeThreshold(ThresholdRegister& reg)
+{
+    int context_id = reg.getProp().context_id;
+    DPRINTF(RiscvPlic,
+        "Threshold updated - context: %d, val: %d\n",
+        context_id, reg.get());
+}
+
+void
+Plic::readClaim(ClaimRegister& reg)
+{
+    int context_id = reg.getProp().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(RiscvPlic,
+                "Claim success - context: %d, interrupt ID: %d\n",
+                context_id, max_int_id);
+            clear(max_int_id);
+            reg.update(max_int_id);
+        } else {
+            DPRINTF(RiscvPlic,
+                "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);
+    }
+}
+
+void
+Plic::writeClaim(ClaimRegister& reg)
+{
+    int context_id = reg.getProp().context_id;
+    /**
+     * 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(RiscvPlic,
+        "Complete - context: %d, interrupt ID: %d\n",
+        context_id, reg.get());
+}
+
+void
+Plic::propagateOutput()
+{
+    // Calculate new output
+    PlicOutput new_output{
+        std::vector<uint32_t>(nContext, 0x0),
+        std::vector<uint32_t>(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(RiscvPlic, "Update scheduled - tick: %d\n", next_update);
+        schedule(update, next_update);
+    }
+}
+
+void
+Plic::updateOutput()
+{
+    DPRINTF(RiscvPlic, "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(RiscvPlic, "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(RiscvPlic,
+                "Int posted - thread: %d, int id: %d, ",
+                thread_id, int_id);
+            DPRINTF(RiscvPlic,
+                "pri: %d, thres: %d\n", priority, threshold);
+            intrctrl->post(thread_id, int_id, 0);
+        } else {
+            if (priority > 0) {
+                DPRINTF(RiscvPlic,
+                    "Int filtered - thread: %d, int id: %d, ",
+                    thread_id, int_id);
+                DPRINTF(RiscvPlic,
+                    "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 &reg: registers.pending) {
+        paramOut(cp, reg.name(), reg);
+    }
+    for (auto const &reg: registers.priority) {
+        paramOut(cp, reg.name(), reg);
+    }
+    for (auto const &reg: registers.enable) {
+        for (auto const &reg_inner: reg) {
+            paramOut(cp, reg_inner.name(), reg_inner);
+        }
+    }
+    for (auto const &reg: registers.threshold) {
+        paramOut(cp, reg.name(), reg);
+    }
+    for (auto const &reg: 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 &reg: registers.pending) {
+        paramIn(cp, reg.name(), reg);
+    }
+    for (auto &reg: registers.priority) {
+        paramIn(cp, reg.name(), reg);
+    }
+    for (auto &reg: registers.enable) {
+        for (auto &reg_inner: reg) {
+            paramIn(cp, reg_inner.name(), reg_inner);
+        }
+    }
+    for (auto &reg: registers.threshold) {
+        paramIn(cp, reg.name(), reg);
+    }
+    for (auto &reg: registers.claim) {
+        paramIn(cp, reg.name(), reg);
+    }
+    for (int i = 0; i < n_outputs; i++) {
+        Tick output_tick;
+        std::vector<uint32_t> output_id;
+        std::vector<uint32_t> 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 0000000..b73297b
--- /dev/null
+++ b/src/dev/riscv/plic.hh
@@ -0,0 +1,299 @@
+/*
+ * 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 <bitset>
+#include <map>
+
+#include "arch/riscv/interrupts.hh"
+#include "cpu/intr_control.hh"
+#include "dev/io_device.hh"
+#include "dev/reg_bank.hh"
+#include "dev/riscv/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.
+ */
+
+/**
+ * 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<uint32_t> maxID;
+  std::vector<uint32_t> 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;
+
+    const Params &
+    params() const
+    {
+        return dynamic_cast<const Params &>(_params);
+    }
+    Plic(const Params &params);
+
+  // 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<uint32_t>
+     *   (index: source_id, size: n_src)
+     *
+     * ... reserved[0]
+     *
+     * Pending:
+     * - memory map: 0x0001000 - 0x0001080
+     *   (1024 sources, 1 bit each)
+     * - gem5: vector<bitset<32>>
+     *   (index: addr_offset, size: ceil(n_src/32))
+     *
+     * ... reserved[1]
+     *
+     * Enable:
+     * - memory map: 0x0002000 - 0x01F2000
+     *   (15872 contexts, 1024 sources, 1 bit each)
+     * - gem5: vector<vector<bitset<32>>>
+ * (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<uint32_t>
+     *   (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 PropRegisterBankLE {
+      public:
+        struct SrcID
+        {
+          const int src_id;
+        };
+        struct Src32ID
+        {
+          const int src32_id;
+        };
+        struct Src32CtxID
+        {
+          const int src32_id;
+          const int context_id;
+        };
+        struct CtxID
+        {
+          const int context_id;
+        };
+
+        using PendingRegister = PropRegister<Src32ID, uint32_t>;
+        using PriorityRegister = PropRegister<SrcID, uint32_t>;
+        using EnableRegister = PropRegister<Src32CtxID, uint32_t>;
+        using ThresholdRegister = PropRegister<CtxID, uint32_t>;
+        using ClaimRegister = PropRegister<CtxID, uint32_t>;
+
+        std::vector<PendingRegister> pending;
+        std::vector<PriorityRegister> priority;
+        std::vector<std::vector<EnableRegister>> enable;
+        std::vector<ThresholdRegister> threshold;
+        std::vector<ClaimRegister> claim;
+        std::vector<RegisterRaz> enable_holes;
+        std::vector<RegisterRaz> claim_holes;
+        std::vector<RegisterRaz> reserved;
+
+        PlicRegisters(const std::string &name, Addr base, Plic* plic) :
+          PropRegisterBankLE(name, base),
+          plic(plic) {}
+
+        Plic* plic;
+
+        void init();
+
+    } registers;
+
+    using PendingRegister = PlicRegisters::PendingRegister;
+    using PriorityRegister = PlicRegisters::PriorityRegister;
+    using EnableRegister = PlicRegisters::EnableRegister;
+    using ThresholdRegister = PlicRegisters::ThresholdRegister;
+    using ClaimRegister = PlicRegisters::ClaimRegister;
+
+    /**
+     * Register read / write callbacks
+     */
+    void writePriority(PriorityRegister& reg);
+    void writeEnable(EnableRegister& reg);
+    void writeThreshold(ThresholdRegister& reg);
+    void readClaim(ClaimRegister& reg);
+    void writeClaim(ClaimRegister& reg);
+
+  // Latency Model
+  private:
+
+    // Internal states
+    // per-source pending * priority
+    std::vector<uint32_t> pendingPriority;
+    // per-context, per-source pendingPriority * enable
+    std::vector<std::vector<uint32_t>> effPriority;
+    // per-context last-claimed id
+    std::vector<uint32_t> 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<Tick, PlicOutput> 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__

--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/40598
To unsubscribe, or for help writing mail filters, visit https://gem5-review.googlesource.com/settings

Gerrit-Project: public/gem5
Gerrit-Branch: develop
Gerrit-Change-Id: I571c7bd3bd2918c92e4f207a1b57cf9d06e9c72f
Gerrit-Change-Number: 40598
Gerrit-PatchSet: 1
Gerrit-Owner: Peter Yuen <[email protected]>
Gerrit-MessageType: newchange
_______________________________________________
gem5-dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s

Reply via email to