diff --git a/lib/grpc-interfaces b/lib/grpc-interfaces index a92e0436a..d662deb63 160000 --- a/lib/grpc-interfaces +++ b/lib/grpc-interfaces @@ -1 +1 @@ -Subproject commit a92e0436a644f8990ed1830bcfd33caed2deb63e +Subproject commit d662deb6393476a5d8217d2b53978ad6e23f5fb0 diff --git a/src/Makefile b/src/Makefile index e35880944..1f7ee0025 100644 --- a/src/Makefile +++ b/src/Makefile @@ -314,6 +314,8 @@ LIBCARTESI_OBJS:= \ pma-driver.o \ clint.o \ clint-factory.o \ + plic.o \ + plic-factory.o \ dtb.o \ os.o \ htif.o \ diff --git a/src/cartesi-machine.lua b/src/cartesi-machine.lua index 3239cc2ae..f9a8ce6ac 100755 --- a/src/cartesi-machine.lua +++ b/src/cartesi-machine.lua @@ -1323,6 +1323,12 @@ local function store_machine_config(config, output) output(" mtimecmp = 0x%x,", clint.mtimecmp or def.clint.mtimecmp) comment_default(clint.mtimecmp, def.clint.mtimecmp) output(" },\n") + local plic = config.plic or {} + output(" plic = {\n") + output(" girqpend = 0x%x,", plic.girqpend or def.plic.girqpend) + output(" girqsrvd = 0x%x,", plic.girqsrvd or def.plic.girqsrvd) + comment_default(plic.girqpend, def.plic.girqpend) + output(" },\n") output(" flash_drive = {\n") for _, f in ipairs(config.flash_drive) do output(" ") diff --git a/src/clua-i-virtual-machine.cpp b/src/clua-i-virtual-machine.cpp index aa09bbaea..0785ea223 100644 --- a/src/clua-i-virtual-machine.cpp +++ b/src/clua-i-virtual-machine.cpp @@ -142,6 +142,8 @@ IMPL_MACHINE_OBJ_READ_WRITE(htif_ihalt) IMPL_MACHINE_OBJ_READ_WRITE(htif_iconsole) IMPL_MACHINE_OBJ_READ_WRITE(htif_iyield) IMPL_MACHINE_OBJ_READ_WRITE(clint_mtimecmp) +IMPL_MACHINE_OBJ_READ_WRITE(plic_girqpend) +IMPL_MACHINE_OBJ_READ_WRITE(plic_girqsrvd) IMPL_MACHINE_OBJ_READ_WRITE(uarch_cycle) IMPL_MACHINE_OBJ_READ_WRITE(uarch_pc) @@ -547,6 +549,8 @@ static const auto machine_obj_index = cartesi::clua_make_luaL_Reg_array({ {"get_initial_config", machine_obj_index_get_initial_config}, {"get_root_hash", machine_obj_index_get_root_hash}, {"read_clint_mtimecmp", machine_obj_index_read_clint_mtimecmp}, + {"read_plic_girqpend", machine_obj_index_read_plic_girqpend}, + {"read_plic_girqsrvd", machine_obj_index_read_plic_girqsrvd}, {"read_csr", machine_obj_index_read_csr}, {"read_htif_fromhost", machine_obj_index_read_htif_fromhost}, {"read_htif_tohost", machine_obj_index_read_htif_tohost}, @@ -609,6 +613,8 @@ static const auto machine_obj_index = cartesi::clua_make_luaL_Reg_array({ {"verify_dirty_page_maps", machine_obj_index_verify_dirty_page_maps}, {"verify_merkle_tree", machine_obj_index_verify_merkle_tree}, {"write_clint_mtimecmp", machine_obj_index_write_clint_mtimecmp}, + {"write_plic_girqpend", machine_obj_index_write_plic_girqpend}, + {"write_plic_girqsrvd", machine_obj_index_write_plic_girqsrvd}, {"write_csr", machine_obj_index_write_csr}, {"write_htif_fromhost", machine_obj_index_write_htif_fromhost}, {"write_htif_fromhost_data", machine_obj_index_write_htif_fromhost_data}, diff --git a/src/clua-machine-util.cpp b/src/clua-machine-util.cpp index 9a6033f07..b6478383a 100644 --- a/src/clua-machine-util.cpp +++ b/src/clua-machine-util.cpp @@ -557,6 +557,8 @@ CM_PROC_CSR clua_check_cm_proc_csr(lua_State *L, int idx) try { {"ilrsc", CM_PROC_ILRSC}, {"iflags", CM_PROC_IFLAGS}, {"clint_mtimecmp", CM_PROC_CLINT_MTIMECMP}, + {"plic_girqpend", CM_PROC_PLIC_GIRQPEND}, + {"plic_girqsrvd", CM_PROC_PLIC_GIRQSRVD}, {"htif_tohost", CM_PROC_HTIF_TOHOST}, {"htif_fromhost", CM_PROC_HTIF_FROMHOST}, {"htif_ihalt", CM_PROC_HTIF_IHALT}, @@ -861,6 +863,15 @@ static void push_cm_clint_config(lua_State *L, const cm_clint_config *c) { clua_setintegerfield(L, c->mtimecmp, "mtimecmp", -1); } +/// \brief Pushes an cm_plic_config to the Lua stack +/// \param L Lua state. +/// \param c Plic configuration to be pushed. +static void push_cm_plic_config(lua_State *L, const cm_plic_config *c) { + lua_newtable(L); + clua_setintegerfield(L, c->girqpend, "girqpend", -1); + clua_setintegerfield(L, c->girqsrvd, "girqsrvd", -1); +} + /// \brief Pushes cm_memory_range_config to the Lua stack /// \param L Lua state. /// \param m Memory range config to be pushed. @@ -949,6 +960,8 @@ void clua_push_cm_machine_config(lua_State *L, const cm_machine_config *c) { lua_setfield(L, -2, "htif"); // config push_cm_clint_config(L, &c->clint); // config clint lua_setfield(L, -2, "clint"); // config + push_cm_plic_config(L, &c->plic); // config plic + lua_setfield(L, -2, "plic"); // config push_cm_flash_drive_configs(L, &c->flash_drive); // config flash_drive lua_setfield(L, -2, "flash_drive"); // config push_cm_ram_config(L, &c->ram); // config ram @@ -1175,6 +1188,19 @@ static void check_cm_clint_config(lua_State *L, int tabidx, cm_clint_config *c) lua_pop(L, 1); } +/// \brief Loads C api PLIC config from Lua +/// \param L Lua state +/// \param tabidx Config stack index +/// \param c PLIC config structure to receive results +static void check_cm_plic_config(lua_State *L, int tabidx, cm_plic_config *c) { + if (!opt_table_field(L, tabidx, "plic")) { + return; + } + c->girqpend = opt_uint_field(L, -1, "girqpend", c->girqpend); + c->girqsrvd = opt_uint_field(L, -1, "girqsrvd", c->girqsrvd); + lua_pop(L, 1); +} + cm_processor_config get_default_processor_config(lua_State *L) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast): remove const to adjust config const auto *config = cm_new_default_machine_config(); @@ -1264,6 +1290,7 @@ cm_machine_config *clua_check_cm_machine_config(lua_State *L, int tabidx, int ct check_cm_tlb_config(L, tabidx, &config->tlb); check_cm_htif_config(L, tabidx, &config->htif); check_cm_clint_config(L, tabidx, &config->clint); + check_cm_plic_config(L, tabidx, &config->plic); check_cm_uarch_config(L, tabidx, &config->uarch); check_cm_rollup_config(L, tabidx, &config->rollup); check_cm_flash_drive_configs(L, tabidx, &config->flash_drive); diff --git a/src/device-state-access.h b/src/device-state-access.h index 518ba195b..c761b1947 100644 --- a/src/device-state-access.h +++ b/src/device-state-access.h @@ -95,6 +95,22 @@ class device_state_access : public i_device_state_access { return m_a.write_clint_mtimecmp(val); } + uint64_t do_read_plic_girqpend(void) override { + return m_a.read_plic_girqpend(); + } + + void do_write_plic_girqpend(uint64_t val) override { + return m_a.write_plic_girqpend(val); + } + + uint64_t do_read_plic_girqsrvd(void) override { + return m_a.read_plic_girqsrvd(); + } + + void do_write_plic_girqsrvd(uint64_t val) override { + return m_a.write_plic_girqsrvd(val); + } + uint64_t do_read_htif_fromhost(void) override { return m_a.read_htif_fromhost(); } diff --git a/src/dtb.cpp b/src/dtb.cpp index 95c8022c5..ab48521f4 100644 --- a/src/dtb.cpp +++ b/src/dtb.cpp @@ -45,7 +45,7 @@ static std::string misa_to_isa_string(uint64_t misa) { void dtb_init(const machine_config &c, unsigned char *dtb_start, uint64_t dtb_length) { using namespace std::string_literals; - constexpr uint32_t INTC_PHANDLE = 1; + enum : uint32_t { INTC_PHANDLE = 1, PLIC_PHANDLE }; constexpr uint32_t X_HOST = 13; constexpr uint32_t BOOTARGS_MAX_LEN = 4096; @@ -122,6 +122,18 @@ void dtb_init(const machine_config &c, unsigned char *dtb_start, uint64_t dtb_le {INTC_PHANDLE, MIP_MSIP_SHIFT, INTC_PHANDLE, MIP_MTIP_SHIFT}); fdt.end_node(); } + { // plic + fdt.begin_node_num("plic", PMA_PLIC_START); + fdt.prop_u32("#interrupt-cells", 1); + fdt.prop_empty("interrupt-controller"); + fdt.prop_string("compatible", "riscv,plic0"); + fdt.prop_u32("riscv,ndev", PMA_PLIC_MAX_IRQ); + fdt.prop_u64_list<2>("reg", {PMA_PLIC_START, PMA_PLIC_LENGTH}); + fdt.prop_u32_list<4>("interrupts-extended", + {INTC_PHANDLE, MIP_SEIP_SHIFT, INTC_PHANDLE, MIP_MEIP_SHIFT}); + fdt.prop_u32("phandle", PLIC_PHANDLE); + fdt.end_node(); + } { // htif fdt.begin_node_num("htif", PMA_HTIF_START); fdt.prop_string("compatible", "ucb,htif0"); diff --git a/src/grpc-virtual-machine.cpp b/src/grpc-virtual-machine.cpp index ddae4f03d..2f89e5f70 100644 --- a/src/grpc-virtual-machine.cpp +++ b/src/grpc-virtual-machine.cpp @@ -815,6 +815,22 @@ void grpc_virtual_machine::do_write_clint_mtimecmp(uint64_t val) { write_csr(csr::clint_mtimecmp, val); } +uint64_t grpc_virtual_machine::do_read_plic_girqpend(void) const { + return read_csr(csr::plic_girqpend); +} + +void grpc_virtual_machine::do_write_plic_girqpend(uint64_t val) { + write_csr(csr::plic_girqpend, val); +} + +uint64_t grpc_virtual_machine::do_read_plic_girqsrvd(void) const { + return read_csr(csr::plic_girqsrvd); +} + +void grpc_virtual_machine::do_write_plic_girqsrvd(uint64_t val) { + write_csr(csr::plic_girqsrvd, val); +} + void grpc_virtual_machine::do_get_root_hash(hash_type &hash) const { GetRootHashResponse response; const Void request; diff --git a/src/grpc-virtual-machine.h b/src/grpc-virtual-machine.h index b99665f2b..d615b78df 100644 --- a/src/grpc-virtual-machine.h +++ b/src/grpc-virtual-machine.h @@ -210,6 +210,10 @@ class grpc_virtual_machine : public i_virtual_machine { void do_write_htif_iyield(uint64_t val) override; uint64_t do_read_clint_mtimecmp(void) const override; void do_write_clint_mtimecmp(uint64_t val) override; + uint64_t do_read_plic_girqpend(void) const override; + void do_write_plic_girqpend(uint64_t val) override; + uint64_t do_read_plic_girqsrvd(void) const override; + void do_write_plic_girqsrvd(uint64_t val) override; void do_get_root_hash(hash_type &hash) const override; machine_merkle_tree::proof_type do_get_proof(uint64_t address, int log2_size) const override; void do_replace_memory_range(const memory_range_config &new_range) override; diff --git a/src/i-device-state-access.h b/src/i-device-state-access.h index 83183a3a4..190ca1655 100644 --- a/src/i-device-state-access.h +++ b/src/i-device-state-access.h @@ -102,6 +102,30 @@ class i_device_state_access { return do_write_clint_mtimecmp(val); } + /// \brief Reads PLIC's girqpend. + /// \returns Register value. + uint64_t read_plic_girqpend(void) { + return do_read_plic_girqpend(); + } + + /// \brief Writes PLIC's girqpend. + /// \param val New register value. + void write_plic_girqpend(uint64_t val) { + return do_write_plic_girqpend(val); + } + + /// \brief Reads PLIC's girqsrvd. + /// \returns Register value. + uint64_t read_plic_girqsrvd(void) { + return do_read_plic_girqsrvd(); + } + + /// \brief Writes PLIC's girqsrvd. + /// \param val New register value. + void write_plic_girqsrvd(uint64_t val) { + return do_write_plic_girqsrvd(val); + } + /// \brief Reads HTIF's fromhost. /// \returns Register value. uint64_t read_htif_fromhost(void) { @@ -178,6 +202,10 @@ class i_device_state_access { virtual void do_set_iflags_X(void) = 0; virtual uint64_t do_read_clint_mtimecmp(void) = 0; virtual void do_write_clint_mtimecmp(uint64_t val) = 0; + virtual uint64_t do_read_plic_girqpend(void) = 0; + virtual void do_write_plic_girqpend(uint64_t val) = 0; + virtual uint64_t do_read_plic_girqsrvd(void) = 0; + virtual void do_write_plic_girqsrvd(uint64_t val) = 0; virtual uint64_t do_read_htif_fromhost(void) = 0; virtual void do_write_htif_fromhost(uint64_t val) = 0; virtual uint64_t do_read_htif_tohost(void) = 0; diff --git a/src/i-state-access.h b/src/i-state-access.h index 99571cb3e..7b2862019 100644 --- a/src/i-state-access.h +++ b/src/i-state-access.h @@ -536,6 +536,30 @@ class i_state_access { // CRTP return derived().do_write_clint_mtimecmp(val); } + /// \brief Reads PLIC's girqpend. + /// \returns Register value. + uint64_t read_plic_girqpend(void) { + return derived().do_read_plic_girqpend(); + } + + /// \brief Writes PLIC's girqpend. + /// \param val New register value. + void write_plic_girqpend(uint64_t val) { + return derived().do_write_plic_girqpend(val); + } + + /// \brief Reads PLIC's girqsrvd. + /// \returns Register value. + uint64_t read_plic_girqsrvd(void) { + return derived().do_read_plic_girqsrvd(); + } + + /// \brief Writes PLIC's girqsrvd. + /// \param val New register value. + void write_plic_girqsrvd(uint64_t val) { + return derived().do_write_plic_girqsrvd(val); + } + /// \brief Reads HTIF's fromhost. /// \returns Register value. uint64_t read_htif_fromhost(void) { diff --git a/src/i-virtual-machine.h b/src/i-virtual-machine.h index 55b0a269b..8ec4d7653 100644 --- a/src/i-virtual-machine.h +++ b/src/i-virtual-machine.h @@ -572,6 +572,26 @@ class i_virtual_machine { do_write_clint_mtimecmp(val); } + /// \brief Reads PLIC's girqpend + uint64_t read_plic_girqpend(void) const { + return do_read_plic_girqpend(); + } + + /// \brief Writes PLIC's girqpend + void write_plic_girqpend(uint64_t val) { + do_write_plic_girqpend(val); + } + + /// \brief Reads PLIC's girqsrvd + uint64_t read_plic_girqsrvd(void) const { + return do_read_plic_girqsrvd(); + } + + /// \brief Writes PLIC's girqsrvd + void write_plic_girqsrvd(uint64_t val) { + do_write_plic_girqsrvd(val); + } + /// \brief Reads the value of a microarchitecture register. /// \param i Register index. Between 0 and UARCH_X_REG_COUNT-1, inclusive. /// \returns The value of the register. @@ -743,6 +763,10 @@ class i_virtual_machine { virtual void do_write_htif_iyield(uint64_t val) = 0; virtual uint64_t do_read_clint_mtimecmp(void) const = 0; virtual void do_write_clint_mtimecmp(uint64_t val) = 0; + virtual uint64_t do_read_plic_girqpend(void) const = 0; + virtual void do_write_plic_girqpend(uint64_t val) = 0; + virtual uint64_t do_read_plic_girqsrvd(void) const = 0; + virtual void do_write_plic_girqsrvd(uint64_t val) = 0; virtual void do_replace_memory_range(const memory_range_config &new_range) = 0; virtual uint64_t do_read_word(uint64_t address) const = 0; virtual bool do_verify_dirty_page_maps(void) const = 0; diff --git a/src/json-util.cpp b/src/json-util.cpp index 81639f857..1c8883b0f 100644 --- a/src/json-util.cpp +++ b/src/json-util.cpp @@ -84,6 +84,8 @@ static auto csr_from_name(const std::string &name) { {"ilrsc", csr::ilrsc}, {"iflags", csr::iflags}, {"clint_mtimecmp", csr::clint_mtimecmp}, + {"plic_girqpend", csr::plic_girqpend}, + {"plic_girqsrvd", csr::plic_girqsrvd}, {"htif_tohost", csr::htif_tohost}, {"htif_fromhost", csr::htif_fromhost}, {"htif_ihalt", csr::htif_ihalt}, @@ -165,6 +167,10 @@ static auto csr_to_name(machine::csr reg) { return "iflags"; case csr::clint_mtimecmp: return "clint_mtimecmp"; + case csr::plic_girqpend: + return "plic_girqpend"; + case csr::plic_girqsrvd: + return "plic_girqsrvd"; case csr::htif_tohost: return "htif_tohost"; case csr::htif_fromhost: @@ -949,6 +955,23 @@ template void ju_get_opt_field(const nlohmann::json &j, const uint64_t template void ju_get_opt_field(const nlohmann::json &j, const std::string &key, clint_config &value, const std::string &path); +template +void ju_get_opt_field(const nlohmann::json &j, const K &key, plic_config &value, const std::string &path) { + if (!contains(j, key)) { + return; + } + const auto &jconfig = j[key]; + const auto new_path = path + to_string(key) + "/"; + ju_get_opt_field(jconfig, "girqpend"s, value.girqpend, new_path); + ju_get_opt_field(jconfig, "girqsrvd"s, value.girqsrvd, new_path); +} + +template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, plic_config &value, + const std::string &path); + +template void ju_get_opt_field(const nlohmann::json &j, const std::string &key, plic_config &value, + const std::string &path); + template void ju_get_opt_field(const nlohmann::json &j, const K &key, htif_config &value, const std::string &path) { if (!contains(j, key)) { @@ -1078,6 +1101,7 @@ void ju_get_opt_field(const nlohmann::json &j, const K &key, machine_config &val ju_get_opt_field(config, "flash_drive"s, value.flash_drive, new_path); ju_get_opt_field(config, "tlb"s, value.tlb, new_path); ju_get_opt_field(config, "clint"s, value.clint, new_path); + ju_get_opt_field(config, "plic"s, value.plic, new_path); ju_get_opt_field(config, "htif"s, value.htif, new_path); ju_get_opt_field(config, "uarch"s, value.uarch, new_path); ju_get_opt_field(config, "rollup"s, value.rollup, new_path); @@ -1258,6 +1282,13 @@ void to_json(nlohmann::json &j, const clint_config &config) { }; } +void to_json(nlohmann::json &j, const plic_config &config) { + j = nlohmann::json{ + {"girqpend", config.girqpend}, + {"girqsrvd", config.girqsrvd}, + }; +} + void to_json(nlohmann::json &j, const htif_config &config) { j = nlohmann::json{ {"fromhost", config.fromhost}, @@ -1308,6 +1339,7 @@ void to_json(nlohmann::json &j, const machine_config &config) { {"flash_drive", config.flash_drive}, {"tlb", config.tlb}, {"clint", config.clint}, + {"plic", config.plic}, {"htif", config.htif}, {"uarch", config.uarch}, }; diff --git a/src/json-util.h b/src/json-util.h index 54f9ce670..e7deb9f98 100644 --- a/src/json-util.h +++ b/src/json-util.h @@ -327,6 +327,15 @@ void ju_get_opt_field(const nlohmann::json &j, const K &key, tlb_config &value, template void ju_get_opt_field(const nlohmann::json &j, const K &key, clint_config &value, const std::string &path = "params/"); +/// \brief Attempts to load a plic_config object from a field in a JSON object +/// \tparam K Key type (explicit extern declarations for uint64_t and std::string are provided) +/// \param j JSON object to load from +/// \param key Key to load value from +/// \param value Object to store value +/// \param path Path to j +template +void ju_get_opt_field(const nlohmann::json &j, const K &key, plic_config &value, const std::string &path = "params/"); + /// \brief Attempts to load an htif_config object from a field in a JSON object /// \tparam K Key type (explicit extern declarations for uint64_t and std::string are provided) /// \param j JSON object to load from @@ -564,6 +573,7 @@ void to_json(nlohmann::json &j, const ram_config &config); void to_json(nlohmann::json &j, const dtb_config &config); void to_json(nlohmann::json &j, const tlb_config &config); void to_json(nlohmann::json &j, const clint_config &config); +void to_json(nlohmann::json &j, const plic_config &config); void to_json(nlohmann::json &j, const htif_config &config); void to_json(nlohmann::json &j, const rollup_config &config); void to_json(nlohmann::json &j, const uarch_processor_config &config); @@ -685,6 +695,10 @@ extern template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &k const std::string &base = "params/"); extern template void ju_get_opt_field(const nlohmann::json &j, const std::string &key, clint_config &value, const std::string &base = "params/"); +extern template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, plic_config &value, + const std::string &base = "params/"); +extern template void ju_get_opt_field(const nlohmann::json &j, const std::string &key, plic_config &value, + const std::string &base = "params/"); extern template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, htif_config &value, const std::string &base = "params/"); extern template void ju_get_opt_field(const nlohmann::json &j, const std::string &key, htif_config &value, diff --git a/src/jsonrpc-discover.json b/src/jsonrpc-discover.json index 3235dcf73..f54eb3f68 100644 --- a/src/jsonrpc-discover.json +++ b/src/jsonrpc-discover.json @@ -1178,6 +1178,8 @@ "ilrsc", "iflags", "clint_mtimecmp", + "plic_girqpend", + "plic_girqsrvd", "htif_tohost", "htif_fromhost", "htif_ihalt", @@ -1632,6 +1634,19 @@ } }, + "PLICConfig": { + "title": "PLICConfig", + "type": "object", + "properties": { + "girqpend": { + "$ref": "#/components/schemas/UnsignedInteger" + }, + "girqsrvd": { + "$ref": "#/components/schemas/UnsignedInteger" + }, + } + }, + "HTIFConfig": { "title": "HTIFConfig", "type": "object", @@ -1740,6 +1755,9 @@ "clint": { "$ref": "#/components/schemas/CLINTConfig" }, + "plic": { + "$ref": "#/components/schemas/PLICConfig" + }, "htif": { "$ref": "#/components/schemas/HTIFConfig" }, diff --git a/src/jsonrpc-virtual-machine.cpp b/src/jsonrpc-virtual-machine.cpp index c82e66b36..de4c65309 100644 --- a/src/jsonrpc-virtual-machine.cpp +++ b/src/jsonrpc-virtual-machine.cpp @@ -790,6 +790,22 @@ void jsonrpc_virtual_machine::do_write_clint_mtimecmp(uint64_t val) { write_csr(csr::clint_mtimecmp, val); } +uint64_t jsonrpc_virtual_machine::do_read_plic_girqpend(void) const { + return read_csr(csr::plic_girqpend); +} + +void jsonrpc_virtual_machine::do_write_plic_girqpend(uint64_t val) { + write_csr(csr::plic_girqpend, val); +} + +uint64_t jsonrpc_virtual_machine::do_read_plic_girqsrvd(void) const { + return read_csr(csr::plic_girqsrvd); +} + +void jsonrpc_virtual_machine::do_write_plic_girqsrvd(uint64_t val) { + write_csr(csr::plic_girqsrvd, val); +} + void jsonrpc_virtual_machine::do_get_root_hash(hash_type &hash) const { jsonrpc_request(m_mgr->get_mgr(), m_mgr->get_remote_address(), "machine.get_root_hash", std::tie(), hash); } diff --git a/src/jsonrpc-virtual-machine.h b/src/jsonrpc-virtual-machine.h index 19ee9a6df..d0807b9ea 100644 --- a/src/jsonrpc-virtual-machine.h +++ b/src/jsonrpc-virtual-machine.h @@ -172,6 +172,10 @@ class jsonrpc_virtual_machine final : public i_virtual_machine { void do_write_htif_iyield(uint64_t val) override; uint64_t do_read_clint_mtimecmp(void) const override; void do_write_clint_mtimecmp(uint64_t val) override; + uint64_t do_read_plic_girqpend(void) const override; + void do_write_plic_girqpend(uint64_t val) override; + uint64_t do_read_plic_girqsrvd(void) const override; + void do_write_plic_girqsrvd(uint64_t val) override; void do_get_root_hash(hash_type &hash) const override; machine_merkle_tree::proof_type do_get_proof(uint64_t address, int log2_size) const override; void do_replace_memory_range(const memory_range_config &new_range) override; diff --git a/src/machine-c-api.cpp b/src/machine-c-api.cpp index 1ddab7ab0..2282dc370 100644 --- a/src/machine-c-api.cpp +++ b/src/machine-c-api.cpp @@ -251,6 +251,24 @@ static cm_clint_config convert_to_c(const cartesi::clint_config &cpp_config) { return new_c_clint_config; } +// ---------------------------------------------- +// PLIC configuration conversion functions +// ---------------------------------------------- +static cartesi::plic_config convert_from_c(const cm_plic_config *c_config) { + cartesi::plic_config new_cpp_plic_config{}; + new_cpp_plic_config.girqpend = c_config->girqpend; + new_cpp_plic_config.girqsrvd = c_config->girqsrvd; + return new_cpp_plic_config; +} + +static cm_plic_config convert_to_c(const cartesi::plic_config &cpp_config) { + cm_plic_config new_c_plic_config{}; + memset(&new_c_plic_config, 0, sizeof(cm_plic_config)); + new_c_plic_config.girqpend = cpp_config.girqpend; + new_c_plic_config.girqsrvd = cpp_config.girqsrvd; + return new_c_plic_config; +} + // ---------------------------------------------- // HTIF configuration conversion functions // ---------------------------------------------- @@ -384,6 +402,7 @@ cartesi::machine_config convert_from_c(const cm_machine_config *c_config) { new_cpp_machine_config.dtb = convert_from_c(&c_config->dtb); new_cpp_machine_config.tlb = convert_from_c(&c_config->tlb); new_cpp_machine_config.clint = convert_from_c(&c_config->clint); + new_cpp_machine_config.plic = convert_from_c(&c_config->plic); new_cpp_machine_config.htif = convert_from_c(&c_config->htif); new_cpp_machine_config.uarch = convert_from_c(&c_config->uarch); new_cpp_machine_config.rollup = convert_from_c(&c_config->rollup); @@ -414,6 +433,7 @@ cm_machine_config *convert_to_c(const cartesi::machine_config &cpp_config) { new_machine_config->flash_drive = convert_to_c(cpp_config.flash_drive); new_machine_config->tlb = convert_to_c(cpp_config.tlb); new_machine_config->clint = convert_to_c(cpp_config.clint); + new_machine_config->plic = convert_to_c(cpp_config.plic); new_machine_config->htif = convert_to_c(cpp_config.htif); new_machine_config->uarch = convert_to_c(cpp_config.uarch); new_machine_config->rollup = convert_to_c(cpp_config.rollup); @@ -1239,6 +1259,8 @@ IMPL_MACHINE_READ_WRITE(htif_ihalt) IMPL_MACHINE_READ_WRITE(htif_iconsole) IMPL_MACHINE_READ_WRITE(htif_iyield) IMPL_MACHINE_READ_WRITE(clint_mtimecmp) +IMPL_MACHINE_READ_WRITE(plic_girqpend) +IMPL_MACHINE_READ_WRITE(plic_girqsrvd) IMPL_MACHINE_READ_WRITE(uarch_cycle) IMPL_MACHINE_READ_WRITE(uarch_pc) // clang-format-on diff --git a/src/machine-c-api.h b/src/machine-c-api.h index 77f3e2f48..a374203b7 100644 --- a/src/machine-c-api.h +++ b/src/machine-c-api.h @@ -130,6 +130,8 @@ typedef enum { // NOLINT(modernize-use-using) CM_PROC_ILRSC, CM_PROC_IFLAGS, CM_PROC_CLINT_MTIMECMP, + CM_PROC_PLIC_GIRQPEND, + CM_PROC_PLIC_GIRQSRVD, CM_PROC_HTIF_TOHOST, CM_PROC_HTIF_FROMHOST, CM_PROC_HTIF_IHALT, @@ -221,6 +223,12 @@ typedef struct { // NOLINT(modernize-use-using) uint64_t mtimecmp; ///< Value of mtimecmp CSR } cm_clint_config; +/// \brief PLIC device state configuration +typedef struct { // NOLINT(modernize-use-using) + uint64_t girqpend; ///< Value of girqpend CSR + uint64_t girqsrvd; ///< Value of girqsrvd CSR +} cm_plic_config; + /// \brief HTIF device state configuration typedef struct { // NOLINT(modernize-use-using) uint64_t fromhost; ///< Value of fromhost CSR @@ -267,6 +275,7 @@ typedef struct { // NOLINT(modernize-use-using) cm_memory_range_config_array flash_drive; cm_tlb_config tlb; cm_clint_config clint; + cm_plic_config plic; cm_htif_config htif; cm_rollup_config rollup; cm_uarch_config uarch; @@ -1460,6 +1469,46 @@ CM_API int cm_read_clint_mtimecmp(const cm_machine *m, uint64_t *val, char **err /// \returns 0 for success, non zero code for error CM_API int cm_write_clint_mtimecmp(cm_machine *m, uint64_t val, char **err_msg); +/// \brief Reads the value of PLIC's girqpend register. +/// \param m Pointer to valid machine instance +/// \param val Receives value of the register. +/// \param err_msg Receives the error message if function execution fails +/// or NULL in case of successful function execution. In case of failure error_msg +/// must be deleted by the function caller using cm_delete_error_message. +/// err_msg can be NULL, meaning the error message won't be received. +/// \returns 0 for success, non zero code for error +CM_API int cm_read_plic_girqpend(const cm_machine *m, uint64_t *val, char **err_msg); + +/// \brief Writes the value of PLIC's girqpend register. +/// \param m Pointer to valid machine instance +/// \param val New register value. +/// \param err_msg Receives the error message if function execution fails +/// or NULL in case of successful function execution. In case of failure error_msg +/// must be deleted by the function caller using cm_delete_error_message. +/// err_msg can be NULL, meaning the error message won't be received. +/// \returns 0 for success, non zero code for error +CM_API int cm_write_plic_girqpend(cm_machine *m, uint64_t val, char **err_msg); + +/// \brief Reads the value of PLIC's girqsrvd register. +/// \param m Pointer to valid machine instance +/// \param val Receives value of the register. +/// \param err_msg Receives the error message if function execution fails +/// or NULL in case of successful function execution. In case of failure error_msg +/// must be deleted by the function caller using cm_delete_error_message. +/// err_msg can be NULL, meaning the error message won't be received. +/// \returns 0 for success, non zero code for error +CM_API int cm_read_plic_girqsrvd(const cm_machine *m, uint64_t *val, char **err_msg); + +/// \brief Writes the value of PLIC's girqsrvd register. +/// \param m Pointer to valid machine instance +/// \param val New register value. +/// \param err_msg Receives the error message if function execution fails +/// or NULL in case of successful function execution. In case of failure error_msg +/// must be deleted by the function caller using cm_delete_error_message. +/// err_msg can be NULL, meaning the error message won't be received. +/// \returns 0 for success, non zero code for error +CM_API int cm_write_plic_girqsrvd(cm_machine *m, uint64_t val, char **err_msg); + /// \brief Checks the value of the iflags_X flag. /// \param m Pointer to valid machine instance /// \param val Receives the flag value diff --git a/src/machine-config.h b/src/machine-config.h index 91bdb67db..12e403f9d 100644 --- a/src/machine-config.h +++ b/src/machine-config.h @@ -107,6 +107,12 @@ struct clint_config final { uint64_t mtimecmp{MTIMECMP_INIT}; ///< Value of mtimecmp CSR }; +/// \brief PLIC device state configuration +struct plic_config final { + uint64_t girqpend{GIRQPEND_INIT}; ///< Value of girqpend CSR + uint64_t girqsrvd{GIRQSRVD_INIT}; ///< Value of girqsrvd CSR +}; + /// \brief HTIF device state configuration struct htif_config final { uint64_t fromhost{FROMHOST_INIT}; ///< Value of fromhost CSR @@ -135,6 +141,7 @@ struct machine_config final { flash_drive_configs flash_drive{}; ///< Flash drives state tlb_config tlb{}; ///< TLB device state clint_config clint{}; ///< CLINT device state + plic_config plic{}; ///< PLIC device state htif_config htif{}; ///< HTIF device state uarch_config uarch{}; ///< microarchitecture configuration std::optional rollup{}; ///< Rollup state diff --git a/src/machine-state.h b/src/machine-state.h index c042a6ee1..791310019 100644 --- a/src/machine-state.h +++ b/src/machine-state.h @@ -103,6 +103,12 @@ struct machine_state { uint64_t mtimecmp; ///< CSR mtimecmp. } clint; + /// \brief PLIC state + struct { + uint64_t girqpend; ///< CSR girqpend (global interrupts pending). + uint64_t girqsrvd; ///< CSR girqsrvd (global interrupts served). + } plic; + /// \brief TLB state shadow_tlb_state tlb; diff --git a/src/machine.cpp b/src/machine.cpp index 7ecafc055..82ab4e7f0 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -26,6 +26,7 @@ #include "htif-factory.h" #include "interpret.h" #include "machine.h" +#include "plic-factory.h" #include "riscv-constants.h" #include "rtc.h" #include "shadow-pmas-factory.h" @@ -416,11 +417,18 @@ machine::machine(const machine_config &c, const machine_runtime_config &r) : const uint64_t htif_iyield = static_cast(m_c.htif.yield_manual) << HTIF_YIELD_MANUAL | static_cast(m_c.htif.yield_automatic) << HTIF_YIELD_AUTOMATIC; write_htif_iyield(htif_iyield); - // Resiter CLINT device + + // Register CLINT device register_pma_entry(make_clint_pma_entry(PMA_CLINT_START, PMA_CLINT_LENGTH)); // Copy CLINT state to from config to machine write_clint_mtimecmp(m_c.clint.mtimecmp); + // Register PLIC device + register_pma_entry(make_plic_pma_entry(PMA_PLIC_START, PMA_PLIC_LENGTH)); + // Copy PLIC state from config to machine + write_plic_girqpend(m_c.plic.girqpend); + write_plic_girqsrvd(m_c.plic.girqsrvd); + // Register TLB device register_pma_entry(make_shadow_tlb_pma_entry(PMA_SHADOW_TLB_START, PMA_SHADOW_TLB_LENGTH)); @@ -554,6 +562,9 @@ machine_config machine::get_serialization_config(void) const { c.processor.iflags = read_iflags(); // Copy current CLINT state to config c.clint.mtimecmp = read_clint_mtimecmp(); + // Copy current PLIC state to config + c.plic.girqpend = read_plic_girqpend(); + c.plic.girqsrvd = read_plic_girqsrvd(); // Copy current HTIF state to config c.htif.tohost = read_htif_tohost(); c.htif.fromhost = read_htif_fromhost(); @@ -1091,6 +1102,22 @@ void machine::write_clint_mtimecmp(uint64_t val) { m_s.clint.mtimecmp = val; } +uint64_t machine::read_plic_girqpend(void) const { + return m_s.plic.girqpend; +} + +void machine::write_plic_girqpend(uint64_t val) { + m_s.plic.girqpend = val; +} + +uint64_t machine::read_plic_girqsrvd(void) const { + return m_s.plic.girqsrvd; +} + +void machine::write_plic_girqsrvd(uint64_t val) { + m_s.plic.girqsrvd = val; +} + uint64_t machine::read_csr(csr r) const { switch (r) { case csr::pc: @@ -1155,6 +1182,10 @@ uint64_t machine::read_csr(csr r) const { return read_iflags(); case csr::clint_mtimecmp: return read_clint_mtimecmp(); + case csr::plic_girqpend: + return read_plic_girqpend(); + case csr::plic_girqsrvd: + return read_plic_girqsrvd(); case csr::htif_tohost: return read_htif_tohost(); case csr::htif_fromhost: @@ -1235,6 +1266,10 @@ void machine::write_csr(csr csr, uint64_t value) { return write_iflags(value); case csr::clint_mtimecmp: return write_clint_mtimecmp(value); + case csr::plic_girqpend: + return write_plic_girqpend(value); + case csr::plic_girqsrvd: + return write_plic_girqsrvd(value); case csr::htif_tohost: return write_htif_tohost(value); case csr::htif_fromhost: @@ -1336,6 +1371,10 @@ uint64_t machine::get_csr_address(csr csr) { return shadow_state_get_csr_abs_addr(shadow_state_csr::htif_iyield); case csr::clint_mtimecmp: return shadow_state_get_csr_abs_addr(shadow_state_csr::clint_mtimecmp); + case csr::plic_girqpend: + return shadow_state_get_csr_abs_addr(shadow_state_csr::plic_girqpend); + case csr::plic_girqsrvd: + return shadow_state_get_csr_abs_addr(shadow_state_csr::plic_girqsrvd); case csr::uarch_pc: return shadow_uarch_state_get_csr_abs_addr(shadow_uarch_state_csr::pc); case csr::uarch_cycle: diff --git a/src/machine.h b/src/machine.h index efb6cb3c1..751fd3a98 100644 --- a/src/machine.h +++ b/src/machine.h @@ -168,6 +168,8 @@ class machine final { ilrsc, iflags, clint_mtimecmp, + plic_girqpend, + plic_girqsrvd, htif_tohost, htif_fromhost, htif_ihalt, @@ -714,6 +716,22 @@ class machine final { /// \param value New register value. void write_clint_mtimecmp(uint64_t value); + /// \brief Reads the value of PLIC's girqpend register. + /// \returns The value of the register. + uint64_t read_plic_girqpend(void) const; + + /// \brief Writes the value of PLIC's girqpend register. + /// \param val New register value. + void write_plic_girqpend(uint64_t val); + + /// \brief Reads the value of PLIC's girqsrvd register. + /// \returns The value of the register. + uint64_t read_plic_girqsrvd(void) const; + + /// \brief Writes the value of PLIC's girqsrvd register. + /// \param val New register value. + void write_plic_girqsrvd(uint64_t val); + /// \brief Checks the value of the iflags_X flag. /// \returns The flag value. bool read_iflags_X(void) const; diff --git a/src/plic-factory.cpp b/src/plic-factory.cpp new file mode 100644 index 000000000..5ac59fa65 --- /dev/null +++ b/src/plic-factory.cpp @@ -0,0 +1,42 @@ +// Copyright Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along +// with this program (see COPYING). If not, see . +// + +#include "plic-factory.h" + +namespace cartesi { + +/// \brief PLIC device peek callback. See ::pma_peek. +static bool plic_peek(const pma_entry &pma, const machine &m, uint64_t page_offset, const unsigned char **page_data, + unsigned char *) { + (void) m; + // PLIC range can be represented as pristine because its state is already represented in shadow CSRs + *page_data = nullptr; + return (page_offset % PMA_PAGE_SIZE) == 0 && page_offset < pma.get_length(); +} + +pma_entry make_plic_pma_entry(uint64_t start, uint64_t length) { + const pma_entry::flags f{ + true, // R + true, // W + false, // X + false, // IR + false, // IW + PMA_ISTART_DID::PLIC // DID + }; + return make_device_pma_entry("PLIC device", start, length, plic_peek, &plic_driver).set_flags(f); +} + +} // namespace cartesi diff --git a/src/plic-factory.h b/src/plic-factory.h new file mode 100644 index 000000000..c72a85cdb --- /dev/null +++ b/src/plic-factory.h @@ -0,0 +1,35 @@ +// Copyright Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along +// with this program (see COPYING). If not, see . +// + +#ifndef PLIC_FACTORY_H +#define PLIC_FACTORY_H + +#include + +#include "plic.h" +#include "pma.h" + +namespace cartesi { + +/// \brief Creates a PMA entry for the PLIC device +/// \param start Start address for memory range. +/// \param length Length of memory range. +/// \returns Corresponding PMA entry +pma_entry make_plic_pma_entry(uint64_t start, uint64_t length); + +} // namespace cartesi + +#endif diff --git a/src/plic.cpp b/src/plic.cpp new file mode 100644 index 000000000..8b110425d --- /dev/null +++ b/src/plic.cpp @@ -0,0 +1,224 @@ +// Copyright Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along +// with this program (see COPYING). If not, see . +// + +#include "plic.h" +#include "i-device-state-access.h" +#include "pma-constants.h" +#include "riscv-constants.h" + +#include +#include + +// Enable these defines to debug PLIC +// #define DEBUG_PLIC +// #define DEBUG_PLIC_MMIO + +namespace cartesi { + +// The return value is undefined if v == 0 +// This works on gcc and clang and uses the lzcnt instruction +static inline uint32_t ilog2(uint32_t v) { + return 31 - __builtin_clz(v); +} + +/// \brief Called only bu the driver when it wants retrieve current pending interrupt requests. +static uint32_t plic_read_pending(i_device_state_access *a) { + // The actual pending interrupt sources are masked by interrupts being served + const uint32_t girqpend = a->read_plic_girqpend(); + const uint32_t girqsrvd = a->read_plic_girqsrvd(); + const uint32_t ipmask = girqpend & ~girqsrvd; +#ifdef DEBUG_PLIC + (void) fprintf(stderr, "plic: read pending ipmask=%d\n", ipmask); +#endif + return ipmask; +} + +/// \brief Called only by the driver when it begins serving a pending interrupt request. +static bool plic_read_claim_complete(i_device_state_access *a, uint64_t *val) { + const uint32_t girqpend = a->read_plic_girqpend(); + uint32_t girqsrvd = a->read_plic_girqsrvd(); + uint32_t ipmask = girqpend & ~girqsrvd; + // Are there pending interrupts that have yet to be served? + if (ipmask != 0) { + // On receiving a claim message, + // the PLIC will atomically determine the interrupt source id + // of the highest-priority pending interrupt for the target + // and then clear down the corresponding source's IP bit. + // We actually clear the source IP bit by masking girqsrvd until its claim is complete. + const uint32_t irq_id = ilog2(ipmask); + const uint32_t irq_mask = UINT32_C(1) << irq_id; + girqsrvd |= irq_mask; + a->write_plic_girqsrvd(girqsrvd); + // The PLIC will then return the interrupt source id to the target + *val = irq_id; + // If all pending interrupts have been served, reset mip. + ipmask = girqpend & ~girqsrvd; + if (ipmask == 0) { + a->reset_mip(MIP_MEIP_MASK | MIP_SEIP_MASK); + } + } else { + // The PLIC will return an id of zero, if there were no pending interrupts for the target + *val = 0; + } +#ifdef DEBUG_PLIC + (void) fprintf(stderr, "plic: claim irq_id=%d\n", (int) *val); +#endif + return true; +} + +/// \brief Called only by the driver when it completes serving a pending interrupt request. +static execute_status plic_write_claim_complete(i_device_state_access *a, uint32_t val) { +#ifdef DEBUG_PLIC + (void) fprintf(stderr, "plic: claim complete irq_id=%d\n", val); +#endif + if (val >= 1 && val <= PMA_PLIC_MAX_IRQ_DEF) { + // On completing, we need to clear its corresponding girqsrvd mask + const uint32_t girqpend = a->read_plic_girqpend(); + uint32_t girqsrvd = a->read_plic_girqsrvd(); + const uint32_t irq_mask = UINT32_C(1) << val; + girqsrvd &= ~irq_mask; + a->write_plic_girqsrvd(girqsrvd); + // If all pending interrupts have been served, reset mip. Otherwise, set mip. + const uint32_t ipmask = girqpend & ~girqsrvd; + if (ipmask == 0) { + a->reset_mip(MIP_MEIP_MASK | MIP_SEIP_MASK); + } else { + a->set_mip(MIP_MEIP_MASK | MIP_SEIP_MASK); + return execute_status::success_and_serve_interrupts; + } + } + return execute_status::success; +} + +/// \brief PLIC device read callback. See ::pma_read. +static bool plic_read(void *context, i_device_state_access *a, uint64_t offset, uint64_t *val, int log2_size) { + (void) context; +#ifdef DEBUG_PLIC_MMIO + (void) fprintf(stderr, "plic: mmio read offset=0x%lx log2_size=%d\n", (long) offset, log2_size); +#endif + + // Our PLIC only supports aligned 32-bit reads + if (offset & 3 || log2_size != 2 || offset > PMA_PLIC_LENGTH) { + return false; + } + + switch (offset) { + case plic_csr_rel_addr::priority1: + case plic_csr_rel_addr::priority2: + case plic_csr_rel_addr::priority3: + case plic_csr_rel_addr::priority4: + case plic_csr_rel_addr::priority5: + case plic_csr_rel_addr::priority6: + case plic_csr_rel_addr::priority7: + case plic_csr_rel_addr::priority8: + case plic_csr_rel_addr::priority9: + case plic_csr_rel_addr::priority10: + case plic_csr_rel_addr::priority11: + case plic_csr_rel_addr::priority12: + case plic_csr_rel_addr::priority13: + case plic_csr_rel_addr::priority14: + case plic_csr_rel_addr::priority15: + case plic_csr_rel_addr::priority16: + case plic_csr_rel_addr::priority17: + case plic_csr_rel_addr::priority18: + case plic_csr_rel_addr::priority19: + case plic_csr_rel_addr::priority20: + case plic_csr_rel_addr::priority21: + case plic_csr_rel_addr::priority22: + case plic_csr_rel_addr::priority23: + case plic_csr_rel_addr::priority24: + case plic_csr_rel_addr::priority25: + case plic_csr_rel_addr::priority26: + case plic_csr_rel_addr::priority27: + case plic_csr_rel_addr::priority28: + case plic_csr_rel_addr::priority29: + case plic_csr_rel_addr::priority30: + case plic_csr_rel_addr::priority31: + // A valid implementation can hardwire all input priority levels. + // We hardwire all supported interrupt sources to the lowest priority + *val = PLIC_LOWEST_IRQ_PRIORITY; + return true; + case plic_csr_rel_addr::pending: + *val = plic_read_pending(a); + return true; + case plic_csr_rel_addr::enabled: + // A valid implementation can hardwire interrupt routing to a fixed hart context. + // We hardwire all supported interrupt source to be always enabled in context 0. + *val = PLIC_ENABLED_IRQ_MASK; + return true; + case plic_csr_rel_addr::claim_complete: + return plic_read_claim_complete(a, val); + default: + // Other PLIC CSRs are WARL hardwired to 0 + *val = 0; + return true; + } +} + +/// \brief PLIC device read callback. See ::pma_write. +static execute_status plic_write(void *context, i_device_state_access *a, uint64_t offset, uint64_t val, + int log2_size) { + (void) context; +#ifdef DEBUG_PLIC_MMIO + (void) fprintf(stderr, "plic: mmio write offset=0x%lx log2_size=%d val=0x%x\n", (long) offset, log2_size, + (int) val); +#endif + + // Our PLIC only supports aligned 32-bit reads + if (offset & 3 || log2_size != 2 || offset > PMA_PLIC_LENGTH) { + return execute_status::failure; + } + + switch (offset) { + case plic_csr_rel_addr::claim_complete: + return plic_write_claim_complete(a, val); + default: + // Most CSRs in PLIC spec are WARL, + // therefore we just ignore writes + return execute_status::success; + } +} + +void plic_set_pending_irq(i_device_state_access *a, uint32_t irq_id) { + const uint32_t irq_mask = UINT32_C(1) << irq_id; + const uint32_t girqsrvd = a->read_plic_girqsrvd(); + uint32_t girqpend = a->read_plic_girqpend(); + girqpend |= irq_mask; + a->write_plic_girqpend(girqpend); + // Set mip only if we the pending interrupt is not already being served. + // In case it's being served, mip will be set just after next claim complete. + const uint32_t ipmask = girqpend & ~girqsrvd; + if (ipmask != 0) { + a->set_mip(MIP_MEIP_MASK | MIP_SEIP_MASK); + } +} + +void plic_reset_pending_irq(i_device_state_access *a, uint32_t irq_id) { + const uint32_t irq_mask = UINT32_C(1) << irq_id; + const uint32_t girqsrvd = a->read_plic_girqsrvd(); + uint32_t girqpend = a->read_plic_girqpend(); + girqpend &= ~irq_mask; + a->write_plic_girqpend(girqpend); + // If all pending interrupts have been served, reset mip. + const uint32_t ipmask = girqpend & ~girqsrvd; + if (ipmask == 0) { + a->reset_mip(MIP_MEIP_MASK | MIP_SEIP_MASK); + } +} + +const pma_driver plic_driver = {"PLIC", plic_read, plic_write}; + +} // namespace cartesi diff --git a/src/plic.h b/src/plic.h new file mode 100644 index 000000000..f1cf19e02 --- /dev/null +++ b/src/plic.h @@ -0,0 +1,89 @@ +// Copyright Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along +// with this program (see COPYING). If not, see . +// + +#ifndef PLIC_H +#define PLIC_H + +#include "pma-driver.h" +#include + +/// \file +/// \brief Clock interruptor device. + +namespace cartesi { + +enum PLIC_constants : uint64_t { + PLIC_ENABLED_IRQ_MASK = UINT64_C(0xfffffffe), // Interrupt mask for all enabled interrupt sources (1-31) + PLIC_LOWEST_IRQ_PRIORITY = 1, +}; + +/// \brief Mapping between CSRs and their relative addresses in PLIC memory +enum plic_csr_rel_addr : uint64_t { + priority1 = UINT64_C(0x4), // Interrupt source 1 priority + priority2 = UINT64_C(0x8), // Interrupt source 2 priority + priority3 = UINT64_C(0xc), // Interrupt source 3 priority + priority4 = UINT64_C(0x10), // Interrupt source 4 priority + priority5 = UINT64_C(0x14), // Interrupt source 5 priority + priority6 = UINT64_C(0x18), // Interrupt source 6 priority + priority7 = UINT64_C(0x1c), // Interrupt source 7 priority + priority8 = UINT64_C(0x20), // Interrupt source 8 priority + priority9 = UINT64_C(0x24), // Interrupt source 9 priority + priority10 = UINT64_C(0x28), // Interrupt source 10 priority + priority11 = UINT64_C(0x2c), // Interrupt source 11 priority + priority12 = UINT64_C(0x30), // Interrupt source 12 priority + priority13 = UINT64_C(0x34), // Interrupt source 13 priority + priority14 = UINT64_C(0x38), // Interrupt source 14 priority + priority15 = UINT64_C(0x3c), // Interrupt source 15 priority + priority16 = UINT64_C(0x40), // Interrupt source 16 priority + priority17 = UINT64_C(0x44), // Interrupt source 17 priority + priority18 = UINT64_C(0x48), // Interrupt source 18 priority + priority19 = UINT64_C(0x4c), // Interrupt source 19 priority + priority20 = UINT64_C(0x50), // Interrupt source 20 priority + priority21 = UINT64_C(0x54), // Interrupt source 21 priority + priority22 = UINT64_C(0x58), // Interrupt source 22 priority + priority23 = UINT64_C(0x5c), // Interrupt source 23 priority + priority24 = UINT64_C(0x60), // Interrupt source 24 priority + priority25 = UINT64_C(0x64), // Interrupt source 25 priority + priority26 = UINT64_C(0x68), // Interrupt source 26 priority + priority27 = UINT64_C(0x6c), // Interrupt source 27 priority + priority28 = UINT64_C(0x70), // Interrupt source 28 priority + priority29 = UINT64_C(0x74), // Interrupt source 29 priority + priority30 = UINT64_C(0x78), // Interrupt source 30 priority + priority31 = UINT64_C(0x7c), // Interrupt source 31 priority + // ... Interrupt source priority 32-1023 (unsupported) + pending = UINT64_C(0x1000), // Interrupt pending bits for sources 0-31 + // ... Interrupt pending bits 32-1023 (unsupported) + enabled = UINT64_C(0x2000), // Interrupt enabled bits for sources 0-31 on context 0 + // ... Interrupt enabled bits for sources 0-1023 on contexts 1-15871 (unsupported) + threshold = UINT64_C(0x200000), // Priority threshold for context 0 + claim_complete = UINT64_C(0x200004), // Claim/complete for context 0 + // .. Interrupt threshold and claim_complete for other sources and contexts (unsupported) +}; + +/// \brief Sets a new pending interrupt request. +/// \details This is called only by devices to notify an external interrupt. +void plic_set_pending_irq(i_device_state_access *a, uint32_t irq_id); + +/// \brief Clears a pending interrupt request. +/// \details This is called only by devices to remove an external interrupt notification. +void plic_reset_pending_irq(i_device_state_access *a, uint32_t irq_id); + +/// \brief Global PLIC device driver instance +extern const pma_driver plic_driver; + +} // namespace cartesi + +#endif diff --git a/src/pma-constants.h b/src/pma-constants.h index b8d1922c2..cfca33e68 100644 --- a/src/pma-constants.h +++ b/src/pma-constants.h @@ -42,16 +42,17 @@ enum PMA_ranges : uint64_t { EXPAND_UINT64_C(PMA_SHADOW_UARCH_STATE_LENGTH_DEF), ///< Length of microarchitecture shadow state range PMA_CLINT_START = EXPAND_UINT64_C(PMA_CLINT_START_DEF), ///< Start of CLINT range PMA_CLINT_LENGTH = EXPAND_UINT64_C(PMA_CLINT_LENGTH_DEF), ///< Length of CLINT range + PMA_PLIC_START = EXPAND_UINT64_C(PMA_PLIC_START_DEF), ///< Start of PLIC range + PMA_PLIC_LENGTH = EXPAND_UINT64_C(PMA_PLIC_LENGTH_DEF), ///< Length of PLIC range PMA_HTIF_START = EXPAND_UINT64_C(PMA_HTIF_START_DEF), ///< Start of HTIF range PMA_HTIF_LENGTH = EXPAND_UINT64_C(PMA_HTIF_LENGTH_DEF), ///< Length of HTIF range PMA_UARCH_RAM_START = EXPAND_UINT64_C(PMA_UARCH_RAM_START_DEF), ///< Start of microarchitecture RAM range PMA_UARCH_RAM_LENGTH = EXPAND_UINT64_C(PMA_UARCH_RAM_LENGTH_DEF), ///< Length of microarchitecture RAM range - // PMA_FIRST_VIRTIO_START = EXPAND_UINT64_C(PMA_FIRST_VIRTIO_START_DEF), ///< Start of first VIRTIO range - // PMA_VIRTIO_LENGTH = EXPAND_UINT64_C(PMA_VIRTIO_LENGTH_DEF), ///< Length of each VIRTIO range - // PMA_LAST_VIRTIO_END = EXPAND_UINT64_C(PMA_LAST_VIRTIO_END_DEF), ///< End of last VIRTIO range - // PMA_PLIC_START = EXPAND_UINT64_C(PMA_PLIC_START_DEF), ///< Start of PLIC range - // PMA_PLIC_LENGTH = EXPAND_UINT64_C(PMA_PLIC_LENGTH_DEF), ///< Length of PLIC range + PMA_FIRST_VIRTIO_START = EXPAND_UINT64_C(PMA_FIRST_VIRTIO_START_DEF), ///< Start of first VIRTIO range + PMA_VIRTIO_LENGTH = EXPAND_UINT64_C(PMA_VIRTIO_LENGTH_DEF), ///< Length of each VIRTIO range + PMA_LAST_VIRTIO_END = EXPAND_UINT64_C(PMA_LAST_VIRTIO_END_DEF), ///< End of last VIRTIO range + PMA_RAM_START = EXPAND_UINT64_C(PMA_RAM_START_DEF), ///< Start of RAM range }; @@ -68,6 +69,11 @@ enum PMA_tlb_constants : uint64_t { PMA_TLB_SIZE = EXPAND_UINT64_C(PMA_TLB_SIZE_DEF), ///< Number for entries per TLB type }; +/// \brief PMA PLIC constants. +enum PMA_plic_constants : uint64_t { + PMA_PLIC_MAX_IRQ = EXPAND_UINT64_C(PMA_PLIC_MAX_IRQ_DEF), ///< Maximum PLIC interrupt +}; + /// \brief PMA masks. enum PMA_masks : uint64_t { PMA_ADDRESSABLE_MASK = ((UINT64_C(1) << 56) - 1) ///< Mask for addressable PMA ranges. @@ -109,7 +115,9 @@ enum class PMA_ISTART_DID { shadow_TLB = PMA_SHADOW_TLB_DID_DEF, ///< DID for shadow TLB device flash_drive = PMA_FLASH_DRIVE_DID_DEF, ///< DID for drive device CLINT = PMA_CLINT_DID_DEF, ///< DID for CLINT device + PLIC = PMA_PLIC_DID_DEF, ///< DID for PLIC device HTIF = PMA_HTIF_DID_DEF, ///< DID for HTIF device + VIRTIO = PMA_VIRTIO_DID_DEF, ///< DID for VirtIO devices rollup_rx_buffer = PMA_ROLLUP_RX_BUFFER_DID_DEF, ///< DID for rollup receive buffer rollup_tx_buffer = PMA_ROLLUP_TX_BUFFER_DID_DEF, ///< DID for rollup transmit buffer rollup_input_metadata = PMA_ROLLUP_INPUT_METADATA_DID_DEF, ///< DID for rollup input metadata memory range diff --git a/src/pma-defines.h b/src/pma-defines.h index c41ee21c8..d9f602361 100644 --- a/src/pma-defines.h +++ b/src/pma-defines.h @@ -29,15 +29,15 @@ #define PMA_UARCH_RAM_LENGTH_DEF 0x200000 ///< microarchitecture RAM length #define PMA_CLINT_START_DEF 0x2000000 ///< CLINT start address #define PMA_CLINT_LENGTH_DEF 0xC0000 ///< CLINT length in bytes +#define PMA_PLIC_START_DEF 0x40100000 ///< Start of PLIC range +#define PMA_PLIC_LENGTH_DEF 0x00400000 ///< Length of PLIC range #define PMA_HTIF_START_DEF 0x40008000 ///< HTIF base address (to_host) #define PMA_HTIF_LENGTH_DEF 0x1000 ///< HTIF length in bytes -#define PMA_FIRST_VIRTIO_START_DEF 0x40010000 ///< Start of first VIRTIO range (RESERVED) -#define PMA_VIRTIO_LENGTH_DEF 0x1000 ///< Length of each VIRTIO range (RESERVED) -#define PMA_LAST_VIRTIO_END_DEF 0x40020000 ///< End of last VIRTIO range (RESERVED) +#define PMA_FIRST_VIRTIO_START_DEF 0x40010000 ///< Start of first VIRTIO range +#define PMA_VIRTIO_LENGTH_DEF 0x1000 ///< Length of each VIRTIO range +#define PMA_LAST_VIRTIO_END_DEF 0x40020000 ///< End of last VIRTIO range #define PMA_DHD_START_DEF 0x40030000 ///< Start of DEHASH range #define PMA_DHD_LENGTH_DEF 0x1000 ///< Length of in bytes -#define PMA_PLIC_START_DEF 0x40100000 ///< Start of PLIC range (RESERVED) -#define PMA_PLIC_LENGTH_DEF 0x00400000 ///< Length of PLIC range (RESERVED) #define PMA_DTB_START_DEF 0x7ff00000 ///< DTB start address #define PMA_DTB_LENGTH_DEF 0x100000 ///< DTB length in bytes #define PMA_RAM_START_DEF 0x80000000 ///< RAM start address @@ -46,6 +46,7 @@ #define PMA_WORD_SIZE_DEF 8 ///< Physical memory word size. #define PMA_MAX_DEF 32 ///< Maximum number of PMAs #define PMA_TLB_SIZE_DEF 256 ///< Number for entries per TLB type +#define PMA_PLIC_MAX_IRQ_DEF 31 ///< Maximum PLIC interrupt #define PMA_MEMORY_DID_DEF 0 ///< Device ID for memory #define PMA_SHADOW_STATE_DID_DEF 1 ///< Device ID for shadow state device @@ -60,6 +61,8 @@ #define PMA_ROLLUP_VOUCHER_HASHES_DID_DEF 10 ///< Device ID for rollup voucher hashes buffer #define PMA_ROLLUP_NOTICE_HASHES_DID_DEF 11 ///< Device ID for rollup notice hashes buffer #define PMA_DHD_DID_DEF 12 ///< Device ID for DHD device +#define PMA_PLIC_DID_DEF 13 ///< Device ID for PLIC device +#define PMA_VIRTIO_DID_DEF 14 ///< Device ID for VirtIO devices #define PMA_SHADOW_UARCH_STATE_DID_DEF 15 ///< Device ID for uarch shadow state device // helper for using UINT64_C with defines diff --git a/src/protobuf-util.cpp b/src/protobuf-util.cpp index e24b77433..a9d94f988 100644 --- a/src/protobuf-util.cpp +++ b/src/protobuf-util.cpp @@ -62,6 +62,9 @@ void set_proto_machine_config(const machine_config &c, CartesiMachine::MachineCo proto_htif->set_tohost(c.htif.tohost); auto *proto_clint = proto_c->mutable_clint(); proto_clint->set_mtimecmp(c.clint.mtimecmp); + auto *proto_plic = proto_c->mutable_plic(); + proto_plic->set_girqpend(c.plic.girqpend); + proto_plic->set_girqsrvd(c.plic.girqsrvd); auto *proto_p = proto_c->mutable_processor(); proto_p->set_x1(c.processor.x[1]); proto_p->set_x2(c.processor.x[2]); @@ -885,6 +888,15 @@ machine_config get_proto_machine_config(const CartesiMachine::MachineConfig &pro c.clint.mtimecmp = clint.mtimecmp(); } } + if (proto_c.has_plic()) { + const auto &plic = proto_c.plic(); + if (plic.has_girqpend()) { + c.plic.girqpend = plic.girqpend(); + } + if (plic.has_girqsrvd()) { + c.plic.girqsrvd = plic.girqsrvd(); + } + } if (proto_c.has_htif()) { const auto &htif = proto_c.htif(); if (htif.has_fromhost()) { diff --git a/src/riscv-constants.h b/src/riscv-constants.h index 5bae8b9e4..8d0bd49a8 100644 --- a/src/riscv-constants.h +++ b/src/riscv-constants.h @@ -463,6 +463,8 @@ enum CARTESI_init : uint64_t { ILRSC_INIT = UINT64_C(-1), ///< Initial value for ilrsc IFLAGS_INIT = static_cast(PRV_M) << IFLAGS_PRV_SHIFT, ///< Initial value for iflags MTIMECMP_INIT = UINT64_C(0), ///< Initial value for mtimecmp + GIRQPEND_INIT = UINT64_C(0), ///< Initial value for girqpend + GIRQSRVD_INIT = UINT64_C(0), ///< Initial value for girqsrvd FROMHOST_INIT = UINT64_C(0), ///< Initial value for fromhost TOHOST_INIT = UINT64_C(0), ///< Initial value for tohost MENVCFG_INIT = UINT64_C(0), ///< Initial value for menvcfg diff --git a/src/shadow-state-factory.cpp b/src/shadow-state-factory.cpp index aa035b315..f71fe88bd 100644 --- a/src/shadow-state-factory.cpp +++ b/src/shadow-state-factory.cpp @@ -86,6 +86,8 @@ static bool shadow_state_peek(const pma_entry &pma, const machine &m, uint64_t p s->ilrsc = m.read_ilrsc(); s->iflags = m.read_iflags(); s->clint_mtimecmp = m.read_clint_mtimecmp(); + s->plic_girqpend = m.read_plic_girqpend(); + s->plic_girqsrvd = m.read_plic_girqsrvd(); s->htif_tohost = m.read_htif_tohost(); s->htif_fromhost = m.read_htif_fromhost(); s->htif_ihalt = m.read_htif_ihalt(); diff --git a/src/shadow-state.h b/src/shadow-state.h index 37868de86..e41c49f0f 100644 --- a/src/shadow-state.h +++ b/src/shadow-state.h @@ -64,6 +64,8 @@ struct shadow_state { uint64_t ilrsc; uint64_t iflags; uint64_t clint_mtimecmp; + uint64_t plic_girqpend; + uint64_t plic_girqsrvd; uint64_t htif_tohost; uint64_t htif_fromhost; uint64_t htif_ihalt; @@ -108,6 +110,8 @@ enum class shadow_state_csr { ilrsc = offsetof(shadow_state, ilrsc), iflags = offsetof(shadow_state, iflags), clint_mtimecmp = offsetof(shadow_state, clint_mtimecmp), + plic_girqpend = offsetof(shadow_state, plic_girqpend), + plic_girqsrvd = offsetof(shadow_state, plic_girqsrvd), htif_tohost = offsetof(shadow_state, htif_tohost), htif_fromhost = offsetof(shadow_state, htif_fromhost), htif_ihalt = offsetof(shadow_state, htif_ihalt), diff --git a/src/state-access.h b/src/state-access.h index e0bcdec02..1b8c8ee54 100644 --- a/src/state-access.h +++ b/src/state-access.h @@ -370,6 +370,22 @@ class state_access : public i_state_access { m_m.get_state().clint.mtimecmp = val; } + uint64_t do_read_plic_girqpend(void) const { + return m_m.get_state().plic.girqpend; + } + + void do_write_plic_girqpend(uint64_t val) { + m_m.get_state().plic.girqpend = val; + } + + uint64_t do_read_plic_girqsrvd(void) const { + return m_m.get_state().plic.girqsrvd; + } + + void do_write_plic_girqsrvd(uint64_t val) { + m_m.get_state().plic.girqsrvd = val; + } + uint64_t do_read_htif_fromhost(void) const { return m_m.get_state().htif.fromhost; } diff --git a/src/test-machine-c-api.cpp b/src/test-machine-c-api.cpp index f04acd6c5..44f049a8f 100644 --- a/src/test-machine-c-api.cpp +++ b/src/test-machine-c-api.cpp @@ -198,6 +198,7 @@ class incomplete_machine_fixture : public default_machine_fixture { target->tlb.image_filename = new_cstr(source->tlb.image_filename); target->clint = source->clint; + target->plic = source->plic; target->htif = source->htif; target->rollup = source->rollup; @@ -403,6 +404,10 @@ bool operator==(const cm_clint_config &lhs, const cm_clint_config &rhs) { return (lhs.mtimecmp == rhs.mtimecmp); } +bool operator==(const cm_plic_config &lhs, const cm_plic_config &rhs) { + return (lhs.girqpend == rhs.girqpend && lhs.girqsrvd == rhs.girqsrvd); +} + bool operator==(const cm_htif_config &lhs, const cm_htif_config &rhs) { return (lhs.fromhost == rhs.fromhost && lhs.tohost == rhs.tohost && lhs.console_getchar == rhs.console_getchar && lhs.yield_manual == rhs.yield_manual && lhs.yield_automatic == rhs.yield_automatic); @@ -410,7 +415,7 @@ bool operator==(const cm_htif_config &lhs, const cm_htif_config &rhs) { bool operator==(const cm_machine_config &lhs, const cm_machine_config &rhs) { return ((lhs.processor == rhs.processor) && (lhs.dtb == rhs.dtb) && (lhs.ram == rhs.ram) && (lhs.tlb == rhs.tlb) && - (lhs.clint == rhs.clint) && (lhs.htif == rhs.htif)); + (lhs.clint == rhs.clint) && (lhs.plic == rhs.plic) && (lhs.htif == rhs.htif)); } std::ostream &boost_test_print_type(std::ostream &ostr, const cm_machine_config &rhs) { @@ -1067,6 +1072,8 @@ CHECK_READER_FAILS_ON_nullptr_MACHINE(uint64_t, htif_ihalt) CHECK_READER_FAILS_ON_nullptr_MACHINE(uint64_t, htif_iconsole) CHECK_READER_FAILS_ON_nullptr_MACHINE(uint64_t, htif_iyield) CHECK_READER_FAILS_ON_nullptr_MACHINE(uint64_t, clint_mtimecmp) +CHECK_READER_FAILS_ON_nullptr_MACHINE(uint64_t, plic_girqpend) +CHECK_READER_FAILS_ON_nullptr_MACHINE(uint64_t, plic_girqsrvd) CHECK_READER_FAILS_ON_nullptr_MACHINE(uint64_t, mvendorid) CHECK_READER_FAILS_ON_nullptr_MACHINE(uint64_t, marchid) CHECK_READER_FAILS_ON_nullptr_MACHINE(uint64_t, mimpid) @@ -1119,6 +1126,8 @@ CHECK_WRITER_FAILS_ON_nullptr_MACHINE(htif_ihalt) CHECK_WRITER_FAILS_ON_nullptr_MACHINE(htif_iconsole) CHECK_WRITER_FAILS_ON_nullptr_MACHINE(htif_iyield) CHECK_WRITER_FAILS_ON_nullptr_MACHINE(clint_mtimecmp) +CHECK_WRITER_FAILS_ON_nullptr_MACHINE(plic_girqpend) +CHECK_WRITER_FAILS_ON_nullptr_MACHINE(plic_girqsrvd) CHECK_WRITER_FAILS_ON_nullptr_MACHINE(uarch_cycle) CHECK_WRITER_FAILS_ON_nullptr_MACHINE(uarch_pc) // clang-format on @@ -1170,6 +1179,8 @@ CHECK_REGISTER_READ_WRITE(htif_ihalt) CHECK_REGISTER_READ_WRITE(htif_iconsole) CHECK_REGISTER_READ_WRITE(htif_iyield) CHECK_REGISTER_READ_WRITE(clint_mtimecmp) +CHECK_REGISTER_READ_WRITE(plic_girqpend) +CHECK_REGISTER_READ_WRITE(plic_girqsrvd) CHECK_REGISTER_READ_WRITE(uarch_cycle) CHECK_REGISTER_READ_WRITE(uarch_pc) // clang-format on diff --git a/src/tests/machine-bind.lua b/src/tests/machine-bind.lua index f938f7dbd..f250e2332 100755 --- a/src/tests/machine-bind.lua +++ b/src/tests/machine-bind.lua @@ -199,11 +199,13 @@ local cpu_csr_addr = { ilrsc = 736, iflags = 744, clint_mtimecmp = 752, - htif_tohost = 760, - htif_fromhost = 768, - htif_ihalt = 776, - htif_iconsole = 784, - htif_iyield = 792, + plic_girqpend = 760, + plic_girqsrvd = 768, + htif_tohost = 776, + htif_fromhost = 784, + htif_ihalt = 792, + htif_iconsole = 800, + htif_iyield = 808, } local function get_cpu_csr_test_values() @@ -421,7 +423,7 @@ end local function test_config(config) assert(type(config) == "table", "config not a table") - for _, field in ipairs({ "processor", "htif", "clint", "flash_drive", "ram", "dtb" }) do + for _, field in ipairs({ "processor", "htif", "clint", "plic", "flash_drive", "ram", "dtb" }) do assert(config[field] and type(config[field]) == "table", "invalid field " .. field) end for i = 1, 31 do @@ -435,6 +437,9 @@ local function test_config(config) assert(type(htif.fromhost) == "number", "invalid htif.fromhost") local clint = config.clint assert(type(clint.mtimecmp) == "number", "invalid clint.mtimecmp") + local plic = config.plic + assert(type(plic.girqpend) == "number", "invalid plic.girqpend") + assert(type(plic.girqsrvd) == "number", "invalid plic.girqsrvd") local ram = config.ram assert(type(ram.length) == "number", "invalid ram.length") assert(ram.image_filename == nil or type(ram.image_filename) == "string", "invalid ram.image_filename") @@ -494,6 +499,8 @@ do_test("should have expected values", function(machine) assert(initial_config.processor.ilrsc == 0x2e0, "wrong ilrsc reg initial config value") assert(initial_config.processor.mstatus == 0x230, "wrong mstatus reg initial config value") assert(initial_config.clint.mtimecmp == 0, "wrong clint mtimecmp initial config value") + assert(initial_config.plic.girqpend == 0, "wrong plic girqpend initial config value") + assert(initial_config.plic.girqsrvd == 0, "wrong plic girqsrvd initial config value") assert(initial_config.htif.fromhost == 0, "wrong htif fromhost initial config value") assert(initial_config.htif.tohost == 0, "wrong htif tohost initial config value") assert(initial_config.htif.yield_automatic == false, "wrong htif yield automatic initial config value") @@ -516,6 +523,8 @@ do_test("should return expected values", function(machine) local to_ignore = { iflags = true, clint_mtimecmp = true, + plic_girqpend = true, + plic_girqsrvd = true, htif_ihalt = true, htif_iconsole = true, } diff --git a/src/uarch-bridge.h b/src/uarch-bridge.h index f40d5f449..c1a91447e 100644 --- a/src/uarch-bridge.h +++ b/src/uarch-bridge.h @@ -137,6 +137,12 @@ class uarch_bridge { case shadow_state_csr::clint_mtimecmp: s.clint.mtimecmp = data; return; + case shadow_state_csr::plic_girqpend: + s.plic.girqpend = data; + return; + case shadow_state_csr::plic_girqsrvd: + s.plic.girqsrvd = data; + return; case shadow_state_csr::htif_tohost: s.htif.tohost = data; return; @@ -246,6 +252,10 @@ class uarch_bridge { return s.read_iflags(); case shadow_state_csr::clint_mtimecmp: return s.clint.mtimecmp; + case shadow_state_csr::plic_girqpend: + return s.plic.girqpend; + case shadow_state_csr::plic_girqsrvd: + return s.plic.girqsrvd; case shadow_state_csr::htif_tohost: return s.htif.tohost; case shadow_state_csr::htif_fromhost: @@ -335,6 +345,10 @@ class uarch_bridge { return "iflags"; case shadow_state_csr::clint_mtimecmp: return "clint.mtimecmp"; + case shadow_state_csr::plic_girqpend: + return "plic.girqpend"; + case shadow_state_csr::plic_girqsrvd: + return "plic.girqsrvd"; case shadow_state_csr::htif_tohost: return "htif.tohost"; case shadow_state_csr::htif_fromhost: diff --git a/src/virtual-machine.cpp b/src/virtual-machine.cpp index 4ba642179..dea2bee13 100644 --- a/src/virtual-machine.cpp +++ b/src/virtual-machine.cpp @@ -416,6 +416,22 @@ void virtual_machine::do_write_clint_mtimecmp(uint64_t val) { return m_machine->write_clint_mtimecmp(val); } +uint64_t virtual_machine::do_read_plic_girqpend(void) const { + return m_machine->read_plic_girqpend(); +} + +void virtual_machine::do_write_plic_girqpend(uint64_t val) { + return m_machine->write_plic_girqpend(val); +} + +uint64_t virtual_machine::do_read_plic_girqsrvd(void) const { + return m_machine->read_plic_girqsrvd(); +} + +void virtual_machine::do_write_plic_girqsrvd(uint64_t val) { + return m_machine->write_plic_girqsrvd(val); +} + void virtual_machine::do_replace_memory_range(const memory_range_config &new_range) { m_machine->replace_memory_range(new_range); } diff --git a/src/virtual-machine.h b/src/virtual-machine.h index 1b6d1c966..b6801dd18 100644 --- a/src/virtual-machine.h +++ b/src/virtual-machine.h @@ -137,6 +137,10 @@ class virtual_machine : public i_virtual_machine { void do_write_htif_iyield(uint64_t val) override; uint64_t do_read_clint_mtimecmp(void) const override; void do_write_clint_mtimecmp(uint64_t val) override; + uint64_t do_read_plic_girqpend(void) const override; + void do_write_plic_girqpend(uint64_t val) override; + uint64_t do_read_plic_girqsrvd(void) const override; + void do_write_plic_girqsrvd(uint64_t val) override; void do_replace_memory_range(const memory_range_config &new_range) override; uint64_t do_read_word(uint64_t address) const override; bool do_verify_dirty_page_maps(void) const override; diff --git a/uarch/Makefile b/uarch/Makefile index f6e4ab606..409094607 100644 --- a/uarch/Makefile +++ b/uarch/Makefile @@ -67,6 +67,7 @@ EMULATOR_SOURCES=\ shadow-state.cpp \ shadow-uarch-state.cpp \ shadow-pmas.cpp \ + plic.cpp \ clint.cpp UARCH_OBJS = $(patsubst %.c,%.uarch_c.o,$(patsubst %.cpp,%.uarch_cpp.o,$(UARCH_SOURCES))) diff --git a/uarch/uarch-machine-state-access.h b/uarch/uarch-machine-state-access.h index da33e7083..130a7781f 100644 --- a/uarch/uarch-machine-state-access.h +++ b/uarch/uarch-machine-state-access.h @@ -20,6 +20,7 @@ #include "uarch-runtime.h" // must be included first, because of assert #include "clint.h" +#include "plic.h" #include "device-state-access.h" #include "htif.h" #include "i-state-access.h" @@ -471,13 +472,28 @@ class uarch_machine_state_access : public i_state_access(shadow_state_get_csr_abs_addr(shadow_state_csr::clint_mtimecmp)); - } void do_write_clint_mtimecmp(uint64_t val) { raw_write_memory(shadow_state_get_csr_abs_addr(shadow_state_csr::clint_mtimecmp), val); } + uint64_t do_read_plic_girqpend(void) { + return raw_read_memory(shadow_state_get_csr_abs_addr(shadow_state_csr::plic_girqpend)); + } + + void do_write_plic_girqpend(uint64_t val) { + raw_write_memory(shadow_state_get_csr_abs_addr(shadow_state_csr::plic_girqpend), val); + } + + uint64_t do_read_plic_girqsrvd(void) { + return raw_read_memory(shadow_state_get_csr_abs_addr(shadow_state_csr::plic_girqsrvd)); + } + + void do_write_plic_girqsrvd(uint64_t val) { + raw_write_memory(shadow_state_get_csr_abs_addr(shadow_state_csr::plic_girqsrvd), val); + } + uint64_t do_read_htif_fromhost(void) { return raw_read_memory(shadow_state_get_csr_abs_addr(shadow_state_csr::htif_fromhost)); } @@ -591,6 +607,9 @@ class uarch_machine_state_access : public i_state_access