Update Kelvin memory access
Kelvin reserves the 31st bit in the memory address to trigger cache invalidation. Update the load/store semantic functions to support that.
PiperOrigin-RevId: 556955595
diff --git a/sim/kelvin_state.h b/sim/kelvin_state.h
index 0cfada0..b323b01 100644
--- a/sim/kelvin_state.h
+++ b/sim/kelvin_state.h
@@ -20,6 +20,8 @@
// https://spacebeaker.googlesource.com/shodan/hw/kelvin/+/refs/heads/master/hdl/chisel/src/kelvin/Parameters.scala#13.
inline constexpr uint32_t kVectorLengthInBits = 256;
+constexpr uint64_t kKelvinMaxMemoryAddress = 0x3f'ffffULL; // 4MB
+
template <typename T>
using AccArrayTemplate = std::array<T, kVectorLengthInBits / 32>;
diff --git a/sim/kelvin_top.h b/sim/kelvin_top.h
index aa1fc56..b279ebd 100644
--- a/sim/kelvin_top.h
+++ b/sim/kelvin_top.h
@@ -29,8 +29,6 @@
namespace kelvin::sim {
-constexpr uint64_t kKelvinMaxMemoryAddress = 0x3f'ffffULL; // 4MB
-
using ::mpact::sim::generic::DataBuffer;
// Top level class for the Kelvin simulator. This is the main interface for
diff --git a/sim/kelvin_vector_memory_instructions.cc b/sim/kelvin_vector_memory_instructions.cc
index 19f3d42..c595a20 100644
--- a/sim/kelvin_vector_memory_instructions.cc
+++ b/sim/kelvin_vector_memory_instructions.cc
@@ -18,6 +18,14 @@
using mpact::sim::riscv::RV32VectorDestinationOperand;
using mpact::sim::riscv::RV32VectorSourceOperand;
+// Kelvin HW reserves the 31st bit as the magic cache invalidation bit.
+// SW can update the load/store address to include that bit to trigger immediate
+// cache invalidation. The actual address should exclude that bit. In ISS the
+// invalidation is no-op and the actual address should be in the lower bits.
+//
+// Note the core supports up to 2GB memory (4MB is actually integrated in RTL).
+constexpr uint64_t kMemMask = 0x0000'0000'7fff'ffff;
+
// Vector load instruction with optional data length, stride and address
// register post-increment.
template <typename T>
@@ -29,6 +37,14 @@
const auto num_ops = strip_mine ? 4 : 1;
auto addr = GetInstructionSource<uint32_t>(inst, 0, 0);
+ // Check and exclude the cache invalidation bit. However, the semihost tests
+ // use the memory space greater than the kelvin HW configuration and do not
+ // comply to the magic bit setting. Exclude the check and mask for those
+ // tests.
+ if (state->max_physical_address() <=
+ kKelvinMaxMemoryAddress) { // exclude semihost tests
+ addr &= kMemMask;
+ }
uint32_t elts_to_load = num_ops * elts_per_register;
if (has_length) {
@@ -146,6 +162,10 @@
const auto num_ops = strip_mine ? 4 : 1;
auto mem_addr = GetInstructionSource<uint32_t>(inst, 1, 0);
+ if (state->max_physical_address() <=
+ kKelvinMaxMemoryAddress) { // exclude semihost tests
+ mem_addr &= kMemMask;
+ }
auto vs = static_cast<RV32VectorSourceOperand *>(inst->Source(0));
auto base_addr = mem_addr;
diff --git a/sim/test/kelvin_vector_instructions_test_base.h b/sim/test/kelvin_vector_instructions_test_base.h
index 6f3170e..d5c4f70 100644
--- a/sim/test/kelvin_vector_instructions_test_base.h
+++ b/sim/test/kelvin_vector_instructions_test_base.h
@@ -67,6 +67,10 @@
}
memory_->Store(kDataLoadAddress - 4096, db);
db->DecRef();
+ // data_buffer has the size of 8192, while memory stores from
+ // kDataLoadAddress - 4096, the maximum address is at kDataLoadAddress +
+ // 4095.
+ state_->set_max_physical_address(kDataLoadAddress + 4095);
for (int i = 1; i < 32; i++) {
xreg_[i] = state_->GetRegister<RV32Register>(absl::StrCat("x", i)).first;
}
diff --git a/sim/test/kelvin_vector_memory_instructions_test.cc b/sim/test/kelvin_vector_memory_instructions_test.cc
index 3445d6f..49d058d 100644
--- a/sim/test/kelvin_vector_memory_instructions_test.cc
+++ b/sim/test/kelvin_vector_memory_instructions_test.cc
@@ -39,7 +39,8 @@
void MemoryLoadStoreOpTestHelper(absl::string_view name, bool has_length,
bool has_stride, bool strip_mine,
bool post_increment, bool x_variant,
- bool is_load, bool is_quad) {
+ bool is_load, bool is_quad,
+ bool is_uncached = false) {
InstructionPtr child_instruction(
new Instruction(next_instruction_address_, state_),
[](Instruction *inst) { inst->DecRef(); });
@@ -123,8 +124,13 @@
continue;
}
// Set input register values.
+ // The 31-bit of the address defines the cache invalidation. Set it to `1`
+ // when the cache invalidation is tested.
+ auto data_address =
+ is_uncached ? (kelvin::sim::test::kDataLoadAddress | 0x8000'0000U)
+ : kelvin::sim::test::kDataLoadAddress;
SetRegisterValues<uint32_t>(
- {{kelvin::sim::test::kRs1Name, kelvin::sim::test::kDataLoadAddress}});
+ {{kelvin::sim::test::kRs1Name, data_address}});
if (!x_variant) {
SetRegisterValues<uint32_t>(
@@ -292,6 +298,13 @@
kPostIncrement, kNotXVariant, is_load,
kNotQuad);
}
+
+ // Extra test for cache invalidation address
+ auto subname = absl::StrCat(name_with_type, "X");
+ MemoryLoadStoreOpTestHelper<T>(subname, kNoLength, kNoStride,
+ false /*strip_mine*/,
+ false /*post_increment*/, kXVariant, is_load,
+ kNotQuad, true /*is_uncached*/);
}
template <typename T>