mirror of
https://github.com/YosysHQ/yosys
synced 2025-07-24 13:18:56 +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]);
|
||||
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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue