Program memory cache
This commit is contained in:
@@ -3,6 +3,7 @@ target_sources(
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TargetDescription/TargetDescriptionFile.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TargetRegister.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TargetMemoryCache.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AVR/AVR8/Avr8.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AVR/AVR8/Avr8TargetConfig.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AVR/AVR8/PhysicalInterface.cpp
|
||||
|
||||
125
src/Targets/TargetMemoryCache.cpp
Normal file
125
src/Targets/TargetMemoryCache.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
#include "TargetMemoryCache.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace Targets
|
||||
{
|
||||
TargetMemoryCache::TargetMemoryCache(const TargetMemoryDescriptor& memoryDescriptor)
|
||||
: memoryDescriptor(memoryDescriptor)
|
||||
, data(TargetMemoryBuffer(memoryDescriptor.size(), 0x00))
|
||||
{}
|
||||
|
||||
TargetMemoryBuffer TargetMemoryCache::fetch(TargetMemoryAddress startAddress, TargetMemorySize bytes) const {
|
||||
const auto startIndex = startAddress - this->memoryDescriptor.addressRange.startAddress;
|
||||
|
||||
if (
|
||||
startAddress < this->memoryDescriptor.addressRange.startAddress
|
||||
|| (startIndex + bytes) > this->data.size()
|
||||
) {
|
||||
throw Exceptions::Exception("Invalid cache access");
|
||||
}
|
||||
|
||||
return TargetMemoryBuffer(this->data.begin() + startIndex, this->data.begin() + startIndex + bytes);
|
||||
}
|
||||
|
||||
bool TargetMemoryCache::contains(TargetMemoryAddress startAddress, TargetMemorySize bytes) const {
|
||||
const auto intersectingSegmentIt = this->intersectingSegment(startAddress);
|
||||
|
||||
return
|
||||
intersectingSegmentIt != this->populatedSegments.end()
|
||||
&& intersectingSegmentIt->first <= startAddress
|
||||
&& intersectingSegmentIt->second >= (startAddress + bytes - 1);
|
||||
}
|
||||
|
||||
void TargetMemoryCache::insert(TargetMemoryAddress startAddress, const TargetMemoryBuffer& data) {
|
||||
const auto startIndex = startAddress - this->memoryDescriptor.addressRange.startAddress;
|
||||
|
||||
std::copy(data.begin(), data.end(), this->data.begin() + startIndex);
|
||||
|
||||
const auto endAddress = static_cast<Targets::TargetMemoryAddress>(startAddress + data.size() - 1);
|
||||
|
||||
const auto intersectingStartSegmentIt = this->intersectingSegment(startAddress);
|
||||
const auto intersectingEndSegmentIt = this->intersectingSegment(endAddress);
|
||||
|
||||
if (
|
||||
intersectingStartSegmentIt == this->populatedSegments.end()
|
||||
&& intersectingEndSegmentIt == this->populatedSegments.end()
|
||||
) {
|
||||
this->populatedSegments.insert(std::pair(startAddress, endAddress));
|
||||
return;
|
||||
}
|
||||
|
||||
if (intersectingStartSegmentIt == intersectingEndSegmentIt) {
|
||||
// We already have a populated segment containing this address range. Nothing to do here.
|
||||
return;
|
||||
}
|
||||
|
||||
auto newStartSegment = std::optional<decltype(this->populatedSegments)::value_type>();
|
||||
auto newEndSegment = std::optional<decltype(this->populatedSegments)::value_type>();
|
||||
|
||||
if (intersectingStartSegmentIt != this->populatedSegments.end()) {
|
||||
newStartSegment.emplace(intersectingStartSegmentIt->first, endAddress);
|
||||
this->populatedSegments.erase(intersectingStartSegmentIt);
|
||||
}
|
||||
|
||||
if (intersectingEndSegmentIt != this->populatedSegments.end()) {
|
||||
newEndSegment.emplace(startAddress, intersectingEndSegmentIt->second);
|
||||
this->populatedSegments.erase(intersectingEndSegmentIt);
|
||||
}
|
||||
|
||||
if (newStartSegment.has_value() && newEndSegment.has_value()) {
|
||||
// The two new segments overlap. Merge them into one and remove any segments between them
|
||||
newStartSegment.emplace(newStartSegment->first, newEndSegment->second);
|
||||
newEndSegment.reset();
|
||||
|
||||
for (
|
||||
auto segmentIt = this->populatedSegments.upper_bound(newStartSegment->first);
|
||||
segmentIt != this->populatedSegments.end();
|
||||
) {
|
||||
if (segmentIt->second > newStartSegment->second) {
|
||||
break;
|
||||
}
|
||||
|
||||
this->populatedSegments.erase(segmentIt++);
|
||||
}
|
||||
}
|
||||
|
||||
if (newStartSegment.has_value()) {
|
||||
this->populatedSegments.insert(*newStartSegment);
|
||||
}
|
||||
|
||||
if (newEndSegment.has_value()) {
|
||||
this->populatedSegments.insert(*newEndSegment);
|
||||
}
|
||||
}
|
||||
|
||||
void TargetMemoryCache::clear() {
|
||||
this->populatedSegments.clear();
|
||||
}
|
||||
|
||||
TargetMemoryCache::SegmentIt TargetMemoryCache::intersectingSegment(TargetMemoryAddress address) const {
|
||||
if (this->populatedSegments.empty()) {
|
||||
return this->populatedSegments.end();
|
||||
}
|
||||
|
||||
auto lowerBoundSegmentIt = this->populatedSegments.lower_bound(address);
|
||||
|
||||
if (lowerBoundSegmentIt != this->populatedSegments.end()) {
|
||||
if (lowerBoundSegmentIt->first != address && lowerBoundSegmentIt != this->populatedSegments.begin()) {
|
||||
--lowerBoundSegmentIt;
|
||||
}
|
||||
|
||||
return lowerBoundSegmentIt->first <= address && lowerBoundSegmentIt->second >= address
|
||||
? lowerBoundSegmentIt
|
||||
: this->populatedSegments.end();
|
||||
}
|
||||
|
||||
// All the segments precede the given address. If the last segment doesn't intersect, none of them will.
|
||||
const auto lastSegment = std::prev(this->populatedSegments.end());
|
||||
return lastSegment->first <= address && lastSegment->second >= address
|
||||
? lastSegment
|
||||
: this->populatedSegments.end();
|
||||
}
|
||||
}
|
||||
72
src/Targets/TargetMemoryCache.hpp
Normal file
72
src/Targets/TargetMemoryCache.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
|
||||
#include "TargetMemory.hpp"
|
||||
|
||||
namespace Targets
|
||||
{
|
||||
class TargetMemoryCache
|
||||
{
|
||||
public:
|
||||
TargetMemoryCache(const TargetMemoryDescriptor& memoryDescriptor);
|
||||
|
||||
/**
|
||||
* Fetches data from the cache.
|
||||
*
|
||||
* @param startAddress
|
||||
* @param bytes
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
TargetMemoryBuffer fetch(TargetMemoryAddress startAddress, TargetMemorySize bytes) const;
|
||||
|
||||
/**
|
||||
* Checks if the cache currently holds data within the given address range.
|
||||
*
|
||||
* @param startAddress
|
||||
* @param bytes
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
bool contains(TargetMemoryAddress startAddress, TargetMemorySize bytes) const;
|
||||
|
||||
/**
|
||||
* Inserts data into the cache and performs any necessary bookkeeping.
|
||||
*
|
||||
* @param startAddress
|
||||
* @param data
|
||||
*/
|
||||
void insert(TargetMemoryAddress startAddress, const TargetMemoryBuffer& data);
|
||||
|
||||
/**
|
||||
* Clears the cache.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
private:
|
||||
const TargetMemoryDescriptor& memoryDescriptor;
|
||||
TargetMemoryBuffer data;
|
||||
|
||||
/**
|
||||
* A populated segment is just an address range in the cache that we know we've populated.
|
||||
*
|
||||
* populatedSegments::value_type::first = The start address (inclusive) of the populated range
|
||||
* populatedSegments::value_type::second = The end address (inclusive) of the populated range
|
||||
*/
|
||||
std::map<TargetMemoryAddress, TargetMemoryAddress> populatedSegments = {};
|
||||
|
||||
/**
|
||||
* Finds the segment that intersects with the given address. Segments cannot overlap, so only one segment can
|
||||
* intersect with the given address, at any given time.
|
||||
*
|
||||
* @param address
|
||||
*
|
||||
* @return
|
||||
* An iterator to the intersecting segment, or populatedSegments::end() if none is found.
|
||||
*/
|
||||
using SegmentIt = decltype(TargetMemoryCache::populatedSegments)::const_iterator;
|
||||
SegmentIt intersectingSegment(TargetMemoryAddress address) const;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user