mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-03 21:09:12 +00:00 
			
		
		
		
	add MemContents class to mem.h
This commit is contained in:
		
							parent
							
								
									6d329e142d
								
							
						
					
					
						commit
						12a31a4418
					
				
					 2 changed files with 332 additions and 0 deletions
				
			
		
							
								
								
									
										216
									
								
								kernel/mem.cc
									
										
									
									
									
								
							
							
						
						
									
										216
									
								
								kernel/mem.cc
									
										
									
									
									
								
							| 
						 | 
					@ -1679,3 +1679,219 @@ SigSpec MemWr::decompress_en(const std::vector<int> &swizzle, SigSpec sig) {
 | 
				
			||||||
		res.append(sig[i]);
 | 
							res.append(sig[i]);
 | 
				
			||||||
	return res;
 | 
						return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using addr_t = MemContents::addr_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MemContents::MemContents(Mem *mem) :
 | 
				
			||||||
 | 
						MemContents(ceil_log2(mem->size), mem->width)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						for(const auto &init : mem->inits) {
 | 
				
			||||||
 | 
							if(init.en.is_fully_zero()) continue;
 | 
				
			||||||
 | 
							log_assert(init.en.size() == _data_width);
 | 
				
			||||||
 | 
							if(init.en.is_fully_ones())
 | 
				
			||||||
 | 
								insert_concatenated(init.addr.as_int(), init.data);
 | 
				
			||||||
 | 
							else {
 | 
				
			||||||
 | 
								// TODO: this case could be handled more efficiently by adding
 | 
				
			||||||
 | 
								// a flag to reserve_range that tells it to preserve
 | 
				
			||||||
 | 
								// previous contents
 | 
				
			||||||
 | 
								addr_t addr = init.addr.as_int();
 | 
				
			||||||
 | 
								addr_t words = init.data.size() / _data_width;
 | 
				
			||||||
 | 
								RTLIL::Const data = init.data;
 | 
				
			||||||
 | 
								log_assert(data.size() % _data_width == 0);
 | 
				
			||||||
 | 
								for(addr_t i = 0; i < words; i++) {
 | 
				
			||||||
 | 
									RTLIL::Const previous = (*this)[addr + i];
 | 
				
			||||||
 | 
									for(int j = 0; j < _data_width; j++)
 | 
				
			||||||
 | 
										if(init.en[j] != State::S1)
 | 
				
			||||||
 | 
											data[_data_width * i + j] = previous[j];
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								insert_concatenated(init.addr.as_int(), data);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MemContents::iterator & MemContents::iterator::operator++() {
 | 
				
			||||||
 | 
						auto it = _memory->_values.upper_bound(_addr);
 | 
				
			||||||
 | 
						if(it == _memory->_values.end()) {
 | 
				
			||||||
 | 
							_memory = nullptr;
 | 
				
			||||||
 | 
							_addr = ~(addr_t) 0;
 | 
				
			||||||
 | 
						} else
 | 
				
			||||||
 | 
							_addr = it->first;
 | 
				
			||||||
 | 
						return *this;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MemContents::check() {
 | 
				
			||||||
 | 
						log_assert(_addr_width > 0 && _addr_width < (int)sizeof(addr_t) * 8);
 | 
				
			||||||
 | 
						log_assert(_data_width > 0);
 | 
				
			||||||
 | 
						log_assert(_default_value.size() == _data_width);
 | 
				
			||||||
 | 
						if(_values.empty()) return;
 | 
				
			||||||
 | 
						auto it = _values.begin();
 | 
				
			||||||
 | 
						for(;;) {
 | 
				
			||||||
 | 
							log_assert(!it->second.empty());
 | 
				
			||||||
 | 
							log_assert(it->second.size() % _data_width == 0);
 | 
				
			||||||
 | 
							auto end1 = _range_end(it);
 | 
				
			||||||
 | 
							log_assert(_range_begin(it) < (1<<_addr_width));
 | 
				
			||||||
 | 
							log_assert(end1 <= (1<<_addr_width));
 | 
				
			||||||
 | 
							if(++it == _values.end())
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							// check that ranges neither overlap nor touch
 | 
				
			||||||
 | 
							log_assert(_range_begin(it) > end1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool MemContents::_range_contains(std::map<addr_t, RTLIL::Const>::iterator it, addr_t addr) const {
 | 
				
			||||||
 | 
						// if addr < begin, the subtraction will overflow, and the comparison will always fail
 | 
				
			||||||
 | 
						// (since we have an invariant that begin + size <= 2^(addr_t bits))
 | 
				
			||||||
 | 
						return it != _values.end() && addr - _range_begin(it) < _range_size(it);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool MemContents::_range_contains(std::map<addr_t, RTLIL::Const>::iterator it, addr_t begin_addr, addr_t end_addr) const {
 | 
				
			||||||
 | 
						// note that we assume begin_addr <= end_addr
 | 
				
			||||||
 | 
						return it != _values.end() && _range_begin(it) <= begin_addr && end_addr - _range_begin(it) <= _range_size(it);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool MemContents::_range_overlaps(std::map<addr_t, RTLIL::Const>::iterator it, addr_t begin_addr, addr_t end_addr) const {
 | 
				
			||||||
 | 
						if(it == _values.end() || begin_addr >= end_addr)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						auto top1 = _range_end(it) - 1;
 | 
				
			||||||
 | 
						auto top2 = end_addr - 1;
 | 
				
			||||||
 | 
						return !(top1 < begin_addr || top2 < _range_begin(it));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::map<addr_t, RTLIL::Const>::iterator MemContents::_range_at(addr_t addr) const {
 | 
				
			||||||
 | 
						// allow addr == 1<<_addr_width (which will just return end())
 | 
				
			||||||
 | 
						log_assert(addr <= 1<<_addr_width);
 | 
				
			||||||
 | 
						// get the first range with base > addr
 | 
				
			||||||
 | 
						// (we use const_cast since map::iterators are only passed around internally and not exposed to the user
 | 
				
			||||||
 | 
						// and using map::iterator in both the const and non-const case simplifies the code a little,
 | 
				
			||||||
 | 
						// at the cost of having to be a little careful when implementing const methods)
 | 
				
			||||||
 | 
						auto it = const_cast<std::map<addr_t, RTLIL::Const> &>(_values).upper_bound(addr);
 | 
				
			||||||
 | 
						// if we get the very first range, all ranges are past the addr, so return the first one
 | 
				
			||||||
 | 
						if(it == _values.begin())
 | 
				
			||||||
 | 
							return it;
 | 
				
			||||||
 | 
						// otherwise, go back to the previous interval
 | 
				
			||||||
 | 
						// this must be the last interval with base <= addr
 | 
				
			||||||
 | 
						auto it_prev = std::next(it, -1);
 | 
				
			||||||
 | 
						if(_range_contains(it_prev, addr))
 | 
				
			||||||
 | 
							return it_prev;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return it;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RTLIL::Const MemContents::operator[](addr_t addr) const {
 | 
				
			||||||
 | 
						auto it = _range_at(addr);
 | 
				
			||||||
 | 
						if(_range_contains(it, addr))
 | 
				
			||||||
 | 
							return it->second.extract(_range_offset(it, addr), _data_width);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return _default_value;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					addr_t MemContents::count_range(addr_t begin_addr, addr_t end_addr) const {
 | 
				
			||||||
 | 
						addr_t count = 0;
 | 
				
			||||||
 | 
						for(auto it = _range_at(begin_addr); _range_overlaps(it, begin_addr, end_addr); it++) {
 | 
				
			||||||
 | 
							auto first = std::max(_range_begin(it), begin_addr);
 | 
				
			||||||
 | 
							auto last = std::min(_range_end(it), end_addr);
 | 
				
			||||||
 | 
							count += last - first;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return count;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MemContents::clear_range(addr_t begin_addr, addr_t end_addr) {
 | 
				
			||||||
 | 
						if(begin_addr >= end_addr) return;
 | 
				
			||||||
 | 
						// identify which ranges are affected by this operation
 | 
				
			||||||
 | 
						// the first iterator affected is the first one containing any addr >= begin_addr
 | 
				
			||||||
 | 
						auto begin_it = _range_at(begin_addr);
 | 
				
			||||||
 | 
						// the first iterator *not* affected is the first one with base addr > end_addr - 1
 | 
				
			||||||
 | 
						auto end_it = _values.upper_bound(end_addr - 1);
 | 
				
			||||||
 | 
						if(begin_it == end_it)
 | 
				
			||||||
 | 
							return; // nothing to do
 | 
				
			||||||
 | 
						// the last iterator affected is one before the first one not affected
 | 
				
			||||||
 | 
						auto last_it = std::next(end_it, -1);
 | 
				
			||||||
 | 
						// the first and last range may need to be truncated, the rest can just be deleted
 | 
				
			||||||
 | 
						// to handle the begin_it == last_it case correctly, do the end case first by inserting a new range past the end
 | 
				
			||||||
 | 
						if(_range_contains(last_it, end_addr - 1)) {
 | 
				
			||||||
 | 
							auto new_begin = end_addr;
 | 
				
			||||||
 | 
							auto end = _range_end(last_it);
 | 
				
			||||||
 | 
							// if there is data past the end address, preserve it by creating a new range
 | 
				
			||||||
 | 
							if(new_begin != end)
 | 
				
			||||||
 | 
								end_it = _values.emplace_hint(last_it, new_begin, last_it->second.extract(_range_offset(last_it, new_begin), (_range_end(last_it) - new_begin) * _data_width));
 | 
				
			||||||
 | 
							// the original range will either be truncated in the next if() block or deleted in the erase, so we can leave it untruncated
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if(_range_contains(begin_it, begin_addr)) {
 | 
				
			||||||
 | 
							auto new_end = begin_addr;
 | 
				
			||||||
 | 
							// if there is data before the start address, truncate but don't delete
 | 
				
			||||||
 | 
							if(new_end != begin_it->first) {
 | 
				
			||||||
 | 
								begin_it->second.extu(_range_offset(begin_it, new_end));
 | 
				
			||||||
 | 
								++begin_it;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// else: begin_it will be deleted
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_values.erase(begin_it, end_it);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::map<addr_t, RTLIL::Const>::iterator MemContents::_reserve_range(addr_t begin_addr, addr_t end_addr) {
 | 
				
			||||||
 | 
						if(begin_addr >= end_addr)
 | 
				
			||||||
 | 
							return _values.end(); // need a dummy value to return, end() is cheap
 | 
				
			||||||
 | 
						// find the first range containing any addr >= begin_addr - 1
 | 
				
			||||||
 | 
						auto lower_it = begin_addr == 0 ? _values.begin() : _range_at(begin_addr - 1);
 | 
				
			||||||
 | 
						// check if our range is already covered by a single range
 | 
				
			||||||
 | 
						// note that since ranges are not allowed to touch, if any range contains begin_addr, lower_it equals that range
 | 
				
			||||||
 | 
						if (_range_contains(lower_it, begin_addr, end_addr))
 | 
				
			||||||
 | 
							return lower_it;
 | 
				
			||||||
 | 
						// find the first range containing any addr >= end_addr
 | 
				
			||||||
 | 
						auto upper_it = _range_at(end_addr);
 | 
				
			||||||
 | 
						// check if either of the two ranges we just found touch our range
 | 
				
			||||||
 | 
						bool lower_touch = begin_addr > 0 && _range_contains(lower_it, begin_addr - 1);
 | 
				
			||||||
 | 
						bool upper_touch = _range_contains(upper_it, end_addr);
 | 
				
			||||||
 | 
						if (lower_touch && upper_touch) {
 | 
				
			||||||
 | 
							log_assert (lower_it != upper_it); // lower_it == upper_it should be excluded by the check above
 | 
				
			||||||
 | 
							// we have two different ranges touching at either end, we need to merge them
 | 
				
			||||||
 | 
							auto upper_end = _range_end(upper_it);
 | 
				
			||||||
 | 
							// make range bigger (maybe reserve here instead of resize?)
 | 
				
			||||||
 | 
							lower_it->second.bits.resize(_range_offset(lower_it, upper_end), State::Sx);
 | 
				
			||||||
 | 
							// copy only the data beyond our range
 | 
				
			||||||
 | 
							std::copy(_range_data(upper_it, end_addr), _range_data(upper_it, upper_end), _range_data(lower_it, end_addr));
 | 
				
			||||||
 | 
							// keep lower_it, but delete upper_it
 | 
				
			||||||
 | 
							_values.erase(std::next(lower_it), std::next(upper_it));
 | 
				
			||||||
 | 
							return lower_it;
 | 
				
			||||||
 | 
						} else if (lower_touch) {
 | 
				
			||||||
 | 
							// we have a range to the left, just make it bigger and delete any other that may exist.
 | 
				
			||||||
 | 
							lower_it->second.bits.resize(_range_offset(lower_it, end_addr), State::Sx);
 | 
				
			||||||
 | 
							// keep lower_it and upper_it
 | 
				
			||||||
 | 
							_values.erase(std::next(lower_it), upper_it);
 | 
				
			||||||
 | 
							return lower_it;
 | 
				
			||||||
 | 
						} else if (upper_touch) {
 | 
				
			||||||
 | 
							// we have a range to the right, we need to expand it
 | 
				
			||||||
 | 
							// since we need to erase and reinsert to a new address, steal the data
 | 
				
			||||||
 | 
							RTLIL::Const data = std::move(upper_it->second);
 | 
				
			||||||
 | 
							// note that begin_addr is not in upper_it, otherwise the whole range covered check would have tripped
 | 
				
			||||||
 | 
							data.bits.insert(data.bits.begin(), (_range_begin(upper_it) - begin_addr) * _data_width, State::Sx);
 | 
				
			||||||
 | 
							// delete lower_it and upper_it, then reinsert
 | 
				
			||||||
 | 
							_values.erase(lower_it, std::next(upper_it));
 | 
				
			||||||
 | 
							return _values.emplace(begin_addr, std::move(data)).first;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// no ranges are touching, so just delete all ranges in our range and allocate a new one
 | 
				
			||||||
 | 
							// could try to resize an existing range but not sure if that actually helps
 | 
				
			||||||
 | 
							_values.erase(lower_it, upper_it);
 | 
				
			||||||
 | 
							return _values.emplace(begin_addr, RTLIL::Const(State::Sx, (end_addr - begin_addr) * _data_width)).first;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MemContents::insert_concatenated(addr_t addr, RTLIL::Const const &values) {
 | 
				
			||||||
 | 
						addr_t words = (values.size() + _data_width - 1) / _data_width;
 | 
				
			||||||
 | 
						log_assert(addr < 1<<_addr_width);
 | 
				
			||||||
 | 
						log_assert(words <= (1<<_addr_width) - addr);
 | 
				
			||||||
 | 
						auto it = _reserve_range(addr, addr + words);
 | 
				
			||||||
 | 
						auto to_begin = _range_data(it, addr);
 | 
				
			||||||
 | 
						std::copy(values.bits.begin(), values.bits.end(), to_begin);
 | 
				
			||||||
 | 
						// if values is not word-aligned, fill any missing bits with 0
 | 
				
			||||||
 | 
						std::fill(to_begin + values.size(), to_begin + words * _data_width, State::S0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<State>::iterator MemContents::_range_write(std::vector<State>::iterator it, RTLIL::Const const &word) {
 | 
				
			||||||
 | 
						auto from_end = word.size() <= _data_width ? word.bits.end() : word.bits.begin() + _data_width;
 | 
				
			||||||
 | 
						auto to_end = std::copy(word.bits.begin(), from_end, it);
 | 
				
			||||||
 | 
						auto it_next = std::next(it, _data_width);
 | 
				
			||||||
 | 
						std::fill(to_end, it_next, State::S0);
 | 
				
			||||||
 | 
						return it_next;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										116
									
								
								kernel/mem.h
									
										
									
									
									
								
							
							
						
						
									
										116
									
								
								kernel/mem.h
									
										
									
									
									
								
							| 
						 | 
					@ -224,6 +224,122 @@ struct Mem : RTLIL::AttrObject {
 | 
				
			||||||
	Mem(Module *module, IdString memid, int width, int start_offset, int size) : module(module), memid(memid), packed(false), mem(nullptr), cell(nullptr), width(width), start_offset(start_offset), size(size) {}
 | 
						Mem(Module *module, IdString memid, int width, int start_offset, int size) : module(module), memid(memid), packed(false), mem(nullptr), cell(nullptr), width(width), start_offset(start_offset), size(size) {}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// this class is used for implementing operator-> on iterators that return values rather than references
 | 
				
			||||||
 | 
					// it's necessary because in C++ operator-> is called recursively until a raw pointer is obtained
 | 
				
			||||||
 | 
					template<class T>
 | 
				
			||||||
 | 
					struct arrow_proxy {
 | 
				
			||||||
 | 
						T v;
 | 
				
			||||||
 | 
						explicit arrow_proxy(T const & v) : v(v) {}
 | 
				
			||||||
 | 
						T* operator->() { return &v; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MemContents efficiently represents the contents of a potentially sparse memory by storing only those segments that are actually defined
 | 
				
			||||||
 | 
					class MemContents {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						class range; class iterator;
 | 
				
			||||||
 | 
						using addr_t = uint32_t;
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						// we ban _addr_width == sizeof(addr_t) * 8 because it adds too many cornercases
 | 
				
			||||||
 | 
						int _addr_width;
 | 
				
			||||||
 | 
						int _data_width;
 | 
				
			||||||
 | 
						RTLIL::Const _default_value;
 | 
				
			||||||
 | 
						// for each range, store the concatenation of the words at the start address
 | 
				
			||||||
 | 
						// invariants:
 | 
				
			||||||
 | 
						// - no overlapping or adjacent ranges
 | 
				
			||||||
 | 
						// - no empty ranges
 | 
				
			||||||
 | 
						// - all Consts are a multiple of the word size
 | 
				
			||||||
 | 
						std::map<addr_t, RTLIL::Const> _values;
 | 
				
			||||||
 | 
						// returns an iterator to the range containing addr, if it exists, or the first range past addr
 | 
				
			||||||
 | 
						std::map<addr_t, RTLIL::Const>::iterator _range_at(addr_t addr) const;
 | 
				
			||||||
 | 
						addr_t _range_size(std::map<addr_t, RTLIL::Const>::iterator it) const { return it->second.size() / _data_width; }
 | 
				
			||||||
 | 
						addr_t _range_begin(std::map<addr_t, RTLIL::Const>::iterator it) const { return it->first; }
 | 
				
			||||||
 | 
						addr_t _range_end(std::map<addr_t, RTLIL::Const>::iterator it) const { return _range_begin(it) + _range_size(it); }
 | 
				
			||||||
 | 
						// check if the iterator points to a range containing addr
 | 
				
			||||||
 | 
						bool _range_contains(std::map<addr_t, RTLIL::Const>::iterator it, addr_t addr) const;
 | 
				
			||||||
 | 
						// check if the iterator points to a range containing [begin_addr, end_addr). assumes end_addr >= begin_addr.
 | 
				
			||||||
 | 
						bool _range_contains(std::map<addr_t, RTLIL::Const>::iterator it, addr_t begin_addr, addr_t end_addr) const;
 | 
				
			||||||
 | 
						// check if the iterator points to a range overlapping with [begin_addr, end_addr)
 | 
				
			||||||
 | 
						bool _range_overlaps(std::map<addr_t, RTLIL::Const>::iterator it, addr_t begin_addr, addr_t end_addr) const;
 | 
				
			||||||
 | 
						// return the offset the addr would have in the range at `it`
 | 
				
			||||||
 | 
						size_t _range_offset(std::map<addr_t, RTLIL::Const>::iterator it, addr_t addr) const { return (addr - it->first) * _data_width; }
 | 
				
			||||||
 | 
						// assuming _range_contains(it, addr), return an iterator pointing to the data at addr
 | 
				
			||||||
 | 
						std::vector<State>::iterator _range_data(std::map<addr_t, RTLIL::Const>::iterator it, addr_t addr) { return it->second.bits.begin() + _range_offset(it, addr); }
 | 
				
			||||||
 | 
						// internal version of reserve_range that returns an iterator to the range
 | 
				
			||||||
 | 
						std::map<addr_t, RTLIL::Const>::iterator _reserve_range(addr_t begin_addr, addr_t end_addr);
 | 
				
			||||||
 | 
						// write a single word at addr, return iterator to next word
 | 
				
			||||||
 | 
						std::vector<State>::iterator _range_write(std::vector<State>::iterator it, RTLIL::Const const &data);
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						class range {
 | 
				
			||||||
 | 
							int _data_width;
 | 
				
			||||||
 | 
							addr_t _base;
 | 
				
			||||||
 | 
							RTLIL::Const const &_values;
 | 
				
			||||||
 | 
							friend class iterator;
 | 
				
			||||||
 | 
							range(int data_width, addr_t base, RTLIL::Const const &values)
 | 
				
			||||||
 | 
							: _data_width(data_width), _base(base), _values(values) {}
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
							addr_t base() const { return _base; }
 | 
				
			||||||
 | 
							addr_t size() const { return ((addr_t) _values.size()) / _data_width; }
 | 
				
			||||||
 | 
							addr_t limit() const { return _base + size(); }
 | 
				
			||||||
 | 
							RTLIL::Const const &concatenated() const { return _values; }
 | 
				
			||||||
 | 
							RTLIL::Const operator[](addr_t addr) const {
 | 
				
			||||||
 | 
								log_assert(addr - _base < size());
 | 
				
			||||||
 | 
								return _values.extract((addr - _base) * _data_width, _data_width);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							RTLIL::Const at_offset(addr_t offset) const { return (*this)[_base + offset]; }
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						class iterator {
 | 
				
			||||||
 | 
							MemContents const *_memory;
 | 
				
			||||||
 | 
							// storing addr instead of an iterator gives more well-defined behaviour under insertions/deletions
 | 
				
			||||||
 | 
							// use ~0 for end so that all end iterators compare the same
 | 
				
			||||||
 | 
							addr_t _addr;
 | 
				
			||||||
 | 
							friend class MemContents;
 | 
				
			||||||
 | 
							iterator(MemContents const *memory, addr_t addr) : _memory(memory), _addr(addr) {}
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
							using iterator_category = std::input_iterator_tag;
 | 
				
			||||||
 | 
							using value_type = range;
 | 
				
			||||||
 | 
							using pointer = arrow_proxy<range>;
 | 
				
			||||||
 | 
							using reference = range;
 | 
				
			||||||
 | 
							using difference_type = addr_t;
 | 
				
			||||||
 | 
							reference operator *() const { return range(_memory->_data_width, _addr, _memory->_values.at(_addr)); }
 | 
				
			||||||
 | 
							pointer operator->() const { return arrow_proxy<range>(**this); }
 | 
				
			||||||
 | 
							bool operator !=(iterator const &other) const { return _memory != other._memory || _addr != other._addr; }
 | 
				
			||||||
 | 
							iterator &operator++();
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						MemContents(int addr_width, int data_width, RTLIL::Const default_value)
 | 
				
			||||||
 | 
							: _addr_width(addr_width), _data_width(data_width)
 | 
				
			||||||
 | 
							, _default_value((default_value.extu(data_width), std::move(default_value)))
 | 
				
			||||||
 | 
						{ log_assert(_addr_width > 0 && _addr_width < (int)sizeof(addr_t) * 8); log_assert(_data_width > 0); }
 | 
				
			||||||
 | 
						MemContents(int addr_width, int data_width) : MemContents(addr_width, data_width, RTLIL::Const(State::Sx, data_width)) {}
 | 
				
			||||||
 | 
						explicit MemContents(Mem *mem);
 | 
				
			||||||
 | 
						int addr_width() const { return _addr_width; }
 | 
				
			||||||
 | 
						int data_width() const { return _data_width; }
 | 
				
			||||||
 | 
						RTLIL::Const const &default_value() const { return _default_value; }
 | 
				
			||||||
 | 
						// return the value at the address if it exists, the default_value of the memory otherwise. address must not exceed 2**addr_width.
 | 
				
			||||||
 | 
						RTLIL::Const operator [](addr_t addr) const;
 | 
				
			||||||
 | 
						// return the number of defined words in the range [begin_addr, end_addr)
 | 
				
			||||||
 | 
						addr_t count_range(addr_t begin_addr, addr_t end_addr) const;
 | 
				
			||||||
 | 
						// allocate memory for the range [begin_addr, end_addr), but leave the contents undefined.
 | 
				
			||||||
 | 
						void reserve_range(addr_t begin_addr, addr_t end_addr) { _reserve_range(begin_addr, end_addr); }
 | 
				
			||||||
 | 
						// insert multiple words (provided as a single concatenated RTLIL::Const) at the given address, overriding any previous assignment.
 | 
				
			||||||
 | 
						void insert_concatenated(addr_t addr, RTLIL::Const const &values);
 | 
				
			||||||
 | 
						// insert multiple words at the given address, overriding any previous assignment.
 | 
				
			||||||
 | 
						template<typename Iterator> void insert_range(addr_t addr, Iterator begin, Iterator end) {
 | 
				
			||||||
 | 
							auto words = end - begin;
 | 
				
			||||||
 | 
							log_assert(addr < 1<<_addr_width); log_assert(words <= (1<<_addr_width) - addr);
 | 
				
			||||||
 | 
							auto range = _reserve_range(addr, addr + words);
 | 
				
			||||||
 | 
							auto it = _range_data(range, addr);
 | 
				
			||||||
 | 
							for(; begin != end; ++begin)
 | 
				
			||||||
 | 
								it = _range_write(it, *begin);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// undefine all words in the range [begin_addr, end_addr)
 | 
				
			||||||
 | 
						void clear_range(addr_t begin_addr, addr_t end_addr);
 | 
				
			||||||
 | 
						// check invariants, abort if invariants failed
 | 
				
			||||||
 | 
						void check();
 | 
				
			||||||
 | 
						iterator end() const { return iterator(nullptr, ~(addr_t) 0); }
 | 
				
			||||||
 | 
						iterator begin() const { return _values.empty() ? end() : iterator(this, _values.begin()->first); }
 | 
				
			||||||
 | 
						bool empty() const { return _values.empty(); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
YOSYS_NAMESPACE_END
 | 
					YOSYS_NAMESPACE_END
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue