| /* |
| * 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/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<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(Plic, |
| "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() |
| { |
| // 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<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(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<Register32>()); |
| 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<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(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<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); |
| } |