From bcf455755e7abf2fde3dddcb8a1c1917aa8a91ca Mon Sep 17 00:00:00 2001 From: Yu-Cheng Chang Date: Tue, 27 Feb 2024 02:32:53 +0800 Subject: [PATCH] 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 --- src/dev/riscv/HiFive.py | 6 +- src/dev/riscv/Plic.py | 45 +++++++--- src/dev/riscv/plic.cc | 83 ++++++++++++++----- src/dev/riscv/plic.hh | 22 ++--- .../boards/experimental/lupv_board.py | 19 +++-- .../gem5/components/boards/riscv_board.py | 23 +++-- .../riscvmatched/riscvmatched_board.py | 23 +++-- 7 files changed, 155 insertions(+), 66 deletions(-) diff --git a/src/dev/riscv/HiFive.py b/src/dev/riscv/HiFive.py index 04b2672ea8..17c4b35a29 100755 --- a/src/dev/riscv/HiFive.py +++ b/src/dev/riscv/HiFive.py @@ -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): diff --git a/src/dev/riscv/Plic.py b/src/dev/riscv/Plic.py index b4486b9350..ff80323d48 100644 --- a/src/dev/riscv/Plic.py +++ b/src/dev/riscv/Plic.py @@ -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")) diff --git a/src/dev/riscv/plic.cc b/src/dev/riscv/plic.cc index fd42920dc5..ca5acbd27b 100644 --- a/src/dev/riscv/plic.cc +++ b/src/dev/riscv/plic.cc @@ -57,10 +57,18 @@ Plic::Plic(const Params ¶ms) : 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 context_priority(nSrc, 0x0); effPriority.push_back(context_priority); } - lastID.resize(nContext, 0x0); + lastID.resize(contextConfigs.size(), 0x0); // Setup outputs output = PlicOutput{ - std::vector(nContext, 0x0), - std::vector(nContext, 0x0)}; + std::vector(contextConfigs.size(), 0x0), + std::vector(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()); 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(nContext, 0x0), - std::vector(nContext, 0x0)}; + std::vector(contextConfigs.size(), 0x0), + std::vector(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]; diff --git a/src/dev/riscv/plic.hh b/src/dev/riscv/plic.hh index 00128ee56c..ef5eee8df8 100644 --- a/src/dev/riscv/plic.hh +++ b/src/dev/riscv/plic.hh @@ -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> contextConfigs; public: typedef PlicParams Params; @@ -261,6 +255,12 @@ class Plic : public PlicBase std::vector 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 diff --git a/src/python/gem5/components/boards/experimental/lupv_board.py b/src/python/gem5/components/boards/experimental/lupv_board.py index 2448ede45e..62ecfd59e4 100644 --- a/src/python/gem5/components/boards/experimental/lupv_board.py +++ b/src/python/gem5/components/boards/experimental/lupv_board.py @@ -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")) diff --git a/src/python/gem5/components/boards/riscv_board.py b/src/python/gem5/components/boards/riscv_board.py index dcb6fab7c5..555f723df1 100644 --- a/src/python/gem5/components/boards/riscv_board.py +++ b/src/python/gem5/components/boards/riscv_board.py @@ -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")) diff --git a/src/python/gem5/prebuilt/riscvmatched/riscvmatched_board.py b/src/python/gem5/prebuilt/riscvmatched/riscvmatched_board.py index 0de69a40f2..570effaec7 100644 --- a/src/python/gem5/prebuilt/riscvmatched/riscvmatched_board.py +++ b/src/python/gem5/prebuilt/riscvmatched/riscvmatched_board.py @@ -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"))