The Sparta Modeling Framework
Loading...
Searching...
No Matches
ArchData.hpp
1// <ArchData.hpp> -*- C++ -*-
2
3#pragma once
4
5#include <iostream>
6#include <ios>
7#include <iomanip>
8#include <algorithm>
9#include <math.h>
10#include <list>
11#include <cstring>
12#include <unordered_map>
13
16#include "sparta/utils/TieredMap.hpp"
17#include "sparta/utils/Utils.hpp"
19#include "sparta/functional/ArchDataSegment.hpp"
22
23
24namespace sparta
25{
39 {
40 ArchData(const ArchData &) = delete;
41 ArchData(ArchData &&) = delete;
42 ArchData& operator=(const ArchData &) = delete;
43 ArchData& operator=(ArchData &&) = delete;
44
45 public:
46
51
52 class Line;
53
57
58 typedef ArchDataSegment::offset_type offset_type;
59 typedef offset_type line_idx_type;
60 typedef std::vector<ArchDataSegment*> SegmentList;
61
62 typedef std::list<Line*> LineList;
64
69 typedef std::unordered_map<ArchDataSegment::ident_type, ArchDataSegment*> LayoutHelperMap;
70
79 typedef std::vector<ArchDataSegment*> LayoutHelperVector;
80
83
87
88 static const offset_type DEFAULT_LINE_SIZE = 512; // //! Default line size in bytes of an ArchData line.
89
97 static const offset_type MAX_LINE_SIZE = 0x80000000;
98
102 static const uint64_t DEFAULT_INITIAL_FILL = 0xcc;
103
107 static const uint16_t DEFAULT_INITIAL_FILL_SIZE = 1;
108
112 static const line_idx_type INVALID_LINE_IDX = std::numeric_limits<line_idx_type>::max();
113
116
117
127 static void fillValue(uint8_t* buf, uint32_t size, uint64_t fill, uint16_t fill_val_size, uint16_t fill_pattern_offset=0) {
128 switch(fill_val_size) {
129 case 1:
130 memset(buf, fill, size); // Initialze with fill_val_size (pattern offset does not matter)
131 break;
132 case 2:
133 fillWith_<uint16_t>(buf, size, static_cast<uint16_t>(fill), fill_pattern_offset);
134 break;
135 case 4:
136 fillWith_<uint32_t>(buf, size, static_cast<uint32_t>(fill), fill_pattern_offset);
137 break;
138 case 8:
139 fillWith_<uint64_t>(buf, size, static_cast<uint64_t>(fill), fill_pattern_offset);
140 break;
141 default:
142 throw SpartaException("Failed to fill ArchData Line with fill value ")
143 << std::hex << fill << " because fill value size was " << std::dec
144 << fill_val_size;
145 }
146 }
147
157 class Line
158 {
159 public:
160
161 static constexpr char QUICK_CHECKPOINT_PREFIX[] = "<L>";
162 static const uint32_t QUICK_CHECKPOINT_OFFSET_SIZE = 7;
163
184 offset_type offset,
185 offset_type size,
186 uint64_t initial,
187 uint32_t initial_val_size,
188 uint8_t* pool_ptr=0) :
189 idx_(idx),
190 offset_(offset),
191 size_(size),
192 is_pool_(pool_ptr != 0),
193 dirty_(true)
194 {
195 sparta_assert(size > 0);
197
198 if(is_pool_){
199 data_ = pool_ptr;
200 }else{
201 alloc_data_.reset(new uint8_t[size]);
202 data_ = alloc_data_.get();
203 sparta_assert(data_ != 0);
204 }
205
206 fillWithInitial(initial, initial_val_size);
207 }
208
210 Line(const Line &) = delete;
211 Line(Line&&) = delete;
212 Line & operator=(const Line &) = delete;
213
214 void updateFrom(const Line& other)
215 {
216 sparta_assert(size_ == other.size_);
217 memcpy(data_, other.data_, size_);
218 dirty_ = true;
219 }
220
226 void flagDirty() {
227 dirty_ = true;
228 }
229
235 void fillWithInitial(uint64_t initial, uint32_t initial_val_size) {
236 ArchData::fillValue(data_, size_, initial, initial_val_size, 0);
237 }
238
248 template <typename StorageT>
249 void restore(StorageT& in) {
250 in.copyLineBytes((char*)data_, size_);
251 dirty_ = false;
252 }
253
263 template <typename StorageT>
264 void save(StorageT& out) {
265 out.writeLineBytes((char*)data_, size_);
266 dirty_ = false;
267 }
268
273 return idx_;
274 }
275
279 offset_type getOffset() const {
280 return offset_;
281 }
282
288 offset_type getLayoutSize() const {
289 return size_;
290 }
291
296 bool isDirty() const {
297 return dirty_;
298 }
299
303
315 template <typename T, ByteOrder BO>
316 T read(offset_type offset, uint32_t idx=0) const {
317 offset_type loc = offset + (idx*sizeof(T));
318 sparta_assert(loc + sizeof(T) <= size_,
319 "Read at ArchData::line offset 0x" << std::hex
320 << loc << " with size " << std::dec << sizeof(T) << " B");
321
322 uint8_t* d = data_ + loc;
323
324 T val = *reinterpret_cast<T*>(d);
325 return reorder<T,BO>(val);
326 }
327
335 void read(offset_type offset, offset_type size, uint8_t* data) const {
336 sparta_assert(offset + size <= size_,
337 "Read on ArchData::line offset 0x" << std::hex
338 << offset << " with size " << std::dec << size << " B");
339
340 memcpy(data, data_ + offset, size);
341 }
342
353 template <typename T, ByteOrder BO>
354 void write(offset_type offset, const T& t, uint32_t idx=0) {
355 offset_type loc = offset + (idx*sizeof(T));
356 sparta_assert(loc + sizeof(T) <= size_,
357 "Write on ArchData::line offset 0x" << std::hex
358 << loc << " with size " << std::dec << sizeof(T) << " B");
359
360 uint8_t* d = data_ + loc;
361
362 dirty_ = true;
363 T& val = *reinterpret_cast<T*>(d);
364 val = reorder<T,BO>(t);
365 }
366
375 void write(offset_type offset, offset_type size, const uint8_t* data) const {
376 sparta_assert(offset + size <= size_,
377 "Read on ArchData::line offset 0x" << std::hex
378 << offset << " with size " << std::dec << size << " B");
379
380 memcpy(data_ + offset, data, size);
381 dirty_ = true;
382 }
383
386
395 const uint8_t* getDataPointer(offset_type offset) const {
396 return data_ + offset;
397 }
398
403 uint8_t* getRawDataPtr(const offset_type offset) { return (data_ + offset); }
404
405 private:
406
407 line_idx_type idx_;
408 offset_type offset_;
409 offset_type size_;
410 bool is_pool_;
411 mutable bool dirty_;
412 uint8_t * data_ = nullptr;
413 std::unique_ptr<uint8_t[]> alloc_data_;
414
415 }; // class Line
416
417
421
448 ArchData(TreeNode* owner_node=nullptr,
449 offset_type line_size=DEFAULT_LINE_SIZE,
450 uint64_t initial=DEFAULT_INITIAL_FILL,
451 uint16_t initial_val_size=DEFAULT_INITIAL_FILL_SIZE,
452 bool can_free_lines=true) :
453 owner_node_(owner_node),
454 line_size_(line_size),
455 initial_(initial),
456 initial_val_size_(initial_val_size),
457 line_lsb_(0),
458 line_mask_(0),
459 num_lines_laid_out_(0),
460 size_(0),
461 is_laid_out_(false),
462 layout_padding_waste_(0),
463 layout_line_waste_(0),
464 can_free_lines_(can_free_lines)
465 {
466 sparta_assert(initial_val_size_ > 0 && initial_val_size_ <= 8 && isPowerOf2(initial_val_size_),
467 "ArchData initial_val_size type must be a power of 2 between 1 and 8 inclusive, is "
468 << initial_val_size_);
469 sparta_assert((initial_val_size_ == 8) || (initial >> (uint64_t)(8*initial_val_size_) == 0),
470 "ArchData initial val has nonzero bits above initial_val_size. initial val: "
471 << std::hex << initial_ << " initial_val_size:" << std::dec << initial_val_size_);
472
473 if(line_size >= 1){
474 sparta_assert(line_size <= MAX_LINE_SIZE);
475 double tmp = log2(line_size);
476 if(tmp != floor(tmp)){
477 SpartaException ex("line_size must be a power of 2, is ");
478 ex << line_size;
479 throw ex;
480 }
481 line_lsb_ = (offset_type)tmp;
482 line_mask_ = ~((1 << line_lsb_) - 1);
483
484 sparta_assert((1ul << line_lsb_) == line_size);
485 }else{
486 line_lsb_ = sizeof(offset_type) * 8;
487 line_mask_ = (offset_type)0;
488 }
489
490 if(owner_node_){
491 owner_node_->associateArchData_(this);
492 }
493
494 all_archdatas_->push_back(this);
495 }
496
497 virtual ~ArchData() {
498
499 // Deregister self. This exists in case of failed construction
500 if(owner_node_){
501 owner_node_->disassociateArchData_(this);
502 }
503
504 // Remove from all_archdatas_
505 auto itr = std::find(all_archdatas_->begin(), all_archdatas_->end(), this);
506 sparta_abort(itr != all_archdatas_->end());
507 all_archdatas_->erase(itr);
508
509 // Do not delete segments!
510 // It should be save for subclasses to free their registered
511 // segments in their destructors.
512
513 // Free all lines which are allocated
514 for(LineMap::iterator eitr = line_map_.begin(); eitr != line_map_.end(); ++eitr){
515 delete *eitr;
516 }
517 }
518
521
525
535 if(is_laid_out_){
536 throw SpartaException("This ArchData has already been laid out. New segments cannot be registered (segment id=")
537 << seg->getLayoutID() << ')';
538 }
539
540 checkDataSize(seg->getLayoutSize()); // Validate size of segment against size of a Line in this ArchData
541
542 for(const ArchDataSegment* ls : seg_list_){
543 if(ls == seg){
544 throw SpartaException("Segment @")
545 << std::hex << (void*)seg << " with id=0x" << seg->getLayoutID() << " already exists in ArchData @"
546 << (void*)this << std::dec;
547 }
548 if(seg->getLayoutID() != ArchDataSegment::INVALID_ID && ls->getLayoutID() == seg->getLayoutID()){
549 throw SpartaException("Segment id=")
550 << std::hex << "0x" << seg->getLayoutID() << " already exists in ArchData @"
551 << (void*)this << std::dec;
552 }
553 }
554
555 seg_list_.push_back(seg);
556 }
557
562 const SegmentList getSegments() const { return seg_list_; }
563
565 uint32_t getNumSegments() const { return seg_list_.size(); }
566
569
573
586 void layout() {
587
588 if(is_laid_out_){
589 throw SpartaException("This ArchData has already been laid out");
590 }
591
592 LayoutHelperMap helper_map; // Map for lookup by ID
593 LayoutHelperMap::const_iterator it;
594
595 for(ArchDataSegment* seg : seg_list_){
596 ArchDataSegment::ident_type lid = seg->getLayoutID();
598 // If ID is valid, add to helper map
599 it = helper_map.find(seg->getLayoutID());
600 if(it != helper_map.end()){
601 throw SpartaException("Found duplicate Segment id=")
602 << it->first << " in the same ArchData @" << (void*)this;
603 }
604
605 helper_map[seg->getLayoutID()] = seg; // Need to resolve IDs to names quickly
606 }
607 }
608
609 for(ArchDataSegment*& seg : seg_list_){
610 // Note: Segments being layed out are always less than the size
611 // of a single line. This is enforced in registerSegment
612 placeSegment_(seg, helper_map);
613 }
614
615 is_laid_out_ = true; // Disallow another layout of this ArchData
616
617 // Write initial values for each segment
618 for(ArchDataSegment* ls : seg_list_){
619 ls->writeInitial();
620 }
621 }
622
634 void layoutRange(offset_type size) {
635
636 if(is_laid_out_){
637 throw SpartaException("This ArchData has already been laid out");
638 }
639
640 if(seg_list_.size() != 0){
641 throw SpartaException("This ArchData has ")
642 << seg_list_.size() << " segments so it cannot be layed out using layoutRange";
643 }
644
645 size_ = size; // Simply set the size an allow internal structures to use it
646 is_laid_out_ = true; // Disallow another layout of this ArchData
647 }
648
663 Line& getLine(offset_type offset) {
665 "Cannot access this ArchData at offset: 0x"
666 << std::hex << offset << " ArchData size= "
667 << size_ << " B.");
668
669 line_idx_type ln_idx = getLineIndex(offset);
670 Line* ln;
671 //LineMap::iterator lnitr;
672 //if((lnitr = line_map_.find(ln_idx)) == line_map_.end()){
673 LineMap::pair_t* lnitr;
674 if((lnitr = line_map_.find(ln_idx)) == nullptr){
675 ln = allocateLine_(ln_idx); // Adds to line
676 }else{
677 ln = lnitr->second;
678 if(ln->getOffset() > offset){
679 ln = line_map_.find(ln_idx)->second;
680 }
681 }
682 return *ln;
683 }
684
697 const Line* tryGetLine(offset_type offset) const {
699 "Cannot access this ArchData at offset: 0x"
700 << std::hex << offset << " ArchData size= "
701 << size_ << " B.");
702
703 line_idx_type ln_idx = getLineIndex(offset);
704 //LineMap::const_iterator lnitr;
705 //if((lnitr = line_map_.find(ln_idx)) == line_map_.end()){
706 const LineMap::pair_t* lnitr;
707 if((lnitr = line_map_.find(ln_idx)) == nullptr){
708 return nullptr;
709 }
710 return lnitr->second;
711 }
712
716
718 const LineMap& getLineMap() const {
719 if(false == is_laid_out_){
720 throw SpartaException("Cannot get ArchData lines map until layout completes");
721 }
722 return line_map_;
723 }
724
739 void clean() {
740 if(false == is_laid_out_){
741 throw SpartaException("Cannot clear ArchData until layout completes");
742 }
743
744 if(canFreeLines()){
745 // Delete all lines allocated first (map contains pointers to lines)
746 for(LineMap::iterator itr = line_map_.begin(); itr != line_map_.end(); ++itr){
747 delete *itr;
748 }
749
750 // Delete all structures within the map
751 line_map_.clear();
752 }else{
753 // Overwrite all lines with initial bytes
754 for(LineMap::iterator itr = line_map_.begin(); itr != line_map_.end(); ++itr){
755 (*itr)->fillWithInitial(initial_, initial_val_size_);
756 }
757
758 }
759 }
760
768 void reset() {
769 clean(); // Checks for is_laid_out_
770
771 // Write initial values for each segment
772 for(ArchDataSegment* ls : seg_list_){
773 ls->writeInitial();
774 }
775 }
776
778 offset_type getLineSize() const {
779 return line_size_;
780 }
781
787 return line_map_.size();
788 }
789
796 line_idx_type getLineIndex(offset_type offset) const {
797 return offset >> line_lsb_;
798 }
799
809 offset_type getLineOffset(line_idx_type idx) const {
810 return line_size_ * idx;
811 }
812
822 bool canFreeLines() const { return can_free_lines_; }
823
826
830
846 void checkCanAccess(offset_type offset, offset_type bytes) const {
847 checkInSingleLine(offset, bytes);
848 sparta_assert(offset + bytes <= size_,
849 "Generic access validity test on ArchData::line offset 0x" << std::hex
850 << offset << " with size " << std::dec << bytes << " B");
851 }
852
863 bool containsAddress(offset_type offset) const noexcept {
864 return offset < size_;
865 }
866
874 void checkDataSize(offset_type size) const {
875 static_assert(std::is_unsigned<offset_type>::value == true);
876 if(size == 0){
877 throw SpartaException("Segment size (")
878 << size << ") must be larger than 0 and less than line size ("
879 << line_size_ << ")";
880 }
881 if(line_size_ != 0 && size > line_size_){
882 throw SpartaException("Segment size (")
883 << size << ") exceeds that of an ArchData line (" << line_size_ << ")";
884 }
885 }
886
896 void checkSegment(offset_type offset, offset_type size) const {
897
898 checkDataSize(size);
899
900 checkInSingleLine(offset, size);
901
902 if(__builtin_expect((offset + size) > size_, 0)){
903 throw SpartaException("Segment end (0x")
904 << std::hex << offset+size << ") extends pased end of ArchData (0x"
905 << size_ << ") by " << std::dec << (offset + size) - size_ << " B";
906 }
907 }
908
916 void checkInSingleLine(offset_type offset, offset_type size) const {
917 if(__builtin_expect((offset + size) - (offset & line_mask_) > line_size_, 0)){
918 throw SpartaException("Segment spans multiple ArchData lines: from ")
919 << getLineIndex(offset) << " to " << getLineIndex(offset+size-1);
920 }
921 }
922
925
929
930 virtual void updateFrom(const ArchData& other)
931 {
932 // Iterate through the other's line map...
933 for (LineMap::const_iterator itr = other.line_map_.begin(); itr != other.line_map_.end(); ++itr) {
934 const Line* other_ln = *itr;
935 if (other_ln != nullptr) {
936 // Attempt to find a corresponding line in this
937 line_idx_type other_idx = other_ln->getIdx();
938 LineMap::pair_t* pln = line_map_.find(other_idx);
939 if (pln != nullptr) {
940 // Found corresponding line, copy over the data
941 pln->second->updateFrom(*other_ln);
942 } else {
943 // Allocate a new line in this
944 Line* ln = allocateLine_(other_idx);
945 ln->updateFrom(*other_ln);
946 }
947 }
948 }
949 }
950
953
957
962 template <typename StorageT>
963 void save(StorageT& out) {
964 // Iterate lines and restore
965 sparta_assert(out.good(),
966 "Saving delta checkpoint to bad ostream for " << getOwnerNode()->getLocation());
967
968 for(LineMap::iterator itr = line_map_.begin(); itr != line_map_.end(); ++itr){
969 Line* ln = *itr;
970 if(ln != nullptr && ln->isDirty()){
971 out.beginLine(ln->getIdx());
972 ln->save(out);
973 }
974 }
975 out.endArchData();
976 }
977
983 template <typename StorageT>
984 void saveAll(StorageT& out) {
985 sparta_assert(out.good(),
986 "Saving delta checkpoint to bad ostream for " << getOwnerNode()->getLocation());
987
988 for(LineMap::iterator itr = line_map_.begin(); itr != line_map_.end(); ++itr){
989 Line* ln = *itr;
990 if(ln != nullptr){
991 out.beginLine(ln->getIdx());
992 ln->save(out);
993 }
994 }
995 out.endArchData();
996 }
997
1005 template <typename StorageT>
1006 void restore(StorageT& in) {
1007 // Iterate lines and restore
1008 sparta_assert(in.good(),
1009 "Encountered bad checkpoint data (invalid stream) for " << getOwnerNode()->getLocation());
1010
1011 while(1){
1012 line_idx_type ln_idx = in.getNextRestoreLine();
1013 if(ln_idx == INVALID_LINE_IDX){
1014 break; // Done with this ArchData
1015 }
1016 Line& ln = getLine(ln_idx * line_size_);
1017 ln.restore(in);
1018 }
1019 }
1020
1028 template <typename StorageT>
1029 void restoreAll(StorageT& in) {
1030 // Fresh, empty state, ready to be overwritten
1031 clean();
1032
1033 restore(in);
1034 }
1035
1038
1044
1050 const TreeNode* getOwnerNode() const { return owner_node_; }
1051
1056 {
1057 sparta_assert(owner_node_ == nullptr, "Owner already set");
1058
1059 owner_node_ = node;
1060 if (owner_node_) {
1061 owner_node_->associateArchData_(this);
1062 }
1063 }
1064
1069 bool isLaidOut() const { return is_laid_out_; }
1070
1077 offset_type getSize() const {
1078 if(false == is_laid_out_){
1079 throw SpartaException("Cannot get layout size until layout completes");
1080 }
1081 return size_;
1082 }
1083
1087 uint64_t getInitial() const {
1088 return initial_;
1089 }
1090
1094 uint32_t getInitialValSize() const {
1095 return initial_val_size_;
1096 }
1097
1099 uint32_t getTotalWaste() const {
1100 return layout_padding_waste_ + layout_line_waste_;
1101 }
1102
1104 uint32_t getPaddingWaste() const {
1105 return layout_padding_waste_;
1106 }
1107
1109 uint32_t getLineWaste() const {
1110 return layout_line_waste_;
1111 }
1112
1114 static bool compareSegmentOffsets(const ArchDataSegment* s1, const ArchDataSegment* s2){
1115 if(s1->getOffset() < s2->getOffset()){
1116 return true; // S1 < S2
1117 }else if(s1->getOffset() > s2->getOffset()){
1118 return false; // S1 > S2
1119 }
1120
1121 if(s1->getLayoutSize() >= s2->getLayoutSize()){
1122 return true; // S1 larger than or equal in size to S2 so it should be considered before
1123 }
1124 return false; // S1 is smaller than S2, so it should be considered after
1125 }
1126
1131 void dumpLayout(std::ostream& o) const {
1132 if(false == is_laid_out_){
1133 throw SpartaException("Cannot dump ArchData layout until layout completes");
1134 }
1135
1136 SegmentList sorted = seg_list_;
1137 std::sort(sorted.begin(), sorted.end(), compareSegmentOffsets);
1138
1139 offset_type last_line_off = 0; // offset of last line
1140 offset_type last_end = 0; // end of last segment (expected start of next)
1141
1142 dumpLayout_(o, sorted, last_line_off, last_end, true);
1143 }
1144
1161 std::vector<std::string> getLineStates() const {
1162 std::vector<std::string> result;
1163 for(LineMap::const_iterator itr = line_map_.begin(); itr != line_map_.end(); ++itr){
1164 const Line* ln = *itr;
1165 std::stringstream state;
1166 state << std::setw(5) << std::hex << ln->getIdx()
1167 << ":";
1168 if(ln != 0){
1169 if(ln->isDirty()){
1170 state << 'd';
1171 }else{
1172 state << 'c';
1173 }
1174 }else{
1175 state << '!';
1176 }
1177 result.push_back(state.str());
1178 }
1179 return result;
1180 }
1181
1182 uint64_t getNumTiers() const {
1183 return line_map_.getNumTiers();
1184 }
1185
1188
1193
1200 static const std::vector<const ArchData*> getAllArchDatas() {
1201 return *all_archdatas_;
1202 }
1203
1206
1207 private:
1208
1223 template <typename FillT>
1224 static void fillWith_(uint8_t* buf, uint32_t size, FillT fill, uint16_t buf_offset=0) {
1225 sparta_assert(buf != nullptr,
1226 "Null buf given");
1227 sparta_assert(buf_offset < sizeof(FillT),
1228 "Cannot have a buf_offset larger than FillT size. Must be buffer offset % fill size");
1229
1230 // Realign the pattern so it coincides with the misalignment of buf
1231 const FillT shifted_fill = (fill >> (buf_offset * 8)) | (fill << ((sizeof(FillT) - buf_offset) * 8));
1232
1233 // Handle case where buf is smaller than fill pattern
1234 if(sizeof(FillT) >= size) {
1235 // Line size is 1,2,4 bytes and fill value is same size or
1236 // smaller. Just copy what will fit
1237 memcpy(buf, static_cast<const void*>(&shifted_fill), size);
1238 return;
1239 }
1240
1241 uint8_t* ptr = buf;
1242
1243 // Address of first byte followinf buf which is not written
1244 const uint8_t* const end = buf + size;
1245 sparta_assert(end > buf,
1246 "buf (" << (void*)buf << ") was too large and adding size (" << size << ") rolled over to 0");
1247
1248 // Stop point before writing last, partial value
1249 const uint8_t* const stop = end - sizeof(FillT);
1250
1251 // Write entire fill pattern up until stop point to prevent overrun
1252 while(ptr < stop) {
1253 // Line size is > fill value size. Since line size is power
1254 // of 2, a line size greater than some power of two integer size is guaranteed
1255 // to fit a whole number of that integer
1256 memcpy(static_cast<void*>(ptr), static_cast<const void*>(&shifted_fill), sizeof(FillT));
1257 ptr += sizeof(FillT);
1258 sparta_assert(ptr >= buf, "ptr overflowed when adding fill size (" << sizeof(FillT) << ")");
1259 }
1260
1261 // Write remainder of buffer size
1262 int32_t rem = end - ptr;
1263 sparta_assert(rem <= (int32_t)sizeof(FillT) && rem >= 0,
1264 "fillWith_ remainder size was 0x" << std::hex << rem
1265 << " ptr=" << (void*)ptr << " end=" << (const void*)end
1266 << " sizeof(FillT)=" << sizeof(FillT));
1267 sparta_assert(ptr != nullptr,
1268 "Somehow encountered a null pointer during arithmetic based on a pointer at " << (void*) buf);
1269
1270 if(rem > 0) {
1271 // Remaining size is 1,2, or 4 bytes and rem fill value is same size or
1272 // smaller. Just copy what will fit
1273
1274 memcpy(static_cast<void*>(ptr), static_cast<const void*>(&shifted_fill), rem);
1275 }
1276 }
1277
1290 void dumpLayout_(std::ostream& o,
1291 SegmentList& sorted,
1292 offset_type last_line_off,
1293 offset_type last_end,
1294 bool show_line_nums) const {
1295
1296 // Print first line start
1297 if(show_line_nums){
1298 o << 'x' << std::hex << std::setw(5) << std::right << (last_end & line_mask_) << ": " << std::dec;
1299 }else{
1300 o << " \": "; // no line num (implies same as above)
1301 }
1302
1303 SegmentList::const_iterator it;
1304 std::vector<ArchDataSegment*> nestings;
1305 for(it = sorted.begin(); it != sorted.end(); ++it){
1306 offset_type off = (*it)->getOffset();
1307
1308 // End line of offset moved to next
1309 if(last_line_off != (off & line_mask_)){
1310 // Print skipped bytes at end of the line
1311 dumpSkippedBytes_(o, (off & line_mask_) - last_end, true, true);
1312 o << std::endl;
1313
1314 // Handle nestings
1315 if(nestings.size() > 0){
1316 dumpLayout_(o, nestings, last_line_off, last_line_off, false);
1317 }
1318 nestings.clear();
1319
1320 // Print new line start
1321 if(show_line_nums){
1322 o << 'x' << std::hex << std::setw(5) << std::right << (last_end & line_mask_) << ": " << std::dec;
1323 }else{
1324 o << " \": "; // no line num (implies same as above)
1325 }
1326
1327 last_line_off = (off & line_mask_);
1328 last_end = last_line_off;
1329 }
1330
1331 if(off < last_end){
1332 nestings.push_back(*it);
1333 continue; // Do not print, duplicate (nested)
1334 }else{
1335 // Print skipped bytes betweeen last seg and this
1336 offset_type jump = off - last_end;
1337 dumpSkippedBytes_(o, jump, false, false);
1338 }
1339
1340 offset_type size = (*it)->getLayoutSize();
1341 if(size == 1){
1342 o << "|"; // Only 1B
1343 }else if(size == 2){
1344 o << "|2"; // Only 2B
1345 }else{
1346 o << "|";
1347 for(offset_type i=1; i<size; ++i){
1348 if(i == (offset_type)(size/2)){
1349 o << std::left << std::dec;
1350 if(size >= 1000){
1351 o << std::setw(4) << size; // 4-char size
1352 }else if(size >= 100){
1353 o << std::setw(3) << size; // 3-char size
1354 }else if(size >= 10){
1355 o << std::setw(2) << size; // 2-char size
1356 }else{
1357 o << std::setw(1) << size; // 1-char size
1358 }
1359 }else{
1360 if(size >= 1000 && (i > (size/2)+1 && i < (size/2)+3)){
1361 // More than 4 digits printed for size. Skip
1362 }else if(size >= 100 && (i > (size/2)+1 && i < (size/2)+2)){
1363 // Print nothing. 3-digit size was just printed
1364 }else if(size >= 10 && i == (size/2)+1){
1365 // Print nothing. 2-digit size was just printed
1366 }else{
1367 o << '-'; // 1 char per byte.
1368 }
1369 }
1370 } // for( ... size ... )
1371 } // else // if(size == 1)
1372
1373 last_end = off + size;
1374 }
1375
1376 // Complete final line
1377 if((last_end & ~line_mask_) == 0){
1378 o << '|' << std::endl;
1379 }else{
1380 // Print skipped bytes at end of the line
1381 offset_type leftover = line_size_ - (last_end & ~line_mask_);
1382 dumpSkippedBytes_(o, leftover, true, true);
1383 o << std::endl;
1384 }
1385
1386 // Handle nestings on final line
1387 if(nestings.size() > 0){
1388 dumpLayout_(o, nestings, last_line_off, last_line_off, false);
1389 }
1390 nestings.clear();
1391 }
1392
1404 void dumpSkippedBytes_(std::ostream& o,
1405 offset_type num,
1406 bool condense,
1407 bool end_row) const {
1408 if(num == 0){
1409 // Print nothing yet
1410 }else if(num == 1){
1411 o << '/';
1412 }else if(num <= 16 || !condense){
1413 o << '|';
1414 for(offset_type i=1; i < num; ++i){
1415 //o << "+";
1416 o << " ";
1417 }
1418 }else{
1419 //o << "|+++" << std::dec << num << " ";
1420 o << "|+ " << std::dec << num << " ";
1421 }
1422
1423 if(end_row){
1424 o << '|';
1425 }
1426 }
1427
1434 Line* allocateLine_(line_idx_type idx) {
1435 if(idx * line_size_ > size_){
1436 throw SpartaException("Cannot allocate Line at idx ")
1437 << idx
1438 << " because idx*line_size is 0x"
1439 << std::hex << (idx * line_size_)
1440 << "and the current ArchData size is only 0x" << size_;
1441 }
1442
1443 // This test introduces a bit of overhead and should be removed
1444#ifndef NDEBUG
1445 //if(line_map_.find(idx) != line_map_.end()){
1446 if(line_map_.find(idx) != nullptr){
1447 throw SpartaException("Line is already allocated at index ") << idx;
1448 }
1449#endif // NDEBUG
1450
1451 // Allocate infinite-length lines
1452 if(0 == line_size_){
1453 if(idx != 0){
1454 throw SpartaException("Cannot allocate a line at index other than 0 when ArchData line size is 0 (infinite)");
1455 }
1456 sparta_assert(line_map_.size() == 0); // Cannot yet have a line
1457
1458 Line* ln = new Line(0, 0, size_, initial_, initial_val_size_);
1459 //lines_.push_back(ln);
1460 line_map_[0] = ln;
1461 return ln;
1462 }
1463
1464 // Allocate finite-length lines
1465 offset_type ln_off = getLineOffset(idx);
1466
1467 // Always use the full line size instead of trying to compute the
1468 // bytes leftover. When a line is being allocated, we may not know
1469 // the full size.
1470 Line* ln = new Line(idx, ln_off, line_size_, initial_, initial_val_size_);
1471 //LineList::iterator lnitr = lines_.begin();
1472 //for(; lnitr != lines_.end(); ++lnitr){
1473 // if((*lnitr)->getIdx() > idx){
1474 // break;
1475 // }
1476 //}
1477 //lines_.insert(lnitr, ln);
1478 line_map_[idx] = ln;
1479 return ln;
1480 }
1481
1482
1496 void placeSegment_(ArchDataSegment* seg,
1497 LayoutHelperMap& helper_map,
1498 uint32_t depth=0) {
1499
1500 sparta_assert(seg != 0); // Segment should never be NULL
1501
1502 if(seg->isPlaced()){
1503 /*
1504 for(uint32_t i=0; i<depth; ++i){ std::cout << " "; }
1505 std::cout << "seg " << std::dec << seg->getLayoutID() << " is already placed @ 0x"
1506 << std::hex << seg->getOffset() << std::dec << std::endl;
1507 */
1508 return; // Done
1509 }
1510
1511
1512 offset_type placement = 0; // Where to place the segment in the whole ArchData (calculated below)
1513 const offset_type size = seg->getLayoutSize(); // Size of the segment
1514 LayoutHelperMap::iterator it;
1515
1516 if(seg->getSubsetOf() != ArchDataSegment::INVALID_ID){
1517 // Place within another segment (subset)
1518
1519 ArchDataSegment::ident_type sub_of = seg->getSubsetOf();
1520
1521 //for(uint32_t i=0; i<depth; ++i){ std::cout << " "; }
1522 //std::cout << "seg " << std::dec << seg->getLayoutID() << " is subset @ +0x"
1523 // << std::hex << seg->getSubsetOffset() << "; attempting to place base: "
1524 // << std::dec << sub_of << std::endl;
1525
1526 // Find parent
1527 it = helper_map.find(sub_of);
1528 if(it == helper_map.end()){
1529 throw SpartaException("A Segment with identifier ")
1530 << seg->getLayoutID() << " claimed to be a "
1531 << "subset of Segment with identifier "
1532 << sub_of << ", which does not exist in this ArchData";
1533 }
1534 ArchDataSegment* parent_seg = it->second;
1535 sparta_assert(parent_seg->getLayoutID() == sub_of); // Parent must match expected ID
1536
1537 // Place parent first
1538 placeSegment_(it->second, helper_map, depth+1);
1539 sparta_assert(parent_seg->isPlaced()); // Parent must be placed at this point
1540
1541 if(seg->getLayoutSize() + seg->getSubsetOffset() > parent_seg->getLayoutSize()){
1542 throw SpartaException("Segment id=")
1543 << seg->getLayoutID() << " had size 0x" << std::hex << seg->getLayoutSize()
1544 << " and subset offset 0x" << std::hex << seg->getSubsetOffset()
1545 << " which makes it larger than the parent id=" << std::dec
1546 << parent_seg->getLayoutID() << " with size " << std::hex
1547 << parent_seg->getLayoutSize() << " of which it is a child";
1548 }
1549 placement = parent_seg->getOffset() + seg->getSubsetOffset();
1550 }else{
1551 // Place at the root (not subset)
1552
1553 // Word-alignment is probably important for reinterpret casting
1554 // Pad to the next word. size_ refers to ArchData size and offset of
1555 // next data.
1556 // Note that this only applies to segments placed at the root.
1557 if(size_ % HOST_INT_SIZE != 0){
1558 offset_type delta = HOST_INT_SIZE - (size_ % HOST_INT_SIZE);
1559 layout_padding_waste_ += delta;
1560 size_ += delta;
1561 }
1562
1563 // Ensure there is an ArchData line of appropriate size.
1564 // NOTE that segments CANNOT span lines.
1565 offset_type start_line_addr = size_ & line_mask_;
1566 offset_type end_line_addr = (size_ + size - 1) & line_mask_;
1567 if(start_line_addr != end_line_addr){
1568 // Needs to be moved to the next line
1569 sparta_assert(end_line_addr > start_line_addr); // Cannot allow overflow of offset_type. TODO: Test overflow case?
1570
1571 offset_type next = (size_ & line_mask_) + line_size_; // start of next line
1572 layout_line_waste_ += next - size_; // Waste some bytes
1573
1574 // std::cout << "Exceeds lines at offset 0x" << std::hex << size_ << std::dec << " + " << size << " B" << std::endl;
1575
1576 size_ = next;
1577 //lines_.push_back(0); // NULL pointer
1578 ++num_lines_laid_out_;
1579 }else if(start_line_addr >= num_lines_laid_out_ * line_size_){//(lines_.size() * line_size_)){
1580 // Starts naturally on a new line
1581 sparta_assert((size_ & ~line_mask_) == 0); // Must start at beginning of line
1582
1583 // std::cout << "Added next line at offset 0x" << std::hex << size_ << std::dec << std::endl;
1584
1585 //lines_.push_back(0); // NULL pointer
1586 ++num_lines_laid_out_;
1587 }
1588
1589 placement = size_;
1590 size_ += size; // Increase size so that the segment can be placed.
1591 sparta_assert((placement % HOST_INT_SIZE) == 0); // Just to be sure
1592 }
1593
1594 seg->place(placement);
1595
1597 }
1598
1599 private:
1600
1605 TreeNode *owner_node_ = nullptr;
1606
1610 offset_type line_size_;
1611
1615 const uint64_t initial_;
1616
1620 const uint32_t initial_val_size_;
1621
1625 offset_type line_lsb_;
1626
1630 offset_type line_mask_;
1631
1635 uint32_t num_lines_laid_out_;
1636
1640 LineMap line_map_;
1641
1645 SegmentList seg_list_;
1646
1650 offset_type size_;
1651
1655 bool is_laid_out_;
1656
1660 uint32_t layout_padding_waste_;
1661
1666 uint32_t layout_line_waste_;
1667
1671 bool can_free_lines_;
1672
1677 static std::vector<const ArchData*> *all_archdatas_;
1678
1679 }; // class ArchData
1680
1681} // namespace sparta
1682
1683
1685#define SPARTA_ARCHDATA_BODY
Byte order types and byte-swapping routines.
#define HOST_INT_SIZE
Definition ByteOrder.hpp:28
Set of macros for Sparta assertions. Caught by the framework.
#define sparta_abort(...)
Simple variatic assertion that will print a message to std::cerr and call std::terminate()
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
Exception class for all of Sparta.
Helpers for enforcing StaticInitialization order.
Basic Node framework in sparta device tree composite pattern.
static const ident_type INVALID_ID
Indicates an invalid ID for an ArchDataSegment or any refinement.
offset_type getOffset() const
Gets the offset of this segment once placed.
ident_type getLayoutID() const
Gets the layout Identifier of this segment.
offset_type getLayoutSize() const
Gets the layout size of this segment (number of bytes)
Line object which composes part of an entire ArchData.
Definition ArchData.hpp:158
uint8_t * getRawDataPtr(const offset_type offset)
return the raw data pointer for this line for direct read and write. No error checking is performed....
Definition ArchData.hpp:403
void write(offset_type offset, const T &t, uint32_t idx=0)
Write to this line, reordering bytes based on byte order if required.
Definition ArchData.hpp:354
void restore(StorageT &in)
Restore data from input buffer.
Definition ArchData.hpp:249
T read(offset_type offset, uint32_t idx=0) const
Read from this line, reordering bytes based on byte order if required.
Definition ArchData.hpp:316
const uint8_t * getDataPointer(offset_type offset) const
Gets a pointer to data within this line for direct read access.
Definition ArchData.hpp:395
Line(line_idx_type idx, offset_type offset, offset_type size, uint64_t initial, uint32_t initial_val_size, uint8_t *pool_ptr=0)
Line constructor.
Definition ArchData.hpp:183
bool isDirty() const
Has this line been modified since the last save or restore. Immediately after construction,...
Definition ArchData.hpp:296
void save(StorageT &out)
Store data to output buffer.
Definition ArchData.hpp:264
offset_type getLayoutSize() const
Size of this line's data including padding. Accessing bytes from this line with an offset greater tha...
Definition ArchData.hpp:288
Line(const Line &)=delete
Disallow copies, moves, and assignments.
void read(offset_type offset, offset_type size, uint8_t *data) const
Read a number of contiguous bytes from this line.
Definition ArchData.hpp:335
static const uint32_t QUICK_CHECKPOINT_OFFSET_SIZE
Size of offset and size entries in quick checkpoint.
Definition ArchData.hpp:162
offset_type getOffset() const
Offset into the owning ArchData.
Definition ArchData.hpp:279
void flagDirty()
Allows caller to explicitly flag this line as dirty.
Definition ArchData.hpp:226
void write(offset_type offset, offset_type size, const uint8_t *data) const
Write a number of contiguous bytes to this line.
Definition ArchData.hpp:375
line_idx_type getIdx() const
Index of this line (typically offset/line_size)
Definition ArchData.hpp:272
void fillWithInitial(uint64_t initial, uint32_t initial_val_size)
Fills the line's data with the initial value.
Definition ArchData.hpp:235
static constexpr char QUICK_CHECKPOINT_PREFIX[]
Prefix for Line checkpoint entries.
Definition ArchData.hpp:161
Contains a set of contiguous line of architectural data which can be referred to by any architected o...
Definition ArchData.hpp:39
TieredMap< line_idx_type, Line * > LineMap
List of Line pointers.
Definition ArchData.hpp:63
bool containsAddress(offset_type offset) const noexcept
Determines if this ArchData contains a byte for the specified address.
Definition ArchData.hpp:863
const TreeNode * getOwnerNode() const
Returns the owner TreeNode.
offset_type getLineOffset(line_idx_type idx) const
Gets offset associated with a particular line that exists in this ArchData.
Definition ArchData.hpp:809
std::list< Line * > LineList
List of ArchDataSegment.
Definition ArchData.hpp:62
std::unordered_map< ArchDataSegment::ident_type, ArchDataSegment * > LayoutHelperMap
Helper map for quick lookup from ArchDataSegment::ident_type to an ArchDataSegment*.
Definition ArchData.hpp:69
uint64_t getInitial() const
Gets the value used to initialize unwritten memory.
line_idx_type getNumAllocatedLines() const
Gets the number of lines with allocated data;.
Definition ArchData.hpp:786
void dumpLayout(std::ostream &o) const
Prints content of each ArchData Line in order.
const SegmentList getSegments() const
Gets the list of segments within this ArchData.
Definition ArchData.hpp:562
void setOwnerNode(TreeNode *node)
Set the owner tree node.
static const uint64_t DEFAULT_INITIAL_FILL
Default initial fill value for an ArchData when allocated.
Definition ArchData.hpp:102
bool isLaidOut() const
Has this ArchData been laid out yet.
uint32_t getInitialValSize() const
Gets the size of the initial value.
std::vector< std::string > getLineStates() const
Gets state information for each line in this ArchData.
void restoreAll(StorageT &in)
Restores a full checkpoint snapshot (not a delta). This removes all lines NOT found in the snapshot d...
std::vector< ArchDataSegment * > LayoutHelperVector
Vector of ArchDataSegment pointers.
Definition ArchData.hpp:79
void clean()
Deletes (if possible) data held within the ArchData, restoring it to a 'clean-state'....
Definition ArchData.hpp:739
line_idx_type getLineIndex(offset_type offset) const
Gets Index of a line containing the specified offset.
Definition ArchData.hpp:796
uint32_t getPaddingWaste() const
Number of bytes wasted during layout because of optimal word-alignment.
static const line_idx_type INVALID_LINE_IDX
Invalid line index.
Definition ArchData.hpp:112
void layout()
Organizes the Segments into overlapping regions as needed, eventually calling ArchDataSegment::place ...
Definition ArchData.hpp:586
void restore(StorageT &in)
Restores a delta checkpoint (not a full snapshot). This contains only additive changes.
uint32_t getLineWaste() const
Number of bytes wasted during layout because of line-ending alignment.
void registerSegment(ArchDataSegment *seg)
All constructed segments must register themselves through this method to be laid out within the ArchD...
Definition ArchData.hpp:534
static const uint16_t DEFAULT_INITIAL_FILL_SIZE
Number of of bytes from DEFAULT_INITIAL_FILL to use as a default.
Definition ArchData.hpp:107
void saveAll(StorageT &out)
Writes snapshot checkpointing data (all lines) from this ArchData to a stream regardless of dirtiness...
Definition ArchData.hpp:984
void layoutRange(offset_type size)
Lays out the archdata to contain a range of addresses without specifying any segments.
Definition ArchData.hpp:634
void reset()
Cleans the ArchData (see clean) then applies all initial values through ArchDataSegment::writeInitial...
Definition ArchData.hpp:768
const LineMap & getLineMap() const
Only const access is allowed to internal map.
Definition ArchData.hpp:718
bool canFreeLines() const
Indicates whether this ArchData can free its lines after allocating them. Otherwise,...
Definition ArchData.hpp:822
static void fillValue(uint8_t *buf, uint32_t size, uint64_t fill, uint16_t fill_val_size, uint16_t fill_pattern_offset=0)
Fill a buffer with a fill pattern.
Definition ArchData.hpp:127
std::vector< ArchDataSegment * > SegmentList
ArchData line index.
Definition ArchData.hpp:60
void checkInSingleLine(offset_type offset, offset_type size) const
Determines whether the access defined by offset and size spans multiple ArchData Lines.
Definition ArchData.hpp:916
void checkCanAccess(offset_type offset, offset_type bytes) const
Determines if an access of size 'bytes' can be performed at the given offset based only on the size o...
Definition ArchData.hpp:846
offset_type line_idx_type
Represents offsets into this ArchData.
Definition ArchData.hpp:59
const Line * tryGetLine(offset_type offset) const
Gets the line associated with this offset only if it already exists.
Definition ArchData.hpp:697
static const offset_type MAX_LINE_SIZE
ArchData construction line size maximum in bytes.
Definition ArchData.hpp:97
uint32_t getTotalWaste() const
Number of bytes wasted total during layout for any reason.
uint32_t getNumSegments() const
Gets number of segments in this ArchData.
Definition ArchData.hpp:565
static bool compareSegmentOffsets(const ArchDataSegment *s1, const ArchDataSegment *s2)
Comparison functions for sorting by segment.
ArchData(TreeNode *owner_node=nullptr, offset_type line_size=DEFAULT_LINE_SIZE, uint64_t initial=DEFAULT_INITIAL_FILL, uint16_t initial_val_size=DEFAULT_INITIAL_FILL_SIZE, bool can_free_lines=true)
Constructor.
Definition ArchData.hpp:448
void checkSegment(offset_type offset, offset_type size) const
Checks that a segment is valid within this archdata by its given offset and size.
Definition ArchData.hpp:896
offset_type getLineSize() const
Gets the size of a line within this ArchData instance.
Definition ArchData.hpp:778
void save(StorageT &out)
Writes checkpointing data from this ArchData to a stream.
Definition ArchData.hpp:963
static const std::vector< const ArchData * > getAllArchDatas()
Static method to return a const vector of all ArchVectors that currently exist.
offset_type getSize() const
Gets the current size of the layout for this ArchData.
Line & getLine(offset_type offset)
Gets the line associated with this offset, allocating a new line if necessary.
Definition ArchData.hpp:663
void checkDataSize(offset_type size) const
Checks to see that the size of the data is a valid access size.
Definition ArchData.hpp:874
Used to construct and throw a standard C++ exception. Inherits from std::exception.
Static-initialization order controller.
N-Tier lookup map for sparse-representation of large memory spaces. This is essentially a M-Tree wher...
Definition TieredMap.hpp:40
std::pair< const line_idx_type, Line * > pair_t
This map contains mappings between a unique set of keys to values. This type represents that mapping ...
Definition TieredMap.hpp:52
uint64_t getNumTiers() const
Number of tiers used by this map to represent the entire space containing the largest key added....
const pair_t * find(const KeyT &k) const
Finds a mapping for key k if one exists in this map.
uint64_t size() const
Number of elements allocated in this map.
void clear()
Clears the map. Frees all mappings and data structures.
Node in a composite tree representing a sparta Tree item.
Definition TreeNode.hpp:205
Macros for handling exponential backoff.
bool isPowerOf2(uint64_t x)
Determines if input is 0 or a power of 2.
Definition Utils.hpp:142