This adds a cache class that can be used as part of the memory hierarchy. As an example, an instruction cache is added (with an optional flag) to mpact_riscv and mpact_cheriot. PiperOrigin-RevId: 678760853 Change-Id: Iccbe311bf7478ee79b38996d35b7101a45ae304f
diff --git a/cheriot/BUILD b/cheriot/BUILD index a08047b..87dd61c 100644 --- a/cheriot/BUILD +++ b/cheriot/BUILD
@@ -491,6 +491,7 @@ ":cheriot_state", ":riscv_cheriot_isa", "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/flags:flag", "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/functional:bind_front", "@com_google_absl//absl/log", @@ -511,6 +512,7 @@ "@com_google_mpact-sim//mpact/sim/generic:decode_cache", "@com_google_mpact-sim//mpact/sim/generic:type_helpers", "@com_google_mpact-sim//mpact/sim/util/memory", + "@com_google_mpact-sim//mpact/sim/util/memory:cache", "@com_googlesource_code_re2//:re2", ], )
diff --git a/cheriot/cheriot_top.cc b/cheriot/cheriot_top.cc index 6df56ad..9c663d0 100644 --- a/cheriot/cheriot_top.cc +++ b/cheriot/cheriot_top.cc
@@ -18,9 +18,10 @@ #include <cstdint> #include <cstring> #include <string> -#include <thread> +#include <thread> // NOLINT: third party code. #include <utility> +#include "absl/flags/flag.h" #include "absl/functional/any_invocable.h" #include "absl/functional/bind_front.h" #include "absl/log/check.h" @@ -43,6 +44,7 @@ #include "mpact/sim/generic/decode_cache.h" #include "mpact/sim/generic/type_helpers.h" #include "mpact/sim/util/memory/atomic_memory.h" +#include "mpact/sim/util/memory/cache.h" #include "mpact/sim/util/memory/memory_interface.h" #include "mpact/sim/util/memory/memory_watcher.h" #include "mpact/sim/util/memory/tagged_flat_demand_memory.h" @@ -53,6 +55,9 @@ #include "riscv//riscv_csr.h" #include "riscv//riscv_register.h" +// Flag to enable and configure instruction cache. +ABSL_FLAG(std::string, icache, "", "Instruction cache configuration"); + namespace mpact { namespace sim { namespace cheriot { @@ -60,6 +65,7 @@ using ::mpact::sim::generic::ActionPointManagerBase; using ::mpact::sim::generic::BreakpointManager; using ::mpact::sim::riscv::RiscVActionPointMemoryInterface; +using ::mpact::sim::util::Cache; using EC = ::mpact::sim::cheriot::ExceptionCode; using PB = ::mpact::sim::cheriot::CheriotRegister::PermissionBits; @@ -88,6 +94,8 @@ if (branch_trace_db_ != nullptr) branch_trace_db_->DecRef(); + delete icache_; + if (inst_db_) inst_db_->DecRef(); delete rv_bp_manager_; delete cheriot_decode_cache_; delete atomic_memory_; @@ -144,6 +152,16 @@ } return false; }); + // Instruction cache. + if (!absl::GetFlag(FLAGS_icache).empty()) { + icache_ = new Cache("icache", this); + absl::Status status = + icache_->Configure(absl::GetFlag(FLAGS_icache), &counter_num_cycles_); + if (!status.ok()) { + LOG(ERROR) << "Failed to configure instruction cache: " << status; + } + inst_db_ = state_->db_factory()->Allocate<uint32_t>(1); + } // Make sure the architectural and abi register aliases are added. std::string reg_name; @@ -235,6 +253,7 @@ real_inst->IncRef(); uint64_t next_pc = pc + real_inst->size(); bool executed = false; + if (icache_) ICacheFetch(pc); do { executed = ExecuteInstruction(real_inst); counter_num_cycles_.Increment(1); @@ -303,6 +322,7 @@ // Set the next_pc to the next sequential instruction. next_pc = pc + inst->size(); bool executed = false; + if (icache_) ICacheFetch(pc); do { executed = ExecuteInstruction(inst); counter_num_cycles_.Increment(1); @@ -405,6 +425,7 @@ // executed will overwrite this. next_pc = pc + inst->size(); bool executed = false; + if (icache_) ICacheFetch(pc); do { // Try executing the instruction. If it fails, advance a cycle // and try again. @@ -1015,6 +1036,10 @@ } } +void CheriotTop::ICacheFetch(uint64_t address) { + icache_->Load(address, inst_db_, nullptr, nullptr); +} + } // namespace cheriot } // namespace sim } // namespace mpact
diff --git a/cheriot/cheriot_top.h b/cheriot/cheriot_top.h index 655bc94..e187fac 100644 --- a/cheriot/cheriot_top.h +++ b/cheriot/cheriot_top.h
@@ -38,6 +38,7 @@ #include "mpact/sim/generic/data_buffer.h" #include "mpact/sim/generic/decode_cache.h" #include "mpact/sim/generic/decoder_interface.h" +#include "mpact/sim/util/memory/cache.h" #include "mpact/sim/util/memory/memory_interface.h" #include "mpact/sim/util/memory/memory_watcher.h" #include "mpact/sim/util/memory/tagged_memory_watcher.h" @@ -52,6 +53,7 @@ using ::mpact::sim::generic::BreakpointManager; using ::mpact::sim::generic::DecoderInterface; using ::mpact::sim::riscv::RiscVActionPointMemoryInterface; +using ::mpact::sim::util::Cache; struct BranchTraceEntry { uint32_t from; @@ -160,6 +162,7 @@ absl::Status StepPastBreakpoint(); // Set the pc value. void SetPc(uint64_t value); + void ICacheFetch(uint64_t address); // Branch tracing. void AddToBranchTrace(uint64_t from, uint64_t to); // The DB factory is used to manage data buffers for memory read/writes. @@ -215,6 +218,9 @@ LazyRE2 cap_reg_re_; // Flag for breaking on a control flow change. bool break_on_control_flow_change_ = false; + // ICache. + Cache *icache_ = nullptr; + DataBuffer *inst_db_ = nullptr; }; } // namespace cheriot