arch-riscv,dev: Update the PLIC implementation (#886)

Update the PLIC based on the
[riscv-plic-spec](https://github.com/riscv/riscv-plic-spec) in the PR:
- Support customized PLIC hardID and privilege mode configuration
- Backward compatable with the n_contexts parameter, will generate the
config like {0,M}, {0,S}, {1,M} ...

Change-Id: Ibff736827edb7c97921e01fa27f503574a27a562
This commit is contained in:
Yu-Cheng Chang
2024-02-27 02:32:53 +08:00
committed by GitHub
parent 521a7c1de0
commit bcf455755e
7 changed files with 155 additions and 66 deletions

View File

@@ -221,10 +221,10 @@ class HiFive(HiFiveBase):
self.plic.n_src = max(plic_srcs) + 1
def setNumCores(self, num_cpu):
"""Sets the PLIC and CLINT to have the right number of threads and
contexts. Assumes that the cores have a single hardware thread.
"""Sets the CLINT to number of threads and the PLIC hartID/pmode for
each contexts. Assumes that the cores have a single hardware thread.
"""
self.plic.n_contexts = num_cpu * 2
self.plic.hart_config = ",".join(["MS" for _ in range(num_cpu)])
self.clint.num_threads = num_cpu
def generateDeviceTree(self, state):

View File

@@ -58,10 +58,8 @@ class PlicBase(BasicPioDevice):
class Plic(PlicBase):
"""
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
the riscv-plic-spec repository:
https://github.com/riscv/riscv-plic-spec/releases/tag/1.0.0
"""
type = "Plic"
@@ -69,9 +67,20 @@ class Plic(PlicBase):
cxx_class = "gem5::Plic"
pio_size = 0x4000000
n_src = Param.Int("Number of interrupt sources")
# Ref: https://github.com/qemu/qemu/blob/760b4dc/hw/intc/sifive_plic.c#L285
hart_config = Param.String(
"",
"String represent for PLIC hart/pmode config like QEMU plic"
"Ex."
"'M' 1 hart with M mode"
"'MS,MS' 2 harts, 0-1 with M and S mode"
"'M,MS,MS,MS,MS' 5 harts, 0 with M mode, 1-5 with M and S mode",
)
n_contexts = Param.Int(
0,
"Deprecated, use `hart_config` instead. "
"Number of interrupt contexts. Usually the number "
"of threads * 2. One for M mode, one for S mode"
"of threads * 2. One for M mode, one for S mode",
)
def generateDeviceTree(self, state):
@@ -89,12 +98,26 @@ class Plic(PlicBase):
cpus = self.system.unproxy(self).cpu
int_extended = list()
for cpu in cpus:
phandle = int_state.phandle(cpu)
int_extended.append(phandle)
int_extended.append(0xB)
int_extended.append(phandle)
int_extended.append(0x9)
if self.n_contexts != 0:
for cpu in cpus:
phandle = int_state.phandle(cpu)
int_extended.append(phandle)
int_extended.append(0xB)
int_extended.append(phandle)
int_extended.append(0x9)
elif self.hart_config != "":
cpu_id = 0
phandle = int_state.phandle(cpus[cpu_id])
for c in self.hart_config:
if c == ",":
cpu_id += 1
phandle = int_state.phandle(cpus[cpu_id])
elif c == "S":
int_extended.append(phandle)
int_extended.append(0x9)
elif c == "M":
int_extended.append(phandle)
int_extended.append(0xB)
node.append(FdtPropertyWords("interrupts-extended", int_extended))
node.append(FdtProperty("interrupt-controller"))

View File

@@ -57,10 +57,18 @@ Plic::Plic(const Params &params) :
PlicBase(params),
system(params.system),
nSrc(params.n_src),
nContext(params.n_contexts),
registers(params.name, pioAddr, this),
update([this]{updateOutput();}, name() + ".update")
{
fatal_if(params.hart_config != "" && params.n_contexts != 0,
"the hart_config and n_contexts can't be set simultaneously");
if (params.n_contexts != 0) {
initContextFromNContexts(params.n_contexts);
}
if (params.hart_config != "") {
initContextFromHartConfig(params.hart_config);
}
}
void
@@ -80,7 +88,7 @@ Plic::post(int src_id)
// Update states
pendingPriority[src_id] = registers.priority[src_id].get();
for (int i = 0; i < nContext; i++) {
for (int i = 0; i < contextConfigs.size(); i++) {
bool enabled = bits(registers.enable[i][src_index].get(), src_offset);
effPriority[i][src_id] = enabled ? pendingPriority[src_id] : 0;
}
@@ -109,7 +117,7 @@ Plic::clear(int src_id)
// Update states
pendingPriority[src_id] = 0;
for (int i = 0; i < nContext; i++) {
for (int i = 0; i < contextConfigs.size(); i++) {
effPriority[i][src_id] = 0;
}
DPRINTF(Plic,
@@ -174,20 +182,20 @@ Plic::init()
// Setup internal states
pendingPriority.resize(nSrc, 0x0);
for (int i = 0; i < nContext; i++) {
for (int i = 0; i < contextConfigs.size(); i++) {
std::vector<uint32_t> context_priority(nSrc, 0x0);
effPriority.push_back(context_priority);
}
lastID.resize(nContext, 0x0);
lastID.resize(contextConfigs.size(), 0x0);
// Setup outputs
output = PlicOutput{
std::vector<uint32_t>(nContext, 0x0),
std::vector<uint32_t>(nContext, 0x0)};
std::vector<uint32_t>(contextConfigs.size(), 0x0),
std::vector<uint32_t>(contextConfigs.size(), 0x0)};
DPRINTF(Plic,
"Device init - %d contexts, %d sources, %d pending registers\n",
nContext, nSrc, nSrc32);
contextConfigs.size(), nSrc, nSrc32);
BasicPioDevice::init();
}
@@ -204,15 +212,15 @@ Plic::PlicRegisters::init()
- plic->nSrc32 * 4;
reserved.emplace_back("reserved1", reserve1_size);
const size_t reserve2_size = thresholdStart - enableStart
- plic->nContext * enablePadding;
- plic->contextConfigs.size() * enablePadding;
reserved.emplace_back("reserved2", reserve2_size);
const size_t reserve3_size = plic->pioSize - thresholdStart
- plic->nContext * thresholdPadding;
- plic->contextConfigs.size() * thresholdPadding;
reserved.emplace_back("reserved3", reserve3_size);
// Sanity check
assert(plic->pioSize >= thresholdStart
+ plic->nContext * thresholdPadding);
+ plic->contextConfigs.size() * thresholdPadding);
assert((int) plic->pioSize <= maxBankSize);
// Calculate hole sizes
@@ -228,7 +236,7 @@ Plic::PlicRegisters::init()
pending.emplace_back(
std::string("pending") + std::to_string(i), 0);
}
for (int i = 0; i < plic->nContext; i++) {
for (int i = 0; i < plic->contextConfigs.size(); i++) {
enable.push_back(std::vector<Register32>());
for (int j = 0; j < plic->nSrc32; j++) {
@@ -264,7 +272,7 @@ Plic::PlicRegisters::init()
addRegister(reserved[1]);
// Enable
for (int i = 0; i < plic->nContext; i++) {
for (int i = 0; i < plic->contextConfigs.size(); 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);
@@ -275,7 +283,7 @@ Plic::PlicRegisters::init()
addRegister(reserved[2]);
// Threshold and claim
for (int i = 0; i < plic->nContext; i++) {
for (int i = 0; i < plic->contextConfigs.size(); 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);
@@ -301,7 +309,7 @@ Plic::writePriority(Register32& reg, const uint32_t& data, const int src_id)
// 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++) {
for (int i = 0; i < contextConfigs.size(); i++) {
bool enabled = bits(
registers.enable[i][src_index].get(), src_offset);
effPriority[i][src_id] = enabled ? pendingPriority[src_id] : 0;
@@ -394,11 +402,11 @@ Plic::propagateOutput()
{
// Calculate new output
PlicOutput new_output{
std::vector<uint32_t>(nContext, 0x0),
std::vector<uint32_t>(nContext, 0x0)};
std::vector<uint32_t>(contextConfigs.size(), 0x0),
std::vector<uint32_t>(contextConfigs.size(), 0x0)};
uint32_t max_id;
uint32_t max_priority;
for (int i = 0; i < nContext; i++) {
for (int i = 0; i < contextConfigs.size(); i++) {
max_id = max_element(effPriority[i].begin(),
effPriority[i].end()) - effPriority[i].begin();
max_priority = effPriority[i][max_id];
@@ -421,6 +429,39 @@ Plic::propagateOutput()
}
}
void
Plic::initContextFromNContexts(int n_contexts)
{
contextConfigs.reserve(n_contexts);
for (uint32_t i = 0; i < (uint32_t)n_contexts; i += 2) {
contextConfigs.emplace_back((i >> 1), ExceptionCode::INT_EXT_MACHINE);
contextConfigs.emplace_back((i >> 1), ExceptionCode::INT_EXT_SUPER);
}
}
void
Plic::initContextFromHartConfig(const std::string& hart_config)
{
contextConfigs.reserve(hart_config.size());
uint32_t hart_id = 0;
for (char c: hart_config) {
switch (c) {
case ',':
hart_id++;
break;
case 'M':
contextConfigs.emplace_back(hart_id, ExceptionCode::INT_EXT_MACHINE);
break;
case 'S':
contextConfigs.emplace_back(hart_id, ExceptionCode::INT_EXT_SUPER);
break;
default:
fatal("hart_config should not contains the value: %c", c);
break;
}
}
}
void
Plic::updateOutput()
{
@@ -443,10 +484,8 @@ 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;
for (int i = 0; i < contextConfigs.size(); i++) {
auto [thread_id, int_id] = contextConfigs[i];
auto tc = system->threads[thread_id];
uint32_t max_id = output.maxID[i];

View File

@@ -57,11 +57,9 @@ namespace gem5
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
* This implementation of PLIC is based on
* he riscv-plic-spec repository:
* https://github.com/riscv/riscv-plic-spec/releases/tag/1.0.0
*/
/**
@@ -124,13 +122,9 @@ class Plic : public PlicBase
*/
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
* PLIC hart/pmode address configs, stored in the format {hartID, pmode}
*/
int nContext;
std::vector<std::pair<uint32_t, ExceptionCode>> contextConfigs;
public:
typedef PlicParams Params;
@@ -261,6 +255,12 @@ class Plic : public PlicBase
std::vector<uint32_t> lastID;
PlicOutput output;
/**
* The function for handling context config from params
*/
void initContextFromNContexts(int n_contexts);
void initContextFromHartConfig(const std::string& hart_config);
/**
* Trigger:
* - Plic::post

View File

@@ -185,7 +185,7 @@ class LupvBoard(AbstractSystemBoard, KernelDiskWorkload):
# point for our bbl to use upon startup, and will
# remain unused during the simulation
self.pic.n_src = 0
self.pic.n_contexts = 0
self.pic.hart_config = ""
self.lupio_pic.n_src = max(pic_srcs) + 1
self.lupio_pic.num_threads = self.processor.get_num_cores()
@@ -403,10 +403,19 @@ class LupvBoard(AbstractSystemBoard, KernelDiskWorkload):
plic_node.append(FdtPropertyWords("riscv,ndev", 0))
int_extended = list()
for i, core in enumerate(self.get_processor().get_cores()):
phandle = state.phandle(f"cpu@{i}.int_state")
int_extended.append(phandle)
int_extended.append(self._excep_code["INT_EXT_MACHINE"])
cpu_id = 0
phandle = int_state.phandle(f"cpu@{cpu_id}.int_state")
for c in plic.hart_config:
if c == ",":
cpu_id += 1
assert cpu_id < self.get_processor().get_num_cores()
phandle = int_state.phandle(f"cpu@{cpu_id}.int_state")
elif c == "S":
int_extended.append(phandle)
int_extended.append(self._excep_code["INT_SOFT_SUPER"])
elif c == "M":
int_extended.append(phandle)
int_extended.append(self._excep_code["INT_EXT_MACHINE"])
plic_node.append(FdtPropertyWords("interrupts-extended", int_extended))
plic_node.append(FdtProperty("interrupt-controller"))

View File

@@ -102,7 +102,9 @@ class RiscvBoard(AbstractSystemBoard, KernelDiskWorkload):
# Contains a CLINT, PLIC, UART, and some functions for the dtb, etc.
self.platform = HiFive()
# Note: This only works with single threaded cores.
self.platform.plic.n_contexts = self.processor.get_num_cores() * 2
self.platform.plic.hart_config = ",".join(
["MS" for _ in range(self.processor.get_num_cores())]
)
self.platform.attachPlic()
self.platform.clint.num_threads = self.processor.get_num_cores()
@@ -353,12 +355,19 @@ class RiscvBoard(AbstractSystemBoard, KernelDiskWorkload):
plic_node.append(FdtPropertyWords("riscv,ndev", [plic.n_src - 1]))
int_extended = list()
for i, core in enumerate(self.get_processor().get_cores()):
phandle = state.phandle(f"cpu@{i}.int_state")
int_extended.append(phandle)
int_extended.append(0xB)
int_extended.append(phandle)
int_extended.append(0x9)
cpu_id = 0
phandle = int_state.phandle(f"cpu@{cpu_id}.int_state")
for c in plic.hart_config:
if c == ",":
cpu_id += 1
assert cpu_id < self.get_processor().get_num_cores()
phandle = int_state.phandle(f"cpu@{cpu_id}.int_state")
elif c == "S":
int_extended.append(phandle)
int_extended.append(0x9)
elif c == "M":
int_extended.append(phandle)
int_extended.append(0xB)
plic_node.append(FdtPropertyWords("interrupts-extended", int_extended))
plic_node.append(FdtProperty("interrupt-controller"))

View File

@@ -149,7 +149,9 @@ class RISCVMatchedBoard(
# Contains a CLINT, PLIC, UART, and some functions for the dtb, etc.
self.platform = HiFive()
# Note: This only works with single threaded cores.
self.platform.plic.n_contexts = self.processor.get_num_cores() * 2
self.platform.plic.hart_config = ",".join(
["MS" for _ in range(self.processor.get_num_cores())]
)
self.platform.attachPlic()
self.platform.clint.num_threads = self.processor.get_num_cores()
@@ -433,12 +435,19 @@ class RISCVMatchedBoard(
plic_node.append(FdtPropertyWords("riscv,ndev", [plic.n_src - 1]))
int_extended = list()
for i, core in enumerate(self.get_processor().get_cores()):
phandle = state.phandle(f"cpu@{i}.int_state")
int_extended.append(phandle)
int_extended.append(0xB)
int_extended.append(phandle)
int_extended.append(0x9)
cpu_id = 0
phandle = int_state.phandle(f"cpu@{cpu_id}.int_state")
for c in plic.hart_config:
if c == ",":
cpu_id += 1
assert cpu_id < self.get_processor().get_num_cores()
phandle = int_state.phandle(f"cpu@{cpu_id}.int_state")
elif c == "S":
int_extended.append(phandle)
int_extended.append(0x9)
elif c == "M":
int_extended.append(phandle)
int_extended.append(0xB)
plic_node.append(FdtPropertyWords("interrupts-extended", int_extended))
plic_node.append(FdtProperty("interrupt-controller"))