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
465 ArchData(TreeNode* owner_node=nullptr,
466 offset_type line_size=DEFAULT_LINE_SIZE,
467 uint64_t initial=DEFAULT_INITIAL_FILL,
468 uint16_t initial_val_size=DEFAULT_INITIAL_FILL_SIZE,
469 bool can_free_lines=true) :
470 owner_node_(owner_node),
471 line_size_(line_size),
472 initial_(initial),
473 initial_val_size_(initial_val_size),
474 line_lsb_(0),
475 line_mask_(0),
476 num_lines_laid_out_(0),
477 size_(0),
478 is_laid_out_(false),
479 layout_padding_waste_(0),
480 layout_line_waste_(0),
481 can_free_lines_(can_free_lines)
482 {
483 sparta_assert(initial_val_size_ > 0 && initial_val_size_ <= 8 && isPowerOf2(initial_val_size_),
484 "ArchData initial_val_size type must be a power of 2 between 1 and 8 inclusive, is "
485 << initial_val_size_);
486 sparta_assert((initial_val_size_ == 8) || (initial >> (uint64_t)(8*initial_val_size_) == 0),
487 "ArchData initial val has nonzero bits above initial_val_size. initial val: "
488 << std::hex << initial_ << " initial_val_size:" << std::dec << initial_val_size_);
489
490 if(line_size >= 1){
491 sparta_assert(line_size <= MAX_LINE_SIZE);
492 double tmp = log2(line_size);
493 if(tmp != floor(tmp)){
494 SpartaException ex("line_size must be a power of 2, is ");
495 ex << line_size;
496 throw ex;
497 }
498 line_lsb_ = (offset_type)tmp;
499 line_mask_ = ~((1 << line_lsb_) - 1);
500
501 sparta_assert((1ul << line_lsb_) == line_size);
502 }else{
503 line_lsb_ = sizeof(offset_type) * 8;
504 line_mask_ = (offset_type)0;
505 }
506
507 if(owner_node_){
508 owner_node_->associateArchData_(this);
509 }
510
511 all_archdatas_->push_back(this);
512 }
513
514 virtual ~ArchData() {
515
516 // Deregister self. This exists in case of failed construction
517 if(owner_node_){
518 owner_node_->disassociateArchData_(this);
519 }
520
521 // Remove from all_archdatas_
522 auto itr = std::find(all_archdatas_->begin(), all_archdatas_->end(), this);
523 sparta_abort(itr != all_archdatas_->end());
524 all_archdatas_->erase(itr);
525
526 // Do not delete segments!
527 // It should be save for subclasses to free their registered
528 // segments in their destructors.
529
530 // Free all lines which are allocated
531 for(LineMap::iterator eitr = line_map_.begin(); eitr != line_map_.end(); ++eitr){
532 delete *eitr;
533 }
534 }
535
538
542
552 if(is_laid_out_){
553 throw SpartaException("This ArchData has already been laid out. New segments cannot be registered (segment id=")
554 << seg->getLayoutID() << ')';
555 }
556
557 checkDataSize(seg->getLayoutSize()); // Validate size of segment against size of a Line in this ArchData
558
559 for(const ArchDataSegment* ls : seg_list_){
560 if(ls == seg){
561 throw SpartaException("Segment @")
562 << std::hex << (void*)seg << " with id=0x" << seg->getLayoutID() << " already exists in ArchData @"
563 << (void*)this << std::dec;
564 }
565 if(seg->getLayoutID() != ArchDataSegment::INVALID_ID && ls->getLayoutID() == seg->getLayoutID()){
566 throw SpartaException("Segment id=")
567 << std::hex << "0x" << seg->getLayoutID() << " already exists in ArchData @"
568 << (void*)this << std::dec;
569 }
570 }
571
572 seg_list_.push_back(seg);
573 }
574
579 const SegmentList getSegments() const { return seg_list_; }
580
582 uint32_t getNumSegments() const { return seg_list_.size(); }
583
586
590
603 void layout() {
604
605 if(is_laid_out_){
606 throw SpartaException("This ArchData has already been laid out");
607 }
608
609 LayoutHelperMap helper_map; // Map for lookup by ID
610 LayoutHelperMap::const_iterator it;
611
612 for(ArchDataSegment* seg : seg_list_){
613 ArchDataSegment::ident_type lid = seg->getLayoutID();
615 // If ID is valid, add to helper map
616 it = helper_map.find(seg->getLayoutID());
617 if(it != helper_map.end()){
618 throw SpartaException("Found duplicate Segment id=")
619 << it->first << " in the same ArchData @" << (void*)this;
620 }
621
622 helper_map[seg->getLayoutID()] = seg; // Need to resolve IDs to names quickly
623 }
624 }
625
626 for(ArchDataSegment*& seg : seg_list_){
627 // Note: Segments being layed out are always less than the size
628 // of a single line. This is enforced in registerSegment
629 placeSegment_(seg, helper_map);
630 }
631
632 is_laid_out_ = true; // Disallow another layout of this ArchData
633
634 // Write initial values for each segment
635 for(ArchDataSegment* ls : seg_list_){
636 ls->writeInitial();
637 }
638 }
639
651 void layoutRange(offset_type size) {
652
653 if(is_laid_out_){
654 throw SpartaException("This ArchData has already been laid out");
655 }
656
657 if(seg_list_.size() != 0){
658 throw SpartaException("This ArchData has ")
659 << seg_list_.size() << " segments so it cannot be layed out using layoutRange";
660 }
661
662 size_ = size; // Simply set the size an allow internal structures to use it
663 is_laid_out_ = true; // Disallow another layout of this ArchData
664 }
665
680 Line& getLine(offset_type offset) {
682 "Cannot access this ArchData at offset: 0x"
683 << std::hex << offset << " ArchData size= "
684 << size_ << " B.");
685
686 line_idx_type ln_idx = getLineIndex(offset);
687 Line* ln;
688 //LineMap::iterator lnitr;
689 //if((lnitr = line_map_.find(ln_idx)) == line_map_.end()){
690 LineMap::pair_t* lnitr;
691 if((lnitr = line_map_.find(ln_idx)) == nullptr){
692 ln = allocateLine_(ln_idx); // Adds to line
693 }else{
694 ln = lnitr->second;
695 if(ln->getOffset() > offset){
696 ln = line_map_.find(ln_idx)->second;
697 }
698 }
699 return *ln;
700 }
701
714 const Line* tryGetLine(offset_type offset) const {
716 "Cannot access this ArchData at offset: 0x"
717 << std::hex << offset << " ArchData size= "
718 << size_ << " B.");
719
720 line_idx_type ln_idx = getLineIndex(offset);
721 //LineMap::const_iterator lnitr;
722 //if((lnitr = line_map_.find(ln_idx)) == line_map_.end()){
723 const LineMap::pair_t* lnitr;
724 if((lnitr = line_map_.find(ln_idx)) == nullptr){
725 return nullptr;
726 }
727 return lnitr->second;
728 }
729
733
735 const LineMap& getLineMap() const {
736 if(false == is_laid_out_){
737 throw SpartaException("Cannot get ArchData lines map until layout completes");
738 }
739 return line_map_;
740 }
741
756 void clean() {
757 if(false == is_laid_out_){
758 throw SpartaException("Cannot clear ArchData until layout completes");
759 }
760
761 if(canFreeLines()){
762 // Delete all lines allocated first (map contains pointers to lines)
763 for(LineMap::iterator itr = line_map_.begin(); itr != line_map_.end(); ++itr){
764 delete *itr;
765 }
766
767 // Delete all structures within the map
768 line_map_.clear();
769 }else{
770 // Overwrite all lines with initial bytes
771 for(LineMap::iterator itr = line_map_.begin(); itr != line_map_.end(); ++itr){
772 (*itr)->fillWithInitial(initial_, initial_val_size_);
773 }
774
775 }
776 }
777
785 void reset() {
786 clean(); // Checks for is_laid_out_
787
788 // Write initial values for each segment
789 for(ArchDataSegment* ls : seg_list_){
790 ls->writeInitial();
791 }
792 }
793
795 offset_type getLineSize() const {
796 return line_size_;
797 }
798
804 return line_map_.size();
805 }
806
813 line_idx_type getLineIndex(offset_type offset) const {
814 return offset >> line_lsb_;
815 }
816
826 offset_type getLineOffset(line_idx_type idx) const {
827 return line_size_ * idx;
828 }
829
839 bool canFreeLines() const { return can_free_lines_; }
840
843
847
863 void checkCanAccess(offset_type offset, offset_type bytes) const {
864 checkInSingleLine(offset, bytes);
865 sparta_assert(offset + bytes <= size_,
866 "Generic access validity test on ArchData::line offset 0x" << std::hex
867 << offset << " with size " << std::dec << bytes << " B");
868 }
869
880 bool containsAddress(offset_type offset) const noexcept {
881 return offset < size_;
882 }
883
891 void checkDataSize(offset_type size) const {
892 static_assert(std::is_unsigned<offset_type>::value == true);
893 if(size == 0){
894 throw SpartaException("Segment size (")
895 << size << ") must be larger than 0 and less than line size ("
896 << line_size_ << ")";
897 }
898 if(line_size_ != 0 && size > line_size_){
899 throw SpartaException("Segment size (")
900 << size << ") exceeds that of an ArchData line (" << line_size_ << ")";
901 }
902 }
903
913 void checkSegment(offset_type offset, offset_type size) const {
914
915 checkDataSize(size);
916
917 checkInSingleLine(offset, size);
918
919 if(__builtin_expect((offset + size) > size_, 0)){
920 throw SpartaException("Segment end (0x")
921 << std::hex << offset+size << ") extends pased end of ArchData (0x"
922 << size_ << ") by " << std::dec << (offset + size) - size_ << " B";
923 }
924 }
925
933 void checkInSingleLine(offset_type offset, offset_type size) const {
934 if(__builtin_expect((offset + size) - (offset & line_mask_) > line_size_, 0)){
935 throw SpartaException("Segment spans multiple ArchData lines: from ")
936 << getLineIndex(offset) << " to " << getLineIndex(offset+size-1);
937 }
938 }
939
942
946
947 virtual void updateFrom(const ArchData& other)
948 {
949 // Iterate through the other's line map...
950 for (LineMap::const_iterator itr = other.line_map_.begin(); itr != other.line_map_.end(); ++itr) {
951 const Line* other_ln = *itr;
952 if (other_ln != nullptr) {
953 // Attempt to find a corresponding line in this
954 line_idx_type other_idx = other_ln->getIdx();
955 LineMap::pair_t* pln = line_map_.find(other_idx);
956 if (pln != nullptr) {
957 // Found corresponding line, copy over the data
958 pln->second->updateFrom(*other_ln);
959 } else {
960 // Allocate a new line in this
961 Line* ln = allocateLine_(other_idx);
962 ln->updateFrom(*other_ln);
963 }
964 }
965 }
966 }
967
970
974
979 template <typename StorageT>
980 void save(StorageT& out) {
981 // Iterate lines and restore
982 sparta_assert(out.good(),
983 "Saving delta checkpoint to bad ostream for " << getOwnerNode()->getLocation());
984
985 for(LineMap::iterator itr = line_map_.begin(); itr != line_map_.end(); ++itr){
986 Line* ln = *itr;
987 if(ln != nullptr && ln->isDirty()){
988 out.beginLine(ln->getIdx());
989 ln->save(out);
990 }
991 }
992 out.endArchData();
993 }
994
1000 template <typename StorageT>
1001 void saveAll(StorageT& out) {
1002 sparta_assert(out.good(),
1003 "Saving delta checkpoint to bad ostream for " << getOwnerNode()->getLocation());
1004
1005 for(LineMap::iterator itr = line_map_.begin(); itr != line_map_.end(); ++itr){
1006 Line* ln = *itr;
1007 if(ln != nullptr){
1008 out.beginLine(ln->getIdx());
1009 ln->save(out);
1010 }
1011 }
1012 out.endArchData();
1013 }
1014
1022 template <typename StorageT>
1023 void restore(StorageT& in) {
1024 // Iterate lines and restore
1025 sparta_assert(in.good(),
1026 "Encountered bad checkpoint data (invalid stream) for " << getOwnerNode()->getLocation());
1027
1028 while(1){
1029 line_idx_type ln_idx = in.getNextRestoreLine();
1030 if(ln_idx == INVALID_LINE_IDX){
1031 break; // Done with this ArchData
1032 }
1033 Line& ln = getLine(ln_idx * line_size_);
1034 ln.restore(in);
1035 }
1036 }
1037
1045 template <typename StorageT>
1046 void restoreAll(StorageT& in) {
1047 // Fresh, empty state, ready to be overwritten
1048 clean();
1049
1050 restore(in);
1051 }
1052
1055
1061
1067 const TreeNode* getOwnerNode() const { return owner_node_; }
1068
1073 {
1074 sparta_assert(owner_node_ == nullptr, "Owner already set");
1075
1076 owner_node_ = node;
1077 if (owner_node_) {
1078 owner_node_->associateArchData_(this);
1079 }
1080 }
1081
1086 bool isLaidOut() const { return is_laid_out_; }
1087
1094 offset_type getSize() const {
1095 if(false == is_laid_out_){
1096 throw SpartaException("Cannot get layout size until layout completes");
1097 }
1098 return size_;
1099 }
1100
1104 uint64_t getInitial() const {
1105 return initial_;
1106 }
1107
1111 uint32_t getInitialValSize() const {
1112 return initial_val_size_;
1113 }
1114
1116 uint32_t getTotalWaste() const {
1117 return layout_padding_waste_ + layout_line_waste_;
1118 }
1119
1121 uint32_t getPaddingWaste() const {
1122 return layout_padding_waste_;
1123 }
1124
1126 uint32_t getLineWaste() const {
1127 return layout_line_waste_;
1128 }
1129
1131 static bool compareSegmentOffsets(const ArchDataSegment* s1, const ArchDataSegment* s2){
1132 if(s1->getOffset() < s2->getOffset()){
1133 return true; // S1 < S2
1134 }else if(s1->getOffset() > s2->getOffset()){
1135 return false; // S1 > S2
1136 }
1137
1138 if(s1->getLayoutSize() >= s2->getLayoutSize()){
1139 return true; // S1 larger than or equal in size to S2 so it should be considered before
1140 }
1141 return false; // S1 is smaller than S2, so it should be considered after
1142 }
1143
1148 void dumpLayout(std::ostream& o) const {
1149 if(false == is_laid_out_){
1150 throw SpartaException("Cannot dump ArchData layout until layout completes");
1151 }
1152
1153 SegmentList sorted = seg_list_;
1154 std::sort(sorted.begin(), sorted.end(), compareSegmentOffsets);
1155
1156 offset_type last_line_off = 0; // offset of last line
1157 offset_type last_end = 0; // end of last segment (expected start of next)
1158
1159 dumpLayout_(o, sorted, last_line_off, last_end, true);
1160 }
1161
1178 std::vector<std::string> getLineStates() const {
1179 std::vector<std::string> result;
1180 for(LineMap::const_iterator itr = line_map_.begin(); itr != line_map_.end(); ++itr){
1181 const Line* ln = *itr;
1182 std::stringstream state;
1183 state << std::setw(5) << std::hex << ln->getIdx()
1184 << ":";
1185 if(ln != 0){
1186 if(ln->isDirty()){
1187 state << 'd';
1188 }else{
1189 state << 'c';
1190 }
1191 }else{
1192 state << '!';
1193 }
1194 result.push_back(state.str());
1195 }
1196 return result;
1197 }
1198
1199 uint64_t getNumTiers() const {
1200 return line_map_.getNumTiers();
1201 }
1202
1205
1210
1217 static const std::vector<const ArchData*> getAllArchDatas() {
1218 return *all_archdatas_;
1219 }
1220
1223
1224 private:
1225
1240 template <typename FillT>
1241 static void fillWith_(uint8_t* buf, uint32_t size, FillT fill, uint16_t buf_offset=0) {
1242 sparta_assert(buf != nullptr,
1243 "Null buf given");
1244 sparta_assert(buf_offset < sizeof(FillT),
1245 "Cannot have a buf_offset larger than FillT size. Must be buffer offset % fill size");
1246
1247 // Realign the pattern so it coincides with the misalignment of buf
1248 const FillT shifted_fill = (fill >> (buf_offset * 8)) | (fill << ((sizeof(FillT) - buf_offset) * 8));
1249
1250 // Handle case where buf is smaller than fill pattern
1251 if(sizeof(FillT) >= size) {
1252 // Line size is 1,2,4 bytes and fill value is same size or
1253 // smaller. Just copy what will fit
1254 memcpy(buf, static_cast<const void*>(&shifted_fill), size);
1255 return;
1256 }
1257
1258 uint8_t* ptr = buf;
1259
1260 // Address of first byte followinf buf which is not written
1261 const uint8_t* const end = buf + size;
1262 sparta_assert(end > buf,
1263 "buf (" << (void*)buf << ") was too large and adding size (" << size << ") rolled over to 0");
1264
1265 // Stop point before writing last, partial value
1266 const uint8_t* const stop = end - sizeof(FillT);
1267
1268 // Write entire fill pattern up until stop point to prevent overrun
1269 while(ptr < stop) {
1270 // Line size is > fill value size. Since line size is power
1271 // of 2, a line size greater than some power of two integer size is guaranteed
1272 // to fit a whole number of that integer
1273 memcpy(static_cast<void*>(ptr), static_cast<const void*>(&shifted_fill), sizeof(FillT));
1274 ptr += sizeof(FillT);
1275 sparta_assert(ptr >= buf, "ptr overflowed when adding fill size (" << sizeof(FillT) << ")");
1276 }
1277
1278 // Write remainder of buffer size
1279 int32_t rem = end - ptr;
1280 sparta_assert(rem <= (int32_t)sizeof(FillT) && rem >= 0,
1281 "fillWith_ remainder size was 0x" << std::hex << rem
1282 << " ptr=" << (void*)ptr << " end=" << (const void*)end
1283 << " sizeof(FillT)=" << sizeof(FillT));
1284 sparta_assert(ptr != nullptr,
1285 "Somehow encountered a null pointer during arithmetic based on a pointer at " << (void*) buf);
1286
1287 if(rem > 0) {
1288 // Remaining size is 1,2, or 4 bytes and rem fill value is same size or
1289 // smaller. Just copy what will fit
1290
1291 memcpy(static_cast<void*>(ptr), static_cast<const void*>(&shifted_fill), rem);
1292 }
1293 }
1294
1307 void dumpLayout_(std::ostream& o,
1308 SegmentList& sorted,
1309 offset_type last_line_off,
1310 offset_type last_end,
1311 bool show_line_nums) const {
1312
1313 // Print first line start
1314 if(show_line_nums){
1315 o << 'x' << std::hex << std::setw(5) << std::right << (last_end & line_mask_) << ": " << std::dec;
1316 }else{
1317 o << " \": "; // no line num (implies same as above)
1318 }
1319
1320 SegmentList::const_iterator it;
1321 std::vector<ArchDataSegment*> nestings;
1322 for(it = sorted.begin(); it != sorted.end(); ++it){
1323 offset_type off = (*it)->getOffset();
1324
1325 // End line of offset moved to next
1326 if(last_line_off != (off & line_mask_)){
1327 // Print skipped bytes at end of the line
1328 dumpSkippedBytes_(o, (off & line_mask_) - last_end, true, true);
1329 o << std::endl;
1330
1331 // Handle nestings
1332 if(nestings.size() > 0){
1333 dumpLayout_(o, nestings, last_line_off, last_line_off, false);
1334 }
1335 nestings.clear();
1336
1337 // Print new line start
1338 if(show_line_nums){
1339 o << 'x' << std::hex << std::setw(5) << std::right << (last_end & line_mask_) << ": " << std::dec;
1340 }else{
1341 o << " \": "; // no line num (implies same as above)
1342 }
1343
1344 last_line_off = (off & line_mask_);
1345 last_end = last_line_off;
1346 }
1347
1348 if(off < last_end){
1349 nestings.push_back(*it);
1350 continue; // Do not print, duplicate (nested)
1351 }else{
1352 // Print skipped bytes betweeen last seg and this
1353 offset_type jump = off - last_end;
1354 dumpSkippedBytes_(o, jump, false, false);
1355 }
1356
1357 offset_type size = (*it)->getLayoutSize();
1358 if(size == 1){
1359 o << "|"; // Only 1B
1360 }else if(size == 2){
1361 o << "|2"; // Only 2B
1362 }else{
1363 o << "|";
1364 for(offset_type i=1; i<size; ++i){
1365 if(i == (offset_type)(size/2)){
1366 o << std::left << std::dec;
1367 if(size >= 1000){
1368 o << std::setw(4) << size; // 4-char size
1369 }else if(size >= 100){
1370 o << std::setw(3) << size; // 3-char size
1371 }else if(size >= 10){
1372 o << std::setw(2) << size; // 2-char size
1373 }else{
1374 o << std::setw(1) << size; // 1-char size
1375 }
1376 }else{
1377 if(size >= 1000 && (i > (size/2)+1 && i < (size/2)+3)){
1378 // More than 4 digits printed for size. Skip
1379 }else if(size >= 100 && (i > (size/2)+1 && i < (size/2)+2)){
1380 // Print nothing. 3-digit size was just printed
1381 }else if(size >= 10 && i == (size/2)+1){
1382 // Print nothing. 2-digit size was just printed
1383 }else{
1384 o << '-'; // 1 char per byte.
1385 }
1386 }
1387 } // for( ... size ... )
1388 } // else // if(size == 1)
1389
1390 last_end = off + size;
1391 }
1392
1393 // Complete final line
1394 if((last_end & ~line_mask_) == 0){
1395 o << '|' << std::endl;
1396 }else{
1397 // Print skipped bytes at end of the line
1398 offset_type leftover = line_size_ - (last_end & ~line_mask_);
1399 dumpSkippedBytes_(o, leftover, true, true);
1400 o << std::endl;
1401 }
1402
1403 // Handle nestings on final line
1404 if(nestings.size() > 0){
1405 dumpLayout_(o, nestings, last_line_off, last_line_off, false);
1406 }
1407 nestings.clear();
1408 }
1409
1421 void dumpSkippedBytes_(std::ostream& o,
1422 offset_type num,
1423 bool condense,
1424 bool end_row) const {
1425 if(num == 0){
1426 // Print nothing yet
1427 }else if(num == 1){
1428 o << '/';
1429 }else if(num <= 16 || !condense){
1430 o << '|';
1431 for(offset_type i=1; i < num; ++i){
1432 //o << "+";
1433 o << " ";
1434 }
1435 }else{
1436 //o << "|+++" << std::dec << num << " ";
1437 o << "|+ " << std::dec << num << " ";
1438 }
1439
1440 if(end_row){
1441 o << '|';
1442 }
1443 }
1444
1451 Line* allocateLine_(line_idx_type idx) {
1452 if(idx * line_size_ > size_){
1453 throw SpartaException("Cannot allocate Line at idx ")
1454 << idx
1455 << " because idx*line_size is 0x"
1456 << std::hex << (idx * line_size_)
1457 << "and the current ArchData size is only 0x" << size_;
1458 }
1459
1460 // This test introduces a bit of overhead and should be removed
1461#ifndef NDEBUG
1462 //if(line_map_.find(idx) != line_map_.end()){
1463 if(line_map_.find(idx) != nullptr){
1464 throw SpartaException("Line is already allocated at index ") << idx;
1465 }
1466#endif // NDEBUG
1467
1468 // Allocate infinite-length lines
1469 if(0 == line_size_){
1470 if(idx != 0){
1471 throw SpartaException("Cannot allocate a line at index other than 0 when ArchData line size is 0 (infinite)");
1472 }
1473 sparta_assert(line_map_.size() == 0); // Cannot yet have a line
1474
1475 Line* ln = new Line(0, 0, size_, initial_, initial_val_size_);
1476 //lines_.push_back(ln);
1477 line_map_[0] = ln;
1478 return ln;
1479 }
1480
1481 // Allocate finite-length lines
1482 offset_type ln_off = getLineOffset(idx);
1483
1484 // Always use the full line size instead of trying to compute the
1485 // bytes leftover. When a line is being allocated, we may not know
1486 // the full size.
1487 Line* ln = new Line(idx, ln_off, line_size_, initial_, initial_val_size_);
1488 //LineList::iterator lnitr = lines_.begin();
1489 //for(; lnitr != lines_.end(); ++lnitr){
1490 // if((*lnitr)->getIdx() > idx){
1491 // break;
1492 // }
1493 //}
1494 //lines_.insert(lnitr, ln);
1495 line_map_[idx] = ln;
1496 return ln;
1497 }
1498
1499
1513 void placeSegment_(ArchDataSegment* seg,
1514 LayoutHelperMap& helper_map,
1515 uint32_t depth=0) {
1516
1517 sparta_assert(seg != 0); // Segment should never be NULL
1518
1519 if(seg->isPlaced()){
1520 /*
1521 for(uint32_t i=0; i<depth; ++i){ std::cout << " "; }
1522 std::cout << "seg " << std::dec << seg->getLayoutID() << " is already placed @ 0x"
1523 << std::hex << seg->getOffset() << std::dec << std::endl;
1524 */
1525 return; // Done
1526 }
1527
1528
1529 offset_type placement = 0; // Where to place the segment in the whole ArchData (calculated below)
1530 const offset_type size = seg->getLayoutSize(); // Size of the segment
1531 LayoutHelperMap::iterator it;
1532
1533 if(seg->getSubsetOf() != ArchDataSegment::INVALID_ID){
1534 // Place within another segment (subset)
1535
1536 ArchDataSegment::ident_type sub_of = seg->getSubsetOf();
1537
1538 //for(uint32_t i=0; i<depth; ++i){ std::cout << " "; }
1539 //std::cout << "seg " << std::dec << seg->getLayoutID() << " is subset @ +0x"
1540 // << std::hex << seg->getSubsetOffset() << "; attempting to place base: "
1541 // << std::dec << sub_of << std::endl;
1542
1543 // Find parent
1544 it = helper_map.find(sub_of);
1545 if(it == helper_map.end()){
1546 throw SpartaException("A Segment with identifier ")
1547 << seg->getLayoutID() << " claimed to be a "
1548 << "subset of Segment with identifier "
1549 << sub_of << ", which does not exist in this ArchData";
1550 }
1551 ArchDataSegment* parent_seg = it->second;
1552 sparta_assert(parent_seg->getLayoutID() == sub_of); // Parent must match expected ID
1553
1554 // Place parent first
1555 placeSegment_(it->second, helper_map, depth+1);
1556 sparta_assert(parent_seg->isPlaced()); // Parent must be placed at this point
1557
1558 if(seg->getLayoutSize() + seg->getSubsetOffset() > parent_seg->getLayoutSize()){
1559 throw SpartaException("Segment id=")
1560 << seg->getLayoutID() << " had size 0x" << std::hex << seg->getLayoutSize()
1561 << " and subset offset 0x" << std::hex << seg->getSubsetOffset()
1562 << " which makes it larger than the parent id=" << std::dec
1563 << parent_seg->getLayoutID() << " with size " << std::hex
1564 << parent_seg->getLayoutSize() << " of which it is a child";
1565 }
1566 placement = parent_seg->getOffset() + seg->getSubsetOffset();
1567 }else{
1568 // Place at the root (not subset)
1569
1570 // Word-alignment is probably important for reinterpret casting
1571 // Pad to the next word. size_ refers to ArchData size and offset of
1572 // next data.
1573 // Note that this only applies to segments placed at the root.
1574 if(size_ % HOST_INT_SIZE != 0){
1575 offset_type delta = HOST_INT_SIZE - (size_ % HOST_INT_SIZE);
1576 layout_padding_waste_ += delta;
1577 size_ += delta;
1578 }
1579
1580 // Ensure there is an ArchData line of appropriate size.
1581 // NOTE that segments CANNOT span lines.
1582 offset_type start_line_addr = size_ & line_mask_;
1583 offset_type end_line_addr = (size_ + size - 1) & line_mask_;
1584 if(start_line_addr != end_line_addr){
1585 // Needs to be moved to the next line
1586 sparta_assert(end_line_addr > start_line_addr); // Cannot allow overflow of offset_type. TODO: Test overflow case?
1587
1588 offset_type next = (size_ & line_mask_) + line_size_; // start of next line
1589 layout_line_waste_ += next - size_; // Waste some bytes
1590
1591 // std::cout << "Exceeds lines at offset 0x" << std::hex << size_ << std::dec << " + " << size << " B" << std::endl;
1592
1593 size_ = next;
1594 //lines_.push_back(0); // NULL pointer
1595 ++num_lines_laid_out_;
1596 }else if(start_line_addr >= num_lines_laid_out_ * line_size_){//(lines_.size() * line_size_)){
1597 // Starts naturally on a new line
1598 sparta_assert((size_ & ~line_mask_) == 0); // Must start at beginning of line
1599
1600 // std::cout << "Added next line at offset 0x" << std::hex << size_ << std::dec << std::endl;
1601
1602 //lines_.push_back(0); // NULL pointer
1603 ++num_lines_laid_out_;
1604 }
1605
1606 placement = size_;
1607 size_ += size; // Increase size so that the segment can be placed.
1608 sparta_assert((placement % HOST_INT_SIZE) == 0); // Just to be sure
1609 }
1610
1611 seg->place(placement);
1612
1614 }
1615
1616 private:
1617
1622 TreeNode *owner_node_ = nullptr;
1623
1627 offset_type line_size_;
1628
1632 const uint64_t initial_;
1633
1637 const uint32_t initial_val_size_;
1638
1642 offset_type line_lsb_;
1643
1647 offset_type line_mask_;
1648
1652 uint32_t num_lines_laid_out_;
1653
1657 LineMap line_map_;
1658
1662 SegmentList seg_list_;
1663
1667 offset_type size_;
1668
1672 bool is_laid_out_;
1673
1677 uint32_t layout_padding_waste_;
1678
1683 uint32_t layout_line_waste_;
1684
1688 bool can_free_lines_;
1689
1694 static std::vector<const ArchData*> *all_archdatas_;
1695
1696 }; // class ArchData
1697
1698} // namespace sparta
1699
1700
1702#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
bool containsAddress(offset_type offset) const noexcept
Determines if this ArchData contains a byte for the specified address.
Definition ArchData.hpp:880
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:826
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.
std::vector< ArchDataSegment * > SegmentList
ArchData line index.
Definition ArchData.hpp:60
line_idx_type getNumAllocatedLines() const
Gets the number of lines with allocated data;.
Definition ArchData.hpp:803
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:579
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...
void clean()
Deletes (if possible) data held within the ArchData, restoring it to a 'clean-state'....
Definition ArchData.hpp:756
line_idx_type getLineIndex(offset_type offset) const
Gets Index of a line containing the specified offset.
Definition ArchData.hpp:813
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:603
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.
std::vector< ArchDataSegment * > LayoutHelperVector
Vector of ArchDataSegment pointers.
Definition ArchData.hpp:79
void registerSegment(ArchDataSegment *seg)
All constructed segments must register themselves through this method to be laid out within the ArchD...
Definition ArchData.hpp:551
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...
void layoutRange(offset_type size)
Lays out the archdata to contain a range of addresses without specifying any segments.
Definition ArchData.hpp:651
void reset()
Cleans the ArchData (see clean) then applies all initial values through ArchDataSegment::writeInitial...
Definition ArchData.hpp:785
const LineMap & getLineMap() const
Only const access is allowed to internal map.
Definition ArchData.hpp:735
bool canFreeLines() const
Indicates whether this ArchData can free its lines after allocating them. Otherwise,...
Definition ArchData.hpp:839
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
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:933
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:863
offset_type line_idx_type
Represents offsets into this ArchData.
Definition ArchData.hpp:59
std::list< Line * > LineList
List of ArchDataSegment.
Definition ArchData.hpp:62
const Line * tryGetLine(offset_type offset) const
Gets the line associated with this offset only if it already exists.
Definition ArchData.hpp:714
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:582
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:465
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:913
TieredMap< line_idx_type, Line * > LineMap
List of Line pointers.
Definition ArchData.hpp:63
offset_type getLineSize() const
Gets the size of a line within this ArchData instance.
Definition ArchData.hpp:795
void save(StorageT &out)
Writes checkpointing data from this ArchData to a stream.
Definition ArchData.hpp:980
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:680
void checkDataSize(offset_type size) const
Checks to see that the size of the data is a valid access size.
Definition ArchData.hpp:891
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
uint64_t getNumTiers() const
Number of tiers used by this map to represent the entire space containing the largest key added....
std::pair< const line_idx_type, Line * > pair_t
Definition TieredMap.hpp:52
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.
std::enable_if< std::is_same< boost::mpl::int_< BO >, boost::mpl::int_< BE > >::value, T >::type reorder(const T &t)
bool isPowerOf2(uint64_t x)
Determines if input is 0 or a power of 2.
Definition Utils.hpp:153