From ae2bd2a41d5a447e0871f23dcea14d89f72e3ad0 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Fri, 13 Apr 2018 15:24:20 +0200 Subject: [PATCH 1/3] lib/ip: alias type for memory block name and cache addres space IDs in IP --- fpga/include/villas/fpga/ip.hpp | 18 ++++++++++++++---- fpga/include/villas/fpga/ips/dma.hpp | 2 +- fpga/include/villas/fpga/ips/fifo.hpp | 2 +- fpga/include/villas/fpga/ips/intc.hpp | 2 +- fpga/include/villas/fpga/ips/switch.hpp | 2 +- fpga/include/villas/fpga/ips/timer.hpp | 2 +- fpga/lib/ip.cpp | 5 ++++- 7 files changed, 23 insertions(+), 10 deletions(-) diff --git a/fpga/include/villas/fpga/ip.hpp b/fpga/include/villas/fpga/ip.hpp index 28f9e14f6..f565aa4a2 100644 --- a/fpga/include/villas/fpga/ip.hpp +++ b/fpga/include/villas/fpga/ip.hpp @@ -127,8 +127,11 @@ public: virtual void dump(); protected: + /// Key-type for accessing maps addressTranslations and slaveAddressSpaces + using MemoryBlockName = std::string; + /// Each IP can declare via this function which memory blocks it requires - virtual std::list + virtual std::list getMemoryBlocks() const { return {}; } @@ -177,11 +180,15 @@ public: protected: uintptr_t - getBaseAddr(const std::string& block) const + getBaseAddr(const MemoryBlockName& block) const { return getLocalAddr(block, 0); } uintptr_t - getLocalAddr(const std::string& block, uintptr_t address) const; + getLocalAddr(const MemoryBlockName& block, uintptr_t address) const; + + MemoryManager::AddressSpaceId + getAddressSpaceId(const MemoryBlockName& block) const + { return slaveAddressSpaces.at(block); } InterruptController* getInterruptController(const std::string& interruptName) const; @@ -206,7 +213,10 @@ protected: std::map irqs; /// Cached translations from the process address space to each memory block - std::map addressTranslations; + std::map addressTranslations; + + /// Lookup for IP's slave address spaces (= memory blocks) + std::map slaveAddressSpaces; /// AXI bus master interfaces to access memory somewhere std::map busMasterInterfaces; diff --git a/fpga/include/villas/fpga/ips/dma.hpp b/fpga/include/villas/fpga/ips/dma.hpp index b5cd34f51..41a1fe115 100644 --- a/fpga/include/villas/fpga/ips/dma.hpp +++ b/fpga/include/villas/fpga/ips/dma.hpp @@ -84,7 +84,7 @@ private: // optional Scatter-Gather interface to access descriptors static constexpr char sgInterface[] = "M_AXI_SG"; - std::list getMemoryBlocks() const + std::list getMemoryBlocks() const { return { registerMemory }; } XAxiDma xDma; diff --git a/fpga/include/villas/fpga/ips/fifo.hpp b/fpga/include/villas/fpga/ips/fifo.hpp index 9d8528237..82fc3156a 100644 --- a/fpga/include/villas/fpga/ips/fifo.hpp +++ b/fpga/include/villas/fpga/ips/fifo.hpp @@ -54,7 +54,7 @@ private: static constexpr char axi4Memory[] = "Mem1"; static constexpr char irqName[] = "interrupt"; - std::list getMemoryBlocks() const + std::list getMemoryBlocks() const { return { registerMemory, axi4Memory }; } XLlFifo xFifo; diff --git a/fpga/include/villas/fpga/ips/intc.hpp b/fpga/include/villas/fpga/ips/intc.hpp index 7ccef8038..d8d3e5f86 100644 --- a/fpga/include/villas/fpga/ips/intc.hpp +++ b/fpga/include/villas/fpga/ips/intc.hpp @@ -64,7 +64,7 @@ private: static constexpr char registerMemory[] = "Reg"; - std::list getMemoryBlocks() const + std::list getMemoryBlocks() const { return { registerMemory }; } diff --git a/fpga/include/villas/fpga/ips/switch.hpp b/fpga/include/villas/fpga/ips/switch.hpp index 796cab711..587ae4b70 100644 --- a/fpga/include/villas/fpga/ips/switch.hpp +++ b/fpga/include/villas/fpga/ips/switch.hpp @@ -58,7 +58,7 @@ private: static constexpr char registerMemory[] = "Reg"; - std::list getMemoryBlocks() const + std::list getMemoryBlocks() const { return { registerMemory }; } struct Path { diff --git a/fpga/include/villas/fpga/ips/timer.hpp b/fpga/include/villas/fpga/ips/timer.hpp index 56231069b..fcf4d75ea 100644 --- a/fpga/include/villas/fpga/ips/timer.hpp +++ b/fpga/include/villas/fpga/ips/timer.hpp @@ -63,7 +63,7 @@ public: private: - std::list getMemoryBlocks() const + std::list getMemoryBlocks() const { return { registerMemory }; } static constexpr char irqName[] = "generateout0"; diff --git a/fpga/lib/ip.cpp b/fpga/lib/ip.cpp index d1d2d33a6..63cffdebc 100644 --- a/fpga/lib/ip.cpp +++ b/fpga/lib/ip.cpp @@ -281,6 +281,9 @@ IpCoreFactory::make(PCIeCard* card, json_t *json_ips) const auto addrSpaceId = MemoryManager::get().findAddressSpace(addrSpaceName); + // ... and save it in IP + ip->slaveAddressSpaces.emplace(memoryBlock, addrSpaceId); + // get the translation to the address space const auto& translation = MemoryManager::get().getTranslationFromProcess(addrSpaceId); @@ -341,7 +344,7 @@ IpCoreFactory::lookup(const Vlnv &vlnv) uintptr_t -IpCore::getLocalAddr(const std::string& block, uintptr_t address) const +IpCore::getLocalAddr(const MemoryBlockName& block, uintptr_t address) const { // throws exception if block not present auto& translation = addressTranslations.at(block); From 5242b87e4c28699dd025f54d581cff3c55cd5be7 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Fri, 13 Apr 2018 15:31:18 +0200 Subject: [PATCH 2/3] 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. --- fpga/include/villas/memory.hpp | 278 +++++++++++++++++-------- fpga/include/villas/memory_manager.hpp | 8 +- fpga/lib/memory.cpp | 131 +++++++++++- fpga/tests/dma.cpp | 7 +- 4 files changed, 325 insertions(+), 99 deletions(-) diff --git a/fpga/include/villas/memory.hpp b/fpga/include/villas/memory.hpp index 85dae02ea..8a1895b23 100644 --- a/fpga/include/villas/memory.hpp +++ b/fpga/include/villas/memory.hpp @@ -8,115 +8,221 @@ namespace villas { +/** + * @brief Basic memory block backed by an address space in the memory graph + * + * This is a generic representation of a chunk of memory in the system. It can + * reside anywhere and represent different types of memory. + */ class MemoryBlock { -protected: - MemoryBlock(MemoryManager::AddressSpaceId addrSpaceId, size_t size) : - addrSpaceId(addrSpaceId), size(size) {} - public: + using deallocator_fn = std::function; + + MemoryBlock(size_t offset, size_t size, MemoryManager::AddressSpaceId addrSpaceId) : + offset(offset), size(size), addrSpaceId(addrSpaceId) {} + MemoryManager::AddressSpaceId getAddrSpaceId() const { return addrSpaceId; } size_t getSize() const { return size; } -private: - MemoryManager::AddressSpaceId addrSpaceId; - size_t size; + size_t getOffset() const + { return offset; } + +protected: + size_t offset; ///< Offset (or address) inside address space + size_t size; ///< Size in bytes of this block + MemoryManager::AddressSpaceId addrSpaceId; ///< Identifier in memory graph }; -class MemoryAllocator { -}; - - -class HostRam : public MemoryAllocator { +/** + * @brief Wrapper for a MemoryBlock to access the underlying memory directly + * + * The underlying memory block has to be accessible for the current process, + * that means it has to be mapped accordingly and registered to the global + * memory graph. + * Furthermore, this wrapper can be owning the memory block when initialized + * with a moved unique pointer. Otherwise, it just stores a reference to the + * memory block and it's the users responsibility to take care that the memory + * block is valid. + */ +template +class MemoryAccessor { public: + using Type = T; + + // take ownership of the MemoryBlock + MemoryAccessor(std::unique_ptr mem) : + translation(MemoryManager::get().getTranslationFromProcess(mem->getAddrSpaceId())), + memoryBlock(std::move(mem)) {} + + // just act as an accessor, do not take ownership of MemoryBlock + MemoryAccessor(const MemoryBlock& mem) : + translation(MemoryManager::get().getTranslationFromProcess(mem.getAddrSpaceId())) {} + + + T& operator*() const { + return *reinterpret_cast(translation.getLocalAddr(0)); + } + + T& operator[](size_t idx) const { + const size_t offset = sizeof(T) * idx; + return *reinterpret_cast(translation.getLocalAddr(offset)); + } + + T* operator&() const { + return reinterpret_cast(translation.getLocalAddr(0)); + } + + T* operator->() const { + return reinterpret_cast(translation.getLocalAddr(0)); + } + + const MemoryBlock& + getMemoryBlock() const + { if(not memoryBlock) throw std::bad_alloc(); else return *memoryBlock; } + +private: + /// cached memory translation for fast access + MemoryTranslation translation; + + /// take the unique pointer in case user wants this class to have ownership + std::unique_ptr memoryBlock; +}; + + +/** + * @brief Base memory allocator + * + * Note the usage of CRTP idiom here to access methods of derived allocators. + * The concept is explained here at [1]. + * + * [1] https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern + */ +template +class BaseAllocator { +public: + /// memoryAddrSpaceId: memory that is managed by this allocator + BaseAllocator(MemoryManager::AddressSpaceId memoryAddrSpaceId) : + memoryAddrSpaceId(memoryAddrSpaceId) + { + // CRTP + derivedAlloc = static_cast(this); + logger = loggerGetOrCreate(derivedAlloc->getName()); + + // default deallocation callback + free = [&](MemoryBlock* mem) { + logger->warn("no free callback defined for addr space {}, not freeing", + mem->getAddrSpaceId()); + }; + } + + virtual std::unique_ptr + allocateBlock(size_t size) = 0; template - class MemoryBlockHostRam : public MemoryBlock { - friend class HostRam; - private: - MemoryBlockHostRam(void* addr, size_t size, MemoryManager::AddressSpaceId foreignAddrSpaceId) : - MemoryBlock(foreignAddrSpaceId, size), - addr(addr), - translation(MemoryManager::get().getTranslationFromProcess(foreignAddrSpaceId)) - {} - public: - using Type = T; - - MemoryBlockHostRam() = delete; - - T& operator*() { - return *reinterpret_cast(translation.getLocalAddr(0)); - } - - T& operator [](int idx) { - const size_t offset = sizeof(T) * idx; - return *reinterpret_cast(translation.getLocalAddr(offset)); - } - - T* operator &() const { - return reinterpret_cast(translation.getLocalAddr(0)); - } - - private: - // addr needed for freeing later - void* addr; - - // cached memory translation for fast access - MemoryTranslation translation; - }; - - template - static MemoryBlockHostRam + MemoryAccessor allocate(size_t num) { - /* Align to next bigger page size chunk */ - size_t length = num * sizeof(T); - if (length & size_t(0xFFF)) { - length += size_t(0x1000); - length &= size_t(~0xFFF); - } - - void* const addr = HostRam::allocate(length); - if(addr == nullptr) { - throw std::bad_alloc(); - } - - auto& mm = MemoryManager::get(); - - // assemble name for this block - std::stringstream name; - name << std::showbase << std::hex << reinterpret_cast(addr); - - auto blockAddrSpaceId = mm.getProcessAddressSpaceMemoryBlock(name.str()); - - // create mapping from VA space of process to this new block - mm.createMapping(reinterpret_cast(addr), 0, length, - "VA", - mm.getProcessAddressSpace(), - blockAddrSpaceId); - - // create object and corresponding address space in memory manager - return MemoryBlockHostRam(addr, length, blockAddrSpaceId); + const size_t size = num * sizeof(T); + auto mem = allocateBlock(size); + return MemoryAccessor(std::move(mem)); } - template - static inline bool - free(const MemoryBlockHostRam& block) +protected: + void insertMemoryBlock(const MemoryBlock& mem) { - // TODO: remove address space from memory manager - // TODO: how to prevent use after free? - return HostRam::free(block.addr, block.size); + auto& mm = MemoryManager::get(); + mm.createMapping(mem.getOffset(), 0, mem.getSize(), + derivedAlloc->getName(), + memoryAddrSpaceId, + mem.getAddrSpaceId()); } + void removeMemoryBlock(const MemoryBlock& mem) + { + // this will also remove any mapping to and from the memory block + auto& mm = MemoryManager::get(); + mm.removeAddressSpace(mem.getAddrSpaceId()); + } + + MemoryManager::AddressSpaceId getAddrSpaceId() const + { return memoryAddrSpaceId; } + +protected: + MemoryBlock::deallocator_fn free; + SpdLogger logger; + private: - static void* - allocate(size_t length, int flags = 0); + MemoryManager::AddressSpaceId memoryAddrSpaceId; + DerivedAllocator* derivedAlloc; +}; - static bool - free(void*, size_t length); + +/** + * @brief Linear memory allocator + * + * This is the simplest kind of allocator. The idea is to keep a pointer at the + * first memory address of your memory chunk and move it every time an + * allocation is done. Due to its simplicity, this allocator doesn't allow + * specific positions of memory to be freed. Usually, all memory is freed + * together. + */ +class LinearAllocator : public BaseAllocator { +public: + LinearAllocator(MemoryManager::AddressSpaceId memoryAddrSpaceId, + size_t memorySize, + size_t internalOffset = 0); + + size_t getAvailableMemory() const + { return memorySize - nextFreeAddress; } + + std::string getName() const; + + std::unique_ptr + allocateBlock(size_t size); + +private: + static constexpr size_t alignBytes = sizeof(uintptr_t); + static constexpr size_t alignMask = alignBytes - 1; + + size_t getAlignmentPadding(uintptr_t addr) const + { return (alignBytes - (addr & alignMask)) & alignMask; } + +private: + size_t nextFreeAddress; ///< next chunk will be allocated here + size_t memorySize; ///< total size of managed memory + size_t internalOffset; ///< offset in address space (usually 0) +}; + + +/** + * @brief Wrapper around mmap() to create villas memory blocks + * + * This class simply wraps around mmap() and munmap() to allocate memory in the + * host memory via the OS. + */ +class HostRam { +public: + class HostRamAllocator : public BaseAllocator { + public: + HostRamAllocator(); + + std::string getName() const + { return "HostRamAlloc"; } + + std::unique_ptr + allocateBlock(size_t size); + }; + + static HostRamAllocator& + getAllocator() + { return allocator; } + +private: + static HostRamAllocator allocator; }; } // namespace villas diff --git a/fpga/include/villas/memory_manager.hpp b/fpga/include/villas/memory_manager.hpp index fb3db33c2..96d1ccda2 100644 --- a/fpga/include/villas/memory_manager.hpp +++ b/fpga/include/villas/memory_manager.hpp @@ -77,7 +77,7 @@ private: // ... and no copying or assigning MemoryManager(const MemoryManager&) = delete; - MemoryManager& operator=(const MemoryManager&) = delete ; + MemoryManager& operator=(const MemoryManager&) = delete; /** * @brief Custom edge in memory graph representing a memory mapping @@ -153,13 +153,17 @@ public: { return getOrCreateAddressSpace("villas-fpga"); } AddressSpaceId - getProcessAddressSpaceMemoryBlock(const std::string& memoryBlock) + 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, diff --git a/fpga/lib/memory.cpp b/fpga/lib/memory.cpp index f3d2802b4..f2def1432 100644 --- a/fpga/lib/memory.cpp +++ b/fpga/lib/memory.cpp @@ -5,20 +5,135 @@ namespace villas { -bool -HostRam::free(void* addr, size_t length) +std::unique_ptr +HostRam::HostRamAllocator::allocateBlock(size_t size) { - return munmap(addr, length) == 0; + /* Align to next bigger page size chunk */ + if (size & size_t(0xFFF)) { + size += size_t(0x1000); + size &= size_t(~0xFFF); + } + + const int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT; + const int mmap_protection = PROT_READ | PROT_WRITE; + + const void* addr = mmap(nullptr, size, mmap_protection, mmap_flags, 0, 0); + if(addr == nullptr) { + throw std::bad_alloc(); + } + + auto& mm = MemoryManager::get(); + + // assemble name for this block + std::stringstream name; + name << std::showbase << std::hex << reinterpret_cast(addr); + + auto blockAddrSpaceId = mm.getProcessAddressSpaceMemoryBlock(name.str()); + + const auto localAddr = reinterpret_cast(addr); + std::unique_ptr + mem(new MemoryBlock(localAddr, size, blockAddrSpaceId), this->free); + + insertMemoryBlock(*mem); + + return mem; } -void* -HostRam::allocate(size_t length, int flags) +LinearAllocator::LinearAllocator(MemoryManager::AddressSpaceId memoryAddrSpaceId, + size_t memorySize, + size_t internalOffset) : + BaseAllocator(memoryAddrSpaceId), + nextFreeAddress(0), + memorySize(memorySize), + internalOffset(internalOffset) { - const int mmap_flags = flags | MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT; - const int mmap_protection = PROT_READ | PROT_WRITE; + // make sure to start at aligned offset, reduce size in case we need padding + if(const size_t paddingBytes = getAlignmentPadding(internalOffset)) { + assert(paddingBytes < memorySize); - return mmap(nullptr, length, mmap_protection, mmap_flags, 0, 0); + internalOffset += paddingBytes; + memorySize -= paddingBytes; + } + + // deallocation callback + free = [&](MemoryBlock* mem) { + logger->debug("freeing {:#x} bytes at local addr {:#x} (addr space {})", + mem->getSize(), mem->getOffset(), mem->getAddrSpaceId()); + logger->warn("free() not implemented"); + logger->debug("available memory: {:#x} bytes", getAvailableMemory()); + }; +} + + +std::string +LinearAllocator::getName() const +{ + std::stringstream name; + name << "LinearAlloc" << getAddrSpaceId() + << "@0x" << std::hex << internalOffset; + return name.str(); +} + + +std::unique_ptr +LinearAllocator::allocateBlock(size_t size) +{ + if(size > getAvailableMemory()) { + throw std::bad_alloc(); + } + + // assign address + const uintptr_t localAddr = nextFreeAddress + internalOffset; + + // reserve memory + nextFreeAddress += size; + + // make sure it is aligned + if(const size_t paddingBytes = getAlignmentPadding(nextFreeAddress)) { + nextFreeAddress += paddingBytes; + + // if next free address is outside this block due to padding, cap it + nextFreeAddress = std::min(nextFreeAddress, memorySize); + } + + + auto& mm = MemoryManager::get(); + + // assemble name for this block + std::stringstream blockName; + blockName << std::showbase << std::hex << localAddr; + + // create address space + auto addrSpaceName = mm.getSlaveAddrSpaceName(getName(), blockName.str()); + auto addrSpaceId = mm.getOrCreateAddressSpace(addrSpaceName); + + logger->debug("Allocated {:#x} bytes for {}, {:#x} bytes remaining", + size, addrSpaceId, getAvailableMemory()); + + std::unique_ptr + mem(new MemoryBlock(localAddr, size, addrSpaceId), this->free); + + // mount block into the memory graph + insertMemoryBlock(*mem); + + + return mem; +} + + +HostRam::HostRamAllocator +HostRam::allocator; + +HostRam::HostRamAllocator::HostRamAllocator() : + BaseAllocator(MemoryManager::get().getProcessAddressSpace()) +{ + free = [&](MemoryBlock* mem) { + if(::munmap(reinterpret_cast(mem->getOffset()), mem->getSize()) != 0) { + logger->warn("munmap() failed for {:#x} of size {:#x}", + mem->getOffset(), mem->getSize()); + } + }; } } // namespace villas diff --git a/fpga/tests/dma.cpp b/fpga/tests/dma.cpp index fd69cfc94..e8bb4c08d 100644 --- a/fpga/tests/dma.cpp +++ b/fpga/tests/dma.cpp @@ -41,15 +41,16 @@ Test(fpga, dma, .description = "DMA") size_t len = 4 * (1 << 10); /* Allocate memory to use with DMA */ - auto src = villas::HostRam::allocate(len); - auto dst = villas::HostRam::allocate(len); + auto src = villas::HostRam::getAllocator().allocate(len); + auto dst = villas::HostRam::getAllocator().allocate(len); /* Get new random data */ const size_t lenRandom = read_random(&src, len); cr_assert(len == lenRandom, "Failed to get random data"); /* Start transfer */ - cr_assert(dma.pingPong(src, dst, len), "DMA ping pong failed"); + cr_assert(dma.pingPong(src.getMemoryBlock(), dst.getMemoryBlock(), len), + "DMA ping pong failed"); /* Compare data */ cr_assert(memcmp(&src, &dst, len) == 0, "Data not equal"); From 3e505c74bf5fe80fe0464c8a365ece2898160c97 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Fri, 13 Apr 2018 15:30:12 +0200 Subject: [PATCH 3/3] ips/bram: add block RAM IP and use it with DMA test --- fpga/include/villas/fpga/ips/bram.hpp | 87 +++++++++++++++++++++++++++ fpga/lib/CMakeLists.txt | 1 + fpga/lib/ips/bram.cpp | 32 ++++++++++ fpga/tests/dma.cpp | 8 ++- 4 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 fpga/include/villas/fpga/ips/bram.hpp create mode 100644 fpga/lib/ips/bram.cpp diff --git a/fpga/include/villas/fpga/ips/bram.hpp b/fpga/include/villas/fpga/ips/bram.hpp new file mode 100644 index 000000000..01a46256f --- /dev/null +++ b/fpga/include/villas/fpga/ips/bram.hpp @@ -0,0 +1,87 @@ +/** Block-Raam related helper functions + * * + * @author Daniel Krebs + * @copyright 2018, Daniel Krebs + * @license GNU General Public License (version 3) + * + * VILLASfpga + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *********************************************************************************/ + +/** @addtogroup fpga VILLASfpga + * @{ + */ + +#pragma once + +#include "memory.hpp" +#include "fpga/ip.hpp" + +namespace villas { +namespace fpga { +namespace ip { + + +class Bram : public IpCore +{ + friend class BramFactory; +public: + + bool init(); + + LinearAllocator& + getAllocator() + { return *allocator; } + +private: + static constexpr const char* memoryBlock = "Mem0"; + std::list getMemoryBlocks() const + { return { memoryBlock }; } + + size_t size; + std::unique_ptr allocator; +}; + + + +class BramFactory : public IpCoreFactory { +public: + + BramFactory() : + IpCoreFactory(getName()) + {} + + bool configureJson(IpCore& ip, json_t *json_ip); + + IpCore* create() + { return new Bram; } + + std::string + getName() const + { return "Bram"; } + + std::string + getDescription() const + { return "Block RAM"; } + + Vlnv getCompatibleVlnv() const + { return {"xilinx.com:ip:axi_bram_ctrl:"}; } +}; + +} // namespace ip +} // namespace fpga +} // namespace villas + +/** @} */ diff --git a/fpga/lib/CMakeLists.txt b/fpga/lib/CMakeLists.txt index d30414b8e..84733d3f8 100644 --- a/fpga/lib/CMakeLists.txt +++ b/fpga/lib/CMakeLists.txt @@ -10,6 +10,7 @@ set(SOURCES ips/intc.cpp ips/pcie.cpp ips/dma.cpp + ips/bram.cpp kernel/kernel.c kernel/pci.c diff --git a/fpga/lib/ips/bram.cpp b/fpga/lib/ips/bram.cpp new file mode 100644 index 000000000..11dda7f67 --- /dev/null +++ b/fpga/lib/ips/bram.cpp @@ -0,0 +1,32 @@ +#include "fpga/ips/bram.hpp" + +namespace villas { +namespace fpga { +namespace ip { + +static BramFactory factory; + +bool +BramFactory::configureJson(IpCore& ip, json_t* json_ip) +{ + auto& bram = reinterpret_cast(ip); + + if(json_unpack(json_ip, "{ s: i }", "size", &bram.size) != 0) { + getLogger()->error("Cannot parse 'size'"); + return false; + } + + return true; +} + +bool Bram::init() +{ + allocator = std::make_unique + (getAddressSpaceId(memoryBlock), this->size, 0); + + return true; +} + +} // namespace ip +} // namespace fpga +} // namespace villas diff --git a/fpga/tests/dma.cpp b/fpga/tests/dma.cpp index e8bb4c08d..63c1c0470 100644 --- a/fpga/tests/dma.cpp +++ b/fpga/tests/dma.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -36,13 +37,18 @@ Test(fpga, dma, .description = "DMA") continue; } + // find a block RAM IP to write to + auto bramIp = state.cards.front()->lookupIp(villas::fpga::Vlnv("xilinx.com:ip:axi_bram_ctrl:")); + auto bram = reinterpret_cast(bramIp); + cr_assert_not_null(bram, "Couldn't find BRAM"); + // Simple DMA can only transfer up to 4 kb due to PCIe page size burst // limitation size_t len = 4 * (1 << 10); /* Allocate memory to use with DMA */ auto src = villas::HostRam::getAllocator().allocate(len); - auto dst = villas::HostRam::getAllocator().allocate(len); + auto dst = bram->getAllocator().allocate(len); /* Get new random data */ const size_t lenRandom = read_random(&src, len);