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