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 <yazakram@ucdavis.edu> Reviewed-by: Jason Lowe-Power <power.jg@gmail.com> Tested-by: kokoro <noreply+kokoro@google.com> Maintainer: Jason Lowe-Power <power.jg@gmail.com>
This commit is contained in:
52
src/dev/riscv/Plic.py
Normal file
52
src/dev/riscv/Plic.py
Normal file
@@ -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")
|
||||
@@ -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')
|
||||
|
||||
544
src/dev/riscv/plic.cc
Normal file
544
src/dev/riscv/plic.cc
Normal file
@@ -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 <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);
|
||||
}
|
||||
283
src/dev/riscv/plic.hh
Normal file
283
src/dev/riscv/plic.hh
Normal file
@@ -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 <bitset>
|
||||
#include <map>
|
||||
|
||||
#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<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;
|
||||
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<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 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<Register32> priority;
|
||||
std::vector<Register32> pending;
|
||||
std::vector<std::vector<Register32>> enable;
|
||||
std::vector<Register32> threshold;
|
||||
std::vector<Register32> claim;
|
||||
std::vector<RegisterRaz> enable_holes;
|
||||
std::vector<RegisterRaz> claim_holes;
|
||||
std::vector<RegisterRaz> 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<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__
|
||||
Reference in New Issue
Block a user