Modify RipesProcessor instr. and data access interface to be non-VSRTL dependent
MODIFY
src/cachesim/cachesim.cpp
src/cachesim/cachesim.cpp
@@ -253,7 +253,7 @@ void CacheSim::popAccessTrace() {
emit hitrateChanged();
}
-void CacheSim::access(AInt address, AccessType type) {
+void CacheSim::access(AInt address, MemoryAccess::Type type) {
address = address & ~0b11; // Disregard unaligned accesses
CacheTrace trace;
CacheWay oldWay;
@@ -264,8 +264,8 @@ void CacheSim::access(AInt address, AccessType type) {
analyzeCacheAccess(transaction);
if (!transaction.isHit) {
- if (type == AccessType::Read ||
- (type == AccessType::Write && getWriteAllocPolicy() == WriteAllocPolicy::WriteAllocate)) {
+ if (type == MemoryAccess::Read ||
+ (type == MemoryAccess::Write && getWriteAllocPolicy() == WriteAllocPolicy::WriteAllocate)) {
oldWay = evictAndUpdate(transaction);
}
} else {
@@ -277,13 +277,13 @@ void CacheSim::access(AInt address, AccessType type) {
// Initially, we need a check for the case of "write + miss + noWriteAlloc". In this case, we should not update
// replacement/dirty fields. In all other cases, this is a valid action.
const bool writeMissNoAlloc =
- !transaction.isHit && type == AccessType::Write && getWriteAllocPolicy() == WriteAllocPolicy::NoWriteAllocate;
+ !transaction.isHit && type == MemoryAccess::Write && getWriteAllocPolicy() == WriteAllocPolicy::NoWriteAllocate;
if (!writeMissNoAlloc) {
// Lazily ensure that the located way has been initialized
m_cacheLines[transaction.index.line][transaction.index.way];
- if (type == AccessType::Write && getWritePolicy() == WritePolicy::WriteBack) {
+ if (type == MemoryAccess::Write && getWritePolicy() == WritePolicy::WriteBack) {
CacheWay& way = m_cacheLines[transaction.index.line][transaction.index.way];
way.dirty = true;
way.dirtyBlocks.insert(transaction.index.block);
@@ -296,7 +296,7 @@ void CacheSim::access(AInt address, AccessType type) {
}
// If our WritePolicy is WriteThrough and this access is a write, the transaction will always result in a WriteBack
- if (type == AccessType::Write && getWritePolicy() == WritePolicy::WriteThrough) {
+ if (type == MemoryAccess::Write && getWritePolicy() == WritePolicy::WriteThrough) {
transaction.isWriteback = true;
}
@@ -311,12 +311,12 @@ void CacheSim::access(AInt address, AccessType type) {
// === Some sanity checking ===
// It should never be possible that a read returns an invalid way index
- if (type == AccessType::Read) {
+ if (type == MemoryAccess::Read) {
transaction.index.assertValid();
}
// It should never be possible that a write returns an invalid way index if we write-allocate
- if (type == AccessType::Write && getWriteAllocPolicy() == WriteAllocPolicy::WriteAllocate) {
+ if (type == MemoryAccess::Write && getWriteAllocPolicy() == WriteAllocPolicy::WriteAllocate) {
transaction.index.assertValid();
}

MODIFY
src/cachesim/cachesim.h
src/cachesim/cachesim.h
#include "../external/VSRTL/core/vsrtl_register.h"
#include "processors/RISC-V/rv_memory.h"
+#include "processors/interface/ripesprocessor.h"
namespace Ripes {
class CacheSim;
@@ -56,13 +57,12 @@ class CacheInterface : public QObject {
Q_OBJECT
public:
CacheInterface(QObject* parent) : QObject(parent) {}
- enum class AccessType { Read, Write };
/**
* @brief access
* A function called by the logical "child" of this cache, indicating that it desires to access this cache
*/
- virtual void access(AInt address, AccessType type) = 0;
+ virtual void access(AInt address, MemoryAccess::Type type) = 0;
void setNextLevelCache(const std::shared_ptr<CacheSim>& cache) { m_nextLevelCache = cache; }
/**
@@ -122,7 +122,7 @@ public:
bool isHit = false;
bool isWriteback = false; // True if the transaction resulted in an eviction of a dirty cacheline
- AccessType type;
+ MemoryAccess::Type type;
bool transToValid = false; // True if the cacheline just transitioned from invalid to valid
bool tagChanged = false; // True if transToValid or the previous entry was evicted
};
@@ -136,8 +136,8 @@ public:
CacheAccessTrace() {}
CacheAccessTrace(const CacheTransaction& transaction) : CacheAccessTrace(CacheAccessTrace(), transaction) {}
CacheAccessTrace(const CacheAccessTrace& pre, const CacheTransaction& transaction) {
- reads = pre.reads + (transaction.type == AccessType::Read ? 1 : 0);
- writes = pre.writes + (transaction.type == AccessType::Write ? 1 : 0);
+ reads = pre.reads + (transaction.type == MemoryAccess::Read ? 1 : 0);
+ writes = pre.writes + (transaction.type == MemoryAccess::Write ? 1 : 0);
writebacks = pre.writebacks + (transaction.isWriteback ? 1 : 0);
hits = pre.hits + (transaction.isHit ? 1 : 0);
misses = pre.misses + (transaction.isHit ? 0 : 1);
@@ -151,7 +151,7 @@ public:
void setWriteAllocatePolicy(WriteAllocPolicy policy);
void setReplacementPolicy(ReplPolicy policy);
- void access(AInt address, AccessType type) override;
+ void access(AInt address, MemoryAccess::Type type) override;
void undo();
void reset() override;

MODIFY
src/cachesim/l1cacheshim.cpp
src/cachesim/l1cacheshim.cpp
namespace Ripes {
L1CacheShim::L1CacheShim(CacheType type, QObject* parent) : CacheInterface(parent), m_type(type) {
- reassociateMemory();
- connect(ProcessorHandler::get(), &ProcessorHandler::processorChanged, this, &L1CacheShim::reassociateMemory);
connect(ProcessorHandler::get(), &ProcessorHandler::processorReset, this, &L1CacheShim::processorReset);
+
+ // We must update the cache statistics on each cycle, in lockstep with the procsesor itself. Connect to
+ // ProcessorHandler::processorClocked and ensure that the handler is executed in the thread that the processor lives
+ // in (direct connection).
connect(ProcessorHandler::get(), &ProcessorHandler::processorClocked, this, &L1CacheShim::processorWasClocked,
Qt::DirectConnection);
connect(ProcessorHandler::get(), &ProcessorHandler::processorReversed, this, &L1CacheShim::processorReversed);
@@ -15,7 +17,7 @@ L1CacheShim::L1CacheShim(CacheType type, QObject* parent) : CacheInterface(paren
processorReset();
}
-void L1CacheShim::access(AInt, AccessType) {
+void L1CacheShim::access(AInt, MemoryAccess::Type) {
// Should never occur; the shim determines accesses based on investigating the associated memory.
Q_ASSERT(false);
}
@@ -24,7 +26,7 @@ void L1CacheShim::processorReset() {
// Propagate a reset through the cache hierarchy
CacheInterface::reset();
- if (m_memory && m_nextLevelCache) {
+ if (m_nextLevelCache) {
// Reload the initial (cycle 0) state of the processor. This is necessary to reflect ie. the instruction which
// is loaded from the instruction memory in cycle 0.
processorWasClocked();
@@ -38,40 +40,26 @@ void L1CacheShim::processorReversed() {
void L1CacheShim::processorWasClocked() {
if (m_type == CacheType::DataCache) {
+ const auto dataAccess = ProcessorHandler::getProcessor()->dataMemAccess();
+
// Determine whether the memory is being accessed in the current cycle, and if so, the access type.
- switch (m_memory->opSig()) {
- case MemOp::SB:
- case MemOp::SH:
- case MemOp::SW:
- if (m_memory->wrEnSig() == 1) {
- m_nextLevelCache->access(m_memory->addressSig(), AccessType::Write);
- }
+ switch (dataAccess.type) {
+ case MemoryAccess::Write:
+ m_nextLevelCache->access(dataAccess.address, MemoryAccess::Write);
break;
- case MemOp::LB:
- case MemOp::LBU:
- case MemOp::LH:
- case MemOp::LHU:
- case MemOp::LW:
- m_nextLevelCache->access(m_memory->addressSig(), AccessType::Read);
+ case MemoryAccess::Read:
+ m_nextLevelCache->access(dataAccess.address, MemoryAccess::Read);
break;
- case MemOp::NOP:
+ case MemoryAccess::None:
default:
break;
}
} else {
- m_nextLevelCache->access(m_memory->addressSig(), AccessType::Read);
- }
+ const auto instrAccess = ProcessorHandler::getProcessor()->instrMemAccess();
+ if (instrAccess.type == MemoryAccess::Read) {
+ m_nextLevelCache->access(instrAccess.address, MemoryAccess::Read);
}
-
-void L1CacheShim::reassociateMemory() {
- if (m_type == CacheType::DataCache) {
- m_memory = ProcessorHandler::getDataMemory();
- } else if (m_type == CacheType::InstrCache) {
- m_memory = ProcessorHandler::getInstrMemory();
- } else {
- Q_ASSERT(false);
}
- Q_ASSERT(m_memory != nullptr);
}
} // namespace Ripes

MODIFY
src/cachesim/l1cacheshim.h
src/cachesim/l1cacheshim.h
@@ -19,10 +19,9 @@ class L1CacheShim : public CacheInterface {
public:
enum class CacheType { DataCache, InstrCache };
L1CacheShim(CacheType type, QObject* parent);
- void access(AInt address, AccessType type) override;
+ void access(AInt address, MemoryAccess::Type type) override;
void setType(CacheType type);
- void reassociateMemory();
private:
void processorReset();
@@ -35,7 +34,6 @@ private:
* VSRTL component signals are dependent on the given type of the memory.
*/
CacheType m_type;
- const vsrtl::core::BaseMemory<true>* m_memory = nullptr;
};
} // namespace Ripes

MODIFY
src/processorhandler.cpp
src/processorhandler.cpp
@@ -101,13 +101,6 @@ vsrtl::core::AddressSpaceMM& ProcessorHandler::_getMemory() {
return m_currentProcessor->getMemory();
}
-const vsrtl::core::BaseMemory<true>* ProcessorHandler::_getDataMemory() const {
- return m_currentProcessor->getDataMemory();
-}
-const vsrtl::core::BaseMemory<true>* ProcessorHandler::_getInstrMemory() const {
- return m_currentProcessor->getInstrMemory();
-}
-
const vsrtl::core::AddressSpace& ProcessorHandler::_getRegisters() const {
return m_currentProcessor->getArchRegisters();
}

MODIFY
src/processorhandler.h
src/processorhandler.h
@@ -95,8 +95,6 @@ public:
*/
static vsrtl::core::AddressSpaceMM& getMemory() { return get()->_getMemory(); }
static const vsrtl::core::AddressSpace& getRegisters() { return get()->_getRegisters(); }
- static const vsrtl::core::BaseMemory<true>* getDataMemory() { return get()->_getDataMemory(); }
- static const vsrtl::core::BaseMemory<true>* getInstrMemory() { return get()->_getInstrMemory(); }
/**
* @brief setRegisterValue
@@ -210,8 +208,6 @@ private:
QString _disassembleInstr(const AInt address) const;
vsrtl::core::AddressSpaceMM& _getMemory();
const vsrtl::core::AddressSpace& _getRegisters() const;
- const vsrtl::core::BaseMemory<true>* _getDataMemory() const;
- const vsrtl::core::BaseMemory<true>* _getInstrMemory() const;
void _setRegisterValue(RegisterFileType rfid, const unsigned idx, VInt value);
void _writeMem(AInt address, VInt value, int size = sizeof(VInt));
VInt _getRegisterValue(RegisterFileType rfid, const unsigned idx) const;

MODIFY
src/processors/RISC-V/rv5s/rv5s.h
src/processors/RISC-V/rv5s/rv5s.h
@@ -432,8 +432,12 @@ public:
}
const std::vector<unsigned> breakpointTriggeringStages() const override { return {IF}; }
- const BaseMemory<true>* getDataMemory() const override { return data_mem; }
- const BaseMemory<true>* getInstrMemory() const override { return instr_mem; }
+ MemoryAccess dataMemAccess() const override { return memToAccessInfo(data_mem); }
+ MemoryAccess instrMemAccess() const override {
+ auto instrAccess = memToAccessInfo(instr_mem);
+ instrAccess.type = MemoryAccess::Read;
+ return instrAccess;
+ }
bool finished() const override {
// The processor is finished when there are no more valid instructions in the pipeline

MODIFY
src/processors/RISC-V/rv5s_no_fw/rv5s_no_fw.h
src/processors/RISC-V/rv5s_no_fw/rv5s_no_fw.h
@@ -408,8 +408,12 @@ public:
}
const std::vector<unsigned> breakpointTriggeringStages() const override { return {IF}; }
- const BaseMemory<true>* getDataMemory() const override { return data_mem; }
- const BaseMemory<true>* getInstrMemory() const override { return instr_mem; }
+ MemoryAccess dataMemAccess() const override { return memToAccessInfo(data_mem); }
+ MemoryAccess instrMemAccess() const override {
+ auto instrAccess = memToAccessInfo(instr_mem);
+ instrAccess.type = MemoryAccess::Read;
+ return instrAccess;
+ }
bool finished() const override {
// The processor is finished when there are no more valid instructions in the pipeline

MODIFY
src/processors/RISC-V/rv5s_no_fw_hz/rv5s_no_fw_hz.h
src/processors/RISC-V/rv5s_no_fw_hz/rv5s_no_fw_hz.h
@@ -359,8 +359,12 @@ public:
}
const std::vector<unsigned> breakpointTriggeringStages() const override { return {IF}; };
- const BaseMemory<true>* getDataMemory() const override { return data_mem; }
- const BaseMemory<true>* getInstrMemory() const override { return instr_mem; }
+ MemoryAccess dataMemAccess() const override { return memToAccessInfo(data_mem); }
+ MemoryAccess instrMemAccess() const override {
+ auto instrAccess = memToAccessInfo(instr_mem);
+ instrAccess.type = MemoryAccess::Read;
+ return instrAccess;
+ }
bool finished() const override {
// The processor is finished when there are no more valid instructions in the pipeline

MODIFY
src/processors/RISC-V/rv5s_no_hz/rv5s_no_hz.h
src/processors/RISC-V/rv5s_no_hz/rv5s_no_hz.h
@@ -392,8 +392,12 @@ public:
}
const std::vector<unsigned> breakpointTriggeringStages() const override { return {IF}; };
- const BaseMemory<true>* getDataMemory() const override { return data_mem; }
- const BaseMemory<true>* getInstrMemory() const override { return instr_mem; }
+ MemoryAccess dataMemAccess() const override { return memToAccessInfo(data_mem); }
+ MemoryAccess instrMemAccess() const override {
+ auto instrAccess = memToAccessInfo(instr_mem);
+ instrAccess.type = MemoryAccess::Read;
+ return instrAccess;
+ }
bool finished() const override {
// The processor is finished when there are no more valid instructions in the pipeline

MODIFY
src/processors/RISC-V/rv6s_dual/rv6s_dual.h
src/processors/RISC-V/rv6s_dual/rv6s_dual.h
@@ -738,8 +738,12 @@ public:
}
const std::vector<unsigned> breakpointTriggeringStages() const override { return {IF_1, IF_2}; };
- const BaseMemory<true>* getDataMemory() const override { return data_mem; }
- const BaseMemory<true>* getInstrMemory() const override { return instr_mem; }
+ MemoryAccess dataMemAccess() const override { return memToAccessInfo(data_mem); }
+ MemoryAccess instrMemAccess() const override {
+ auto instrAccess = memToAccessInfo(instr_mem);
+ instrAccess.type = MemoryAccess::Read;
+ return instrAccess;
+ }
bool finished() const override {
// The processor is finished when there are no more valid instructions in the pipeline

MODIFY
src/processors/RISC-V/rvss/rvss.h
src/processors/RISC-V/rvss/rvss.h
@@ -177,8 +177,12 @@ public:
bool finished() const override { return m_finished; }
const std::vector<unsigned> breakpointTriggeringStages() const override { return {0}; }
- const BaseMemory<true>* getDataMemory() const override { return data_mem; }
- const BaseMemory<true>* getInstrMemory() const override { return instr_mem; }
+ MemoryAccess dataMemAccess() const override { return memToAccessInfo(data_mem); }
+ MemoryAccess instrMemAccess() const override {
+ auto instrAccess = memToAccessInfo(instr_mem);
+ instrAccess.type = MemoryAccess::Read;
+ return instrAccess;
+ }
void setRegister(RegisterFileType, unsigned i, VInt v) override {
setSynchronousValue(registerFile->_wr_mem, i, v);

MODIFY
src/processors/interface/ripesprocessor.h
src/processors/interface/ripesprocessor.h
@@ -27,6 +27,17 @@ struct StageInfo {
bool operator!=(const StageInfo& other) const { return !(*this == other); }
};
+/**
+ * @brief The MemoryAccess struct
+ * Address is byte-aligned, and the accessed bytes are [address : address + bytes[
+ */
+struct MemoryAccess {
+ enum Type { None, Read, Write };
+ Type type = None;
+ AInt address;
+ unsigned bytes;
+};
+
/**
* @brief The RipesProcessor class
* Interface for all Ripes processors. This interface is intended to be simulator-agnostic, and thus provides an opaque
@@ -122,12 +133,12 @@ public:
virtual vsrtl::core::AddressSpaceMM& getMemory() = 0;
/**
- * @brief getData/InstrMemory
- * @returns a pointer to the component which implements the instruction and data memory interfaces. These types may
- * be implementation specific, and as such should be cast to these types at the callers end.
+ * @brief dataMemAccess/instrMemAccess
+ * @returns the state of a current access to the instruction or data memory. If the processor did not access the
+ * respective memory, MemoryAccess::type == None.
*/
- virtual const vsrtl::core::BaseMemory<true>* getDataMemory() const = 0;
- virtual const vsrtl::core::BaseMemory<true>* getInstrMemory() const = 0;
+ virtual MemoryAccess dataMemAccess() const = 0;
+ virtual MemoryAccess instrMemAccess() const = 0;
/**
* @brief getRegister

MODIFY
src/processors/ripesvsrtlprocessor.h
src/processors/ripesvsrtlprocessor.h
* Interface for all VSRTL-based Ripes processors
*/
+#include "RISC-V/riscv.h"
#include "VSRTL/core/vsrtl_design.h"
#include "interface/ripesprocessor.h"
@@ -41,8 +42,65 @@ public:
}
protected:
- // m_instructionsRetired should be modified by the processor when it retires (or "un-retires", while reversing) an
- // instruction
+ MemoryAccess memToAccessInfo(const vsrtl::core::BaseMemory<true>* memory) const {
+ MemoryAccess access;
+ switch (memory->opSig()) {
+ case MemOp::SB: {
+ access.bytes = 1;
+ access.type = MemoryAccess::Write;
+ break;
+ }
+ case MemOp::SH: {
+ access.bytes = 2;
+ access.type = MemoryAccess::Write;
+ break;
+ }
+ case MemOp::SW: {
+ access.bytes = 4;
+ access.type = MemoryAccess::Write;
+ break;
+ }
+ case MemOp::SD: {
+ access.bytes = 8;
+ access.type = MemoryAccess::Write;
+ break;
+ }
+ case MemOp::LB:
+ case MemOp::LBU: {
+ access.bytes = 1;
+ access.type = MemoryAccess::Read;
+ break;
+ }
+ case MemOp::LH:
+ case MemOp::LHU: {
+ access.bytes = 2;
+ access.type = MemoryAccess::Read;
+ break;
+ }
+ case MemOp::LW:
+ case MemOp::LWU: {
+ access.bytes = 4;
+ access.type = MemoryAccess::Read;
+ break;
+ }
+ case MemOp::LD: {
+ access.bytes = 8;
+ access.type = MemoryAccess::Read;
+ break;
+ }
+ case MemOp::NOP:
+ access.type = MemoryAccess::None;
+ break;
+ default:
+ Q_ASSERT("Unknown memory operation");
+ break;
+ }
+ access.address = memory->addressSig();
+ return access;
+ }
+
+ // m_instructionsRetired should be modified by the processor when it retires (or "un-retires", while reversing)
+ // an instruction
long long m_instructionsRetired = 0;
};

Modify RipesProcessor instr. and data access interface to be non-VSRTL dependent