The Sparta Modeling Framework
Loading...
Searching...
No Matches
CachedMemory.hpp
1#pragma once
2
3#include <list>
4#include <cinttypes>
5#include <string>
6
10
11namespace sparta::memory
12{
49 {
50 public:
51
61 StoreData(uint64_t write_id,
62 addr_t paddr,
63 size_t size,
64 const uint8_t * data,
65 const void * in_supplement,
66 void * out_supplement) :
67 write_id_(write_id),
68 paddr_(paddr),
69 size_(size),
70 stored_value_(size),
71 prev_value_(size)
72 {
73 (void) in_supplement;
74 (void) out_supplement;
75
76 // Cache the memory being written to eventually be committed
77 ::memcpy(stored_value_.data(), data, size);
78 }
79
81 uint64_t getWriteID() const { return write_id_; }
82
84 addr_t getPAddr() const { return paddr_; }
85
87 size_t getSize() const { return size_; }
88
90 uint8_t * getStoreDataPtr() { return stored_value_.data(); }
91
93 uint8_t * getPrevDataPtr() { return prev_value_.data(); }
94
96 const uint8_t * getStoreDataPtr() const { return stored_value_.data(); }
97
99 const uint8_t * getPrevDataPtr() const { return prev_value_.data(); }
100
101 private:
102 const uint64_t write_id_;
103 const addr_t paddr_;
104 const size_t size_;
105 std::vector<uint8_t> stored_value_;
106 std::vector<uint8_t> prev_value_;
107 };
108
109 inline std::ostream & operator<<(std::ostream & os, const StoreData & mstore)
110 {
111 os << std::hex << "wid:" << mstore.getWriteID() << " pa:" << mstore.getPAddr()
112 << std::dec << " size:" << mstore.getSize();
113 return os;
114 }
115
163 template<class MemoryWriteType = StoreData>
165 {
166 public:
177 CachedMemory(const std::string & name,
178 uint64_t write_id,
179 uint32_t outstanding_writes_watermark,
180 addr_t block_size,
181 addr_t total_size,
182 sparta::memory::BlockingMemoryIF * downstream_memory);
183
188 uint64_t getWriteID() const { return write_id_; }
189
194 const std::map<uint64_t, MemoryWriteType> & getOutstandingWrites() const {
195 return outstanding_writes_;
196 }
197
209 std::vector<MemoryWriteType> getOutstandingWritesForAddr(addr_t paddr) const;
210
214 uint32_t getNumOutstandingWrites() const { return outstanding_writes_.size(); }
215
224 void commitWrite(const MemoryWriteType & write_to_commit);
225
236 void mergeWrite(addr_t paddr, addr_t size, const uint8_t * buf);
237
243 void dropWrite(const MemoryWriteType & write_to_drop);
244
249 const MemoryWriteType & getLastOutstandingWrite() const {
250 sparta_assert(false == outstanding_writes_.empty());
251 return outstanding_writes_.rbegin()->second;
252 }
253
254 private:
256 // State
257 const uint64_t write_id_;
258 const uint32_t outstanding_writes_watermark_;
259 std::map<uint64_t, MemoryWriteType> outstanding_writes_;
260 uint64_t write_uid_ = 0;
261
263 // Derived methods
264 bool tryRead_(addr_t paddr, addr_t size, uint8_t *buf,
265 const void *in_supplement, void *out_supplement) override final;
266 bool tryWrite_(addr_t paddr, addr_t size, const uint8_t *buf,
267 const void *in_supplement, void *out_supplement) override final;
268 bool tryPeek_(addr_t paddr, addr_t size,
269 uint8_t *buf) const override final;
270 bool tryPoke_(addr_t paddr, addr_t size, const uint8_t *buf) override final;
271
273 // Memory
274 sparta::memory::BlockingMemoryIF * downstream_memory_ = nullptr;
275 sparta::memory::MemoryObject cached_memory_;
276
277 };
278
279 template<class MemoryWriteType>
281 uint64_t write_id,
282 uint32_t outstanding_writes_watermark,
283 addr_t block_size,
284 addr_t total_size,
285 sparta::memory::BlockingMemoryIF * downstream_memory) :
286 sparta::memory::BlockingMemoryIF (name + "_cached_memory", block_size,
287 sparta::memory::DebugMemoryIF::AccessWindow(0, total_size)),
288 write_id_(write_id),
289 outstanding_writes_watermark_(outstanding_writes_watermark),
290 // Use the write ID at the top byte for uniquenes
291 write_uid_(uint64_t(write_id_) << ((sizeof(write_uid_) - 1) * 8)),
292 downstream_memory_(downstream_memory),
293 cached_memory_(nullptr, block_size, total_size, 0)
294 {}
295
296 // Reads always occur on the cached memory. If it "misses," data
297 // will be peeked from downsteam memory but not read loaded into
298 // the cached memory. The cached memory does not maintain notions
299 // of RWITM, etc
300 template<class MemoryWriteType>
301 bool CachedMemory<MemoryWriteType>::tryRead_ (addr_t paddr, addr_t size, uint8_t *buf,
302 const void *in_supplement, void *out_supplement)
303 {
304 if(cached_memory_.tryGetLine(paddr) == nullptr) {
305 downstream_memory_->peek(paddr, size, buf);
306 }
307 else {
308 cached_memory_.read(paddr, size, buf);
309 }
310 return true;
311 }
312
313 // Writes always occur on the cached memory. If not present, the
314 // data will first be loaded into the cache and the write merged
315 template<class MemoryWriteType>
316 bool CachedMemory<MemoryWriteType>::tryWrite_ (addr_t paddr, addr_t size, const uint8_t *buf,
317 const void *in_supplement, void *out_supplement)
318 {
319 sparta_assert(outstanding_writes_.size() != outstanding_writes_watermark_,
320 "Watermark of outstanding writes reached. "
321 "Writes need to be committed or dropped via the CoSim API");
322
323 if(cached_memory_.tryGetLine(paddr) == nullptr) {
324 // This block has not been initialized in cached memory yet because
325 // it has not been written to before.
326 const auto aligned_paddr = paddr & block_mask_;
327 uint8_t * cached_mem_block_ptr = cached_memory_.getLine(aligned_paddr).getRawDataPtr(0);
328 downstream_memory_->peek(aligned_paddr, block_size_, cached_mem_block_ptr);
329 }
330
331 // Store the write into a outstanding chain
332 ++write_uid_;
333
334 MemoryWriteType outstanding_write(write_uid_, paddr, size, buf, in_supplement, out_supplement);
335 auto * prev_data_store = outstanding_write.getPrevDataPtr();
336
337 // Need to get the previous value of memory to restore if the write is dropped
338 cached_memory_.read(paddr, size, prev_data_store);
339
340 // Write the new value of memory
341 cached_memory_.write(paddr, size, buf);
342
343 // Store the write
344 outstanding_writes_.emplace(std::make_pair(write_uid_, outstanding_write));
345
346 return true;
347 }
348
349 // Peeks will always occur on the cached memory as reads. If the
350 // data is not in the cache, downsteam memory is peeked instead
351 template<class MemoryWriteType>
352 bool CachedMemory<MemoryWriteType>::tryPeek_(addr_t paddr, addr_t size, uint8_t *buf) const
353 {
354 // Need to check for CI space or possibly magic memory address
355 if(cached_memory_.tryGetLine(paddr) == nullptr) {
356 downstream_memory_->peek(paddr, size, buf);
357 }
358 else {
359 cached_memory_.read(paddr, size, buf);
360 }
361 return true;
362 }
363
364 // Pokes will always occur on the cached memory and downstream memory.
365 template<class MemoryWriteType>
366 bool CachedMemory<MemoryWriteType>::tryPoke_(addr_t paddr, addr_t size, const uint8_t *buf)
367 {
368 if(cached_memory_.tryGetLine(paddr) == nullptr) {
369 // This memory was never written (or read) before.
370 const auto aligned_paddr = paddr & block_mask_;
371 uint8_t * cached_mem_block_ptr = cached_memory_.getLine(aligned_paddr).getRawDataPtr(0);
372 downstream_memory_->poke(aligned_paddr, block_size_, cached_mem_block_ptr);
373 }
374 cached_memory_.write(paddr, size, buf);
375 downstream_memory_->poke(paddr, size, buf);
376 return true;
377 }
378
379 template<class MemoryWriteType>
380 std::vector<MemoryWriteType>
382 {
383 std::vector<MemoryWriteType> matching_stores;
384 for(const auto & [wuid, maw] : outstanding_writes_) {
385 if((paddr >= maw.getPAddr()) && (paddr < (maw.getPAddr() + maw.getSize()))) {
386 // This store access contains the given paddr
387 matching_stores.emplace_back(maw);
388 }
389 }
390 return matching_stores;
391 }
392
393 template<class MemoryWriteType>
394 void CachedMemory<MemoryWriteType>::commitWrite(const MemoryWriteType & write_to_commit)
395 {
396 sparta_assert(false == outstanding_writes_.empty(),
397 "there are no outstanding writes for commit");
398
399 const auto & mem_write_access = outstanding_writes_.begin()->second;
400
401 if(SPARTA_EXPECT_FALSE(mem_write_access.getWriteID() != write_to_commit.getWriteID()))
402 {
403 std::stringstream msg;
404 msg << __FUNCTION__ << ": error: attempting to commit write out of order: " << write_to_commit
405 << " expected to commit write: " << mem_write_access;
406 msg << "\nOutstanding writes for write ID " << write_id_ << " (oldest to newest):\n";
407 for(const auto & outstanding_write : outstanding_writes_) {
408 msg << "\t" << outstanding_write << "\n";
409 }
410 throw SpartaException(msg.str());
411 }
412
413 // Update downstream memory
414 downstream_memory_->tryWrite(mem_write_access.getPAddr(),
415 mem_write_access.getSize(),
416 mem_write_access.getStoreDataPtr(), (void*)this);
417 outstanding_writes_.erase(outstanding_writes_.begin());
418 }
419
420 template<class MemoryWriteType>
421 void CachedMemory<MemoryWriteType>::dropWrite(const MemoryWriteType & write_to_drop)
422 {
423 sparta_assert(false == outstanding_writes_.empty(),
424 "There are no outstanding writes for dropping");
425
426 if(SPARTA_EXPECT_FALSE(outstanding_writes_.find(write_to_drop.getWriteID()) == outstanding_writes_.end()))
427 {
428 std::stringstream msg;
429 msg << __FUNCTION__ << ": error: attempting to drop a write that is not known by this CachedMemory: "
430 << write_to_drop;
431 msg << "\nOutstanding writes for write ID " << write_id_ << " (oldest to newest):\n";
432 for(const auto & outstanding_writes : outstanding_writes_) {
433 msg << "\t" << outstanding_writes << "\n";
434 }
435 throw SpartaException(msg.str());
436 }
437
438 // Drop all the writes from the newest up to and including the
439 // dropped write
440 auto currently_flushing_write = outstanding_writes_.rbegin();
441 while(currently_flushing_write != outstanding_writes_.rend())
442 {
443 const uint64_t current_flushed_wuid = currently_flushing_write->first;
444 const auto & current_flushed_maw = currently_flushing_write->second;
445 cached_memory_.write(current_flushed_maw.getPAddr(),
446 current_flushed_maw.getSize(),
447 current_flushed_maw.getPrevDataPtr());
448
449 // Drop the write
450 outstanding_writes_.erase((++currently_flushing_write).base());
451
452 if(current_flushed_wuid == write_to_drop.getWriteID()) {
453 break;
454 }
455
456 currently_flushing_write = outstanding_writes_.rbegin();
457 }
458 }
459
460 template<class MemoryWriteType>
461 void CachedMemory<MemoryWriteType>::mergeWrite(addr_t paddr, addr_t size, const uint8_t * buf)
462 {
463 if(getNumOutstandingWrites() > 0) {
464 // Brute force method, but works. Take one byte at a time and
465 // look for collisions. If there are any, don't store it, but
466 // update that outstanding store's previous value. Start with
467 // the oldest write and move to the newest.
468 for(addr_t byte = 0; byte < size; ++byte)
469 {
470 const auto paddr_offset = paddr + byte;
471 bool collision = false;
472 for(auto & [wuid, maw] : outstanding_writes_)
473 {
474 const auto maw_start_paddr = maw.getPAddr();
475 const auto maw_size = maw.getSize();
476 if((paddr_offset >= maw_start_paddr) && (paddr_offset < (maw_start_paddr + maw_size)))
477 {
478 // Found a collision with this byte. Update the
479 // previous value with the merge data
480 collision = true;
481 const uint32_t maw_byte = paddr_offset - maw_start_paddr;
482 maw.getPrevDataPtr()[maw_byte] = *(buf + byte);
483 break;
484 }
485 }
486 if(!collision) {
487 // Write the one byte of data -- no collisions
488 cached_memory_.write(paddr_offset, 1, buf + byte);
489 }
490 }
491 }
492 else {
493 cached_memory_.write(paddr, size, buf);
494 }
495 }
496
497
498}
File that contains BlockingMemoryIF.
File that contains MemoryObject.
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
#define SPARTA_EXPECT_FALSE(x)
A macro for hinting to the compiler a particular condition should be considered most likely false.
Exception class for all of Sparta.
Used to construct and throw a standard C++ exception. Inherits from std::exception.
Pure-virtual memory interface which represents a simple, immediately accessible (blocking) address-sp...
Memory that can be used as a cache for core models.
const MemoryWriteType & getLastOutstandingWrite() const
Return the latest outstanding store access.
std::vector< MemoryWriteType > getOutstandingWritesForAddr(addr_t paddr) const
Given address, find outstanding writes that are not merged/committed.
uint64_t getWriteID() const
Get the write ID for this cache.
CachedMemory(const std::string &name, uint64_t write_id, uint32_t outstanding_writes_watermark, addr_t block_size, addr_t total_size, sparta::memory::BlockingMemoryIF *downstream_memory)
Create a CachedMemory object.
void dropWrite(const MemoryWriteType &write_to_drop)
Drop the outstanding write that matches the given MemoryWriteType.
void commitWrite(const MemoryWriteType &write_to_commit)
Commit the write that matches the given MemoryWriteType.
uint32_t getNumOutstandingWrites() const
Return the number of uncomitted writes.
void mergeWrite(addr_t paddr, addr_t size, const uint8_t *buf)
Merge an incoming write from system memory.
const std::map< uint64_t, MemoryWriteType > & getOutstandingWrites() const
Get all outstanding writes.
Memory interface which represents a simple, immediately accessible (blocking) address-space with supp...
Memory object with sparse storage for large memory representations. Has direct read/write interface w...
size_t getSize() const
Get the size of the write data (in bytes)
const uint8_t * getStoreDataPtr() const
Const methods of above methods.
uint8_t * getPrevDataPtr()
Get the previous data at the address (for restore)
const uint8_t * getPrevDataPtr() const
Const methods of above methods.
uint64_t getWriteID() const
Get the write ID, unique to each write performed.
addr_t getPAddr() const
Get the physical address this write corresponds to.
uint8_t * getStoreDataPtr()
Get the stored data.
StoreData(uint64_t write_id, addr_t paddr, size_t size, const uint8_t *data, const void *in_supplement, void *out_supplement)
Construct a StoreData object.
Namespace containing memory interfaces, types, and storage objects.
uint64_t addr_t
Type for generic address representation in generic interfaces, errors and printouts within SPARTA.
Macros for handling exponential backoff.
std::ostream & operator<<(std::ostream &o, const SimulationInfo &info)
ostream insertion operator for SimulationInfo
Defines an access window within this interface. Accesses through a memory interface are constrained t...