1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-30 00:00:11 +01:00
VILLASnode/fpga/include/villas/memory_manager.hpp
Daniel Krebs 5242b87e4c lib/memory: rework allocators to make them extensible and more abstract
This is change renders memory allocators only dependend on an address
space id that they are managing, allowing easy implementation of other
algorithms and instantiation in memory IP blocks.
2018-04-13 15:35:41 +02:00

228 lines
6.7 KiB
C++

#pragma once
#include <cstdint>
#include <string>
#include <map>
#include <unistd.h>
#include "log.hpp"
#include "directed_graph.hpp"
namespace villas {
/**
* @brief Translation between a local (master) to a foreign (slave) address space
*
* Memory translations can be chained together using the `+=` operator which is
* used internally by the MemoryManager to compute a translation through
* multiple hops (memory mappings).
*/
class MemoryTranslation {
public:
/**
* @brief MemoryTranslation
* @param src Base address of local address space
* @param dst Base address of foreign address space
* @param size Size of "memory window"
*/
MemoryTranslation(uintptr_t src, uintptr_t dst, size_t size) :
src(src), dst(dst), size(size) {}
uintptr_t
getLocalAddr(uintptr_t addrInForeignAddrSpace) const;
uintptr_t
getForeignAddr(uintptr_t addrInLocalAddrSpace) const;
size_t
getSize() const
{ return size; }
friend std::ostream&
operator<< (std::ostream& stream, const MemoryTranslation& translation)
{
return stream << std::hex
<< "(src=0x" << translation.src
<< ", dst=0x" << translation.dst
<< ", size=0x" << translation.size
<< ")";
}
/// Merge two MemoryTranslations together
MemoryTranslation& operator+=(const MemoryTranslation& other);
private:
uintptr_t src; ///< Base address of local address space
uintptr_t dst; ///< Base address of foreign address space
size_t size; ///< Size of "memory window"
};
/**
* @brief Global memory manager to resolve addresses across address spaces
*
* Every entity in the system has to register its (master) address space and
* create mappings to other (slave) address spaces that it can access. A
* directed graph is then constructed which allows to traverse addresses spaces
* through multiple mappings and resolve addresses through this "tunnel" of
* memory mappings.
*/
class MemoryManager {
private:
// This is a singleton, so private constructor ...
MemoryManager() :
memoryGraph("MemoryGraph"),
logger(loggerGetOrCreate("MemoryManager")) {}
// ... and no copying or assigning
MemoryManager(const MemoryManager&) = delete;
MemoryManager& operator=(const MemoryManager&) = delete;
/**
* @brief Custom edge in memory graph representing a memory mapping
*
* A memory mapping maps from one address space into another and can only be
* traversed in the forward direction which reflects the nature of real
* memory mappings.
*
* Implementation Notes:
* The member #src is the address in the "from" address space, where the
* destination address space is mapped. The member #dest is the address in
* the destination address space, where the mapping points to. Often, #dest
* will be zero for mappings to hardware, but consider the example when
* mapping FPGA to application memory:
* The application allocates a block 1kB at address 0x843001000 in its
* address space. The mapping would then have a #dest address of 0x843001000
* and a #size of 1024.
*/
class Mapping : public graph::Edge {
public:
std::string name; ///< Human-readable name
uintptr_t src; ///< Base address in "from" address space
uintptr_t dest; ///< Base address in "to" address space
size_t size; ///< Size of the mapping
friend std::ostream&
operator<< (std::ostream& stream, const Mapping& mapping)
{
return stream << static_cast<const Edge&>(mapping) << " = "
<< mapping.name
<< std::hex
<< "(src=0x" << mapping.src
<< ", dest=0x" << mapping.dest
<< ", size=0x" << mapping.size
<< ")";
}
};
/**
* @brief Custom vertex in memory graph representing an address space
*
* Since most information in the memory graph is stored in the edges (memory
* mappings), this is just a small extension to the default vertex. It only
* associates an additional string #name for human-readability.
*/
class AddressSpace : public graph::Vertex {
public:
std::string name; ///< Human-readable name
friend std::ostream&
operator<< (std::ostream& stream, const AddressSpace& addrSpace)
{
return stream << static_cast<const Vertex&>(addrSpace) << " = "
<< addrSpace.name;
}
};
/// Memory graph with custom edges and vertices for address resolution
using MemoryGraph = graph::DirectedGraph<AddressSpace, Mapping>;
public:
using AddressSpaceId = MemoryGraph::VertexIdentifier;
using MappingId = MemoryGraph::EdgeIdentifier;
/// Get singleton instance
static MemoryManager&
get();
AddressSpaceId
getProcessAddressSpace()
{ return getOrCreateAddressSpace("villas-fpga"); }
AddressSpaceId
getProcessAddressSpaceMemoryBlock(const std::string& memoryBlock)
{ return getOrCreateAddressSpace(getSlaveAddrSpaceName("villas-fpga", memoryBlock)); }
AddressSpaceId
getOrCreateAddressSpace(std::string name);
void
removeAddressSpace(AddressSpaceId addrSpaceId)
{ memoryGraph.removeVertex(addrSpaceId); }
/// Create a default mapping
MappingId
createMapping(uintptr_t src, uintptr_t dest, size_t size,
const std::string& name,
AddressSpaceId fromAddrSpace,
AddressSpaceId toAddrSpace);
/// Add a mapping
///
/// Can be used to derive from Mapping in order to implement custom
/// constructor/destructor.
MappingId
addMapping(std::shared_ptr<Mapping> mapping,
AddressSpaceId fromAddrSpace,
AddressSpaceId toAddrSpace);
AddressSpaceId
findAddressSpace(const std::string& name);
MemoryTranslation
getTranslation(AddressSpaceId fromAddrSpaceId, AddressSpaceId toAddrSpaceId);
MemoryTranslation
getTranslationFromProcess(AddressSpaceId foreignAddrSpaceId)
{ return getTranslation(getProcessAddressSpace(), foreignAddrSpaceId); }
static std::string
getSlaveAddrSpaceName(const std::string& ipInstance, const std::string& memoryBlock)
{ return ipInstance + "/" + memoryBlock; }
static std::string
getMasterAddrSpaceName(const std::string& ipInstance, const std::string& busInterface)
{ return ipInstance + ":" + busInterface; }
void
dump()
{ memoryGraph.dump(); }
private:
/// Convert a Mapping to MemoryTranslation for calculations
static MemoryTranslation
getTranslationFromMapping(const Mapping& mapping)
{ return MemoryTranslation(mapping.src, mapping.dest, mapping.size); }
private:
/// Directed graph that stores address spaces and memory mappings
MemoryGraph memoryGraph;
/// Cache mapping of names to address space ids for fast lookup
std::map<std::string, AddressSpaceId> addrSpaceLookup;
/// Logger for universal access in this class
SpdLogger logger;
/// Static pointer to global instance, because this is a singleton
static MemoryManager* instance;
};
} // namespace villas