The Sparta Modeling Framework
Loading...
Searching...
No Matches
SimpleMemoryMap.hpp
Go to the documentation of this file.
1
7#pragma once
8
10#include "sparta/memory/AddressTypes.hpp"
12
13namespace sparta
14{
15 namespace memory
16 {
54 {
55 public:
56
61 struct Mapping
62 {
67 Mapping(addr_t _start, addr_t _end, BlockingMemoryIF* _memif, addr_t _dest_off) :
68 start(_start),
69 end(_end),
70 dest_off(_dest_off),
71 memif(_memif)
72 {
73 sparta_assert(memif != nullptr);
74 }
75
87
91 const addr_t end;
92
103
109
114 addr_t mapAddress(addr_t input) const noexcept {
115 return (input - start) + dest_off;
116 }
117
122 bool contains(addr_t a) const noexcept {
123 return a >= start && a < end;
124 }
125
132 bool overlaps(addr_t a, addr_t b) const noexcept {
133 return (a >= start && a < end) || (b > start && b <= end) || (a < start && b > end);
134 }
135 };
136
140
141 SimpleMemoryMap() = delete;
142
150 block_size_(block_size),
151 bintree_(nullptr),
152 num_mappings_(0)
153 {
154 sparta_assert(block_size > 0, "block size must be greater than 0");
155 block_idx_rshift_ = (addr_t)log2(block_size);
156 block_offset_mask_ = (1 << block_idx_rshift_) - 1;
157 }
158
163 delete bintree_;
164 }
165
168
172
222 void addMapping(addr_t start,
223 addr_t end,
224 BlockingMemoryIF* memif,
225 addr_t dest_start) {
226 sparta_assert(memif);
227 sparta_assert(memif->getBlockSize() == block_size_);
228 sparta_assert(start < end);
229 sparta_assert((start & block_offset_mask_) == 0);
230 sparta_assert((end & block_offset_mask_) == 0);
231 sparta_assert((dest_start & block_offset_mask_) == 0);
232
233 const addr_t required_range = (end-start) + dest_start;
234 if(memif->getRange() < required_range){
235 throw SpartaException("Total range of destination memory interface is ")
236 << "too small to contain all mappings from SimpleMemoryMap mapping "
237 << "[" << start << ", " << end << ") -> " << memif
238 << " with dest_start" << dest_start << ". Mapped input range size "
239 << "exceeds memory interface (with range " << memif->getRange()
240 << " by " << required_range - memif->getRange();
241 }
242
243 // Verify that the stand and end addresses do not contain do not
244 // overlap any mappings. This information could be extracted
245 // from the tree, but it is simple to explicitly test here to
246 // ensure that no tree changes need to be rolled back. There is no
247 // performance concern for insertion
248 for(auto& m : mappings_) {
249 if(m->overlaps(start,end)){
250 throw SpartaException("Cannot add another mapping [")
251 << start << ", " << end
252 << ") which overlaps another. "
253 << " mapping occupying [" << m->start
254 << ", " << m->end << ")";
255 }
256 }
257
258 addr_t min = 0;
259 addr_t max = ~(addr_t)0;
260 BinTreeNode* n = bintree_;
261
262 // Insert left endpoint
263 if(!n){
264 // No root yet
265 min = start;
266 n = bintree_ = new BinTreeNode(nullptr, start);
267 RBTreeFixup_(n);
268 // No fixing should actually have taken place
269 }else{
270 while(1){
271 if(n->dest != nullptr){
272 Mapping* m = n->dest.get();
273 // Children nodes on a side will always exist if start is outside this mapping on that side
274 if(start < m->start){
275 sparta_assert(n->l);
276 n = n->l;
277 }else if(start >= m->end){
278 sparta_assert(n->r);
279 n = n->r;
280 }else{
281 throw SpartaException("Cannot add another mapping [")
282 << start << ", " << end
283 << ") in a range previously occupied by another. "
284 << "Destination found occupying [" << m->start
285 << ", " << m->end << "). Found when placing left endpoint";
286 }
287 }else if(n->separator == start){
288 // Insert right endpoint immediately
289 min = start;
290 //std::cout << "\nAlready had START sepatator @ " << start << std::endl;
291 break;
292 }else if(n->separator < start){
293 min = n->separator;
294 if(n->r){
295 sparta_assert(n->separator >= min);
296 n = n->r;
297 }else{
298 n->r = new BinTreeNode(n, start);
299 n = n->r;
300 RBTreeFixup_(n);
301 // If n was relocated up in the tree, the right-endpoint
302 // insertion loop will find it.
303 break;
304 }
305 }else{
306 max = n->separator;
307 if(n->l){
308 sparta_assert(n->separator <= max);
309 n = n->l;
310 }else{
311 n->l = new BinTreeNode(n, start);
312 n = n->l;
313 RBTreeFixup_(n);
314 // If n was relocated up in the tree, the right-endpoint
315 // insertion loop will find it.
316 break;
317 }
318 }
319 } // while(1)
320 } // if(!n) { } else {
321
322 // Insert right endpoint
323 while(1){
324 if(n->dest != nullptr){
325 Mapping* m = n->dest.get();
326 // Children nodes on a side will always exist if start is outside this mapping on that side
327 if(end <= m->start){
328 assert(n->l);
329 n = n->l;
330 }else if(end > m->end){
331 sparta_assert(n->r);
332 n = n->r;
333 }else{
334 throw SpartaException("Cannot add another mapping [")
335 << std::hex << start << ", " << end
336 << ") in a range previously occupied by another. "
337 << "Destination found occupying [" << m->start
338 << ", " << m->end << "). Found when placing right endpoint";
339 }
340 }else if(n->separator == end){
341 // Insert destination now
342 max = end;
343 //std::cout << "\nAlready had END sepatator @ " << end << std::endl;
344 break;
345 }else if(n->separator < start){
346 throw SpartaException("Node separator ")
347 << n->separator
348 << " encountered when placing right endpoint cannot be less than the start of the range";
349 }else if(n->separator == start){
350 min = n->separator;
351 if(n->r){
352 sparta_assert(n->separator <= max);
353 n = n->r;
354 }else{
355 // Is new endpoint required or is this node already constrained by ancestors
356 if(max != end){
357 n->r = new BinTreeNode(n, end);
358 n = n->r;
359 RBTreeFixup_(n);
360 }
361 break;
362 }
363 }else if(n->separator < end){
364 throw SpartaException("Cannot add another mapping [")
365 << std::hex << start << ", " << end
366 << ") in a range previously occupied by another. "
367 << "Separator at " << n->separator
368 << " found occupying [" << min << ", " << max << ")";
369 }else{
370 // n->sepatator > end
371 max = n->separator;
372 if(n->l){
373 sparta_assert(n->separator <= max);
374 n = n->l;
375 }else{
376 // Is new endpoint required or is this node already constrained by ancestors
377 if(max != end){
378 n->l = new BinTreeNode(n, end);
379 n = n->l;
380 RBTreeFixup_(n);
381 }
382 break;
383 }
384 }
385 } // while(1)
386
387 // Place final destination node
388 // This will always be a child of a separator node. We cannot
389 // have two destination nodes in a parent-child relationship
390 // because separator nodes always define their edges.
391 sparta_assert(n);
392 sparta_assert(n->separator != ~0ULL);
393 std::unique_ptr<Mapping> m(new Mapping(start, end, memif, dest_start));
394 if(end <= n->separator){
395 if(n->l){
396 // Fixup moved n around and attached a left child where
397 // the destination node would be. Find the start separator node
398 n = findSeparatorNode_(bintree_, start);
399 sparta_assert(!n->r); // Ensure this has no children to the left that we might be about to replace
400 n->r = new BinTreeNode(n, std::move(m)); // Takes ownership
401 n = n->r;
402 }else{
403 n->l = new BinTreeNode(n, std::move(m)); // Takes ownership
404 n = n->l;
405 }
406 }else if(start >= n->separator){
407 if(n->r){
408 // Fixup moved n around and attached a right child where
409 // the destination node would be. Find the end separator node
410 n = findSeparatorNode_(bintree_, end);
411 sparta_assert(!n->l); // Ensure this has no children to the right that we might be about to replace
412 n->l = new BinTreeNode(n, std::move(m)); // Takes ownership
413 n = n->l;
414 }else{
415 n->r = new BinTreeNode(n, std::move(m)); // Takes ownership
416 n = n->r;
417 }
418 }else{
419 throw SpartaException("Error placing destination mapping node [")
420 << start << ", " << end << "). Range somehow spanned a separator at " << n->separator << ". This should not have occurred";
421 }
422 RBTreeFixup_(n);
423
424 num_mappings_ += 1;
425 mappings_.push_back(n->dest.get());
426 }
427
432 void dumpTree(std::ostream& o) const {
433 if(bintree_){
434 bintree_->recursDump(o);
435 }
436 }
437
443 void dumpMappings(std::ostream& o) const {
444 uint32_t desc_len = 1;
445 uint32_t start_len = 1;
446 uint32_t end_len = 1;
447 for(const Mapping* m : mappings_){
448 desc_len = std::max<uint32_t>(m->memif->getDescription().size(), desc_len);
449 std::stringstream tmp;
450 tmp << std::showbase << std::hex << m->start;
451 start_len = std::max<uint32_t>(tmp.str().size(), start_len);
452 tmp.str("");
453 tmp << std::showbase << std::hex << m->end;
454 end_len = std::max<uint32_t>(tmp.str().size(), end_len);
455 }
456
457 // Sort content
458 std::vector<const Mapping*> sorted = mappings_;
459
460 std::sort(sorted.begin(), sorted.end(),
461 [](const Mapping* m1, const Mapping* m2){return m1->start < m2->start;});
462
463 auto f = o.flags();
464 for(const Mapping* m : sorted){
465 o << "map: [" << std::hex << std::showbase << std::setw(start_len)
466 << m->start << ", " << std::setw(end_len) << m->end
467 << std::noshowbase << ") -> \"" << std::setw(desc_len)
468 << m->memif->getDescription() << "\" +0x" << m->dest_off<< '\n';
469 }
470 o.flags(f);
471 }
472
480 Mapping* m = findMapping(addr);
481 if(!m){
482 return nullptr;
483 }
484 return m->memif;
485 }
486
493 // Navigate the bintree to find the addr (if contained)
494 BinTreeNode* n = bintree_;
495 Mapping* m = nullptr;
496
497 // Search tree for first mapping
498 while(n){
499 if(n->dest){
500 m = n->dest.get();
501 if(addr < m->start){
502 n = n->l;
503 }else if(addr >= m->end){
504 n = n->r;
505 }else{
506 return m;
507 }
508 }else if(addr >= n->separator){
509 n = n->r;
510 }else{
511 n = n->l;
512 }
513 }
514 if(!n){
515 return nullptr;
516 }
517
518 return m;
519 }
520
524 const Mapping* findMapping(addr_t addr) const {
525 // Navigate the bintree to find the addr (if contained)
526 const BinTreeNode* n = bintree_;
527 const Mapping* m = nullptr;
528
529 // Search tree for first mapping
530 while(n){
531 if(n->dest){
532 m = n->dest.get();
533 if(addr < m->start){
534 n = n->l;
535 }else if(addr >= m->end){
536 n = n->r;
537 }else{
538 return m;
539 }
540 }else if(addr >= n->separator){
541 n = n->r;
542 }else{
543 n = n->l;
544 }
545 }
546 if(!n){
547 return nullptr;
548 }
549 return m;
550 }
551
559 void verifyHasMapping(addr_t addr, addr_t size) const {
560 const addr_t end = addr+size;
561
562 const BinTreeNode* n = bintree_;
563 const Mapping* m = nullptr;
564
565 while(n){
566 if(n->dest){
567 m = n->dest.get();
568 if(addr < m->start){
569 n = n->l;
570 }else if(addr >= m->end){
571 n = n->r;
572 }else if(end <= m->end){
573 return; // Ok. Reached a destination containing both addr and end
574 }else{
575 throw MemoryAccessError(addr, size, "any", "This access spans more than one mapping");
576 }
577 }else if(addr >= n->separator){
578 n = n->r;
579 }else{
580 n = n->l;
581 }
582 }
583 throw MemoryAccessError(addr, size, "any", "No single mapping found for this address/size");
584 }
585
609 std::pair<const BlockingMemoryIF*, addr_t> mapAddress(addr_t addr) const noexcept {
610 const Mapping* m = findMapping(addr);
611 if(!m){
612 return {nullptr, 0};
613 }
614 return {m->memif, m->mapAddress(addr)};
615 }
616
621 uint32_t getNumMappings() const {
622 return num_mappings_;
623 }
624
628 const std::vector<const Mapping*>& getMappings() const {
629 return mappings_;
630 }
631
634
638
644 addr_t getBlockSize() const { return block_size_; }
645
648
652 std::string stringize(bool pretty=false) const {
653 (void) pretty;
654 std::stringstream ss;
655 ss << "<SimpleMemoryMap " << num_mappings_ << " mappings>";
656 return ss.str();
657 }
658
659 private:
660
667 struct BinTreeNode
668 {
670 BinTreeNode() = delete;
671
673 BinTreeNode(const BinTreeNode&) = delete;
674
676 BinTreeNode& operator=(const BinTreeNode&) = delete;
677
683 BinTreeNode(BinTreeNode* p, addr_t sep) :
684 separator(sep),
685 parent(p),
686 l(nullptr),//new BinTreeNode(this)), // Leaf
687 r(nullptr),//new BinTreeNode(this)), // Leaf
688 dest(nullptr),
689 red(true)
690 { }
691
698 BinTreeNode(BinTreeNode* p, Mapping* d) :
699 separator(~(addr_t)0),
700 parent(p),
701 l(nullptr),//new BinTreeNode(this)), // Leaf
702 r(nullptr),//new BinTreeNode(this)), // Leaf
703 dest(d),
704 red(true)
705 { }
706
715 BinTreeNode(BinTreeNode* p, std::unique_ptr<Mapping>&& d) :
716 separator(~(addr_t)0),
717 parent(p),
718 l(nullptr),//new BinTreeNode(this)), // Leaf
719 r(nullptr),//new BinTreeNode(this)), // Leaf
720 dest(std::move(d)),
721 red(true)
722 { }
723
727 ~BinTreeNode() {
728 delete l;
729 delete r;
730 }
731
736 BinTreeNode* grandparent() {
737 if(parent){
738 return parent->parent;
739 }
740 return nullptr;
741 }
742
747 BinTreeNode* uncle() {
748 BinTreeNode *g = grandparent();
749 if(!g){
750 return nullptr;
751 }
752 if(parent == g->l){
753 return g->r;
754 }
755 return g->l;
756 }
757
765 void recursDump(std::ostream& o, int depth=0) const {
766 auto f = o.flags();
767 if(red){
768 o << "(R) ";
769 }else{
770 o << "(B) ";
771 }
772 if(dest){
773 o << "map: [0x" << std::hex << dest->start << ", 0x" << dest->end
774 << ") -> memif:" << dest->memif << " \"" << dest->memif->getDescription() << "\" dest_offset=+0x" << dest->dest_off<< '\n';
775 }else{
776 o << "sep: " << std::hex << "0x" << separator << '\n';
777 }
778
779 o.flags(f);
780
781 for(int i=0; i<depth; ++i){
782 o << " ";
783 }
784 o << "l: ";
785 if(l){
786 l->recursDump(o, depth+1);
787 }else{
788 o << "-\n";
789 }
790
791 for(int i=0; i<depth; ++i){
792 o << " ";
793 }
794 o << "r: ";
795 if(r){
796 r->recursDump(o, depth+1);
797 }else{
798 o << "-\n";
799 }
800 }
801
802 const addr_t separator;
803 BinTreeNode* parent;
804 BinTreeNode* l;
805 BinTreeNode* r;
806 const std::unique_ptr<Mapping> dest;
807 bool red;
808 };
809
814 void RBTreeFixup_(BinTreeNode* n){
815 sparta_assert(n);
816
817 n->red = true; // Inserted node colored red
818 // Ascend tree and fix RB-Tree rule violations
819 while((bintree_ != n) && (n->parent->red)){
820 if(n->parent == n->parent->parent->l){
821 BinTreeNode* u = n->parent->parent->r;
822 if(u && u->red){
823 // RB-Tree case 1 - Recolor
824 n->parent->red = false;
825 u->red = false;
826 n->parent->parent->red = true;
827 n = n->parent->parent;
828 }else{
829 // Uncle is black (RB-Tree null-leafs would be black)
830 if(n == n->parent->r){
831 // RB-Tree case 2 - move n up and rotate left
832 n = n->parent;
833 rotateLeft_(n);
834 }
835 // RB-Tree case 3 - Recolor
836 n->parent->red = false;
837 n->parent->parent->red = true;
838 rotateRight_(n->parent->parent);
839 }
840 }else{
841 // Identical to logic in previous if statement with l/r reversed
842 BinTreeNode* u = n->parent->parent->l;
843 if(u && u->red){
844 n->parent->red = false;
845 u->red = false;
846 n->parent->parent->red = true;
847 n = n->parent->parent;
848 }else{
849 if(n == n->parent->l){
850 n = n->parent;
851 rotateRight_(n);
852 }
853 n->parent->red = false;
854 n->parent->parent->red = true;
855 rotateLeft_(n->parent->parent);
856 }
857 }
858 }
859
860 bintree_->red = false; // RB-Tree root is always blackx
861 }
862
878 BinTreeNode* findSeparatorNode_(BinTreeNode* root, addr_t addr) {
879 BinTreeNode* n = root;
880 while(1){
881 if(n->dest){
882 const Mapping* m = n->dest.get();
883 if(addr < m->start){
884 n = n->l;
885 }else if(addr >= m->end){
886 n = n->r;
887 }else{
888 throw SpartaException("Looking for a separator node at addr=")
889 << addr << " ended up within a mapping node";
890 }
891 }else if(addr == n->separator){
892 return n;
893 }else if(addr > n->separator){
894 n = n->r;
895 }else{
896 n = n->l;
897 }
898 }
899 return n;
900 }
901
905 void rotateLeft_(BinTreeNode* n){
906 sparta_assert(n);
907 sparta_assert(n->r);
908 BinTreeNode* pivot = n->r;
909 n->r = pivot->l;
910 if(n->r){
911 n->r->parent = n;
912 }
913 pivot->parent = n->parent;
914
915 if(n->parent == nullptr){
916 bintree_ = pivot;
917 }else{
918 if(n == n->parent->l){
919 n->parent->l = pivot;
920 }else{
921 n->parent->r = pivot;
922 }
923 }
924 pivot->l = n;
925 n->parent = pivot;
926 }
927
931 void rotateRight_(BinTreeNode* n){
932 sparta_assert(n);
933 sparta_assert(n->l);
934 BinTreeNode* pivot = n->l;
935 n->l = pivot->r;
936 if(n->l){
937 n->l->parent = n;
938 }
939 pivot->parent = n->parent;
940
941 if(n->parent == nullptr){
942 bintree_ = pivot;
943 }else{
944 if(n == n->parent->r){
945 n->parent->r = pivot;
946 }else{
947 n->parent->l = pivot;
948 }
949 }
950 pivot->r = n;
951 n->parent = pivot;
952 }
953
958 addr_t block_size_;
959
963 BinTreeNode* bintree_;
964
968 uint32_t num_mappings_;
969
974 std::vector<const Mapping*> mappings_;
975
980 addr_t block_idx_rshift_;
981
986 addr_t block_offset_mask_;
987
988 }; // class SimpleMemoryMap
989 } // namespace memory
990} // namespace sparta
991
992
994inline std::ostream& operator<<(std::ostream& out, const sparta::memory::SimpleMemoryMap& mi) {
995 out << mi.stringize();
996 return out;
997}
998
1000inline std::ostream& operator<<(std::ostream& out, const sparta::memory::SimpleMemoryMap* mi) {
1001 if(nullptr == mi){
1002 out << "null";
1003 }else{
1004 out << mi->stringize();
1005 }
1006 return out;
1007}
1008
File that contains BlockingMemoryIF.
File that contains some exception types related to memory interfaces.
std::ostream & operator<<(std::ostream &out, const sparta::memory::SimpleMemoryMap &mi)
SimpleMemoryMap stream operator.
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
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...
addr_t getRange() const
Gets the total span of this interface's valid address range.
addr_t getBlockSize() const
Returns the block size of memory represented by this interface. Read and write accesses must not span...
Indicates that there was an issue accessing a SPARTA memory object or interface.
Memory mapping object which maps addresses onto block-aligned destinations, each of which is a Blocki...
void verifyHasMapping(addr_t addr, addr_t size) const
Determines if a mapping is valid or not.
void dumpTree(std::ostream &o) const
Dumps the tree to an ostream like a directory listing.
addr_t getBlockSize() const
Returns the block size of memory represented by this interface. Read and write accesses must not span...
const Mapping * findMapping(addr_t addr) const
std::pair< const BlockingMemoryIF *, addr_t > mapAddress(addr_t addr) const noexcept
Maps an input address to a destination interface and address within that destination interface.
Mapping * findMapping(addr_t addr)
Finds the Mapping object associated with an address.
BlockingMemoryIF * findInterface(addr_t addr)
Returns the destination memory interface associated with a mapping containing an address.
SimpleMemoryMap(addr_t block_size)
Construct a SimpleMemoryMap.
const std::vector< const Mapping * > & getMappings() const
Returns the vector of current mappings in the order added.
uint32_t getNumMappings() const
Returns the number of mappings successfully added to this map.
void dumpMappings(std::ostream &o) const
Dumps a list of mappings to an ostream with a newline after each mapping entry.
virtual ~SimpleMemoryMap()
Destructor.
void addMapping(addr_t start, addr_t end, BlockingMemoryIF *memif, addr_t dest_start)
Create a mapping from addresses entering this object to a destination memory interface.
std::string stringize(bool pretty=false) const
Render description of this SimpleMemoryMap as a string.
uint64_t addr_t
Type for generic address representation in generic interfaces, errors and printouts within SPARTA.
Macros for handling exponential backoff.
Represents a mapping between an input address and output address for use in a destination BlockingMem...
const addr_t start
Beginning of the mapping input range (inclusive)
bool contains(addr_t a) const noexcept
Returns true if a is in the range [start,end)
const addr_t dest_off
Offset into destination memory interface. This is an offset from 0 which is received when the input a...
addr_t mapAddress(addr_t input) const noexcept
Maps an input address to the address-space for the destination memory interface.
const addr_t end
End of the mapping input address range (exclusive)
BlockingMemoryIF *const memif
Memory interface mapped to (after add/sub are applied to address)
Mapping(addr_t _start, addr_t _end, BlockingMemoryIF *_memif, addr_t _dest_off)
construct a mapping using the same values received from SimpleMemoryMap::addMapping
bool overlaps(addr_t a, addr_t b) const noexcept
Returns true if any part of the range [a,b) is shared with range [start,end)