| // Copyright 2024 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "riscv/riscv_renode.h" |
| |
| #include <cerrno> |
| #include <cstdint> |
| #include <cstdlib> |
| #include <cstring> |
| #include <fstream> |
| #include <functional> |
| #include <iostream> |
| #include <memory> |
| #include <string> |
| #include <string_view> |
| |
| #include "absl/functional/bind_front.h" |
| #include "absl/log/check.h" |
| #include "absl/log/log.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/str_cat.h" |
| #include "mpact/sim/generic/core_debug_interface.h" |
| #include "mpact/sim/generic/type_helpers.h" |
| #include "mpact/sim/proto/component_data.pb.h" |
| #include "mpact/sim/util/memory/atomic_memory.h" |
| #include "mpact/sim/util/memory/flat_demand_memory.h" |
| #include "mpact/sim/util/memory/memory_interface.h" |
| #include "mpact/sim/util/memory/memory_watcher.h" |
| #include "mpact/sim/util/memory/single_initiator_router.h" |
| #include "mpact/sim/util/other/instruction_profiler.h" |
| #include "riscv/debug_command_shell.h" |
| #include "riscv/riscv32_decoder.h" |
| #include "riscv/riscv64_decoder.h" |
| #include "riscv/riscv_arm_semihost.h" |
| #include "riscv/riscv_cli_forwarder.h" |
| #include "riscv/riscv_clint.h" |
| #include "riscv/riscv_debug_info.h" |
| #include "riscv/riscv_debug_interface.h" |
| #include "riscv/riscv_instrumentation_control.h" |
| #include "riscv/riscv_register.h" |
| #include "riscv/riscv_register_aliases.h" |
| #include "riscv/riscv_renode_cli_top.h" |
| #include "riscv/riscv_renode_register_info.h" |
| #include "riscv/riscv_state.h" |
| #include "riscv/riscv_top.h" |
| #include "riscv/stoull_wrapper.h" |
| #include "src/google/protobuf/text_format.h" |
| |
| namespace mpact { |
| namespace sim { |
| namespace riscv { |
| |
| using ::mpact::sim::proto::ComponentData; |
| using ::mpact::sim::proto::ComponentValueEntry; |
| using ::mpact::sim::riscv::RiscVClint; |
| using ::mpact::sim::util::AtomicMemoryOpInterface; |
| using ::mpact::sim::util::MemoryWatcher; |
| |
| using HaltReasonValueType = |
| ::mpact::sim::generic::CoreDebugInterface::HaltReasonValueType; |
| using HaltReason = ::mpact::sim::generic::CoreDebugInterface::HaltReason; |
| using RunStatus = ::mpact::sim::generic::CoreDebugInterface::RunStatus; |
| |
| // Configuration names. |
| constexpr std::string_view kMemoryBase = "memoryBase"; |
| constexpr std::string_view kMemorySize = "memorySize"; |
| constexpr std::string_view kClintMMRBase = "clintMMRBase"; |
| constexpr std::string_view kCLIPort = "cliPort"; |
| constexpr std::string_view kWaitForCLI = "waitForCLI"; |
| constexpr std::string_view kInstProfile = "instProfile"; |
| constexpr std::string_view kMemProfile = "memProfile"; |
| constexpr std::string_view kStackEnd = "stackEnd"; |
| constexpr std::string_view kStackSize = "stackSize"; |
| constexpr std::string_view kICache = "iCache"; |
| constexpr std::string_view kDCache = "dCache"; |
| |
| constexpr char kStackEndSymbolName[] = "__stack_end"; |
| constexpr char kStackSizeSymbolName[] = "__stack_size"; |
| |
| RiscVRenode::RiscVRenode(std::string name, MemoryInterface *renode_sysbus, |
| RiscVXlen xlen) |
| : name_(name), renode_sysbus_(renode_sysbus) { |
| router_ = new util::SingleInitiatorRouter(name + "_router"); |
| renode_router_ = new util::SingleInitiatorRouter(name + "_renode_router"); |
| auto *data_memory = static_cast<MemoryInterface *>(router_); |
| // Instantiate memory profiler, but disable it until the config information |
| // has been received. |
| mem_profiler_ = new MemoryUseProfiler(data_memory); |
| mem_profiler_->set_is_enabled(false); |
| // Set up state, decoder, and top. |
| rv_state_ = new RiscVState("RiscVRenode", xlen, mem_profiler_, |
| static_cast<AtomicMemoryOpInterface *>(router_)); |
| rv_fp_state_ = new RiscVFPState(rv_state_->csr_set(), rv_state_); |
| rv_state_->set_rv_fp(rv_fp_state_); |
| std::string reg_name; |
| if (xlen == RiscVXlen::RV32) { |
| rv_decoder_ = new RiscV32Decoder(rv_state_, data_memory); |
| // Make sure the architectural and abi register aliases are added. |
| for (int i = 0; i < 32; i++) { |
| reg_name = absl::StrCat(RiscVState::kXregPrefix, i); |
| (void)rv_state_->AddRegister<RV32Register>(reg_name); |
| (void)rv_state_->AddRegisterAlias<RV32Register>( |
| reg_name, ::mpact::sim::riscv::kXRegisterAliases[i]); |
| } |
| } else { |
| rv_decoder_ = new RiscV64Decoder(rv_state_, data_memory); |
| for (int i = 0; i < 32; i++) { |
| reg_name = absl::StrCat(RiscVState::kXregPrefix, i); |
| (void)rv_state_->AddRegister<RV64Register>(reg_name); |
| (void)rv_state_->AddRegisterAlias<RV64Register>( |
| reg_name, ::mpact::sim::riscv::kXRegisterAliases[i]); |
| } |
| } |
| |
| for (int i = 0; i < 32; i++) { |
| reg_name = absl::StrCat(RiscVState::kFregPrefix, i); |
| (void)rv_state_->AddRegister<RVFpRegister>(reg_name); |
| (void)rv_state_->AddRegisterAlias<RVFpRegister>( |
| reg_name, ::mpact::sim::riscv::kFRegisterAliases[i]); |
| } |
| |
| riscv_top_ = new RiscVTop(name, rv_state_, rv_decoder_); |
| |
| // Set up the memory router with the system bus. Other devices are added once |
| // config info has been received. Add a tagged default memory transactor, so |
| // that any tagged loads/stores are forward to the sysbus without tags. |
| CHECK_OK(router_->AddDefaultTarget<MemoryInterface>(renode_sysbus)); |
| |
| // Create memory. These memories will be added to the core router when there |
| // is configuration data for the address space that belongs to the core. The |
| // memories will be added to the renode router immediately as the default |
| // target, since memory references from ReNode are only in the memory range |
| // exposed on the sysbus. |
| memory_ = new util::FlatDemandMemory(); |
| atomic_memory_ = new util::AtomicMemory(memory_); |
| |
| CHECK_OK(renode_router_->AddDefaultTarget<MemoryInterface>(memory_)); |
| |
| // Set up semihosting. |
| semihost_ = new RiscVArmSemihost(xlen == RiscVXlen::RV32 |
| ? RiscVArmSemihost::BitWidth::kWord32 |
| : RiscVArmSemihost::BitWidth::kWord64, |
| data_memory, data_memory); |
| // Set up special handlers (ebreak, wfi, ecall). |
| riscv_top_->state()->AddEbreakHandler([this](const Instruction *inst) { |
| if (this->semihost_->IsSemihostingCall(inst)) { |
| this->semihost_->OnEBreak(inst); |
| return true; |
| } |
| if (this->riscv_top_->HasBreakpoint(inst->address())) { |
| this->riscv_top_->RequestHalt(HaltReason::kSoftwareBreakpoint, nullptr); |
| return true; |
| } |
| return false; |
| }); |
| riscv_top_->state()->set_on_wfi([](const Instruction *) { return true; }); |
| riscv_top_->state()->set_on_ecall([](const Instruction *) { return false; }); |
| semihost_->set_exit_callback([this]() { |
| LOG(INFO) << "Simulation halting due to semihosting exit"; |
| this->riscv_top_->RequestHalt(HaltReason::kProgramDone, nullptr); |
| }); |
| } |
| |
| RiscVRenode::~RiscVRenode() { |
| // Halt the core just to be safe. |
| (void)riscv_top_->Halt(); |
| // Write out instruction profile. |
| if (inst_profiler_ != nullptr) { |
| std::string inst_profile_file_name = |
| absl::StrCat("./mpact_riscv_", name_, "_inst_profile.csv"); |
| std::fstream inst_profile_file(inst_profile_file_name.c_str(), |
| std::ios_base::out); |
| if (!inst_profile_file.good()) { |
| LOG(ERROR) << "Failed to write profile to file"; |
| } else { |
| inst_profiler_->WriteProfile(inst_profile_file); |
| } |
| inst_profile_file.close(); |
| } |
| if (mem_profiler_ != nullptr) { |
| std::string mem_profile_file_name = |
| absl::StrCat("./mpact_riscv_", name_, "_mem_profile.csv"); |
| std::fstream mem_profile_file(mem_profile_file_name.c_str(), |
| std::ios_base::out); |
| if (!mem_profile_file.good()) { |
| LOG(ERROR) << "Failed to write profile to file"; |
| } else { |
| mem_profiler_->WriteProfile(mem_profile_file); |
| } |
| mem_profile_file.close(); |
| } |
| // Export counters. |
| auto component_proto = std::make_unique<ComponentData>(); |
| CHECK_OK(riscv_top_->Export(component_proto.get())) |
| << "Failed to export proto"; |
| std::string proto_file_name; |
| proto_file_name = absl::StrCat("./mpact_riscv_", name_, ".proto"); |
| std::fstream proto_file(proto_file_name.c_str(), std::ios_base::out); |
| std::string serialized; |
| if (!proto_file.good()) { |
| LOG(ERROR) << "Failed to open proto file for writing"; |
| } else if (!google::protobuf::TextFormat::PrintToString( |
| *component_proto.get(), &serialized)) { |
| LOG(ERROR) << "Failed to serialize protos"; |
| } else { |
| proto_file << serialized; |
| } |
| proto_file.close(); |
| // Clean up. |
| delete renode_router_; |
| delete mem_profiler_; |
| delete inst_profiler_; |
| delete instrumentation_control_; |
| delete program_loader_; |
| delete cmd_shell_; |
| delete socket_cli_; |
| delete riscv_renode_cli_top_; |
| delete riscv_cli_forwarder_; |
| delete rv_decoder_; |
| delete rv_fp_state_; |
| delete riscv_top_; |
| delete rv_state_; |
| delete semihost_; |
| delete router_; |
| delete atomic_memory_; |
| delete memory_; |
| delete clint_; |
| } |
| |
| absl::StatusOr<uint64_t> RiscVRenode::LoadExecutable(const char *elf_file_name, |
| bool for_symbols_only) { |
| program_loader_ = new ElfProgramLoader(this); |
| uint64_t entry_pt = 0; |
| if (for_symbols_only) { |
| auto res = program_loader_->LoadSymbols(elf_file_name); |
| if (!res.ok()) { |
| return res.status(); |
| } |
| entry_pt = res.value(); |
| } else { |
| auto res = program_loader_->LoadProgram(elf_file_name); |
| if (!res.ok()) { |
| return res.status(); |
| } |
| entry_pt = res.value(); |
| } |
| auto res = program_loader_->GetSymbol("tohost"); |
| // Add watchpoint for tohost if the symbol exists. |
| if (res.ok()) { |
| // If there is a 'tohost' symbol, set up a write watchpoint on that address |
| // to catch writes that mark program exit. |
| uint64_t tohost_addr = res.value().first; |
| // Add to_host watchpoint that halts the execution when program exit is |
| // signaled. |
| auto *db = riscv_top_->state()->db_factory()->Allocate<uint32_t>(2); |
| auto status = riscv_top_->memory_watcher()->SetStoreWatchCallback( |
| MemoryWatcher::AddressRange{tohost_addr, |
| tohost_addr + 2 * sizeof(uint32_t) - 1}, |
| [this, tohost_addr, db](uint64_t addr, int sz) { |
| static DataBuffer *load_db = db; |
| if (load_db == nullptr) return; |
| memory_->Load(tohost_addr, load_db, nullptr, nullptr); |
| uint32_t code = load_db->Get<uint32_t>(0); |
| if (code & 0x1) { |
| // The return code is in the upper 31 bits. |
| code >>= 1; |
| LOG(INFO) << absl::StrCat( |
| "Simulation halting due to tohost write: exit ", |
| absl::Hex(code)); |
| (void)riscv_top_->RequestHalt(HaltReason::kProgramDone, nullptr); |
| load_db->DecRef(); |
| } |
| }); |
| } |
| // Add instruction profiler it hasn't already been added. |
| if (inst_profiler_ == nullptr) { |
| inst_profiler_ = new util::InstructionProfiler(*program_loader_, 2); |
| riscv_top_->counter_pc()->AddListener(inst_profiler_); |
| riscv_top_->counter_pc()->SetIsEnabled(false); |
| } else { |
| // If it has been added already, set the elf loader, and make sure the pc |
| // counter is enabled. |
| inst_profiler_->SetElfLoader(program_loader_); |
| riscv_top_->counter_pc()->SetIsEnabled(true); |
| } |
| return entry_pt; |
| } |
| |
| // Each of the following methods checks to see if the command line enabled |
| // "top" interface is null. If it is not, it uses that to control the simulator, |
| // as it provides proper prioritization and handling of both ReNode and command |
| // line commands. Otherwise it uses the riscv_top interface directly. |
| |
| absl::StatusOr<int> RiscVRenode::Step(int num) { |
| if (riscv_renode_cli_top_ != nullptr) |
| return riscv_renode_cli_top_->RenodeStep(num); |
| return riscv_top_->Step(num); |
| } |
| |
| absl::StatusOr<HaltReasonValueType> RiscVRenode::GetLastHaltReason() { |
| if (riscv_renode_cli_top_ != nullptr) |
| return riscv_renode_cli_top_->RenodeGetLastHaltReason(); |
| return riscv_top_->GetLastHaltReason(); |
| } |
| |
| // Perform direct read of the memory through the renode router. The renode |
| // router avoids routing the request back out to the sysbus. |
| absl::StatusOr<size_t> RiscVRenode::ReadMemory(uint64_t address, void *buf, |
| size_t length) { |
| auto *db = db_factory_.Allocate<uint8_t>(length); |
| renode_router_->Load(address, db, nullptr, nullptr); |
| std::memcpy(buf, db->raw_ptr(), length); |
| db->DecRef(); |
| return length; |
| } |
| |
| // Perform direct write of the memory through the renode router. The renode |
| // router avoids routing the request back out to the sysbus. |
| absl::StatusOr<size_t> RiscVRenode::WriteMemory(uint64_t address, |
| const void *buf, |
| size_t length) { |
| auto *db = db_factory_.Allocate<uint8_t>(length); |
| std::memcpy(db->raw_ptr(), buf, length); |
| renode_router_->Store(address, db); |
| db->DecRef(); |
| return length; |
| } |
| |
| absl::StatusOr<uint64_t> RiscVRenode::ReadRegister(uint32_t reg_id) { |
| auto ptr = RiscVDebugInfo::Instance()->debug_register_map().find(reg_id); |
| if (ptr == RiscVDebugInfo::Instance()->debug_register_map().end()) { |
| return absl::NotFoundError( |
| absl::StrCat("Not found reg id: ", absl::Hex(reg_id))); |
| } |
| if (riscv_renode_cli_top_ != nullptr) |
| return riscv_renode_cli_top_->RenodeReadRegister(ptr->second); |
| return riscv_top_->ReadRegister(ptr->second); |
| } |
| |
| absl::Status RiscVRenode::WriteRegister(uint32_t reg_id, uint64_t value) { |
| auto ptr = RiscVDebugInfo::Instance()->debug_register_map().find(reg_id); |
| if (ptr == RiscVDebugInfo::Instance()->debug_register_map().end()) { |
| return absl::NotFoundError( |
| absl::StrCat("Not found reg id: ", absl::Hex(reg_id))); |
| } |
| if (riscv_renode_cli_top_ != nullptr) |
| return riscv_renode_cli_top_->RenodeWriteRegister(ptr->second, value); |
| return riscv_top_->WriteRegister(ptr->second, value); |
| } |
| |
| int32_t RiscVRenode::GetRenodeRegisterInfoSize() const { |
| return RiscVRenodeRegisterInfo::GetRenodeRegisterInfo().size(); |
| } |
| |
| absl::Status RiscVRenode::GetRenodeRegisterInfo(int32_t index, int32_t max_len, |
| char *name, |
| RenodeCpuRegister &info) { |
| auto const ®ister_info = RiscVRenodeRegisterInfo::GetRenodeRegisterInfo(); |
| if ((index < 0) || (index >= register_info.size())) { |
| return absl::OutOfRangeError( |
| absl::StrCat("Register info index (", index, ") out of range")); |
| } |
| info = register_info[index]; |
| auto const ®_map = RiscVDebugInfo::Instance()->debug_register_map(); |
| auto ptr = reg_map.find(info.index); |
| if (ptr == reg_map.end()) { |
| name[0] = '\0'; |
| } else { |
| strncpy(name, ptr->second.c_str(), max_len); |
| } |
| return absl::OkStatus(); |
| } |
| |
| static absl::StatusOr<uint64_t> ParseNumber(const std::string &number) { |
| if (number.empty()) { |
| return absl::InvalidArgumentError("Empty number"); |
| } |
| absl::StatusOr<uint64_t> res; |
| if ((number.size() > 2) && (number.substr(0, 2) == "0x")) { |
| res = riscv::internal::stoull(number.substr(2), nullptr, 16); |
| } else if (number[0] == '0') { |
| res = riscv::internal::stoull(number.substr(1), nullptr, 8); |
| } else { |
| res = riscv::internal::stoull(number, nullptr, 10); |
| } |
| if (!res.ok()) { |
| LOG(ERROR) << "Invalid number: " << number; |
| return absl::InvalidArgumentError(absl::StrCat("Invalid number: ", number)); |
| } |
| return res.value(); |
| } |
| |
| absl::Status RiscVRenode::SetConfig(const char *config_names[], |
| const char *config_values[], int size) { |
| std::string icache_cfg; |
| std::string dcache_cfg; |
| uint64_t memory_base = 0; |
| uint64_t memory_size = 0; |
| uint64_t clint_mmr_base = 0; |
| uint64_t stack_end_value = 0; |
| bool stack_end_set = false; |
| uint64_t stack_size_value = 0; |
| bool stack_size_set = false; |
| bool do_inst_profile = false; |
| int cli_port = 0; |
| int wait_for_cli = 0; |
| for (int i = 0; i < size; ++i) { |
| std::string name(config_names[i]); |
| if (name == kICache) { |
| icache_cfg = config_values[i]; |
| } else if (name == kDCache) { |
| dcache_cfg = config_values[i]; |
| } else { |
| auto res = ParseNumber(config_values[i]); |
| if (!res.ok()) { |
| return res.status(); |
| } |
| auto value = res.value(); |
| if (name == kMemoryBase) { |
| memory_base = value; |
| } else if (name == kMemorySize) { |
| memory_size = value; |
| } else if (name == kClintMMRBase) { |
| clint_mmr_base = value; |
| } else if (name == kCLIPort) { |
| cli_port = value; |
| } else if (name == kWaitForCLI) { |
| wait_for_cli = value; |
| } else if (name == kInstProfile) { |
| do_inst_profile = value != 0; |
| } else if (name == kMemProfile) { |
| mem_profiler_->set_is_enabled(value != 0); |
| } else if (name == kStackEnd) { |
| stack_end_value = value; |
| stack_end_set = true; |
| } else if (name == kStackSize) { |
| stack_size_value = value; |
| stack_size_set = true; |
| } else { |
| LOG(ERROR) << "Unknown config name: " << name << " " |
| << config_values[i]; |
| } |
| } |
| } |
| if (memory_size == 0) { |
| return absl::InvalidArgumentError("tagged_memory_size is 0"); |
| } |
| // Add the memory targets. |
| CHECK_OK(router_->AddTarget<AtomicMemoryOpInterface>( |
| atomic_memory_, memory_base, memory_base + memory_size - 1)); |
| CHECK_OK(router_->AddTarget<MemoryInterface>(memory_, memory_base, |
| memory_base + memory_size - 1)); |
| // Memory mapped devices. |
| if (clint_mmr_base != 0) { |
| clint_ = new RiscVClint(/*period=*/100, riscv_top_->state()->mip()); |
| riscv_top_->counter_num_cycles()->AddListener(clint_); |
| // Core local interrupt controller - clint. |
| CHECK_OK(router_->AddTarget<MemoryInterface>(clint_, clint_mmr_base, |
| clint_mmr_base + 0xffffULL)); |
| } |
| // Instruction profiler. |
| if (do_inst_profile) { |
| if (inst_profiler_ == nullptr) { |
| if (program_loader_ == nullptr) { |
| // If the program loader is null, assume that it will be added later, |
| // but don't enable the trace until it is. |
| inst_profiler_ = new util::InstructionProfiler(2); |
| riscv_top_->counter_pc()->SetIsEnabled(false); |
| } else { |
| inst_profiler_ = new util::InstructionProfiler(*program_loader_, 2); |
| riscv_top_->counter_pc()->SetIsEnabled(true); |
| } |
| riscv_top_->counter_pc()->AddListener(inst_profiler_); |
| } |
| } |
| // If the cli port has been specified, then instantiate the requisite classes. |
| if (cli_port != 0 && (riscv_renode_cli_top_ == nullptr)) { |
| riscv_renode_cli_top_ = |
| new RiscVRenodeCLITop(riscv_top_, wait_for_cli != 0); |
| riscv_cli_forwarder_ = new RiscVCLIForwarder(riscv_renode_cli_top_); |
| cmd_shell_ = new DebugCommandShell(); |
| instrumentation_control_ = |
| new RiscVInstrumentationControl(cmd_shell_, riscv_top_, mem_profiler_); |
| cmd_shell_->AddCore( |
| {static_cast<RiscVDebugInterface *>(riscv_cli_forwarder_), |
| [this]() { return program_loader_; }}); |
| cmd_shell_->AddCommand( |
| instrumentation_control_->Usage(), |
| absl::bind_front(&RiscVInstrumentationControl::PerformShellCommand, |
| instrumentation_control_)); |
| socket_cli_ = |
| new SocketCLI(cli_port, *cmd_shell_, |
| absl::bind_front(&RiscVRenodeCLITop::SetConnected, |
| riscv_renode_cli_top_)); |
| if (!socket_cli_->good()) { |
| return absl::InternalError( |
| absl::StrCat("Failed to create socket CLI (", errno, ")")); |
| } |
| } |
| // Set up the stack if so specified (either by symbol in executable or by |
| // configuration settings). |
| bool initialize_stack = false; |
| uint64_t stack_end = 0; |
| if (program_loader_ != nullptr) { |
| auto res = program_loader_->GetSymbol(kStackEndSymbolName); |
| if (res.ok()) { |
| stack_end = res.value().first; |
| initialize_stack = true; |
| } |
| } |
| if (stack_end_set) { |
| stack_end = stack_end_value; |
| initialize_stack = true; |
| } |
| |
| if (initialize_stack) { |
| uint64_t stack_size = 32 * 1024; |
| // Does the executable have a valid GNU_STACK segment? If so, override the |
| // default |
| if (program_loader_ != nullptr) { |
| auto loader_res = program_loader_->GetStackSize(); |
| if (loader_res.ok()) { |
| stack_end = loader_res.value(); |
| } |
| |
| // If the __stack_size symbol is defined then override. |
| auto res = program_loader_->GetSymbol(kStackSizeSymbolName); |
| if (res.ok()) { |
| stack_size = res.value().first; |
| } |
| } |
| |
| // If the flag is set, then override. |
| if (stack_size_set) stack_size = stack_size_value; |
| |
| auto sp_write = riscv_top_->WriteRegister("sp", stack_end + stack_size); |
| if (!sp_write.ok()) return sp_write; |
| } |
| if (!icache_cfg.empty()) { |
| ComponentValueEntry icache_value; |
| icache_value.set_name("icache"); |
| icache_value.set_string_value(icache_cfg); |
| auto *cfg = riscv_top_->GetConfig("icache"); |
| auto status = cfg->Import(&icache_value); |
| if (!status.ok()) return status; |
| } |
| if (!dcache_cfg.empty()) { |
| ComponentValueEntry dcache_value; |
| dcache_value.set_name("dcache"); |
| dcache_value.set_string_value(dcache_cfg); |
| auto *cfg = riscv_top_->GetConfig("dcache"); |
| auto status = cfg->Import(&dcache_value); |
| if (!status.ok()) return status; |
| // Hook the cache into the memory port. |
| auto *dcache = riscv_top_->dcache(); |
| dcache->set_memory(riscv_top_->state()->memory()); |
| riscv_top_->state()->set_memory(dcache); |
| } |
| return absl::OkStatus(); |
| } |
| |
| absl::Status RiscVRenode::SetIrqValue(int32_t irq_num, bool irq_value) { |
| switch (irq_num) { |
| case *riscv::InterruptCode::kMachineExternalInterrupt: |
| riscv_top_->state()->mip()->set_meip(irq_value); |
| return absl::OkStatus(); |
| case *riscv::InterruptCode::kMachineTimerInterrupt: |
| riscv_top_->state()->mip()->set_mtip(irq_value); |
| return absl::OkStatus(); |
| case *riscv::InterruptCode::kMachineSoftwareInterrupt: |
| riscv_top_->state()->mip()->set_msip(irq_value); |
| return absl::OkStatus(); |
| default: |
| return absl::NotFoundError( |
| absl::StrCat("Unsupported irq number: ", irq_num)); |
| } |
| } |
| |
| } // namespace riscv |
| } // namespace sim |
| } // namespace mpact |