mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
Reformat all code with clang-format
Signed-off-by: Steffen Vogel <post@steffenvogel.de>
This commit is contained in:
parent
29cf5540a0
commit
3d73c759ea
71 changed files with 6062 additions and 6285 deletions
|
@ -47,6 +47,5 @@ private:
|
|||
|
||||
#ifndef FMT_LEGACY_OSTREAM_FORMATTER
|
||||
template <>
|
||||
class fmt::formatter<villas::graph::Edge>
|
||||
: public fmt::ostream_formatter {};
|
||||
class fmt::formatter<villas::graph::Edge> : public fmt::ostream_formatter {};
|
||||
#endif
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
#include <list>
|
||||
#include <sstream>
|
||||
#include <fmt/ostream.h>
|
||||
#include <villas/config.hpp>
|
||||
|
||||
namespace villas {
|
||||
|
@ -48,6 +48,5 @@ private:
|
|||
|
||||
#ifndef FMT_LEGACY_OSTREAM_FORMATTER
|
||||
template <>
|
||||
class fmt::formatter<villas::graph::Vertex>
|
||||
: public fmt::ostream_formatter {};
|
||||
class fmt::formatter<villas::graph::Vertex> : public fmt::ostream_formatter {};
|
||||
#endif
|
||||
|
|
|
@ -35,7 +35,8 @@ static constexpr size_t EXTENSION_SIZE = VFIO_NOIOMMU_IOMMU + 1;
|
|||
|
||||
class Container {
|
||||
public:
|
||||
Container(std::vector<std::string> required_modules = {"vfio", "vfio_pci", "vfio_iommu_type1"});
|
||||
Container(std::vector<std::string> required_modules = {"vfio", "vfio_pci",
|
||||
"vfio_iommu_type1"});
|
||||
|
||||
// No copying allowed because we manage the vfio state in constructor and destructors
|
||||
Container(Container const &) = delete;
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <fmt/ostream.h>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <fmt/ostream.h>
|
||||
#include <villas/config.hpp>
|
||||
#include <villas/graph/directed.hpp>
|
||||
#include <villas/log.hpp>
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
#include <iostream>
|
||||
#include <jansson.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <fmt/ostream.h>
|
||||
#include <villas/common.hpp>
|
||||
#include <villas/config.hpp>
|
||||
#include <villas/log.hpp>
|
||||
|
@ -152,6 +152,5 @@ template <typename T> void Registry::dump() {
|
|||
|
||||
#ifndef FMT_LEGACY_OSTREAM_FORMATTER
|
||||
template <>
|
||||
class fmt::formatter<villas::plugin::Plugin>
|
||||
: public fmt::ostream_formatter {};
|
||||
class fmt::formatter<villas::plugin::Plugin> : public fmt::ostream_formatter {};
|
||||
#endif
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
|
||||
#include <sstream>
|
||||
|
||||
#include <villas/plugin.hpp>
|
||||
#include <villas/memory_manager.hpp>
|
||||
#include <villas/memory.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/memory.hpp>
|
||||
#include <villas/memory_manager.hpp>
|
||||
#include <villas/plugin.hpp>
|
||||
|
||||
namespace villas {
|
||||
namespace gpu {
|
||||
|
@ -20,81 +20,78 @@ namespace gpu {
|
|||
class GpuAllocator;
|
||||
|
||||
class Gpu {
|
||||
friend GpuAllocator;
|
||||
friend GpuAllocator;
|
||||
|
||||
public:
|
||||
Gpu(int gpuId);
|
||||
~Gpu();
|
||||
Gpu(int gpuId);
|
||||
~Gpu();
|
||||
|
||||
bool init();
|
||||
bool init();
|
||||
|
||||
std::string getName() const;
|
||||
std::string getName() const;
|
||||
|
||||
GpuAllocator &getAllocator() const
|
||||
{ return *allocator; }
|
||||
GpuAllocator &getAllocator() const { return *allocator; }
|
||||
|
||||
bool makeAccessibleToPCIeAndVA(const MemoryBlock &mem);
|
||||
|
||||
bool makeAccessibleToPCIeAndVA(const MemoryBlock &mem);
|
||||
// Make some memory block accssible for this GPU
|
||||
bool makeAccessibleFromPCIeOrHostRam(const MemoryBlock &mem);
|
||||
|
||||
// Make some memory block accssible for this GPU
|
||||
bool makeAccessibleFromPCIeOrHostRam(const MemoryBlock &mem);
|
||||
void memcpySync(const MemoryBlock &src, const MemoryBlock &dst, size_t size);
|
||||
|
||||
void memcpySync(const MemoryBlock &src, const MemoryBlock &dst, size_t size);
|
||||
void memcpyKernel(const MemoryBlock &src, const MemoryBlock &dst,
|
||||
size_t size);
|
||||
|
||||
void memcpyKernel(const MemoryBlock &src, const MemoryBlock &dst, size_t size);
|
||||
|
||||
MemoryTranslation
|
||||
translate(const MemoryBlock &dst);
|
||||
MemoryTranslation translate(const MemoryBlock &dst);
|
||||
|
||||
private:
|
||||
bool registerIoMemory(const MemoryBlock &mem);
|
||||
bool registerHostMemory(const MemoryBlock &mem);
|
||||
bool registerIoMemory(const MemoryBlock &mem);
|
||||
bool registerHostMemory(const MemoryBlock &mem);
|
||||
|
||||
private:
|
||||
class impl;
|
||||
std::unique_ptr<impl> pImpl;
|
||||
class impl;
|
||||
std::unique_ptr<impl> pImpl;
|
||||
|
||||
// Master, will be used to derived slave addr spaces for allocation
|
||||
MemoryManager::AddressSpaceId masterPciEAddrSpaceId;
|
||||
// Master, will be used to derived slave addr spaces for allocation
|
||||
MemoryManager::AddressSpaceId masterPciEAddrSpaceId;
|
||||
|
||||
MemoryManager::AddressSpaceId slaveMemoryAddrSpaceId;
|
||||
MemoryManager::AddressSpaceId slaveMemoryAddrSpaceId;
|
||||
|
||||
Logger logger;
|
||||
Logger logger;
|
||||
|
||||
int gpuId;
|
||||
int gpuId;
|
||||
|
||||
std::unique_ptr<GpuAllocator> allocator;
|
||||
std::unique_ptr<GpuAllocator> allocator;
|
||||
};
|
||||
|
||||
|
||||
class GpuAllocator : public BaseAllocator<GpuAllocator> {
|
||||
public:
|
||||
static constexpr size_t GpuPageSize = 64UL << 10;
|
||||
static constexpr size_t GpuPageSize = 64UL << 10;
|
||||
|
||||
GpuAllocator(Gpu &gpu);
|
||||
GpuAllocator(Gpu &gpu);
|
||||
|
||||
std::string getName() const;
|
||||
std::string getName() const;
|
||||
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
|
||||
allocateBlock(size_t size);
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
|
||||
allocateBlock(size_t size);
|
||||
|
||||
private:
|
||||
Gpu &gpu;
|
||||
// TODO: replace by multimap (key is available memory)
|
||||
std::list<std::unique_ptr<LinearAllocator>> chunks;
|
||||
Gpu &gpu;
|
||||
// TODO: replace by multimap (key is available memory)
|
||||
std::list<std::unique_ptr<LinearAllocator>> chunks;
|
||||
};
|
||||
|
||||
class GpuFactory : public Plugin {
|
||||
public:
|
||||
GpuFactory();
|
||||
GpuFactory();
|
||||
|
||||
std::list<std::unique_ptr<Gpu>>
|
||||
make();
|
||||
std::list<std::unique_ptr<Gpu>> make();
|
||||
|
||||
void run(void*);
|
||||
void run(void *);
|
||||
|
||||
private:
|
||||
Logger logger;
|
||||
Logger logger;
|
||||
};
|
||||
|
||||
} // namespace villas
|
||||
} // namespace gpu
|
||||
} // namespace villas
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
namespace villas {
|
||||
namespace gpu {
|
||||
|
||||
__global__ void
|
||||
kernel_mailbox(volatile uint32_t *mailbox, volatile uint32_t* counter);
|
||||
__global__ void kernel_mailbox(volatile uint32_t *mailbox,
|
||||
volatile uint32_t *counter);
|
||||
|
||||
__global__ void
|
||||
kernel_memcpy(volatile uint8_t* dst, volatile uint8_t* src, size_t length);
|
||||
__global__ void kernel_memcpy(volatile uint8_t *dst, volatile uint8_t *src,
|
||||
size_t length);
|
||||
|
||||
} // namespace villas
|
||||
} // namespace gpu
|
||||
} // namespace villas
|
||||
|
|
|
@ -5,16 +5,16 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include <villas/gpu.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/kernel/pci.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/memory_manager.hpp>
|
||||
|
||||
#include <cuda.h>
|
||||
|
@ -23,486 +23,459 @@
|
|||
|
||||
#include "kernels.hpp"
|
||||
|
||||
|
||||
using namespace villas::gpu;
|
||||
|
||||
static GpuFactory gpuFactory;
|
||||
|
||||
GpuAllocator::GpuAllocator(Gpu &gpu) :
|
||||
BaseAllocator(gpu.masterPciEAddrSpaceId),
|
||||
gpu(gpu)
|
||||
{
|
||||
free = [&](MemoryBlock* mem) {
|
||||
cudaSetDevice(gpu.gpuId);
|
||||
if (cudaFree(reinterpret_cast<void*>(mem->getOffset())) != cudaSuccess) {
|
||||
logger->warn("cudaFree() failed for {:#x} of size {:#x}",
|
||||
mem->getOffset(), mem->getSize());
|
||||
}
|
||||
GpuAllocator::GpuAllocator(Gpu &gpu)
|
||||
: BaseAllocator(gpu.masterPciEAddrSpaceId), gpu(gpu) {
|
||||
free = [&](MemoryBlock *mem) {
|
||||
cudaSetDevice(gpu.gpuId);
|
||||
if (cudaFree(reinterpret_cast<void *>(mem->getOffset())) != cudaSuccess) {
|
||||
logger->warn("cudaFree() failed for {:#x} of size {:#x}",
|
||||
mem->getOffset(), mem->getSize());
|
||||
}
|
||||
|
||||
removeMemoryBlock(*mem);
|
||||
};
|
||||
removeMemoryBlock(*mem);
|
||||
};
|
||||
}
|
||||
|
||||
std::string
|
||||
villas::gpu::GpuAllocator::getName() const
|
||||
{
|
||||
std::stringstream name;
|
||||
name << "GpuAlloc" << getAddrSpaceId();
|
||||
return name.str();
|
||||
std::string villas::gpu::GpuAllocator::getName() const {
|
||||
std::stringstream name;
|
||||
name << "GpuAlloc" << getAddrSpaceId();
|
||||
return name.str();
|
||||
}
|
||||
|
||||
|
||||
GpuFactory::GpuFactory() :
|
||||
Plugin("cuda", "CUDA capable GPUs")
|
||||
{
|
||||
logger = villas::logging.get("gpu:factory");
|
||||
GpuFactory::GpuFactory() : Plugin("cuda", "CUDA capable GPUs") {
|
||||
logger = villas::logging.get("gpu:factory");
|
||||
}
|
||||
|
||||
// Required to be defined here for PIMPL to compile
|
||||
Gpu::~Gpu()
|
||||
{
|
||||
auto &mm = MemoryManager::get();
|
||||
mm.removeAddressSpace(masterPciEAddrSpaceId);
|
||||
Gpu::~Gpu() {
|
||||
auto &mm = MemoryManager::get();
|
||||
mm.removeAddressSpace(masterPciEAddrSpaceId);
|
||||
}
|
||||
|
||||
|
||||
// We use PIMPL in order to hide gdrcopy types from the public header
|
||||
class Gpu::impl {
|
||||
public:
|
||||
gdr_t gdr;
|
||||
struct pci_device pdev;
|
||||
gdr_t gdr;
|
||||
struct pci_device pdev;
|
||||
};
|
||||
|
||||
std::string Gpu::getName() const
|
||||
{
|
||||
cudaDeviceProp deviceProp;
|
||||
if (cudaGetDeviceProperties(&deviceProp, gpuId) != cudaSuccess) {
|
||||
// Logger not yet availabe
|
||||
villas::logging.get("gpu")->error("Cannot retrieve properties for GPU {}", gpuId);
|
||||
throw std::exception();
|
||||
}
|
||||
std::string Gpu::getName() const {
|
||||
cudaDeviceProp deviceProp;
|
||||
if (cudaGetDeviceProperties(&deviceProp, gpuId) != cudaSuccess) {
|
||||
// Logger not yet availabe
|
||||
villas::logging.get("gpu")->error("Cannot retrieve properties for GPU {}",
|
||||
gpuId);
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
std::stringstream name;
|
||||
name << "gpu" << gpuId << "(" << deviceProp.name << ")";
|
||||
std::stringstream name;
|
||||
name << "gpu" << gpuId << "(" << deviceProp.name << ")";
|
||||
|
||||
return name.str();
|
||||
return name.str();
|
||||
}
|
||||
|
||||
bool Gpu::registerIoMemory(const MemoryBlock &mem)
|
||||
{
|
||||
auto &mm = MemoryManager::get();
|
||||
const auto pciAddrSpaceId = mm.getPciAddressSpace();
|
||||
bool Gpu::registerIoMemory(const MemoryBlock &mem) {
|
||||
auto &mm = MemoryManager::get();
|
||||
const auto pciAddrSpaceId = mm.getPciAddressSpace();
|
||||
|
||||
// Check if we need to map anything at all, maybe it's already reachable
|
||||
try {
|
||||
// TODO: there might already be a path through the graph, but there's no
|
||||
// overlapping window, so this will fail badly!
|
||||
auto translation = mm.getTranslation(masterPciEAddrSpaceId,
|
||||
mem.getAddrSpaceId());
|
||||
if (translation.getSize() >= mem.getSize())
|
||||
// There is already a sufficient path
|
||||
logger->debug("Already mapped through another mapping");
|
||||
return true;
|
||||
else
|
||||
logger->warn("There's already a mapping, but too small");
|
||||
} catch (const std::out_of_range&) {
|
||||
// Not yet reachable, that's okay, proceed
|
||||
}
|
||||
// Check if we need to map anything at all, maybe it's already reachable
|
||||
try {
|
||||
// TODO: there might already be a path through the graph, but there's no
|
||||
// overlapping window, so this will fail badly!
|
||||
auto translation =
|
||||
mm.getTranslation(masterPciEAddrSpaceId, mem.getAddrSpaceId());
|
||||
if (translation.getSize() >= mem.getSize())
|
||||
// There is already a sufficient path
|
||||
logger->debug("Already mapped through another mapping");
|
||||
return true;
|
||||
else logger->warn("There's already a mapping, but too small");
|
||||
} catch (const std::out_of_range &) {
|
||||
// Not yet reachable, that's okay, proceed
|
||||
}
|
||||
|
||||
// In order to register IO memory with CUDA, it has to be mapped to the VA
|
||||
// space of the current process (requirement of CUDA API). Check this now.
|
||||
MemoryManager::AddressSpaceId mappedBaseAddrSpaceId;
|
||||
try {
|
||||
auto path = mm.findPath(mm.getProcessAddressSpace(), mem.getAddrSpaceId());
|
||||
// First node in path is the mapped memory space whose virtual address
|
||||
// we need to hand to CUDA
|
||||
mappedBaseAddrSpaceId = path.front();
|
||||
} catch (const std::out_of_range &) {
|
||||
logger->error("Memory not reachable from process, but required by CUDA");
|
||||
return false;
|
||||
}
|
||||
|
||||
// In order to register IO memory with CUDA, it has to be mapped to the VA
|
||||
// space of the current process (requirement of CUDA API). Check this now.
|
||||
MemoryManager::AddressSpaceId mappedBaseAddrSpaceId;
|
||||
try {
|
||||
auto path = mm.findPath(mm.getProcessAddressSpace(), mem.getAddrSpaceId());
|
||||
// First node in path is the mapped memory space whose virtual address
|
||||
// we need to hand to CUDA
|
||||
mappedBaseAddrSpaceId = path.front();
|
||||
} catch (const std::out_of_range&) {
|
||||
logger->error("Memory not reachable from process, but required by CUDA");
|
||||
return false;
|
||||
}
|
||||
// Determine the base address of the mapped memory region needed by CUDA
|
||||
const auto translationProcess =
|
||||
mm.getTranslationFromProcess(mappedBaseAddrSpaceId);
|
||||
const uintptr_t baseAddrForProcess = translationProcess.getLocalAddr(0);
|
||||
|
||||
// Determine the base address of the mapped memory region needed by CUDA
|
||||
const auto translationProcess = mm.getTranslationFromProcess(mappedBaseAddrSpaceId);
|
||||
const uintptr_t baseAddrForProcess = translationProcess.getLocalAddr(0);
|
||||
// Now check that the memory is also reachable via PCIe bus, otherwise GPU
|
||||
// has no means to access it.
|
||||
uintptr_t baseAddrOnPci;
|
||||
size_t sizeOnPci;
|
||||
try {
|
||||
auto translationPci =
|
||||
mm.getTranslation(pciAddrSpaceId, mappedBaseAddrSpaceId);
|
||||
baseAddrOnPci = translationPci.getLocalAddr(0);
|
||||
sizeOnPci = translationPci.getSize();
|
||||
} catch (const std::out_of_range &) {
|
||||
logger->error("Memory is not reachable via PCIe bus");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sizeOnPci < mem.getSize()) {
|
||||
logger->warn(
|
||||
"VA mapping of IO memory is too small: {:#x} instead of {:#x} bytes",
|
||||
sizeOnPci, mem.getSize());
|
||||
logger->warn("If something later on fails or behaves strangely, this might "
|
||||
"be the cause!");
|
||||
}
|
||||
|
||||
// Now check that the memory is also reachable via PCIe bus, otherwise GPU
|
||||
// has no means to access it.
|
||||
uintptr_t baseAddrOnPci;
|
||||
size_t sizeOnPci;
|
||||
try {
|
||||
auto translationPci = mm.getTranslation(pciAddrSpaceId,
|
||||
mappedBaseAddrSpaceId);
|
||||
baseAddrOnPci = translationPci.getLocalAddr(0);
|
||||
sizeOnPci = translationPci.getSize();
|
||||
} catch (const std::out_of_range&) {
|
||||
logger->error("Memory is not reachable via PCIe bus");
|
||||
return false;
|
||||
}
|
||||
cudaSetDevice(gpuId);
|
||||
|
||||
if (sizeOnPci < mem.getSize()) {
|
||||
logger->warn("VA mapping of IO memory is too small: {:#x} instead of {:#x} bytes",
|
||||
sizeOnPci, mem.getSize());
|
||||
logger->warn("If something later on fails or behaves strangely, this might be the cause!");
|
||||
}
|
||||
auto baseAddrVA = reinterpret_cast<void *>(baseAddrForProcess);
|
||||
if (cudaHostRegister(baseAddrVA, sizeOnPci, cudaHostRegisterIoMemory) !=
|
||||
cudaSuccess) {
|
||||
logger->error("Cannot register IO memory for block {}",
|
||||
mem.getAddrSpaceId());
|
||||
return false;
|
||||
}
|
||||
|
||||
void *devicePointer = nullptr;
|
||||
if (cudaHostGetDevicePointer(&devicePointer, baseAddrVA, 0) != cudaSuccess) {
|
||||
logger->error("Cannot retrieve device pointer for IO memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
cudaSetDevice(gpuId);
|
||||
mm.createMapping(reinterpret_cast<uintptr_t>(devicePointer), baseAddrOnPci,
|
||||
sizeOnPci, "CudaIoMem", masterPciEAddrSpaceId,
|
||||
pciAddrSpaceId);
|
||||
|
||||
auto baseAddrVA = reinterpret_cast<void*>(baseAddrForProcess);
|
||||
if (cudaHostRegister(baseAddrVA, sizeOnPci, cudaHostRegisterIoMemory) != cudaSuccess) {
|
||||
logger->error("Cannot register IO memory for block {}", mem.getAddrSpaceId());
|
||||
return false;
|
||||
}
|
||||
|
||||
void* devicePointer = nullptr;
|
||||
if (cudaHostGetDevicePointer(&devicePointer, baseAddrVA, 0) != cudaSuccess) {
|
||||
logger->error("Cannot retrieve device pointer for IO memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
mm.createMapping(reinterpret_cast<uintptr_t>(devicePointer), baseAddrOnPci,
|
||||
sizeOnPci, "CudaIoMem", masterPciEAddrSpaceId, pciAddrSpaceId);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Gpu::registerHostMemory(const MemoryBlock &mem)
|
||||
{
|
||||
auto &mm = MemoryManager::get();
|
||||
bool Gpu::registerHostMemory(const MemoryBlock &mem) {
|
||||
auto &mm = MemoryManager::get();
|
||||
|
||||
auto translation = mm.getTranslationFromProcess(mem.getAddrSpaceId());
|
||||
auto localBase = reinterpret_cast<void*>(translation.getLocalAddr(0));
|
||||
auto translation = mm.getTranslationFromProcess(mem.getAddrSpaceId());
|
||||
auto localBase = reinterpret_cast<void *>(translation.getLocalAddr(0));
|
||||
|
||||
int ret = cudaHostRegister(localBase, mem.getSize(), 0);
|
||||
if (ret != cudaSuccess) {
|
||||
logger->error("Cannot register memory block {} addr={:p} size={:#x} to CUDA: ret={}",
|
||||
mem.getAddrSpaceId(), localBase, mem.getSize(), ret);
|
||||
return false;
|
||||
}
|
||||
int ret = cudaHostRegister(localBase, mem.getSize(), 0);
|
||||
if (ret != cudaSuccess) {
|
||||
logger->error(
|
||||
"Cannot register memory block {} addr={:p} size={:#x} to CUDA: ret={}",
|
||||
mem.getAddrSpaceId(), localBase, mem.getSize(), ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
void* devicePointer = nullptr;
|
||||
ret = cudaHostGetDevicePointer(&devicePointer, localBase, 0);
|
||||
if (ret != cudaSuccess) {
|
||||
logger->error("Cannot retrieve device pointer for IO memory: ret={}", ret);
|
||||
return false;
|
||||
}
|
||||
void *devicePointer = nullptr;
|
||||
ret = cudaHostGetDevicePointer(&devicePointer, localBase, 0);
|
||||
if (ret != cudaSuccess) {
|
||||
logger->error("Cannot retrieve device pointer for IO memory: ret={}", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
mm.createMapping(reinterpret_cast<uintptr_t>(devicePointer), 0, mem.getSize(),
|
||||
"CudaHostMem", masterPciEAddrSpaceId, mem.getAddrSpaceId());
|
||||
mm.createMapping(reinterpret_cast<uintptr_t>(devicePointer), 0, mem.getSize(),
|
||||
"CudaHostMem", masterPciEAddrSpaceId, mem.getAddrSpaceId());
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Gpu::makeAccessibleToPCIeAndVA(const MemoryBlock &mem)
|
||||
{
|
||||
if (pImpl->gdr == nullptr) {
|
||||
logger->warn("GDRcopy not available");
|
||||
return false;
|
||||
}
|
||||
bool Gpu::makeAccessibleToPCIeAndVA(const MemoryBlock &mem) {
|
||||
if (pImpl->gdr == nullptr) {
|
||||
logger->warn("GDRcopy not available");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto &mm = MemoryManager::get();
|
||||
auto &mm = MemoryManager::get();
|
||||
|
||||
try {
|
||||
auto path = mm.findPath(masterPciEAddrSpaceId, mem.getAddrSpaceId());
|
||||
// If first hop is the PCIe bus, we know that memory is off-GPU
|
||||
if (path.front() == mm.getPciAddressSpace())
|
||||
throw std::out_of_range("Memory block is outside of this GPU");
|
||||
try {
|
||||
auto path = mm.findPath(masterPciEAddrSpaceId, mem.getAddrSpaceId());
|
||||
// If first hop is the PCIe bus, we know that memory is off-GPU
|
||||
if (path.front() == mm.getPciAddressSpace())
|
||||
throw std::out_of_range("Memory block is outside of this GPU");
|
||||
|
||||
} catch (const std::out_of_range&) {
|
||||
logger->error("Trying to map non-GPU memory block");
|
||||
return false;
|
||||
}
|
||||
} catch (const std::out_of_range &) {
|
||||
logger->error("Trying to map non-GPU memory block");
|
||||
return false;
|
||||
}
|
||||
|
||||
logger->debug("retrieve complete device pointer from point of view of GPU");
|
||||
logger->debug("retrieve complete device pointer from point of view of GPU");
|
||||
|
||||
// Retrieve complete device pointer from point of view of GPU
|
||||
auto translation = mm.getTranslation(masterPciEAddrSpaceId,
|
||||
mem.getAddrSpaceId());
|
||||
CUdeviceptr devptr = translation.getLocalAddr(0);
|
||||
// Retrieve complete device pointer from point of view of GPU
|
||||
auto translation =
|
||||
mm.getTranslation(masterPciEAddrSpaceId, mem.getAddrSpaceId());
|
||||
CUdeviceptr devptr = translation.getLocalAddr(0);
|
||||
|
||||
int ret;
|
||||
int ret;
|
||||
|
||||
// Required to set this flag before mapping
|
||||
unsigned int enable = 1;
|
||||
ret = cuPointerSetAttribute(&enable, CU_POINTER_ATTRIBUTE_SYNC_MEMOPS, devptr);
|
||||
if (ret != CUDA_SUCCESS) {
|
||||
logger->error("Cannot set pointer attributes on memory block {}: {}",
|
||||
mem.getAddrSpaceId(), ret);
|
||||
return false;
|
||||
}
|
||||
// Required to set this flag before mapping
|
||||
unsigned int enable = 1;
|
||||
ret =
|
||||
cuPointerSetAttribute(&enable, CU_POINTER_ATTRIBUTE_SYNC_MEMOPS, devptr);
|
||||
if (ret != CUDA_SUCCESS) {
|
||||
logger->error("Cannot set pointer attributes on memory block {}: {}",
|
||||
mem.getAddrSpaceId(), ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
gdr_mh_t mh;
|
||||
ret = gdr_pin_buffer(pImpl->gdr, devptr, mem.getSize(), 0, 0, &mh);
|
||||
if (ret != 0) {
|
||||
logger->error("Cannot pin memory block {} via gdrcopy: {}",
|
||||
mem.getAddrSpaceId(), ret);
|
||||
return false;
|
||||
}
|
||||
gdr_mh_t mh;
|
||||
ret = gdr_pin_buffer(pImpl->gdr, devptr, mem.getSize(), 0, 0, &mh);
|
||||
if (ret != 0) {
|
||||
logger->error("Cannot pin memory block {} via gdrcopy: {}",
|
||||
mem.getAddrSpaceId(), ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
void* bar = nullptr;
|
||||
ret = gdr_map(pImpl->gdr, mh, &bar, mem.getSize());
|
||||
if (ret != 0) {
|
||||
logger->error("Cannot map memory block {} via gdrcopy: {}",
|
||||
mem.getAddrSpaceId(), ret);
|
||||
return false;
|
||||
}
|
||||
void *bar = nullptr;
|
||||
ret = gdr_map(pImpl->gdr, mh, &bar, mem.getSize());
|
||||
if (ret != 0) {
|
||||
logger->error("Cannot map memory block {} via gdrcopy: {}",
|
||||
mem.getAddrSpaceId(), ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
gdr_info_t info;
|
||||
ret = gdr_get_info(pImpl->gdr, mh, &info);
|
||||
if (ret != 0) {
|
||||
logger->error("Cannot get info for mapping of memory block {}: {}",
|
||||
mem.getAddrSpaceId(), ret);
|
||||
return false;
|
||||
}
|
||||
gdr_info_t info;
|
||||
ret = gdr_get_info(pImpl->gdr, mh, &info);
|
||||
if (ret != 0) {
|
||||
logger->error("Cannot get info for mapping of memory block {}: {}",
|
||||
mem.getAddrSpaceId(), ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
const uintptr_t offset = info.va - devptr;
|
||||
const uintptr_t userPtr = reinterpret_cast<uintptr_t>(bar) + offset;
|
||||
const uintptr_t offset = info.va - devptr;
|
||||
const uintptr_t userPtr = reinterpret_cast<uintptr_t>(bar) + offset;
|
||||
|
||||
logger->debug("BAR ptr: {:p}", bar);
|
||||
logger->debug("info.va: {:#x}", info.va);
|
||||
logger->debug("info.mapped_size: {:#x}", info.mapped_size);
|
||||
logger->debug("info.page_size: {:#x}", info.page_size);
|
||||
logger->debug("offset: {:#x}", offset);
|
||||
logger->debug("user pointer: {:#x}", userPtr);
|
||||
logger->debug("BAR ptr: {:p}", bar);
|
||||
logger->debug("info.va: {:#x}", info.va);
|
||||
logger->debug("info.mapped_size: {:#x}", info.mapped_size);
|
||||
logger->debug("info.page_size: {:#x}", info.page_size);
|
||||
logger->debug("offset: {:#x}", offset);
|
||||
logger->debug("user pointer: {:#x}", userPtr);
|
||||
|
||||
// Mapping to acceses memory block from process
|
||||
mm.createMapping(userPtr, 0, info.mapped_size, "GDRcopy",
|
||||
mm.getProcessAddressSpace(), mem.getAddrSpaceId());
|
||||
// Mapping to acceses memory block from process
|
||||
mm.createMapping(userPtr, 0, info.mapped_size, "GDRcopy",
|
||||
mm.getProcessAddressSpace(), mem.getAddrSpaceId());
|
||||
|
||||
// Retrieve bus address
|
||||
uint64_t addr[8];
|
||||
ret = gdr_map_dma(pImpl->gdr, mh, 3, 0, 0, addr, 8);
|
||||
// Retrieve bus address
|
||||
uint64_t addr[8];
|
||||
ret = gdr_map_dma(pImpl->gdr, mh, 3, 0, 0, addr, 8);
|
||||
|
||||
for (int i = 0; i < ret; i++)
|
||||
logger->debug("DMA addr[{}]: {:#x}", i, addr[i]);
|
||||
for (int i = 0; i < ret; i++)
|
||||
logger->debug("DMA addr[{}]: {:#x}", i, addr[i]);
|
||||
|
||||
if (ret != 1) {
|
||||
logger->error("Only one DMA address per block supported at the moment");
|
||||
return false;
|
||||
}
|
||||
if (ret != 1) {
|
||||
logger->error("Only one DMA address per block supported at the moment");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mapping to access memory block from peer devices via PCIe
|
||||
mm.createMapping(addr[0], 0, mem.getSize(), "GDRcopyDMA",
|
||||
mm.getPciAddressSpace(), mem.getAddrSpaceId());
|
||||
// Mapping to access memory block from peer devices via PCIe
|
||||
mm.createMapping(addr[0], 0, mem.getSize(), "GDRcopyDMA",
|
||||
mm.getPciAddressSpace(), mem.getAddrSpaceId());
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Gpu::makeAccessibleFromPCIeOrHostRam(const MemoryBlock &mem)
|
||||
{
|
||||
// Check which kind of memory this is and where it resides
|
||||
// There are two possibilities:
|
||||
// - Host memory not managed by CUDA
|
||||
// - IO memory somewhere on the PCIe bus
|
||||
bool Gpu::makeAccessibleFromPCIeOrHostRam(const MemoryBlock &mem) {
|
||||
// Check which kind of memory this is and where it resides
|
||||
// There are two possibilities:
|
||||
// - Host memory not managed by CUDA
|
||||
// - IO memory somewhere on the PCIe bus
|
||||
|
||||
auto &mm = MemoryManager::get();
|
||||
auto &mm = MemoryManager::get();
|
||||
|
||||
bool isIoMemory = false;
|
||||
try {
|
||||
auto path = mm.findPath(mm.getPciAddressSpace(), mem.getAddrSpaceId());
|
||||
isIoMemory = true;
|
||||
} catch (const std::out_of_range&) {
|
||||
// Not reachable via PCI -> not IO memory
|
||||
}
|
||||
bool isIoMemory = false;
|
||||
try {
|
||||
auto path = mm.findPath(mm.getPciAddressSpace(), mem.getAddrSpaceId());
|
||||
isIoMemory = true;
|
||||
} catch (const std::out_of_range &) {
|
||||
// Not reachable via PCI -> not IO memory
|
||||
}
|
||||
|
||||
if (isIoMemory) {
|
||||
logger->debug("Memory block {} is assumed to be IO memory",
|
||||
mem.getAddrSpaceId());
|
||||
if (isIoMemory) {
|
||||
logger->debug("Memory block {} is assumed to be IO memory",
|
||||
mem.getAddrSpaceId());
|
||||
|
||||
return registerIoMemory(mem);
|
||||
}
|
||||
else {
|
||||
logger->debug("Memory block {} is assumed to be non-CUDA host memory",
|
||||
mem.getAddrSpaceId());
|
||||
return registerIoMemory(mem);
|
||||
} else {
|
||||
logger->debug("Memory block {} is assumed to be non-CUDA host memory",
|
||||
mem.getAddrSpaceId());
|
||||
|
||||
return registerHostMemory(mem);
|
||||
}
|
||||
return registerHostMemory(mem);
|
||||
}
|
||||
}
|
||||
|
||||
void Gpu::memcpySync(const MemoryBlock &src, const MemoryBlock &dst, size_t size)
|
||||
{
|
||||
auto &mm = MemoryManager::get();
|
||||
void Gpu::memcpySync(const MemoryBlock &src, const MemoryBlock &dst,
|
||||
size_t size) {
|
||||
auto &mm = MemoryManager::get();
|
||||
|
||||
auto src_translation = mm.getTranslation(masterPciEAddrSpaceId,
|
||||
src.getAddrSpaceId());
|
||||
const void* src_buf = reinterpret_cast<void*>(src_translation.getLocalAddr(0));
|
||||
auto src_translation =
|
||||
mm.getTranslation(masterPciEAddrSpaceId, src.getAddrSpaceId());
|
||||
const void *src_buf =
|
||||
reinterpret_cast<void *>(src_translation.getLocalAddr(0));
|
||||
|
||||
auto dst_translation = mm.getTranslation(masterPciEAddrSpaceId,
|
||||
dst.getAddrSpaceId());
|
||||
void* dst_buf = reinterpret_cast<void*>(dst_translation.getLocalAddr(0));
|
||||
auto dst_translation =
|
||||
mm.getTranslation(masterPciEAddrSpaceId, dst.getAddrSpaceId());
|
||||
void *dst_buf = reinterpret_cast<void *>(dst_translation.getLocalAddr(0));
|
||||
|
||||
cudaSetDevice(gpuId);
|
||||
cudaMemcpy(dst_buf, src_buf, size, cudaMemcpyDefault);
|
||||
cudaSetDevice(gpuId);
|
||||
cudaMemcpy(dst_buf, src_buf, size, cudaMemcpyDefault);
|
||||
}
|
||||
|
||||
void Gpu::memcpyKernel(const MemoryBlock &src, const MemoryBlock &dst, size_t size)
|
||||
{
|
||||
auto &mm = MemoryManager::get();
|
||||
void Gpu::memcpyKernel(const MemoryBlock &src, const MemoryBlock &dst,
|
||||
size_t size) {
|
||||
auto &mm = MemoryManager::get();
|
||||
|
||||
auto src_translation = mm.getTranslation(masterPciEAddrSpaceId,
|
||||
src.getAddrSpaceId());
|
||||
auto src_buf = reinterpret_cast<uint8_t*>(src_translation.getLocalAddr(0));
|
||||
auto src_translation =
|
||||
mm.getTranslation(masterPciEAddrSpaceId, src.getAddrSpaceId());
|
||||
auto src_buf = reinterpret_cast<uint8_t *>(src_translation.getLocalAddr(0));
|
||||
|
||||
auto dst_translation = mm.getTranslation(masterPciEAddrSpaceId,
|
||||
dst.getAddrSpaceId());
|
||||
auto dst_buf = reinterpret_cast<uint8_t*>(dst_translation.getLocalAddr(0));
|
||||
auto dst_translation =
|
||||
mm.getTranslation(masterPciEAddrSpaceId, dst.getAddrSpaceId());
|
||||
auto dst_buf = reinterpret_cast<uint8_t *>(dst_translation.getLocalAddr(0));
|
||||
|
||||
cudaSetDevice(gpuId);
|
||||
kernel_memcpy<<<1, 1>>>(dst_buf, src_buf, size);
|
||||
cudaDeviceSynchronize();
|
||||
cudaSetDevice(gpuId);
|
||||
kernel_memcpy<<<1, 1>>>(dst_buf, src_buf, size);
|
||||
cudaDeviceSynchronize();
|
||||
}
|
||||
|
||||
MemoryTranslation
|
||||
Gpu::translate(const MemoryBlock &dst)
|
||||
{
|
||||
auto &mm = MemoryManager::get();
|
||||
return mm.getTranslation(masterPciEAddrSpaceId, dst.getAddrSpaceId());
|
||||
MemoryTranslation Gpu::translate(const MemoryBlock &dst) {
|
||||
auto &mm = MemoryManager::get();
|
||||
return mm.getTranslation(masterPciEAddrSpaceId, dst.getAddrSpaceId());
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<villas::MemoryBlock, villas::MemoryBlock::deallocator_fn>
|
||||
GpuAllocator::allocateBlock(size_t size)
|
||||
{
|
||||
cudaSetDevice(gpu.gpuId);
|
||||
GpuAllocator::allocateBlock(size_t size) {
|
||||
cudaSetDevice(gpu.gpuId);
|
||||
|
||||
void* addr;
|
||||
auto &mm = MemoryManager::get();
|
||||
void *addr;
|
||||
auto &mm = MemoryManager::get();
|
||||
|
||||
// Search for an existing chunk that has enough free memory
|
||||
auto chunk = std::find_if(chunks.begin(), chunks.end(), [&](const auto &chunk) {
|
||||
return chunk->getAvailableMemory() >= size;
|
||||
});
|
||||
// Search for an existing chunk that has enough free memory
|
||||
auto chunk =
|
||||
std::find_if(chunks.begin(), chunks.end(), [&](const auto &chunk) {
|
||||
return chunk->getAvailableMemory() >= size;
|
||||
});
|
||||
|
||||
if (chunk != chunks.end()) {
|
||||
logger->debug("Found existing chunk that can host the requested block");
|
||||
|
||||
if (chunk != chunks.end()) {
|
||||
logger->debug("Found existing chunk that can host the requested block");
|
||||
return (*chunk)->allocateBlock(size);
|
||||
} else {
|
||||
// Allocate a new chunk
|
||||
|
||||
return (*chunk)->allocateBlock(size);
|
||||
}
|
||||
else {
|
||||
// Allocate a new chunk
|
||||
// Rounded-up multiple of GPU page size
|
||||
const size_t chunkSize = size - (size & (GpuPageSize - 1)) + GpuPageSize;
|
||||
logger->debug("Allocate new chunk of {:#x} bytes", chunkSize);
|
||||
|
||||
// Rounded-up multiple of GPU page size
|
||||
const size_t chunkSize = size - (size & (GpuPageSize - 1)) + GpuPageSize;
|
||||
logger->debug("Allocate new chunk of {:#x} bytes", chunkSize);
|
||||
if (cudaSuccess != cudaMalloc(&addr, chunkSize)) {
|
||||
logger->error("cudaMalloc(..., size={}) failed", chunkSize);
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
if (cudaSuccess != cudaMalloc(&addr, chunkSize)) {
|
||||
logger->error("cudaMalloc(..., size={}) failed", chunkSize);
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
// Assemble name for this block
|
||||
std::stringstream name;
|
||||
name << std::showbase << std::hex << reinterpret_cast<uintptr_t>(addr);
|
||||
|
||||
// Assemble name for this block
|
||||
std::stringstream name;
|
||||
name << std::showbase << std::hex << reinterpret_cast<uintptr_t>(addr);
|
||||
auto blockName = mm.getSlaveAddrSpaceName(getName(), name.str());
|
||||
auto blockAddrSpaceId = mm.getOrCreateAddressSpace(blockName);
|
||||
|
||||
auto blockName = mm.getSlaveAddrSpaceName(getName(), name.str());
|
||||
auto blockAddrSpaceId = mm.getOrCreateAddressSpace(blockName);
|
||||
const auto localAddr = reinterpret_cast<uintptr_t>(addr);
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn> mem(
|
||||
new MemoryBlock(localAddr, chunkSize, blockAddrSpaceId), this->free);
|
||||
|
||||
const auto localAddr = reinterpret_cast<uintptr_t>(addr);
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
|
||||
mem(new MemoryBlock(localAddr, chunkSize, blockAddrSpaceId), this->free);
|
||||
insertMemoryBlock(*mem);
|
||||
|
||||
insertMemoryBlock(*mem);
|
||||
// Already make accessible to CPU
|
||||
gpu.makeAccessibleToPCIeAndVA(*mem);
|
||||
|
||||
// Already make accessible to CPU
|
||||
gpu.makeAccessibleToPCIeAndVA(*mem);
|
||||
// Create a new allocator to manage the chunk and push to chunk list
|
||||
chunks.push_front(std::make_unique<LinearAllocator>(std::move(mem)));
|
||||
|
||||
// Create a new allocator to manage the chunk and push to chunk list
|
||||
chunks.push_front(std::make_unique<LinearAllocator>(std::move(mem)));
|
||||
|
||||
// Call again, this time there's a large enough chunk
|
||||
return allocateBlock(size);
|
||||
}
|
||||
// Call again, this time there's a large enough chunk
|
||||
return allocateBlock(size);
|
||||
}
|
||||
}
|
||||
|
||||
Gpu::Gpu(int gpuId) : pImpl{std::make_unique<impl>()}, gpuId(gpuId) {
|
||||
logger = villas::logging.get(getName());
|
||||
|
||||
Gpu::Gpu(int gpuId) :
|
||||
pImpl{std::make_unique<impl>()},
|
||||
gpuId(gpuId)
|
||||
{
|
||||
logger = villas::logging.get(getName());
|
||||
|
||||
pImpl->gdr = gdr_open();
|
||||
if (pImpl->gdr == nullptr) {
|
||||
logger->warn("No GDRcopy support enabled, cannot open /dev/gdrdrv");
|
||||
}
|
||||
pImpl->gdr = gdr_open();
|
||||
if (pImpl->gdr == nullptr) {
|
||||
logger->warn("No GDRcopy support enabled, cannot open /dev/gdrdrv");
|
||||
}
|
||||
}
|
||||
|
||||
bool Gpu::init()
|
||||
{
|
||||
auto &mm = MemoryManager::get();
|
||||
bool Gpu::init() {
|
||||
auto &mm = MemoryManager::get();
|
||||
|
||||
const auto gpuPciEAddrSpaceName = mm.getMasterAddrSpaceName(getName(), "pcie");
|
||||
masterPciEAddrSpaceId = mm.getOrCreateAddressSpace(gpuPciEAddrSpaceName);
|
||||
const auto gpuPciEAddrSpaceName =
|
||||
mm.getMasterAddrSpaceName(getName(), "pcie");
|
||||
masterPciEAddrSpaceId = mm.getOrCreateAddressSpace(gpuPciEAddrSpaceName);
|
||||
|
||||
allocator = std::make_unique<GpuAllocator>(*this);
|
||||
allocator = std::make_unique<GpuAllocator>(*this);
|
||||
|
||||
cudaDeviceProp deviceProp;
|
||||
cudaGetDeviceProperties(&deviceProp, gpuId);
|
||||
cudaDeviceProp deviceProp;
|
||||
cudaGetDeviceProperties(&deviceProp, gpuId);
|
||||
|
||||
pImpl->pdev.slot = {
|
||||
deviceProp.pciDomainID,
|
||||
deviceProp.pciBusID,
|
||||
deviceProp.pciDeviceID,
|
||||
0};
|
||||
pImpl->pdev.slot = {deviceProp.pciDomainID, deviceProp.pciBusID,
|
||||
deviceProp.pciDeviceID, 0};
|
||||
|
||||
struct pci_region* pci_regions = nullptr;
|
||||
const size_t pci_num_regions = pci_get_regions(&pImpl->pdev, &pci_regions);
|
||||
for (size_t i = 0; i < pci_num_regions; i++) {
|
||||
const size_t region_size = pci_regions[i].end - pci_regions[i].start + 1;
|
||||
logger->info("BAR{}: bus addr={:#x} size={:#x}",
|
||||
pci_regions[i].num, pci_regions[i].start, region_size);
|
||||
struct pci_region *pci_regions = nullptr;
|
||||
const size_t pci_num_regions = pci_get_regions(&pImpl->pdev, &pci_regions);
|
||||
for (size_t i = 0; i < pci_num_regions; i++) {
|
||||
const size_t region_size = pci_regions[i].end - pci_regions[i].start + 1;
|
||||
logger->info("BAR{}: bus addr={:#x} size={:#x}", pci_regions[i].num,
|
||||
pci_regions[i].start, region_size);
|
||||
|
||||
char name[] = "BARx";
|
||||
name[3] = '0' + pci_regions[i].num;
|
||||
char name[] = "BARx";
|
||||
name[3] = '0' + pci_regions[i].num;
|
||||
|
||||
auto gpuBarXAddrSpaceName = mm.getSlaveAddrSpaceName(getName(), name);
|
||||
auto gpuBarXAddrSpaceId = mm.getOrCreateAddressSpace(gpuBarXAddrSpaceName);
|
||||
auto gpuBarXAddrSpaceName = mm.getSlaveAddrSpaceName(getName(), name);
|
||||
auto gpuBarXAddrSpaceId = mm.getOrCreateAddressSpace(gpuBarXAddrSpaceName);
|
||||
|
||||
mm.createMapping(pci_regions[i].start, 0, region_size,
|
||||
std::string("PCI-") + name,
|
||||
mm.getPciAddressSpace(), gpuBarXAddrSpaceId);
|
||||
}
|
||||
mm.createMapping(pci_regions[i].start, 0, region_size,
|
||||
std::string("PCI-") + name, mm.getPciAddressSpace(),
|
||||
gpuBarXAddrSpaceId);
|
||||
}
|
||||
|
||||
free(pci_regions);
|
||||
free(pci_regions);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::list<std::unique_ptr<Gpu>> GpuFactory::make() {
|
||||
int deviceCount = 0;
|
||||
cudaGetDeviceCount(&deviceCount);
|
||||
|
||||
std::list<std::unique_ptr<Gpu>>
|
||||
GpuFactory::make()
|
||||
{
|
||||
int deviceCount = 0;
|
||||
cudaGetDeviceCount(&deviceCount);
|
||||
std::list<std::unique_ptr<Gpu>> gpuList;
|
||||
|
||||
std::list<std::unique_ptr<Gpu>> gpuList;
|
||||
for (int gpuId = 0; gpuId < deviceCount; gpuId++) {
|
||||
if (cudaSetDevice(gpuId) != cudaSuccess) {
|
||||
logger->warn("Cannot activate GPU {}", gpuId);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int gpuId = 0; gpuId < deviceCount; gpuId++) {
|
||||
if (cudaSetDevice(gpuId) != cudaSuccess) {
|
||||
logger->warn("Cannot activate GPU {}", gpuId);
|
||||
continue;
|
||||
}
|
||||
auto gpu = std::make_unique<Gpu>(gpuId);
|
||||
|
||||
auto gpu = std::make_unique<Gpu>(gpuId);
|
||||
if (not gpu->init()) {
|
||||
logger->warn("Cannot initialize GPU {}", gpuId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (not gpu->init()) {
|
||||
logger->warn("Cannot initialize GPU {}", gpuId);
|
||||
continue;
|
||||
}
|
||||
gpuList.emplace_back(std::move(gpu));
|
||||
}
|
||||
|
||||
gpuList.emplace_back(std::move(gpu));
|
||||
}
|
||||
logger->info("Initialized {} GPUs", gpuList.size());
|
||||
for (auto &gpu : gpuList) {
|
||||
logger->debug(" - {}", gpu->getName());
|
||||
}
|
||||
|
||||
logger->info("Initialized {} GPUs", gpuList.size());
|
||||
for (auto &gpu : gpuList) {
|
||||
logger->debug(" - {}", gpu->getName());
|
||||
}
|
||||
|
||||
return gpuList;
|
||||
return gpuList;
|
||||
}
|
||||
|
|
|
@ -18,40 +18,40 @@
|
|||
namespace villas {
|
||||
namespace fpga {
|
||||
|
||||
class Card
|
||||
{
|
||||
class Card {
|
||||
public:
|
||||
bool polling;
|
||||
bool polling;
|
||||
|
||||
std::string name; // The name of the FPGA card
|
||||
std::shared_ptr<kernel::vfio::Container> vfioContainer;
|
||||
std::shared_ptr<kernel::vfio::Device> vfioDevice;
|
||||
std::string name; // The name of the FPGA card
|
||||
std::shared_ptr<kernel::vfio::Container> vfioContainer;
|
||||
std::shared_ptr<kernel::vfio::Device> vfioDevice;
|
||||
|
||||
// Slave address space ID to access the PCIe address space from the
|
||||
// FPGA
|
||||
MemoryManager::AddressSpaceId addrSpaceIdDeviceToHost;
|
||||
// Slave address space ID to access the PCIe address space from the
|
||||
// FPGA
|
||||
MemoryManager::AddressSpaceId addrSpaceIdDeviceToHost;
|
||||
|
||||
// Address space identifier of the master address space of this FPGA
|
||||
// card. This will be used for address resolution of all IPs on this
|
||||
// card.
|
||||
MemoryManager::AddressSpaceId addrSpaceIdHostToDevice;
|
||||
// Address space identifier of the master address space of this FPGA
|
||||
// card. This will be used for address resolution of all IPs on this
|
||||
// card.
|
||||
MemoryManager::AddressSpaceId addrSpaceIdHostToDevice;
|
||||
|
||||
std::list<std::shared_ptr<ip::Core>> ips;
|
||||
std::list<std::shared_ptr<ip::Core>> ips;
|
||||
|
||||
virtual ~Card();
|
||||
virtual ~Card();
|
||||
|
||||
virtual bool mapMemoryBlock(const std::shared_ptr<MemoryBlock> block);
|
||||
virtual bool unmapMemoryBlock(const MemoryBlock &block);
|
||||
virtual bool mapMemoryBlock(const std::shared_ptr<MemoryBlock> block);
|
||||
virtual bool unmapMemoryBlock(const MemoryBlock &block);
|
||||
|
||||
std::shared_ptr<ip::Core> lookupIp(const std::string &name) const;
|
||||
std::shared_ptr<ip::Core> lookupIp(const Vlnv &vlnv) const;
|
||||
std::shared_ptr<ip::Core> lookupIp(const ip::IpIdentifier &id) const;
|
||||
std::shared_ptr<ip::Core> lookupIp(const std::string &name) const;
|
||||
std::shared_ptr<ip::Core> lookupIp(const Vlnv &vlnv) const;
|
||||
std::shared_ptr<ip::Core> lookupIp(const ip::IpIdentifier &id) const;
|
||||
|
||||
protected:
|
||||
// Keep a map of already mapped memory blocks
|
||||
std::map<MemoryManager::AddressSpaceId, std::shared_ptr<MemoryBlock>> memoryBlocksMapped;
|
||||
// Keep a map of already mapped memory blocks
|
||||
std::map<MemoryManager::AddressSpaceId, std::shared_ptr<MemoryBlock>>
|
||||
memoryBlocksMapped;
|
||||
|
||||
Logger logger;
|
||||
Logger logger;
|
||||
};
|
||||
|
||||
} // namespace fpga
|
||||
|
|
|
@ -11,10 +11,10 @@
|
|||
#pragma once
|
||||
|
||||
// PCIe BAR number of VILLASfpga registers
|
||||
#define FPGA_PCI_BAR 0
|
||||
#define FPGA_PCI_VID_XILINX 0x10ee
|
||||
#define FPGA_PCI_PID_VFPGA 0x7022
|
||||
#define FPGA_PCI_BAR 0
|
||||
#define FPGA_PCI_VID_XILINX 0x10ee
|
||||
#define FPGA_PCI_PID_VFPGA 0x7022
|
||||
|
||||
/* AXI Bus frequency for all components
|
||||
* except RTDS AXI Stream bridge which runs at RTDS_HZ (100 Mhz) */
|
||||
#define FPGA_AXI_HZ 125000000 // 125 MHz
|
||||
#define FPGA_AXI_HZ 125000000 // 125 MHz
|
||||
|
|
|
@ -10,17 +10,17 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <jansson.h>
|
||||
#include <fmt/ostream.h>
|
||||
#include <villas/log.hpp>
|
||||
#include <jansson.h>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <villas/colors.hpp>
|
||||
#include <villas/config.hpp>
|
||||
#include <villas/fpga/vlnv.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/memory.hpp>
|
||||
#include <villas/plugin.hpp>
|
||||
#include <villas/fpga/vlnv.hpp>
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
|
@ -37,309 +37,220 @@ class InterruptController;
|
|||
|
||||
class IpIdentifier {
|
||||
public:
|
||||
IpIdentifier(const Vlnv &vlnv = Vlnv::getWildcard(),
|
||||
const std::string &name = "")
|
||||
: vlnv(vlnv), name(name) {}
|
||||
|
||||
IpIdentifier(const Vlnv &vlnv = Vlnv::getWildcard(), const std::string &name = "") :
|
||||
vlnv(vlnv),
|
||||
name(name)
|
||||
{ }
|
||||
IpIdentifier(const std::string &vlnvString, const std::string &name = "")
|
||||
: vlnv(vlnvString), name(name) {}
|
||||
|
||||
IpIdentifier(const std::string &vlnvString, const std::string &name = "") :
|
||||
vlnv(vlnvString),
|
||||
name(name)
|
||||
{ }
|
||||
const std::string &getName() const { return name; }
|
||||
|
||||
const std::string&
|
||||
getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
const Vlnv &getVlnv() const { return vlnv; }
|
||||
|
||||
const Vlnv&
|
||||
getVlnv() const
|
||||
{
|
||||
return vlnv;
|
||||
}
|
||||
friend std::ostream &operator<<(std::ostream &stream,
|
||||
const IpIdentifier &id) {
|
||||
return stream << id.name << " vlnv=" << id.vlnv;
|
||||
}
|
||||
|
||||
friend std::ostream&
|
||||
operator<< (std::ostream &stream, const IpIdentifier &id)
|
||||
{
|
||||
return stream << id.name << " vlnv=" << id.vlnv;
|
||||
}
|
||||
bool operator==(const IpIdentifier &otherId) const {
|
||||
const bool vlnvWildcard = otherId.getVlnv() == Vlnv::getWildcard();
|
||||
const bool nameWildcard =
|
||||
this->getName().empty() or otherId.getName().empty();
|
||||
|
||||
bool
|
||||
operator==(const IpIdentifier &otherId) const {
|
||||
const bool vlnvWildcard = otherId.getVlnv() == Vlnv::getWildcard();
|
||||
const bool nameWildcard = this->getName().empty() or otherId.getName().empty();
|
||||
const bool vlnvMatch = vlnvWildcard or this->getVlnv() == otherId.getVlnv();
|
||||
const bool nameMatch = nameWildcard or this->getName() == otherId.getName();
|
||||
|
||||
const bool vlnvMatch = vlnvWildcard or this->getVlnv() == otherId.getVlnv();
|
||||
const bool nameMatch = nameWildcard or this->getName() == otherId.getName();
|
||||
return vlnvMatch and nameMatch;
|
||||
}
|
||||
|
||||
return vlnvMatch and nameMatch;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const IpIdentifier &otherId) const
|
||||
{
|
||||
return !(*this == otherId);
|
||||
}
|
||||
bool operator!=(const IpIdentifier &otherId) const {
|
||||
return !(*this == otherId);
|
||||
}
|
||||
|
||||
private:
|
||||
Vlnv vlnv;
|
||||
std::string name;
|
||||
Vlnv vlnv;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
class Core {
|
||||
friend CoreFactory;
|
||||
friend CoreFactory;
|
||||
|
||||
public:
|
||||
Core() :
|
||||
card(nullptr)
|
||||
{ }
|
||||
Core() : card(nullptr) {}
|
||||
|
||||
virtual
|
||||
~Core() = default;
|
||||
virtual ~Core() = default;
|
||||
|
||||
public:
|
||||
// Generic management interface for IPs
|
||||
// Generic management interface for IPs
|
||||
|
||||
// Runtime setup of IP, should access and initialize hardware
|
||||
virtual
|
||||
bool init()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Runtime setup of IP, should access and initialize hardware
|
||||
virtual bool init() { return true; }
|
||||
|
||||
// Runtime check of IP, should verify basic functionality
|
||||
virtual
|
||||
bool check()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Runtime check of IP, should verify basic functionality
|
||||
virtual bool check() { return true; }
|
||||
|
||||
// Generic disabling of IP, meaning may depend on IP
|
||||
virtual
|
||||
bool stop()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Generic disabling of IP, meaning may depend on IP
|
||||
virtual bool stop() { return true; }
|
||||
|
||||
// Reset the IP, it should behave like freshly initialized afterwards
|
||||
virtual
|
||||
bool reset()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Reset the IP, it should behave like freshly initialized afterwards
|
||||
virtual bool reset() { return true; }
|
||||
|
||||
// Print some debug information about the IP
|
||||
virtual
|
||||
void dump();
|
||||
// Print some debug information about the IP
|
||||
virtual void dump();
|
||||
|
||||
protected:
|
||||
// Key-type for accessing maps addressTranslations and slaveAddressSpaces
|
||||
using MemoryBlockName = std::string;
|
||||
// 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<MemoryBlockName> getMemoryBlocks() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
// Each IP can declare via this function which memory blocks it requires
|
||||
virtual std::list<MemoryBlockName> getMemoryBlocks() const { return {}; }
|
||||
|
||||
public:
|
||||
const std::string& getInstanceName() const
|
||||
{
|
||||
return id.getName();
|
||||
}
|
||||
const std::string &getInstanceName() const { return id.getName(); }
|
||||
|
||||
// Operators
|
||||
// Operators
|
||||
|
||||
bool operator==(const Vlnv &otherVlnv) const
|
||||
{
|
||||
return id.getVlnv() == otherVlnv;
|
||||
}
|
||||
bool operator==(const Vlnv &otherVlnv) const {
|
||||
return id.getVlnv() == otherVlnv;
|
||||
}
|
||||
|
||||
bool operator!=(const Vlnv &otherVlnv) const
|
||||
{
|
||||
return id.getVlnv() != otherVlnv;
|
||||
}
|
||||
bool operator!=(const Vlnv &otherVlnv) const {
|
||||
return id.getVlnv() != otherVlnv;
|
||||
}
|
||||
|
||||
bool operator==(const IpIdentifier &otherId) const
|
||||
{
|
||||
return this->id == otherId;
|
||||
}
|
||||
bool operator==(const IpIdentifier &otherId) const {
|
||||
return this->id == otherId;
|
||||
}
|
||||
|
||||
bool operator!=(const IpIdentifier &otherId) const
|
||||
{
|
||||
return this->id != otherId;
|
||||
}
|
||||
bool operator!=(const IpIdentifier &otherId) const {
|
||||
return this->id != otherId;
|
||||
}
|
||||
|
||||
bool operator==(const std::string &otherName) const
|
||||
{
|
||||
return getInstanceName() == otherName;
|
||||
}
|
||||
bool operator==(const std::string &otherName) const {
|
||||
return getInstanceName() == otherName;
|
||||
}
|
||||
|
||||
bool operator!=(const std::string &otherName) const
|
||||
{
|
||||
return getInstanceName() != otherName;
|
||||
}
|
||||
bool operator!=(const std::string &otherName) const {
|
||||
return getInstanceName() != otherName;
|
||||
}
|
||||
|
||||
bool operator==(const Core &otherIp) const
|
||||
{
|
||||
return this->id == otherIp.id;
|
||||
}
|
||||
bool operator==(const Core &otherIp) const { return this->id == otherIp.id; }
|
||||
|
||||
bool operator!=(const Core &otherIp) const
|
||||
{
|
||||
return this->id != otherIp.id;
|
||||
}
|
||||
bool operator!=(const Core &otherIp) const { return this->id != otherIp.id; }
|
||||
|
||||
friend
|
||||
std::ostream& operator<< (std::ostream &stream, const Core &ip)
|
||||
{
|
||||
return stream << ip.id;
|
||||
}
|
||||
friend std::ostream &operator<<(std::ostream &stream, const Core &ip) {
|
||||
return stream << ip.id;
|
||||
}
|
||||
|
||||
protected:
|
||||
uintptr_t getBaseAddr(const MemoryBlockName &block) const
|
||||
{
|
||||
return getLocalAddr(block, 0);
|
||||
}
|
||||
uintptr_t getBaseAddr(const MemoryBlockName &block) const {
|
||||
return getLocalAddr(block, 0);
|
||||
}
|
||||
|
||||
uintptr_t getLocalAddr(const MemoryBlockName &block, uintptr_t address) const;
|
||||
uintptr_t getLocalAddr(const MemoryBlockName &block, uintptr_t address) const;
|
||||
|
||||
MemoryManager::AddressSpaceId getAddressSpaceId(const MemoryBlockName &block) const
|
||||
{
|
||||
return slaveAddressSpaces.at(block);
|
||||
}
|
||||
MemoryManager::AddressSpaceId
|
||||
getAddressSpaceId(const MemoryBlockName &block) const {
|
||||
return slaveAddressSpaces.at(block);
|
||||
}
|
||||
|
||||
InterruptController* getInterruptController(const std::string &interruptName) const;
|
||||
InterruptController *
|
||||
getInterruptController(const std::string &interruptName) const;
|
||||
|
||||
MemoryManager::AddressSpaceId getMasterAddrSpaceByInterface(const std::string &masterInterfaceName) const
|
||||
{
|
||||
return busMasterInterfaces.at(masterInterfaceName);
|
||||
}
|
||||
MemoryManager::AddressSpaceId
|
||||
getMasterAddrSpaceByInterface(const std::string &masterInterfaceName) const {
|
||||
return busMasterInterfaces.at(masterInterfaceName);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T readMemory(const std::string &block, uintptr_t address) const
|
||||
{
|
||||
return *(reinterpret_cast<T*>(getLocalAddr(block, address)));
|
||||
}
|
||||
template <typename T>
|
||||
T readMemory(const std::string &block, uintptr_t address) const {
|
||||
return *(reinterpret_cast<T *>(getLocalAddr(block, address)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeMemory(const std::string &block, uintptr_t address, T value)
|
||||
{
|
||||
T* ptr = reinterpret_cast<T*>(getLocalAddr(block, address)); *ptr = value;
|
||||
}
|
||||
template <typename T>
|
||||
void writeMemory(const std::string &block, uintptr_t address, T value) {
|
||||
T *ptr = reinterpret_cast<T *>(getLocalAddr(block, address));
|
||||
*ptr = value;
|
||||
}
|
||||
|
||||
protected:
|
||||
struct IrqPort {
|
||||
int num;
|
||||
InterruptController* irqController;
|
||||
std::string description;
|
||||
};
|
||||
struct IrqPort {
|
||||
int num;
|
||||
InterruptController *irqController;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
// Specialized logger instance with the IPs name set as category
|
||||
Logger logger;
|
||||
// Specialized logger instance with the IPs name set as category
|
||||
Logger logger;
|
||||
|
||||
// FPGA card this IP is instantiated on (populated by FpgaIpFactory)
|
||||
Card* card;
|
||||
// FPGA card this IP is instantiated on (populated by FpgaIpFactory)
|
||||
Card *card;
|
||||
|
||||
// Identifier of this IP with its instance name and VLNV
|
||||
IpIdentifier id;
|
||||
// Identifier of this IP with its instance name and VLNV
|
||||
IpIdentifier id;
|
||||
|
||||
// All interrupts of this IP with their associated interrupt controller
|
||||
std::map<std::string, IrqPort> irqs;
|
||||
// All interrupts of this IP with their associated interrupt controller
|
||||
std::map<std::string, IrqPort> irqs;
|
||||
|
||||
// Cached translations from the process address space to each memory block
|
||||
std::map<MemoryBlockName, MemoryTranslation> addressTranslations;
|
||||
// Cached translations from the process address space to each memory block
|
||||
std::map<MemoryBlockName, MemoryTranslation> addressTranslations;
|
||||
|
||||
// Lookup for IP's slave address spaces (= memory blocks)
|
||||
std::map<MemoryBlockName, MemoryManager::AddressSpaceId> slaveAddressSpaces;
|
||||
// Lookup for IP's slave address spaces (= memory blocks)
|
||||
std::map<MemoryBlockName, MemoryManager::AddressSpaceId> slaveAddressSpaces;
|
||||
|
||||
// AXI bus master interfaces to access memory somewhere
|
||||
std::map<std::string, MemoryManager::AddressSpaceId> busMasterInterfaces;
|
||||
// AXI bus master interfaces to access memory somewhere
|
||||
std::map<std::string, MemoryManager::AddressSpaceId> busMasterInterfaces;
|
||||
};
|
||||
|
||||
class CoreFactory : public plugin::Plugin {
|
||||
public:
|
||||
using plugin::Plugin::Plugin;
|
||||
using plugin::Plugin::Plugin;
|
||||
|
||||
// Returns a running and checked FPGA IP
|
||||
static
|
||||
std::list<std::shared_ptr<Core>> make(Card* card, json_t *json_ips);
|
||||
// Returns a running and checked FPGA IP
|
||||
static std::list<std::shared_ptr<Core>> make(Card *card, json_t *json_ips);
|
||||
|
||||
virtual
|
||||
std::string getType() const
|
||||
{
|
||||
return "core";
|
||||
}
|
||||
virtual std::string getType() const { return "core"; }
|
||||
|
||||
protected:
|
||||
enum PollingMode {
|
||||
POLL,
|
||||
IRQ,
|
||||
};
|
||||
enum PollingMode {
|
||||
POLL,
|
||||
IRQ,
|
||||
};
|
||||
|
||||
Logger getLogger()
|
||||
{
|
||||
return villas::logging.get(getName());
|
||||
}
|
||||
Logger getLogger() { return villas::logging.get(getName()); }
|
||||
|
||||
// Configure IP instance from JSON config
|
||||
virtual
|
||||
void parse(Core &, json_t *)
|
||||
{ }
|
||||
// Configure IP instance from JSON config
|
||||
virtual void parse(Core &, json_t *) {}
|
||||
|
||||
static
|
||||
Logger getStaticLogger()
|
||||
{
|
||||
return villas::logging.get("core:factory");
|
||||
}
|
||||
static Logger getStaticLogger() {
|
||||
return villas::logging.get("core:factory");
|
||||
}
|
||||
|
||||
private:
|
||||
virtual
|
||||
void configurePollingMode(Core &, PollingMode)
|
||||
{ }
|
||||
virtual void configurePollingMode(Core &, PollingMode) {}
|
||||
|
||||
virtual
|
||||
Vlnv getCompatibleVlnv() const = 0;
|
||||
virtual Vlnv getCompatibleVlnv() const = 0;
|
||||
|
||||
// Create a concrete IP instance
|
||||
virtual
|
||||
Core* make() const = 0;
|
||||
// Create a concrete IP instance
|
||||
virtual Core *make() const = 0;
|
||||
|
||||
static
|
||||
CoreFactory* lookup(const Vlnv &vlnv);
|
||||
static CoreFactory *lookup(const Vlnv &vlnv);
|
||||
};
|
||||
|
||||
template<typename T, const char *name, const char *desc, const char *vlnv>
|
||||
template <typename T, const char *name, const char *desc, const char *vlnv>
|
||||
class CorePlugin : public CoreFactory {
|
||||
|
||||
public:
|
||||
virtual
|
||||
std::string getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
virtual std::string getName() const { return name; }
|
||||
|
||||
virtual
|
||||
std::string getDescription() const
|
||||
{
|
||||
return desc;
|
||||
}
|
||||
virtual std::string getDescription() const { return desc; }
|
||||
|
||||
private:
|
||||
virtual
|
||||
Vlnv getCompatibleVlnv() const
|
||||
{
|
||||
return Vlnv(vlnv);
|
||||
}
|
||||
virtual Vlnv getCompatibleVlnv() const { return Vlnv(vlnv); }
|
||||
|
||||
// Create a concrete IP instance
|
||||
Core* make() const
|
||||
{
|
||||
return new T;
|
||||
};
|
||||
// Create a concrete IP instance
|
||||
Core *make() const { return new T; };
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
@ -351,6 +262,5 @@ template <>
|
|||
class fmt::formatter<villas::fpga::ip::IpIdentifier>
|
||||
: public fmt::ostream_formatter {};
|
||||
template <>
|
||||
class fmt::formatter<villas::fpga::ip::Core>
|
||||
: public fmt::ostream_formatter {};
|
||||
class fmt::formatter<villas::fpga::ip::Core> : public fmt::ostream_formatter {};
|
||||
#endif
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct villasfpga_handle_t *villasfpga_handle;
|
||||
|
@ -21,15 +20,18 @@ villasfpga_handle villasfpga_init(const char *configFile);
|
|||
|
||||
void villasfpga_destroy(villasfpga_handle handle);
|
||||
|
||||
int villasfpga_alloc(villasfpga_handle handle, villasfpga_memory *mem, size_t size);
|
||||
int villasfpga_alloc(villasfpga_handle handle, villasfpga_memory *mem,
|
||||
size_t size);
|
||||
int villasfpga_register(villasfpga_handle handle, villasfpga_memory *mem);
|
||||
int villasfpga_free(villasfpga_memory mem);
|
||||
void* villasfpga_get_ptr(villasfpga_memory mem);
|
||||
void *villasfpga_get_ptr(villasfpga_memory mem);
|
||||
|
||||
int villasfpga_read(villasfpga_handle handle, villasfpga_memory mem, size_t size);
|
||||
int villasfpga_read(villasfpga_handle handle, villasfpga_memory mem,
|
||||
size_t size);
|
||||
int villasfpga_read_complete(villasfpga_handle handle, size_t *size);
|
||||
|
||||
int villasfpga_write(villasfpga_handle handle, villasfpga_memory mem, size_t size);
|
||||
int villasfpga_write(villasfpga_handle handle, villasfpga_memory mem,
|
||||
size_t size);
|
||||
int villasfpga_write_complete(villasfpga_handle handle, size_t *size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -15,39 +15,27 @@ namespace ip {
|
|||
|
||||
class Aurora : public Node {
|
||||
public:
|
||||
static constexpr const char* masterPort = "m_axis";
|
||||
static constexpr const char* slavePort = "s_axis";
|
||||
static constexpr const char *masterPort = "m_axis";
|
||||
static constexpr const char *slavePort = "s_axis";
|
||||
|
||||
virtual
|
||||
void dump() override;
|
||||
virtual void dump() override;
|
||||
|
||||
std::list<std::string> getMemoryBlocks() const
|
||||
{
|
||||
return {
|
||||
registerMemory
|
||||
};
|
||||
}
|
||||
std::list<std::string> getMemoryBlocks() const { return {registerMemory}; }
|
||||
|
||||
const StreamVertex&
|
||||
getDefaultSlavePort() const
|
||||
{
|
||||
return getSlavePort(slavePort);
|
||||
}
|
||||
const StreamVertex &getDefaultSlavePort() const {
|
||||
return getSlavePort(slavePort);
|
||||
}
|
||||
|
||||
const StreamVertex&
|
||||
getDefaultMasterPort() const
|
||||
{
|
||||
return getMasterPort(masterPort);
|
||||
}
|
||||
const StreamVertex &getDefaultMasterPort() const {
|
||||
return getMasterPort(masterPort);
|
||||
}
|
||||
|
||||
void
|
||||
setLoopback(bool state);
|
||||
void setLoopback(bool state);
|
||||
|
||||
void
|
||||
resetFrameCounters();
|
||||
void resetFrameCounters();
|
||||
|
||||
private:
|
||||
static constexpr const char registerMemory[] = "reg0";
|
||||
static constexpr const char registerMemory[] = "reg0";
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
|
|
@ -15,20 +15,16 @@ namespace ip {
|
|||
|
||||
class AuroraXilinx : public Node {
|
||||
public:
|
||||
static constexpr const char* masterPort = "USER_DATA_M_AXI_RX";
|
||||
static constexpr const char* slavePort = "USER_DATA_S_AXI_TX";
|
||||
static constexpr const char *masterPort = "USER_DATA_M_AXI_RX";
|
||||
static constexpr const char *slavePort = "USER_DATA_S_AXI_TX";
|
||||
|
||||
const StreamVertex&
|
||||
getDefaultSlavePort() const
|
||||
{
|
||||
return getSlavePort(slavePort);
|
||||
}
|
||||
const StreamVertex &getDefaultSlavePort() const {
|
||||
return getSlavePort(slavePort);
|
||||
}
|
||||
|
||||
const StreamVertex&
|
||||
getDefaultMasterPort() const
|
||||
{
|
||||
return getMasterPort(masterPort);
|
||||
}
|
||||
const StreamVertex &getDefaultMasterPort() const {
|
||||
return getMasterPort(masterPort);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <villas/memory.hpp>
|
||||
#include <villas/fpga/core.hpp>
|
||||
#include <villas/memory.hpp>
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
|
@ -17,64 +17,39 @@ namespace ip {
|
|||
class BramFactory;
|
||||
|
||||
class Bram : public Core {
|
||||
friend class BramFactory;
|
||||
friend class BramFactory;
|
||||
|
||||
public:
|
||||
virtual bool init() override;
|
||||
|
||||
virtual
|
||||
bool init() override;
|
||||
|
||||
LinearAllocator& getAllocator()
|
||||
{
|
||||
return *allocator;
|
||||
}
|
||||
LinearAllocator &getAllocator() { return *allocator; }
|
||||
|
||||
private:
|
||||
static constexpr
|
||||
const char* memoryBlock = "Mem0";
|
||||
static constexpr const char *memoryBlock = "Mem0";
|
||||
|
||||
std::list<MemoryBlockName> getMemoryBlocks() const
|
||||
{
|
||||
return {
|
||||
memoryBlock
|
||||
};
|
||||
}
|
||||
std::list<MemoryBlockName> getMemoryBlocks() const { return {memoryBlock}; }
|
||||
|
||||
size_t size;
|
||||
std::unique_ptr<LinearAllocator> allocator;
|
||||
size_t size;
|
||||
std::unique_ptr<LinearAllocator> allocator;
|
||||
};
|
||||
|
||||
class BramFactory : public CoreFactory {
|
||||
|
||||
public:
|
||||
virtual
|
||||
std::string getName() const
|
||||
{
|
||||
return "bram";
|
||||
}
|
||||
virtual std::string getName() const { return "bram"; }
|
||||
|
||||
virtual
|
||||
std::string getDescription() const
|
||||
{
|
||||
return "Block RAM";
|
||||
}
|
||||
virtual std::string getDescription() const { return "Block RAM"; }
|
||||
|
||||
private:
|
||||
virtual
|
||||
Vlnv getCompatibleVlnv() const
|
||||
{
|
||||
return Vlnv("xilinx.com:ip:axi_bram_ctrl:");
|
||||
}
|
||||
virtual Vlnv getCompatibleVlnv() const {
|
||||
return Vlnv("xilinx.com:ip:axi_bram_ctrl:");
|
||||
}
|
||||
|
||||
// Create a concrete IP instance
|
||||
Core* make() const
|
||||
{
|
||||
return new Bram;
|
||||
};
|
||||
// Create a concrete IP instance
|
||||
Core *make() const { return new Bram; };
|
||||
|
||||
protected:
|
||||
virtual
|
||||
void parse(Core &, json_t *) override;
|
||||
virtual void parse(Core &, json_t *) override;
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
|
|
@ -10,178 +10,156 @@
|
|||
#pragma once
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
#include <xilinx/xaxidma.h>
|
||||
#include <villas/config.hpp>
|
||||
#include <villas/memory.hpp>
|
||||
#include <villas/fpga/node.hpp>
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/fpga/node.hpp>
|
||||
#include <villas/memory.hpp>
|
||||
#include <xilinx/xaxidma.h>
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
namespace ip {
|
||||
|
||||
class Dma : public Node
|
||||
{
|
||||
class Dma : public Node {
|
||||
public:
|
||||
friend class DmaFactory;
|
||||
friend class DmaFactory;
|
||||
|
||||
virtual
|
||||
~Dma();
|
||||
virtual ~Dma();
|
||||
|
||||
virtual
|
||||
bool init() override;
|
||||
virtual bool init() override;
|
||||
|
||||
bool reset() override;
|
||||
bool reset() override;
|
||||
|
||||
// Memory-mapped to stream (MM2S)
|
||||
bool write(const MemoryBlock &mem, size_t len);
|
||||
// Memory-mapped to stream (MM2S)
|
||||
bool write(const MemoryBlock &mem, size_t len);
|
||||
|
||||
// Stream to memory-mapped (S2MM)
|
||||
bool read(const MemoryBlock &mem, size_t len);
|
||||
// Stream to memory-mapped (S2MM)
|
||||
bool read(const MemoryBlock &mem, size_t len);
|
||||
|
||||
struct Completion {
|
||||
Completion() : bytes(0), bds(0), interrupts(0) { }
|
||||
size_t bytes; // Number of bytes transferred
|
||||
size_t bds; // Number of buffer descriptors used (only for scatter-gather)
|
||||
size_t interrupts; // Number of interrupts received since last call (only if interrupts enabled)
|
||||
};
|
||||
struct Completion {
|
||||
Completion() : bytes(0), bds(0), interrupts(0) {}
|
||||
size_t bytes; // Number of bytes transferred
|
||||
size_t bds; // Number of buffer descriptors used (only for scatter-gather)
|
||||
size_t
|
||||
interrupts; // Number of interrupts received since last call (only if interrupts enabled)
|
||||
};
|
||||
|
||||
Completion writeComplete()
|
||||
{
|
||||
return hasScatterGather() ? writeCompleteScatterGather() : writeCompleteSimple();
|
||||
}
|
||||
Completion writeComplete() {
|
||||
return hasScatterGather() ? writeCompleteScatterGather()
|
||||
: writeCompleteSimple();
|
||||
}
|
||||
|
||||
Completion readComplete()
|
||||
{
|
||||
return hasScatterGather() ? readCompleteScatterGather() : readCompleteSimple();
|
||||
}
|
||||
Completion readComplete() {
|
||||
return hasScatterGather() ? readCompleteScatterGather()
|
||||
: readCompleteSimple();
|
||||
}
|
||||
|
||||
bool memcpy(const MemoryBlock &src, const MemoryBlock &dst, size_t len);
|
||||
bool memcpy(const MemoryBlock &src, const MemoryBlock &dst, size_t len);
|
||||
|
||||
void makeAccesibleFromVA(std::shared_ptr<MemoryBlock> mem);
|
||||
bool makeInaccesibleFromVA(const MemoryBlock &mem);
|
||||
void makeAccesibleFromVA(std::shared_ptr<MemoryBlock> mem);
|
||||
bool makeInaccesibleFromVA(const MemoryBlock &mem);
|
||||
|
||||
inline
|
||||
bool hasScatterGather() const
|
||||
{
|
||||
return xConfig.HasSg;
|
||||
}
|
||||
inline bool hasScatterGather() const { return xConfig.HasSg; }
|
||||
|
||||
const StreamVertex& getDefaultSlavePort() const
|
||||
{
|
||||
return getSlavePort(s2mmPort);
|
||||
}
|
||||
const StreamVertex &getDefaultSlavePort() const {
|
||||
return getSlavePort(s2mmPort);
|
||||
}
|
||||
|
||||
const StreamVertex& getDefaultMasterPort() const
|
||||
{
|
||||
return getMasterPort(mm2sPort);
|
||||
}
|
||||
const StreamVertex &getDefaultMasterPort() const {
|
||||
return getMasterPort(mm2sPort);
|
||||
}
|
||||
|
||||
static constexpr const char* s2mmPort = "S2MM";
|
||||
static constexpr const char* mm2sPort = "MM2S";
|
||||
static constexpr const char *s2mmPort = "S2MM";
|
||||
static constexpr const char *mm2sPort = "MM2S";
|
||||
|
||||
bool isMemoryBlockAccesible(const MemoryBlock &mem, const std::string &interface);
|
||||
bool isMemoryBlockAccesible(const MemoryBlock &mem,
|
||||
const std::string &interface);
|
||||
|
||||
virtual void dump() override;
|
||||
|
||||
virtual
|
||||
void dump() override;
|
||||
private:
|
||||
bool writeScatterGather(const void* buf, size_t len);
|
||||
bool readScatterGather(void* buf, size_t len);
|
||||
Completion writeCompleteScatterGather();
|
||||
Completion readCompleteScatterGather();
|
||||
bool writeScatterGather(const void *buf, size_t len);
|
||||
bool readScatterGather(void *buf, size_t len);
|
||||
Completion writeCompleteScatterGather();
|
||||
Completion readCompleteScatterGather();
|
||||
|
||||
bool writeSimple(const void* buf, size_t len);
|
||||
bool readSimple(void* buf, size_t len);
|
||||
Completion writeCompleteSimple();
|
||||
Completion readCompleteSimple();
|
||||
bool writeSimple(const void *buf, size_t len);
|
||||
bool readSimple(void *buf, size_t len);
|
||||
Completion writeCompleteSimple();
|
||||
Completion readCompleteSimple();
|
||||
|
||||
void setupScatterGather();
|
||||
void setupScatterGatherRingRx();
|
||||
void setupScatterGatherRingTx();
|
||||
void setupScatterGather();
|
||||
void setupScatterGatherRingRx();
|
||||
void setupScatterGatherRingTx();
|
||||
|
||||
static constexpr char registerMemory[] = "Reg";
|
||||
static constexpr char registerMemory[] = "Reg";
|
||||
|
||||
static constexpr char mm2sInterrupt[] = "mm2s_introut";
|
||||
static constexpr char mm2sInterface[] = "M_AXI_MM2S";
|
||||
static constexpr char mm2sInterrupt[] = "mm2s_introut";
|
||||
static constexpr char mm2sInterface[] = "M_AXI_MM2S";
|
||||
|
||||
static constexpr char s2mmInterrupt[] = "s2mm_introut";
|
||||
static constexpr char s2mmInterface[] = "M_AXI_S2MM";
|
||||
static constexpr char s2mmInterrupt[] = "s2mm_introut";
|
||||
static constexpr char s2mmInterface[] = "M_AXI_S2MM";
|
||||
|
||||
// Optional Scatter-Gather interface to access descriptors
|
||||
static constexpr char sgInterface[] = "M_AXI_SG";
|
||||
// Optional Scatter-Gather interface to access descriptors
|
||||
static constexpr char sgInterface[] = "M_AXI_SG";
|
||||
|
||||
std::list<MemoryBlockName> getMemoryBlocks() const
|
||||
{
|
||||
return {
|
||||
registerMemory
|
||||
};
|
||||
}
|
||||
std::list<MemoryBlockName> getMemoryBlocks() const {
|
||||
return {registerMemory};
|
||||
}
|
||||
|
||||
XAxiDma xDma;
|
||||
XAxiDma_Config xConfig;
|
||||
XAxiDma xDma;
|
||||
XAxiDma_Config xConfig;
|
||||
|
||||
std::mutex hwLock;
|
||||
std::mutex hwLock;
|
||||
|
||||
bool configDone = false;
|
||||
// use polling to wait for DMA completion or interrupts via efds
|
||||
bool polling = false;
|
||||
// Timeout after which the DMA controller issues in interrupt if no data has been received
|
||||
// Delay is 125 x <delay> x (clock period of SG clock). SG clock is 100 MHz by default.
|
||||
int delay = 0;
|
||||
// Coalesce is the number of messages/BDs to wait for before issuing an interrupt
|
||||
uint32_t writeCoalesce = 1;
|
||||
uint32_t readCoalesce = 1;
|
||||
bool configDone = false;
|
||||
// use polling to wait for DMA completion or interrupts via efds
|
||||
bool polling = false;
|
||||
// Timeout after which the DMA controller issues in interrupt if no data has been received
|
||||
// Delay is 125 x <delay> x (clock period of SG clock). SG clock is 100 MHz by default.
|
||||
int delay = 0;
|
||||
// Coalesce is the number of messages/BDs to wait for before issuing an interrupt
|
||||
uint32_t writeCoalesce = 1;
|
||||
uint32_t readCoalesce = 1;
|
||||
|
||||
// (maximum) size of a single message on the read channel in bytes.
|
||||
// The message buffer/BD should have enough room for this many bytes.
|
||||
size_t readMsgSize = 4;
|
||||
// (maximum) size of a single message on the read channel in bytes.
|
||||
// The message buffer/BD should have enough room for this many bytes.
|
||||
size_t readMsgSize = 4;
|
||||
|
||||
// When using SG: ringBdSize is the maximum number of BDs usable in the ring
|
||||
// Depending on alignment, the actual number of BDs usable can be smaller
|
||||
static constexpr size_t requestedRingBdSize = 2048;
|
||||
static constexpr size_t requestedRingBdSizeMemory = requestedRingBdSize * sizeof(XAxiDma_Bd);
|
||||
uint32_t actualRingBdSize = XAxiDma_BdRingCntCalc(XAXIDMA_BD_MINIMUM_ALIGNMENT, requestedRingBdSizeMemory);
|
||||
std::shared_ptr<MemoryBlock> sgRingTx;
|
||||
std::shared_ptr<MemoryBlock> sgRingRx;
|
||||
// When using SG: ringBdSize is the maximum number of BDs usable in the ring
|
||||
// Depending on alignment, the actual number of BDs usable can be smaller
|
||||
static constexpr size_t requestedRingBdSize = 2048;
|
||||
static constexpr size_t requestedRingBdSizeMemory =
|
||||
requestedRingBdSize * sizeof(XAxiDma_Bd);
|
||||
uint32_t actualRingBdSize = XAxiDma_BdRingCntCalc(
|
||||
XAXIDMA_BD_MINIMUM_ALIGNMENT, requestedRingBdSizeMemory);
|
||||
std::shared_ptr<MemoryBlock> sgRingTx;
|
||||
std::shared_ptr<MemoryBlock> sgRingRx;
|
||||
};
|
||||
|
||||
class DmaFactory : NodeFactory {
|
||||
|
||||
public:
|
||||
virtual
|
||||
std::string getName() const
|
||||
{
|
||||
return "dma";
|
||||
}
|
||||
virtual std::string getName() const { return "dma"; }
|
||||
|
||||
virtual
|
||||
std::string getDescription() const
|
||||
{
|
||||
return "Xilinx's AXI4 Direct Memory Access Controller";
|
||||
}
|
||||
virtual std::string getDescription() const {
|
||||
return "Xilinx's AXI4 Direct Memory Access Controller";
|
||||
}
|
||||
|
||||
private:
|
||||
virtual
|
||||
Vlnv getCompatibleVlnv() const
|
||||
{
|
||||
return Vlnv("xilinx.com:ip:axi_dma:");
|
||||
}
|
||||
virtual Vlnv getCompatibleVlnv() const {
|
||||
return Vlnv("xilinx.com:ip:axi_dma:");
|
||||
}
|
||||
|
||||
// Create a concrete IP instance
|
||||
Core* make() const
|
||||
{
|
||||
return new Dma;
|
||||
};
|
||||
// Create a concrete IP instance
|
||||
Core *make() const { return new Dma; };
|
||||
|
||||
protected:
|
||||
virtual
|
||||
void parse(Core& ip, json_t* json) override;
|
||||
virtual void parse(Core &ip, json_t *json) override;
|
||||
|
||||
virtual
|
||||
void configurePollingMode(Core& ip, PollingMode mode) override
|
||||
{
|
||||
dynamic_cast<Dma&>(ip).polling = (mode == POLL);
|
||||
}
|
||||
virtual void configurePollingMode(Core &ip, PollingMode mode) override {
|
||||
dynamic_cast<Dma &>(ip).polling = (mode == POLL);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
@ -190,6 +168,5 @@ protected:
|
|||
|
||||
#ifndef FMT_LEGACY_OSTREAM_FORMATTER
|
||||
template <>
|
||||
class fmt::formatter<villas::fpga::ip::Dma>
|
||||
: public fmt::ostream_formatter {};
|
||||
class fmt::formatter<villas::fpga::ip::Dma> : public fmt::ostream_formatter {};
|
||||
#endif
|
||||
|
|
|
@ -17,27 +17,21 @@ namespace ip {
|
|||
|
||||
class EMC : public Core {
|
||||
public:
|
||||
virtual bool init() override;
|
||||
|
||||
virtual
|
||||
bool init() override;
|
||||
bool flash(uint32_t offset, const std::string &filename);
|
||||
bool flash(uint32_t offset, uint32_t length, uint8_t *data);
|
||||
|
||||
bool flash(uint32_t offset, const std::string &filename);
|
||||
bool flash(uint32_t offset, uint32_t length, uint8_t *data);
|
||||
|
||||
bool read(uint32_t offset, uint32_t length, uint8_t *data);
|
||||
bool read(uint32_t offset, uint32_t length, uint8_t *data);
|
||||
|
||||
private:
|
||||
XFlash xflash;
|
||||
|
||||
XFlash xflash;
|
||||
static constexpr char registerMemory[] = "Reg";
|
||||
|
||||
static constexpr char registerMemory[] = "Reg";
|
||||
|
||||
std::list<MemoryBlockName> getMemoryBlocks() const
|
||||
{
|
||||
return {
|
||||
registerMemory
|
||||
};
|
||||
}
|
||||
std::list<MemoryBlockName> getMemoryBlocks() const {
|
||||
return {registerMemory};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <xilinx/xllfifo.h>
|
||||
|
@ -21,35 +20,29 @@ namespace ip {
|
|||
|
||||
class Fifo : public Node {
|
||||
public:
|
||||
friend class FifoFactory;
|
||||
friend class FifoFactory;
|
||||
|
||||
virtual
|
||||
bool init() override;
|
||||
virtual bool init() override;
|
||||
|
||||
virtual
|
||||
bool stop() override;
|
||||
virtual bool stop() override;
|
||||
|
||||
size_t write(const void* buf, size_t len);
|
||||
size_t read(void* buf, size_t len);
|
||||
size_t write(const void *buf, size_t len);
|
||||
size_t read(void *buf, size_t len);
|
||||
|
||||
private:
|
||||
static constexpr char registerMemory[] = "Mem0";
|
||||
static constexpr char axi4Memory[] = "Mem1";
|
||||
static constexpr char irqName[] = "interrupt";
|
||||
static constexpr char registerMemory[] = "Mem0";
|
||||
static constexpr char axi4Memory[] = "Mem1";
|
||||
static constexpr char irqName[] = "interrupt";
|
||||
|
||||
std::list<MemoryBlockName> getMemoryBlocks() const
|
||||
{
|
||||
return {
|
||||
registerMemory,
|
||||
axi4Memory
|
||||
};
|
||||
}
|
||||
std::list<MemoryBlockName> getMemoryBlocks() const {
|
||||
return {registerMemory, axi4Memory};
|
||||
}
|
||||
|
||||
XLlFifo xFifo;
|
||||
XLlFifo xFifo;
|
||||
};
|
||||
|
||||
class FifoData : public Node {
|
||||
friend class FifoDataFactory;
|
||||
friend class FifoDataFactory;
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
|
|
@ -16,20 +16,14 @@ namespace ip {
|
|||
|
||||
class Gpio : public Core {
|
||||
public:
|
||||
|
||||
virtual
|
||||
bool init() override;
|
||||
virtual bool init() override;
|
||||
|
||||
private:
|
||||
static constexpr char registerMemory[] = "Reg";
|
||||
|
||||
static constexpr char registerMemory[] = "Reg";
|
||||
|
||||
std::list<MemoryBlockName> getMemoryBlocks() const
|
||||
{
|
||||
return {
|
||||
registerMemory
|
||||
};
|
||||
}
|
||||
std::list<MemoryBlockName> getMemoryBlocks() const {
|
||||
return {registerMemory};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <villas/memory.hpp>
|
||||
#include <villas/fpga/node.hpp>
|
||||
#include <villas/fpga/ips/hls.hpp>
|
||||
#include <villas/fpga/node.hpp>
|
||||
#include <villas/memory.hpp>
|
||||
|
||||
#include <villas/fpga/ips/rtds2gpu/register_types.hpp>
|
||||
#include <villas/fpga/ips/rtds2gpu/xgpu2rtds_hw.h>
|
||||
|
@ -18,59 +18,56 @@ namespace villas {
|
|||
namespace fpga {
|
||||
namespace ip {
|
||||
|
||||
class Gpu2Rtds : public Node, public Hls
|
||||
{
|
||||
class Gpu2Rtds : public Node, public Hls {
|
||||
public:
|
||||
friend class Gpu2RtdsFactory;
|
||||
friend class Gpu2RtdsFactory;
|
||||
|
||||
virtual
|
||||
bool init() override;
|
||||
virtual bool init() override;
|
||||
|
||||
void dump(spdlog::level::level_enum logLevel = spdlog::level::info);
|
||||
bool startOnce(size_t frameSize);
|
||||
void dump(spdlog::level::level_enum logLevel = spdlog::level::info);
|
||||
bool startOnce(size_t frameSize);
|
||||
|
||||
size_t getMaxFrameSize();
|
||||
size_t getMaxFrameSize();
|
||||
|
||||
const StreamVertex&
|
||||
getDefaultMasterPort() const
|
||||
{
|
||||
return getMasterPort(rtdsOutputStreamPort);
|
||||
}
|
||||
const StreamVertex &getDefaultMasterPort() const {
|
||||
return getMasterPort(rtdsOutputStreamPort);
|
||||
}
|
||||
|
||||
MemoryBlock
|
||||
getRegisterMemory() const
|
||||
{
|
||||
return MemoryBlock(0, 1 << 10, getAddressSpaceId(registerMemory));
|
||||
}
|
||||
MemoryBlock getRegisterMemory() const {
|
||||
return MemoryBlock(0, 1 << 10, getAddressSpaceId(registerMemory));
|
||||
}
|
||||
|
||||
private:
|
||||
bool updateStatus();
|
||||
bool updateStatus();
|
||||
|
||||
public:
|
||||
static constexpr const char* rtdsOutputStreamPort = "rtds_output";
|
||||
static constexpr const char *rtdsOutputStreamPort = "rtds_output";
|
||||
|
||||
struct StatusControlRegister { uint32_t
|
||||
status_ap_vld : 1,
|
||||
_res : 31;
|
||||
};
|
||||
struct StatusControlRegister {
|
||||
uint32_t status_ap_vld : 1, _res : 31;
|
||||
};
|
||||
|
||||
using StatusRegister = axilite_reg_status_t;
|
||||
using StatusRegister = axilite_reg_status_t;
|
||||
|
||||
static constexpr uintptr_t registerStatusOffset = XGPU2RTDS_CTRL_ADDR_STATUS_DATA;
|
||||
static constexpr uintptr_t registerStatusCtrlOffset = XGPU2RTDS_CTRL_ADDR_STATUS_CTRL;
|
||||
static constexpr uintptr_t registerFrameSizeOffset = XGPU2RTDS_CTRL_ADDR_FRAME_SIZE_DATA;
|
||||
static constexpr uintptr_t registerFrameOffset = XGPU2RTDS_CTRL_ADDR_FRAME_BASE;
|
||||
static constexpr uintptr_t registerFrameLength = XGPU2RTDS_CTRL_DEPTH_FRAME;
|
||||
static constexpr uintptr_t registerStatusOffset =
|
||||
XGPU2RTDS_CTRL_ADDR_STATUS_DATA;
|
||||
static constexpr uintptr_t registerStatusCtrlOffset =
|
||||
XGPU2RTDS_CTRL_ADDR_STATUS_CTRL;
|
||||
static constexpr uintptr_t registerFrameSizeOffset =
|
||||
XGPU2RTDS_CTRL_ADDR_FRAME_SIZE_DATA;
|
||||
static constexpr uintptr_t registerFrameOffset =
|
||||
XGPU2RTDS_CTRL_ADDR_FRAME_BASE;
|
||||
static constexpr uintptr_t registerFrameLength = XGPU2RTDS_CTRL_DEPTH_FRAME;
|
||||
|
||||
public:
|
||||
StatusRegister* registerStatus;
|
||||
StatusControlRegister* registerStatusCtrl;
|
||||
uint32_t* registerFrameSize;
|
||||
uint32_t* registerFrames;
|
||||
StatusRegister *registerStatus;
|
||||
StatusControlRegister *registerStatusCtrl;
|
||||
uint32_t *registerFrameSize;
|
||||
uint32_t *registerFrames;
|
||||
|
||||
size_t maxFrameSize;
|
||||
size_t maxFrameSize;
|
||||
|
||||
bool started;
|
||||
bool started;
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
|
|
@ -7,154 +7,125 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <villas/memory.hpp>
|
||||
#include <villas/fpga/node.hpp>
|
||||
#include <villas/memory.hpp>
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
namespace ip {
|
||||
|
||||
class Hls : public virtual Core
|
||||
{
|
||||
class Hls : public virtual Core {
|
||||
public:
|
||||
virtual
|
||||
bool init() override
|
||||
{
|
||||
auto ®isters = addressTranslations.at(registerMemory);
|
||||
virtual bool init() override {
|
||||
auto ®isters = addressTranslations.at(registerMemory);
|
||||
|
||||
controlRegister = reinterpret_cast<ControlRegister*>(registers.getLocalAddr(registerControlAddr));
|
||||
globalIntRegister = reinterpret_cast<GlobalIntRegister*>(registers.getLocalAddr(registerGlobalIntEnableAddr));
|
||||
ipIntEnableRegister = reinterpret_cast<IpIntRegister*>(registers.getLocalAddr(registerIntEnableAddr));
|
||||
ipIntStatusRegister = reinterpret_cast<IpIntRegister*>(registers.getLocalAddr(registerIntStatusAddr));
|
||||
controlRegister = reinterpret_cast<ControlRegister *>(
|
||||
registers.getLocalAddr(registerControlAddr));
|
||||
globalIntRegister = reinterpret_cast<GlobalIntRegister *>(
|
||||
registers.getLocalAddr(registerGlobalIntEnableAddr));
|
||||
ipIntEnableRegister = reinterpret_cast<IpIntRegister *>(
|
||||
registers.getLocalAddr(registerIntEnableAddr));
|
||||
ipIntStatusRegister = reinterpret_cast<IpIntRegister *>(
|
||||
registers.getLocalAddr(registerIntStatusAddr));
|
||||
|
||||
setAutoRestart(false);
|
||||
setGlobalInterrupt(false);
|
||||
setAutoRestart(false);
|
||||
setGlobalInterrupt(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start()
|
||||
{
|
||||
controlRegister->ap_start = true;
|
||||
running = true;
|
||||
bool start() {
|
||||
controlRegister->ap_start = true;
|
||||
running = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool isFinished()
|
||||
{
|
||||
updateRunningStatus();
|
||||
virtual bool isFinished() {
|
||||
updateRunningStatus();
|
||||
|
||||
return !running;
|
||||
}
|
||||
return !running;
|
||||
}
|
||||
|
||||
bool isRunning()
|
||||
{
|
||||
updateRunningStatus();
|
||||
bool isRunning() {
|
||||
updateRunningStatus();
|
||||
|
||||
return running;
|
||||
}
|
||||
return running;
|
||||
}
|
||||
|
||||
void setAutoRestart(bool enabled) const
|
||||
{
|
||||
controlRegister->auto_restart = enabled;
|
||||
}
|
||||
void setAutoRestart(bool enabled) const {
|
||||
controlRegister->auto_restart = enabled;
|
||||
}
|
||||
|
||||
void setGlobalInterrupt(bool enabled) const
|
||||
{
|
||||
globalIntRegister->globalInterruptEnable = enabled;
|
||||
}
|
||||
void setGlobalInterrupt(bool enabled) const {
|
||||
globalIntRegister->globalInterruptEnable = enabled;
|
||||
}
|
||||
|
||||
void setReadyInterrupt(bool enabled) const
|
||||
{
|
||||
ipIntEnableRegister->ap_ready = enabled;
|
||||
}
|
||||
void setReadyInterrupt(bool enabled) const {
|
||||
ipIntEnableRegister->ap_ready = enabled;
|
||||
}
|
||||
|
||||
void setDoneInterrupt(bool enabled) const
|
||||
{
|
||||
ipIntEnableRegister->ap_done = enabled;
|
||||
}
|
||||
void setDoneInterrupt(bool enabled) const {
|
||||
ipIntEnableRegister->ap_done = enabled;
|
||||
}
|
||||
|
||||
bool isIdleBit() const
|
||||
{
|
||||
return controlRegister->ap_idle;
|
||||
}
|
||||
bool isIdleBit() const { return controlRegister->ap_idle; }
|
||||
|
||||
bool isReadyBit() const
|
||||
{
|
||||
return controlRegister->ap_ready;
|
||||
}
|
||||
bool isReadyBit() const { return controlRegister->ap_ready; }
|
||||
|
||||
// Warning: the corresponding bit is cleared on read of the register, so if
|
||||
// not used correctly, this function may never return true. Only use this
|
||||
// function if you really know what you are doing!
|
||||
bool isDoneBit() const
|
||||
{
|
||||
return controlRegister->ap_done;
|
||||
}
|
||||
// Warning: the corresponding bit is cleared on read of the register, so if
|
||||
// not used correctly, this function may never return true. Only use this
|
||||
// function if you really know what you are doing!
|
||||
bool isDoneBit() const { return controlRegister->ap_done; }
|
||||
|
||||
bool isAutoRestartBit() const
|
||||
{
|
||||
return controlRegister->auto_restart;
|
||||
}
|
||||
bool isAutoRestartBit() const { return controlRegister->auto_restart; }
|
||||
|
||||
private:
|
||||
void updateRunningStatus()
|
||||
{
|
||||
if (running and isIdleBit())
|
||||
running = false;
|
||||
}
|
||||
void updateRunningStatus() {
|
||||
if (running and isIdleBit())
|
||||
running = false;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Memory block handling
|
||||
// Memory block handling
|
||||
|
||||
static constexpr const char* registerMemory = "Reg";
|
||||
static constexpr const char *registerMemory = "Reg";
|
||||
|
||||
virtual std::list<MemoryBlockName> getMemoryBlocks() const
|
||||
{
|
||||
return {
|
||||
registerMemory
|
||||
};
|
||||
}
|
||||
virtual std::list<MemoryBlockName> getMemoryBlocks() const {
|
||||
return {registerMemory};
|
||||
}
|
||||
|
||||
public:
|
||||
// Register definitions
|
||||
// Register definitions
|
||||
|
||||
static constexpr uintptr_t registerControlAddr = 0x00;
|
||||
static constexpr uintptr_t registerGlobalIntEnableAddr = 0x04;
|
||||
static constexpr uintptr_t registerIntEnableAddr = 0x08;
|
||||
static constexpr uintptr_t registerIntStatusAddr = 0x0c;
|
||||
static constexpr uintptr_t registerControlAddr = 0x00;
|
||||
static constexpr uintptr_t registerGlobalIntEnableAddr = 0x04;
|
||||
static constexpr uintptr_t registerIntEnableAddr = 0x08;
|
||||
static constexpr uintptr_t registerIntStatusAddr = 0x0c;
|
||||
|
||||
union ControlRegister {
|
||||
uint32_t value;
|
||||
struct { uint32_t
|
||||
ap_start : 1,
|
||||
ap_done : 1,
|
||||
ap_idle : 1,
|
||||
ap_ready : 1,
|
||||
_res1 : 3,
|
||||
auto_restart : 1,
|
||||
_res2 : 24;
|
||||
};
|
||||
};
|
||||
union ControlRegister {
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint32_t ap_start : 1, ap_done : 1, ap_idle : 1, ap_ready : 1, _res1 : 3,
|
||||
auto_restart : 1, _res2 : 24;
|
||||
};
|
||||
};
|
||||
|
||||
struct GlobalIntRegister { uint32_t
|
||||
globalInterruptEnable : 1,
|
||||
_res : 31;
|
||||
};
|
||||
struct GlobalIntRegister {
|
||||
uint32_t globalInterruptEnable : 1, _res : 31;
|
||||
};
|
||||
|
||||
struct IpIntRegister {
|
||||
uint32_t ap_done : 1, ap_ready : 1, _res : 30;
|
||||
};
|
||||
|
||||
struct IpIntRegister { uint32_t
|
||||
ap_done : 1,
|
||||
ap_ready : 1,
|
||||
_res : 30;
|
||||
};
|
||||
protected:
|
||||
ControlRegister* controlRegister;
|
||||
GlobalIntRegister* globalIntRegister;
|
||||
IpIntRegister* ipIntEnableRegister;
|
||||
IpIntRegister* ipIntStatusRegister;
|
||||
ControlRegister *controlRegister;
|
||||
GlobalIntRegister *globalIntRegister;
|
||||
IpIntRegister *ipIntEnableRegister;
|
||||
IpIntRegister *ipIntStatusRegister;
|
||||
|
||||
bool running;
|
||||
bool running;
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
|
|
@ -101,29 +101,27 @@ private:
|
|||
void driverWriteBlocking(u8 *dataPtr, size_t size);
|
||||
void driverReadBlocking(u8 *dataPtr, size_t max_read);
|
||||
};
|
||||
class I2cFactory : NodeFactory {
|
||||
class I2cFactory : NodeFactory {
|
||||
|
||||
public:
|
||||
virtual std::string getName() const { return "i2c"; }
|
||||
public:
|
||||
virtual std::string getName() const { return "i2c"; }
|
||||
|
||||
virtual std::string getDescription() const {
|
||||
return "Xilinx's AXI4 iic IP";
|
||||
}
|
||||
virtual std::string getDescription() const { return "Xilinx's AXI4 iic IP"; }
|
||||
|
||||
private:
|
||||
virtual Vlnv getCompatibleVlnv() const {
|
||||
return Vlnv("xilinx.com:ip:axi_iic:");
|
||||
}
|
||||
private:
|
||||
virtual Vlnv getCompatibleVlnv() const {
|
||||
return Vlnv("xilinx.com:ip:axi_iic:");
|
||||
}
|
||||
|
||||
// Create a concrete IP instance
|
||||
Core *make() const { return new I2c; };
|
||||
// Create a concrete IP instance
|
||||
Core *make() const { return new I2c; };
|
||||
|
||||
protected:
|
||||
virtual void parse(Core &ip, json_t *json) override;
|
||||
virtual void configurePollingMode(Core &ip, PollingMode mode) override {
|
||||
dynamic_cast<I2c &>(ip).polling = (mode == POLL);
|
||||
}
|
||||
};
|
||||
protected:
|
||||
virtual void parse(Core &ip, json_t *json) override;
|
||||
virtual void configurePollingMode(Core &ip, PollingMode mode) override {
|
||||
dynamic_cast<I2c &>(ip).polling = (mode == POLL);
|
||||
}
|
||||
};
|
||||
} // namespace ip
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
||||
|
|
|
@ -31,33 +31,29 @@ public:
|
|||
return enableInterrupt(1 << irq.num, polling);
|
||||
}
|
||||
|
||||
bool disableInterrupt(IrqMaskType mask);
|
||||
bool disableInterrupt(IrqPort irq) { return disableInterrupt(1 << irq.num); }
|
||||
bool disableInterrupt(IrqMaskType mask);
|
||||
bool disableInterrupt(IrqPort irq) { return disableInterrupt(1 << irq.num); }
|
||||
|
||||
ssize_t waitForInterrupt(int irq);
|
||||
ssize_t waitForInterrupt(IrqPort irq) { return waitForInterrupt(irq.num); }
|
||||
ssize_t waitForInterrupt(int irq);
|
||||
ssize_t waitForInterrupt(IrqPort irq) { return waitForInterrupt(irq.num); }
|
||||
|
||||
private:
|
||||
static constexpr char registerMemory[] = "reg0";
|
||||
|
||||
static constexpr char registerMemory[] = "reg0";
|
||||
std::list<MemoryBlockName> getMemoryBlocks() const {
|
||||
return {registerMemory};
|
||||
}
|
||||
|
||||
std::list<MemoryBlockName> getMemoryBlocks() const
|
||||
{
|
||||
return {
|
||||
registerMemory
|
||||
};
|
||||
}
|
||||
struct Interrupt {
|
||||
int eventFd; // Event file descriptor
|
||||
int number; // Interrupt number from /proc/interrupts
|
||||
bool polling; // Polled or not
|
||||
};
|
||||
|
||||
struct Interrupt {
|
||||
int eventFd; // Event file descriptor
|
||||
int number; // Interrupt number from /proc/interrupts
|
||||
bool polling; // Polled or not
|
||||
};
|
||||
|
||||
int num_irqs; // Number of available MSI vectors
|
||||
int efds[maxIrqs];
|
||||
int nos[maxIrqs];
|
||||
bool polling[maxIrqs];
|
||||
int num_irqs; // Number of available MSI vectors
|
||||
int efds[maxIrqs];
|
||||
int nos[maxIrqs];
|
||||
bool polling[maxIrqs];
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
|
|
@ -20,60 +20,47 @@ namespace ip {
|
|||
|
||||
class AxiPciExpressBridge : public Core {
|
||||
public:
|
||||
friend class AxiPciExpressBridgeFactory;
|
||||
friend class AxiPciExpressBridgeFactory;
|
||||
|
||||
virtual
|
||||
bool init() override;
|
||||
virtual bool init() override;
|
||||
|
||||
private:
|
||||
static constexpr char axiInterface[] = "M_AXI";
|
||||
static constexpr char pcieMemory[] = "BAR0";
|
||||
static constexpr char axiInterface[] = "M_AXI";
|
||||
static constexpr char pcieMemory[] = "BAR0";
|
||||
|
||||
struct AxiBar {
|
||||
uintptr_t base;
|
||||
size_t size;
|
||||
uintptr_t translation;
|
||||
};
|
||||
struct AxiBar {
|
||||
uintptr_t base;
|
||||
size_t size;
|
||||
uintptr_t translation;
|
||||
};
|
||||
|
||||
struct PciBar {
|
||||
uintptr_t translation;
|
||||
};
|
||||
struct PciBar {
|
||||
uintptr_t translation;
|
||||
};
|
||||
|
||||
std::map<std::string, AxiBar> axiToPcieTranslations;
|
||||
std::map<std::string, PciBar> pcieToAxiTranslations;
|
||||
std::map<std::string, AxiBar> axiToPcieTranslations;
|
||||
std::map<std::string, PciBar> pcieToAxiTranslations;
|
||||
};
|
||||
|
||||
class AxiPciExpressBridgeFactory : CoreFactory {
|
||||
|
||||
public:
|
||||
virtual
|
||||
std::string getName() const
|
||||
{
|
||||
return "pcie";
|
||||
}
|
||||
virtual std::string getName() const { return "pcie"; }
|
||||
|
||||
virtual
|
||||
std::string getDescription() const
|
||||
{
|
||||
return "Xilinx's AXI-PCIe Bridge";
|
||||
}
|
||||
virtual std::string getDescription() const {
|
||||
return "Xilinx's AXI-PCIe Bridge";
|
||||
}
|
||||
|
||||
private:
|
||||
virtual
|
||||
Vlnv getCompatibleVlnv() const
|
||||
{
|
||||
return Vlnv("xilinx.com:ip:axi_pcie:");
|
||||
}
|
||||
virtual Vlnv getCompatibleVlnv() const {
|
||||
return Vlnv("xilinx.com:ip:axi_pcie:");
|
||||
}
|
||||
|
||||
// Create a concrete IP instance
|
||||
Core* make() const
|
||||
{
|
||||
return new AxiPciExpressBridge;
|
||||
};
|
||||
// Create a concrete IP instance
|
||||
Core *make() const { return new AxiPciExpressBridge; };
|
||||
|
||||
protected:
|
||||
virtual
|
||||
void parse(Core &, json_t *) override;
|
||||
virtual void parse(Core &, json_t *) override;
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
|
|
@ -15,38 +15,28 @@ namespace ip {
|
|||
|
||||
class RtdsGtfpga : public Node {
|
||||
public:
|
||||
static constexpr const char* masterPort = "m_axis";
|
||||
static constexpr const char* slavePort = "s_axis";
|
||||
static constexpr const char *masterPort = "m_axis";
|
||||
static constexpr const char *slavePort = "s_axis";
|
||||
|
||||
virtual
|
||||
void dump() override;
|
||||
virtual void dump() override;
|
||||
|
||||
double getDt();
|
||||
double getDt();
|
||||
|
||||
std::list<std::string> getMemoryBlocks() const
|
||||
{
|
||||
return {
|
||||
registerMemory
|
||||
};
|
||||
}
|
||||
std::list<std::string> getMemoryBlocks() const { return {registerMemory}; }
|
||||
|
||||
const StreamVertex&
|
||||
getDefaultSlavePort() const
|
||||
{
|
||||
return getSlavePort(slavePort);
|
||||
}
|
||||
const StreamVertex &getDefaultSlavePort() const {
|
||||
return getSlavePort(slavePort);
|
||||
}
|
||||
|
||||
const StreamVertex&
|
||||
getDefaultMasterPort() const
|
||||
{
|
||||
return getMasterPort(masterPort);
|
||||
}
|
||||
const StreamVertex &getDefaultMasterPort() const {
|
||||
return getMasterPort(masterPort);
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr const char registerMemory[] = "reg0";
|
||||
static constexpr const char* irqTs = "irq_ts";
|
||||
static constexpr const char* irqOverflow = "irq_overflow";
|
||||
static constexpr const char* irqCase = "irq_case";
|
||||
static constexpr const char registerMemory[] = "reg0";
|
||||
static constexpr const char *irqTs = "irq_ts";
|
||||
static constexpr const char *irqOverflow = "irq_overflow";
|
||||
static constexpr const char *irqCase = "irq_case";
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
|
|
@ -7,88 +7,69 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <villas/memory.hpp>
|
||||
#include <villas/fpga/node.hpp>
|
||||
#include <villas/fpga/ips/hls.hpp>
|
||||
#include <villas/fpga/node.hpp>
|
||||
#include <villas/memory.hpp>
|
||||
|
||||
#include "rtds2gpu/xrtds2gpu.h"
|
||||
#include "rtds2gpu/register_types.hpp"
|
||||
#include "rtds2gpu/xrtds2gpu.h"
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
namespace ip {
|
||||
|
||||
union ControlRegister {
|
||||
uint32_t value;
|
||||
struct { uint32_t
|
||||
ap_start : 1,
|
||||
ap_done : 1,
|
||||
ap_idle : 1,
|
||||
ap_ready : 1,
|
||||
_res1 : 3,
|
||||
auto_restart : 1,
|
||||
_res2 : 24;
|
||||
};
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint32_t ap_start : 1, ap_done : 1, ap_idle : 1, ap_ready : 1, _res1 : 3,
|
||||
auto_restart : 1, _res2 : 24;
|
||||
};
|
||||
};
|
||||
|
||||
class Rtds2Gpu : public Node, public Hls
|
||||
{
|
||||
class Rtds2Gpu : public Node, public Hls {
|
||||
public:
|
||||
friend class Rtds2GpuFactory;
|
||||
friend class Rtds2GpuFactory;
|
||||
|
||||
virtual
|
||||
bool init() override;
|
||||
virtual bool init() override;
|
||||
|
||||
void dump(spdlog::level::level_enum logLevel = spdlog::level::info);
|
||||
void dump(spdlog::level::level_enum logLevel = spdlog::level::info);
|
||||
|
||||
virtual
|
||||
void dump() override
|
||||
{
|
||||
dump(spdlog::level::info);
|
||||
}
|
||||
virtual void dump() override { dump(spdlog::level::info); }
|
||||
|
||||
bool startOnce(const MemoryBlock &mem, size_t frameSize, size_t dataOffset, size_t doorbellOffset);
|
||||
bool startOnce(const MemoryBlock &mem, size_t frameSize, size_t dataOffset,
|
||||
size_t doorbellOffset);
|
||||
|
||||
size_t getMaxFrameSize();
|
||||
size_t getMaxFrameSize();
|
||||
|
||||
void dumpDoorbell(uint32_t doorbellRegister) const;
|
||||
void dumpDoorbell(uint32_t doorbellRegister) const;
|
||||
|
||||
bool doorbellIsValid(const uint32_t &doorbellRegister) const
|
||||
{
|
||||
return reinterpret_cast<const reg_doorbell_t&>(doorbellRegister).is_valid;
|
||||
}
|
||||
bool doorbellIsValid(const uint32_t &doorbellRegister) const {
|
||||
return reinterpret_cast<const reg_doorbell_t &>(doorbellRegister).is_valid;
|
||||
}
|
||||
|
||||
void doorbellReset(uint32_t &doorbellRegister) const
|
||||
{
|
||||
doorbellRegister = 0;
|
||||
}
|
||||
void doorbellReset(uint32_t &doorbellRegister) const { doorbellRegister = 0; }
|
||||
|
||||
std::list<MemoryBlockName> getMemoryBlocks() const
|
||||
{
|
||||
return {
|
||||
registerMemory
|
||||
};
|
||||
}
|
||||
std::list<MemoryBlockName> getMemoryBlocks() const {
|
||||
return {registerMemory};
|
||||
}
|
||||
|
||||
const StreamVertex&
|
||||
getDefaultSlavePort() const
|
||||
{
|
||||
return getSlavePort(rtdsInputStreamPort);
|
||||
}
|
||||
const StreamVertex &getDefaultSlavePort() const {
|
||||
return getSlavePort(rtdsInputStreamPort);
|
||||
}
|
||||
|
||||
private:
|
||||
bool updateStatus();
|
||||
bool updateStatus();
|
||||
|
||||
private:
|
||||
static constexpr const char* axiInterface = "m_axi_axi_mm";
|
||||
static constexpr const char* rtdsInputStreamPort = "rtds_input";
|
||||
static constexpr const char *axiInterface = "m_axi_axi_mm";
|
||||
static constexpr const char *rtdsInputStreamPort = "rtds_input";
|
||||
|
||||
XRtds2gpu xInstance;
|
||||
XRtds2gpu xInstance;
|
||||
|
||||
axilite_reg_status_t status;
|
||||
size_t maxFrameSize;
|
||||
axilite_reg_status_t status;
|
||||
size_t maxFrameSize;
|
||||
|
||||
bool started;
|
||||
bool started;
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
|
|
@ -7,55 +7,47 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <stdint.h>
|
||||
|
||||
union axilite_reg_status_t {
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint32_t
|
||||
last_seq_nr : 16,
|
||||
last_count : 6,
|
||||
max_frame_size : 6,
|
||||
invalid_frame_size : 1,
|
||||
frame_too_short : 1,
|
||||
frame_too_long : 1,
|
||||
is_running : 1;
|
||||
};
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint32_t last_seq_nr : 16, last_count : 6, max_frame_size : 6,
|
||||
invalid_frame_size : 1, frame_too_short : 1, frame_too_long : 1,
|
||||
is_running : 1;
|
||||
};
|
||||
};
|
||||
|
||||
union reg_doorbell_t {
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint32_t
|
||||
seq_nr : 16,
|
||||
count : 6,
|
||||
is_valid : 1;
|
||||
};
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint32_t seq_nr : 16, count : 6, is_valid : 1;
|
||||
};
|
||||
|
||||
constexpr reg_doorbell_t() : value(0) {}
|
||||
constexpr reg_doorbell_t() : value(0) {}
|
||||
};
|
||||
|
||||
template<size_t N, typename T = uint32_t>
|
||||
struct Rtds2GpuMemoryBuffer {
|
||||
// This type is only for memory interpretation, it makes no sense to create
|
||||
// an instance so it's forbidden
|
||||
Rtds2GpuMemoryBuffer() = delete;
|
||||
template <size_t N, typename T = uint32_t> struct Rtds2GpuMemoryBuffer {
|
||||
// This type is only for memory interpretation, it makes no sense to create
|
||||
// an instance so it's forbidden
|
||||
Rtds2GpuMemoryBuffer() = delete;
|
||||
|
||||
// T can be a more complex type that wraps multiple values
|
||||
static constexpr size_t rawValueCount = N * (sizeof(T) / 4);
|
||||
// T can be a more complex type that wraps multiple values
|
||||
static constexpr size_t rawValueCount = N * (sizeof(T) / 4);
|
||||
|
||||
// As of C++14, offsetof() is not working for non-standard layout types (i.e.
|
||||
// composed of non-POD members). This might work in C++17 though.
|
||||
// More info: https://gist.github.com/graphitemaster/494f21190bb2c63c5516
|
||||
//static constexpr size_t doorbellOffset = offsetof(Rtds2GpuMemoryBuffer, doorbell);
|
||||
//static constexpr size_t dataOffset = offsetof(Rtds2GpuMemoryBuffer, data);
|
||||
// As of C++14, offsetof() is not working for non-standard layout types (i.e.
|
||||
// composed of non-POD members). This might work in C++17 though.
|
||||
// More info: https://gist.github.com/graphitemaster/494f21190bb2c63c5516
|
||||
//static constexpr size_t doorbellOffset = offsetof(Rtds2GpuMemoryBuffer, doorbell);
|
||||
//static constexpr size_t dataOffset = offsetof(Rtds2GpuMemoryBuffer, data);
|
||||
|
||||
// HACK: This might break horribly, let's just hope C++17 will be there soon
|
||||
static constexpr size_t dataOffset = 0;
|
||||
static constexpr size_t doorbellOffset = N * sizeof(Rtds2GpuMemoryBuffer::data);
|
||||
// HACK: This might break horribly, let's just hope C++17 will be there soon
|
||||
static constexpr size_t dataOffset = 0;
|
||||
static constexpr size_t doorbellOffset =
|
||||
N * sizeof(Rtds2GpuMemoryBuffer::data);
|
||||
|
||||
T data[N];
|
||||
reg_doorbell_t doorbell;
|
||||
T data[N];
|
||||
reg_doorbell_t doorbell;
|
||||
};
|
||||
|
|
|
@ -37,17 +37,16 @@
|
|||
// Word n : bit [31:0] - frame[n]
|
||||
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)
|
||||
|
||||
#define XGPU2RTDS_CTRL_ADDR_AP_CTRL 0x00
|
||||
#define XGPU2RTDS_CTRL_ADDR_GIE 0x04
|
||||
#define XGPU2RTDS_CTRL_ADDR_IER 0x08
|
||||
#define XGPU2RTDS_CTRL_ADDR_ISR 0x0c
|
||||
#define XGPU2RTDS_CTRL_ADDR_AP_CTRL 0x00
|
||||
#define XGPU2RTDS_CTRL_ADDR_GIE 0x04
|
||||
#define XGPU2RTDS_CTRL_ADDR_IER 0x08
|
||||
#define XGPU2RTDS_CTRL_ADDR_ISR 0x0c
|
||||
#define XGPU2RTDS_CTRL_ADDR_FRAME_SIZE_DATA 0x10
|
||||
#define XGPU2RTDS_CTRL_BITS_FRAME_SIZE_DATA 32
|
||||
#define XGPU2RTDS_CTRL_ADDR_STATUS_DATA 0x80
|
||||
#define XGPU2RTDS_CTRL_BITS_STATUS_DATA 32
|
||||
#define XGPU2RTDS_CTRL_ADDR_STATUS_CTRL 0x84
|
||||
#define XGPU2RTDS_CTRL_ADDR_FRAME_BASE 0x40
|
||||
#define XGPU2RTDS_CTRL_ADDR_FRAME_HIGH 0x7f
|
||||
#define XGPU2RTDS_CTRL_WIDTH_FRAME 32
|
||||
#define XGPU2RTDS_CTRL_DEPTH_FRAME 16
|
||||
|
||||
#define XGPU2RTDS_CTRL_ADDR_STATUS_DATA 0x80
|
||||
#define XGPU2RTDS_CTRL_BITS_STATUS_DATA 32
|
||||
#define XGPU2RTDS_CTRL_ADDR_STATUS_CTRL 0x84
|
||||
#define XGPU2RTDS_CTRL_ADDR_FRAME_BASE 0x40
|
||||
#define XGPU2RTDS_CTRL_ADDR_FRAME_HIGH 0x7f
|
||||
#define XGPU2RTDS_CTRL_WIDTH_FRAME 32
|
||||
#define XGPU2RTDS_CTRL_DEPTH_FRAME 16
|
||||
|
|
|
@ -14,21 +14,21 @@ extern "C" {
|
|||
|
||||
/***************************** Include Files *********************************/
|
||||
#ifndef __linux__
|
||||
#include "xil_types.h"
|
||||
#include "xil_assert.h"
|
||||
#include "xstatus.h"
|
||||
#include "xil_io.h"
|
||||
#include "xil_types.h"
|
||||
#include "xstatus.h"
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#endif
|
||||
#include "xrtds2gpu_hw.h"
|
||||
|
||||
|
@ -39,44 +39,45 @@ typedef uint16_t u16;
|
|||
typedef uint32_t u32;
|
||||
#else
|
||||
typedef struct {
|
||||
u16 DeviceId;
|
||||
u32 Ctrl_BaseAddress;
|
||||
u16 DeviceId;
|
||||
u32 Ctrl_BaseAddress;
|
||||
} XRtds2gpu_Config;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
u32 Ctrl_BaseAddress;
|
||||
u32 IsReady;
|
||||
u32 Ctrl_BaseAddress;
|
||||
u32 IsReady;
|
||||
} XRtds2gpu;
|
||||
|
||||
/***************** Macros (Inline Functions) Definitions *********************/
|
||||
#ifndef __linux__
|
||||
#define XRtds2gpu_WriteReg(BaseAddress, RegOffset, Data) \
|
||||
Xil_Out32((BaseAddress) + (RegOffset), (u32)(Data))
|
||||
#define XRtds2gpu_ReadReg(BaseAddress, RegOffset) \
|
||||
Xil_In32((BaseAddress) + (RegOffset))
|
||||
#define XRtds2gpu_WriteReg(BaseAddress, RegOffset, Data) \
|
||||
Xil_Out32((BaseAddress) + (RegOffset), (u32)(Data))
|
||||
#define XRtds2gpu_ReadReg(BaseAddress, RegOffset) \
|
||||
Xil_In32((BaseAddress) + (RegOffset))
|
||||
#else
|
||||
#define XRtds2gpu_WriteReg(BaseAddress, RegOffset, Data) \
|
||||
*(volatile u32*)((BaseAddress) + (RegOffset)) = (u32)(Data)
|
||||
#define XRtds2gpu_ReadReg(BaseAddress, RegOffset) \
|
||||
*(volatile u32*)((BaseAddress) + (RegOffset))
|
||||
#define XRtds2gpu_WriteReg(BaseAddress, RegOffset, Data) \
|
||||
*(volatile u32 *)((BaseAddress) + (RegOffset)) = (u32)(Data)
|
||||
#define XRtds2gpu_ReadReg(BaseAddress, RegOffset) \
|
||||
*(volatile u32 *)((BaseAddress) + (RegOffset))
|
||||
|
||||
#define Xil_AssertVoid(expr) assert(expr)
|
||||
#define Xil_AssertVoid(expr) assert(expr)
|
||||
#define Xil_AssertNonvoid(expr) assert(expr)
|
||||
|
||||
#define XST_SUCCESS 0
|
||||
#define XST_DEVICE_NOT_FOUND 2
|
||||
#define XST_OPEN_DEVICE_FAILED 3
|
||||
#define XIL_COMPONENT_IS_READY 1
|
||||
#define XST_SUCCESS 0
|
||||
#define XST_DEVICE_NOT_FOUND 2
|
||||
#define XST_OPEN_DEVICE_FAILED 3
|
||||
#define XIL_COMPONENT_IS_READY 1
|
||||
#endif
|
||||
|
||||
/************************** Function Prototypes *****************************/
|
||||
#ifndef __linux__
|
||||
int XRtds2gpu_Initialize(XRtds2gpu *InstancePtr, u16 DeviceId);
|
||||
XRtds2gpu_Config* XRtds2gpu_LookupConfig(u16 DeviceId);
|
||||
int XRtds2gpu_CfgInitialize(XRtds2gpu *InstancePtr, XRtds2gpu_Config *ConfigPtr);
|
||||
XRtds2gpu_Config *XRtds2gpu_LookupConfig(u16 DeviceId);
|
||||
int XRtds2gpu_CfgInitialize(XRtds2gpu *InstancePtr,
|
||||
XRtds2gpu_Config *ConfigPtr);
|
||||
#else
|
||||
int XRtds2gpu_Initialize(XRtds2gpu *InstancePtr, const char* InstanceName);
|
||||
int XRtds2gpu_Initialize(XRtds2gpu *InstancePtr, const char *InstanceName);
|
||||
int XRtds2gpu_Release(XRtds2gpu *InstancePtr);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -43,19 +43,18 @@
|
|||
// others - reserved
|
||||
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)
|
||||
|
||||
#define XRTDS2GPU_CTRL_ADDR_AP_CTRL 0x00
|
||||
#define XRTDS2GPU_CTRL_ADDR_GIE 0x04
|
||||
#define XRTDS2GPU_CTRL_ADDR_IER 0x08
|
||||
#define XRTDS2GPU_CTRL_ADDR_ISR 0x0c
|
||||
#define XRTDS2GPU_CTRL_ADDR_BASEADDR_DATA 0x10
|
||||
#define XRTDS2GPU_CTRL_BITS_BASEADDR_DATA 32
|
||||
#define XRTDS2GPU_CTRL_ADDR_DATA_OFFSET_DATA 0x18
|
||||
#define XRTDS2GPU_CTRL_BITS_DATA_OFFSET_DATA 32
|
||||
#define XRTDS2GPU_CTRL_ADDR_AP_CTRL 0x00
|
||||
#define XRTDS2GPU_CTRL_ADDR_GIE 0x04
|
||||
#define XRTDS2GPU_CTRL_ADDR_IER 0x08
|
||||
#define XRTDS2GPU_CTRL_ADDR_ISR 0x0c
|
||||
#define XRTDS2GPU_CTRL_ADDR_BASEADDR_DATA 0x10
|
||||
#define XRTDS2GPU_CTRL_BITS_BASEADDR_DATA 32
|
||||
#define XRTDS2GPU_CTRL_ADDR_DATA_OFFSET_DATA 0x18
|
||||
#define XRTDS2GPU_CTRL_BITS_DATA_OFFSET_DATA 32
|
||||
#define XRTDS2GPU_CTRL_ADDR_DOORBELL_OFFSET_DATA 0x20
|
||||
#define XRTDS2GPU_CTRL_BITS_DOORBELL_OFFSET_DATA 32
|
||||
#define XRTDS2GPU_CTRL_ADDR_FRAME_SIZE_DATA 0x28
|
||||
#define XRTDS2GPU_CTRL_BITS_FRAME_SIZE_DATA 32
|
||||
#define XRTDS2GPU_CTRL_ADDR_STATUS_DATA 0x30
|
||||
#define XRTDS2GPU_CTRL_BITS_STATUS_DATA 32
|
||||
#define XRTDS2GPU_CTRL_ADDR_STATUS_CTRL 0x34
|
||||
|
||||
#define XRTDS2GPU_CTRL_ADDR_FRAME_SIZE_DATA 0x28
|
||||
#define XRTDS2GPU_CTRL_BITS_FRAME_SIZE_DATA 32
|
||||
#define XRTDS2GPU_CTRL_ADDR_STATUS_DATA 0x30
|
||||
#define XRTDS2GPU_CTRL_BITS_STATUS_DATA 32
|
||||
#define XRTDS2GPU_CTRL_ADDR_STATUS_CTRL 0x34
|
||||
|
|
|
@ -21,47 +21,30 @@ namespace fpga {
|
|||
namespace ip {
|
||||
|
||||
class Timer : public Core {
|
||||
friend class TimerFactory;
|
||||
friend class TimerFactory;
|
||||
|
||||
public:
|
||||
virtual bool init() override;
|
||||
|
||||
virtual
|
||||
bool init() override;
|
||||
bool start(uint32_t ticks);
|
||||
bool wait();
|
||||
uint32_t remaining();
|
||||
|
||||
bool start(uint32_t ticks);
|
||||
bool wait();
|
||||
uint32_t remaining();
|
||||
inline bool isRunning() { return remaining() != 0; }
|
||||
|
||||
inline
|
||||
bool isRunning()
|
||||
{
|
||||
return remaining() != 0;
|
||||
}
|
||||
inline bool isFinished() { return remaining() == 0; }
|
||||
|
||||
inline
|
||||
bool isFinished()
|
||||
{
|
||||
return remaining() == 0;
|
||||
}
|
||||
|
||||
static constexpr
|
||||
uint32_t getFrequency()
|
||||
{
|
||||
return FPGA_AXI_HZ;
|
||||
}
|
||||
static constexpr uint32_t getFrequency() { return FPGA_AXI_HZ; }
|
||||
|
||||
private:
|
||||
std::list<MemoryBlockName> getMemoryBlocks() const {
|
||||
return {registerMemory};
|
||||
}
|
||||
|
||||
std::list<MemoryBlockName> getMemoryBlocks() const
|
||||
{
|
||||
return {
|
||||
registerMemory
|
||||
};
|
||||
}
|
||||
static constexpr char irqName[] = "generateout0";
|
||||
static constexpr char registerMemory[] = "Reg";
|
||||
|
||||
static constexpr char irqName[] = "generateout0";
|
||||
static constexpr char registerMemory[] = "Reg";
|
||||
|
||||
XTmrCtr xTmr;
|
||||
XTmrCtr xTmr;
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
#include <jansson.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <jansson.h>
|
||||
#include <fmt/ostream.h>
|
||||
#include <villas/config.hpp>
|
||||
#include <villas/fpga/core.hpp>
|
||||
#include <villas/graph/directed.hpp>
|
||||
|
@ -24,172 +24,135 @@ namespace ip {
|
|||
|
||||
class StreamVertex : public graph::Vertex {
|
||||
public:
|
||||
StreamVertex(const std::string &node, const std::string &port, bool isMaster) :
|
||||
graph::Vertex(),
|
||||
nodeName(node),
|
||||
portName(port),
|
||||
isMaster(isMaster)
|
||||
{ }
|
||||
StreamVertex(const std::string &node, const std::string &port, bool isMaster)
|
||||
: graph::Vertex(), nodeName(node), portName(port), isMaster(isMaster) {}
|
||||
|
||||
std::string getName() const
|
||||
{
|
||||
return nodeName + "/" + portName + "(" + (isMaster ? "M" : "S") + ")";
|
||||
}
|
||||
std::string getName() const {
|
||||
return nodeName + "/" + portName + "(" + (isMaster ? "M" : "S") + ")";
|
||||
}
|
||||
|
||||
friend
|
||||
std::ostream& operator<< (std::ostream &stream, const StreamVertex &vertex)
|
||||
{
|
||||
return stream << vertex.getIdentifier() << ": " << vertex.getName();
|
||||
}
|
||||
friend std::ostream &operator<<(std::ostream &stream,
|
||||
const StreamVertex &vertex) {
|
||||
return stream << vertex.getIdentifier() << ": " << vertex.getName();
|
||||
}
|
||||
|
||||
public:
|
||||
std::string nodeName;
|
||||
std::string portName;
|
||||
bool isMaster;
|
||||
std::string nodeName;
|
||||
std::string portName;
|
||||
bool isMaster;
|
||||
};
|
||||
|
||||
class StreamGraph : public graph::DirectedGraph<StreamVertex> {
|
||||
public:
|
||||
StreamGraph() :
|
||||
graph::DirectedGraph<StreamVertex>("stream:graph")
|
||||
{ }
|
||||
StreamGraph() : graph::DirectedGraph<StreamVertex>("stream:graph") {}
|
||||
|
||||
std::shared_ptr<StreamVertex> getOrCreateStreamVertex(const std::string &node,
|
||||
const std::string &port,
|
||||
bool isMaster)
|
||||
{
|
||||
for (auto &vertexEntry : vertices) {
|
||||
auto &vertex = vertexEntry.second;
|
||||
if (vertex->nodeName == node and vertex->portName == port and vertex->isMaster == isMaster)
|
||||
return vertex;
|
||||
}
|
||||
std::shared_ptr<StreamVertex> getOrCreateStreamVertex(const std::string &node,
|
||||
const std::string &port,
|
||||
bool isMaster) {
|
||||
for (auto &vertexEntry : vertices) {
|
||||
auto &vertex = vertexEntry.second;
|
||||
if (vertex->nodeName == node and vertex->portName == port and
|
||||
vertex->isMaster == isMaster)
|
||||
return vertex;
|
||||
}
|
||||
|
||||
// Vertex not found, create new one
|
||||
auto vertex = std::make_shared<StreamVertex>(node, port, isMaster);
|
||||
addVertex(vertex);
|
||||
// Vertex not found, create new one
|
||||
auto vertex = std::make_shared<StreamVertex>(node, port, isMaster);
|
||||
addVertex(vertex);
|
||||
|
||||
return vertex;
|
||||
}
|
||||
return vertex;
|
||||
}
|
||||
};
|
||||
|
||||
class Node : public virtual Core {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<Node>;
|
||||
|
||||
using Ptr = std::shared_ptr<Node>;
|
||||
friend class NodeFactory;
|
||||
|
||||
friend class NodeFactory;
|
||||
const StreamVertex &getMasterPort(const std::string &name) const {
|
||||
return *portsMaster.at(name);
|
||||
}
|
||||
|
||||
const StreamVertex& getMasterPort(const std::string &name) const
|
||||
{
|
||||
return *portsMaster.at(name);
|
||||
}
|
||||
|
||||
const std::map<std::string, std::shared_ptr<StreamVertex>> &getMasterPorts() const
|
||||
{
|
||||
const std::map<std::string, std::shared_ptr<StreamVertex>> &
|
||||
getMasterPorts() const {
|
||||
return portsMaster;
|
||||
}
|
||||
|
||||
const StreamVertex& getSlavePort(const std::string &name) const
|
||||
{
|
||||
return *portsSlave.at(name);
|
||||
}
|
||||
const StreamVertex &getSlavePort(const std::string &name) const {
|
||||
return *portsSlave.at(name);
|
||||
}
|
||||
|
||||
const std::map<std::string, std::shared_ptr<StreamVertex>> &getSlavePorts() const
|
||||
{
|
||||
const std::map<std::string, std::shared_ptr<StreamVertex>> &
|
||||
getSlavePorts() const {
|
||||
return portsSlave;
|
||||
}
|
||||
|
||||
bool connect(const StreamVertex &from, const StreamVertex &to);
|
||||
bool connect(const StreamVertex &from, const StreamVertex &to, bool reverse)
|
||||
{
|
||||
bool ret;
|
||||
bool connect(const StreamVertex &from, const StreamVertex &to);
|
||||
bool connect(const StreamVertex &from, const StreamVertex &to, bool reverse) {
|
||||
bool ret;
|
||||
|
||||
ret = connect(from, to);
|
||||
ret = connect(from, to);
|
||||
|
||||
if (reverse)
|
||||
ret &= connect(to, from);
|
||||
if (reverse)
|
||||
ret &= connect(to, from);
|
||||
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Easy-usage assuming that the slave IP to connect to only has one slave
|
||||
// port and implements the getDefaultSlavePort() function
|
||||
bool connect(const Node &slaveNode, bool reverse = false)
|
||||
{
|
||||
return this->connect(this->getDefaultMasterPort(), slaveNode.getDefaultSlavePort(), reverse);
|
||||
}
|
||||
// Easy-usage assuming that the slave IP to connect to only has one slave
|
||||
// port and implements the getDefaultSlavePort() function
|
||||
bool connect(const Node &slaveNode, bool reverse = false) {
|
||||
return this->connect(this->getDefaultMasterPort(),
|
||||
slaveNode.getDefaultSlavePort(), reverse);
|
||||
}
|
||||
|
||||
// Used by easy-usage connect, will throw if not implemented by derived node
|
||||
virtual
|
||||
const StreamVertex& getDefaultSlavePort() const;
|
||||
// Used by easy-usage connect, will throw if not implemented by derived node
|
||||
virtual const StreamVertex &getDefaultSlavePort() const;
|
||||
|
||||
// Used by easy-usage connect, will throw if not implemented by derived node
|
||||
virtual
|
||||
const StreamVertex& getDefaultMasterPort() const;
|
||||
// Used by easy-usage connect, will throw if not implemented by derived node
|
||||
virtual const StreamVertex &getDefaultMasterPort() const;
|
||||
|
||||
static
|
||||
const StreamGraph& getGraph()
|
||||
{
|
||||
return streamGraph;
|
||||
}
|
||||
static const StreamGraph &getGraph() { return streamGraph; }
|
||||
|
||||
bool loopbackPossible() const;
|
||||
bool connectLoopback();
|
||||
bool loopbackPossible() const;
|
||||
bool connectLoopback();
|
||||
|
||||
protected:
|
||||
virtual
|
||||
bool connectInternal(const std::string &slavePort,
|
||||
const std::string &masterPort);
|
||||
virtual bool connectInternal(const std::string &slavePort,
|
||||
const std::string &masterPort);
|
||||
|
||||
private:
|
||||
std::pair<std::string, std::string> getLoopbackPorts() const;
|
||||
std::pair<std::string, std::string> getLoopbackPorts() const;
|
||||
|
||||
protected:
|
||||
std::map<std::string, std::shared_ptr<StreamVertex>> portsMaster;
|
||||
std::map<std::string, std::shared_ptr<StreamVertex>> portsSlave;
|
||||
std::map<std::string, std::shared_ptr<StreamVertex>> portsMaster;
|
||||
std::map<std::string, std::shared_ptr<StreamVertex>> portsSlave;
|
||||
|
||||
static
|
||||
StreamGraph streamGraph;
|
||||
static StreamGraph streamGraph;
|
||||
};
|
||||
|
||||
class NodeFactory : public CoreFactory {
|
||||
public:
|
||||
using CoreFactory::CoreFactory;
|
||||
using CoreFactory::CoreFactory;
|
||||
|
||||
virtual
|
||||
void parse(Core &, json_t *);
|
||||
virtual void parse(Core &, json_t *);
|
||||
};
|
||||
|
||||
|
||||
template<typename T, const char *name, const char *desc, const char *vlnv>
|
||||
template <typename T, const char *name, const char *desc, const char *vlnv>
|
||||
class NodePlugin : public NodeFactory {
|
||||
|
||||
public:
|
||||
virtual
|
||||
std::string getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
virtual std::string getName() const { return name; }
|
||||
|
||||
virtual
|
||||
std::string getDescription() const
|
||||
{
|
||||
return desc;
|
||||
}
|
||||
virtual std::string getDescription() const { return desc; }
|
||||
|
||||
private:
|
||||
// Get a VLNV identifier for which this IP / Node type can be used.
|
||||
virtual
|
||||
Vlnv getCompatibleVlnv() const
|
||||
{
|
||||
return Vlnv(vlnv);
|
||||
}
|
||||
// Get a VLNV identifier for which this IP / Node type can be used.
|
||||
virtual Vlnv getCompatibleVlnv() const { return Vlnv(vlnv); }
|
||||
|
||||
// Create a concrete IP instance
|
||||
Core* make() const
|
||||
{
|
||||
return new T;
|
||||
}
|
||||
// Create a concrete IP instance
|
||||
Core *make() const { return new T; }
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
@ -201,6 +164,5 @@ template <>
|
|||
class fmt::formatter<villas::fpga::ip::StreamVertex>
|
||||
: public fmt::ostream_formatter {};
|
||||
template <>
|
||||
class fmt::formatter<villas::fpga::ip::Node>
|
||||
: public fmt::ostream_formatter {};
|
||||
class fmt::formatter<villas::fpga::ip::Node> : public fmt::ostream_formatter {};
|
||||
#endif
|
||||
|
|
|
@ -10,14 +10,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <jansson.h>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <jansson.h>
|
||||
#include <filesystem>
|
||||
|
||||
#include <villas/plugin.hpp>
|
||||
#include <villas/memory.hpp>
|
||||
#include <villas/plugin.hpp>
|
||||
|
||||
#include <villas/kernel/pci.hpp>
|
||||
#include <villas/kernel/vfio_container.hpp>
|
||||
|
@ -35,39 +35,30 @@ class PCIeCardFactory;
|
|||
|
||||
class PCIeCard : public Card {
|
||||
public:
|
||||
~PCIeCard();
|
||||
|
||||
~PCIeCard();
|
||||
bool init();
|
||||
|
||||
bool init();
|
||||
bool stop() { return true; }
|
||||
|
||||
bool stop()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool check() { return true; }
|
||||
|
||||
bool check()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool reset() {
|
||||
// TODO: Try via sysfs?
|
||||
// echo 1 > /sys/bus/pci/devices/0000\:88\:00.0/reset
|
||||
return true;
|
||||
}
|
||||
|
||||
bool reset()
|
||||
{
|
||||
// TODO: Try via sysfs?
|
||||
// echo 1 > /sys/bus/pci/devices/0000\:88\:00.0/reset
|
||||
return true;
|
||||
}
|
||||
void dump() {}
|
||||
|
||||
void dump()
|
||||
{ }
|
||||
public: // TODO: make this private
|
||||
bool doReset; // Reset VILLASfpga during startup?
|
||||
int affinity; // Affinity for MSI interrupts
|
||||
|
||||
public: // TODO: make this private
|
||||
bool doReset; // Reset VILLASfpga during startup?
|
||||
int affinity; // Affinity for MSI interrupts
|
||||
std::shared_ptr<kernel::pci::Device> pdev; // PCI device handle
|
||||
|
||||
std::shared_ptr<kernel::pci::Device> pdev; // PCI device handle
|
||||
|
||||
protected:
|
||||
Logger getLogger() const { return villas::logging.get(name); }
|
||||
protected:
|
||||
Logger getLogger() const { return villas::logging.get(name); }
|
||||
};
|
||||
|
||||
class PCIeCardFactory : public plugin::Plugin {
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
namespace villas {
|
||||
namespace fpga {
|
||||
|
||||
std::shared_ptr<fpga::Card>
|
||||
setupFpgaCard(const std::string &configFile, const std::string &fpgaName);
|
||||
std::shared_ptr<fpga::Card> setupFpgaCard(const std::string &configFile,
|
||||
const std::string &fpgaName);
|
||||
|
||||
std::shared_ptr<fpga::Card>
|
||||
createCard(json_t *config, const std::filesystem::path &searchPath,
|
||||
|
@ -66,82 +66,77 @@ protected:
|
|||
int dstAsInt;
|
||||
};
|
||||
|
||||
class BufferedSampleFormatter {
|
||||
public:
|
||||
virtual void format(float value) = 0;
|
||||
virtual void output(std::ostream &out) {
|
||||
out << buf.data() << std::flush;
|
||||
clearBuf();
|
||||
class BufferedSampleFormatter {
|
||||
public:
|
||||
virtual void format(float value) = 0;
|
||||
virtual void output(std::ostream &out) {
|
||||
out << buf.data() << std::flush;
|
||||
clearBuf();
|
||||
}
|
||||
virtual void clearBuf() {
|
||||
for (size_t i = 0; i < bufSamples && buf[i * bufSampleSize] != '\0'; i++) {
|
||||
buf[i * bufSampleSize] = '\0';
|
||||
}
|
||||
virtual void clearBuf() {
|
||||
for (size_t i = 0; i < bufSamples && buf[i * bufSampleSize] != '\0';
|
||||
i++) {
|
||||
buf[i * bufSampleSize] = '\0';
|
||||
}
|
||||
currentBufLoc = 0;
|
||||
currentBufLoc = 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<char> buf;
|
||||
const size_t bufSamples;
|
||||
const size_t bufSampleSize;
|
||||
size_t currentBufLoc;
|
||||
|
||||
BufferedSampleFormatter(const size_t bufSamples, const size_t bufSampleSize)
|
||||
: buf(bufSamples * bufSampleSize + 1), // Leave room for a final `\0'
|
||||
bufSamples(bufSamples), bufSampleSize(bufSampleSize),
|
||||
currentBufLoc(0){};
|
||||
BufferedSampleFormatter() = delete;
|
||||
BufferedSampleFormatter(const BufferedSampleFormatter &) = delete;
|
||||
virtual char *nextBufPos() { return &buf[(currentBufLoc++) * bufSampleSize]; }
|
||||
};
|
||||
|
||||
class BufferedSampleFormatterShort : public BufferedSampleFormatter {
|
||||
public:
|
||||
BufferedSampleFormatterShort(size_t bufSizeInSamples)
|
||||
: BufferedSampleFormatter(bufSizeInSamples, formatStringSize){};
|
||||
|
||||
virtual void format(float value) override {
|
||||
size_t chars;
|
||||
if ((chars = std::snprintf(nextBufPos(), formatStringSize + 1, formatString,
|
||||
value)) > (int)formatStringSize) {
|
||||
throw RuntimeError("Output buffer too small. Expected " +
|
||||
std::to_string(formatStringSize) +
|
||||
" characters, got " + std::to_string(chars));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<char> buf;
|
||||
const size_t bufSamples;
|
||||
const size_t bufSampleSize;
|
||||
size_t currentBufLoc;
|
||||
protected:
|
||||
static constexpr char formatString[] = "%013.6f\n";
|
||||
static constexpr size_t formatStringSize = 14;
|
||||
};
|
||||
|
||||
BufferedSampleFormatter(const size_t bufSamples, const size_t bufSampleSize)
|
||||
: buf(bufSamples * bufSampleSize + 1), // Leave room for a final `\0'
|
||||
bufSamples(bufSamples), bufSampleSize(bufSampleSize),
|
||||
currentBufLoc(0){};
|
||||
BufferedSampleFormatter() = delete;
|
||||
BufferedSampleFormatter(const BufferedSampleFormatter &) = delete;
|
||||
virtual char *nextBufPos() {
|
||||
return &buf[(currentBufLoc++) * bufSampleSize];
|
||||
class BufferedSampleFormatterLong : public BufferedSampleFormatter {
|
||||
public:
|
||||
BufferedSampleFormatterLong(size_t bufSizeInSamples)
|
||||
: BufferedSampleFormatter(bufSizeInSamples, formatStringSize),
|
||||
sampleCnt(0){};
|
||||
|
||||
virtual void format(float value) override {
|
||||
if (std::snprintf(nextBufPos(), formatStringSize + 1, formatString,
|
||||
sampleCnt, value) > (int)formatStringSize) {
|
||||
throw RuntimeError("Output buffer too small");
|
||||
}
|
||||
};
|
||||
sampleCnt = (sampleCnt + 1) % 100000;
|
||||
}
|
||||
|
||||
class BufferedSampleFormatterShort : public BufferedSampleFormatter {
|
||||
public:
|
||||
BufferedSampleFormatterShort(size_t bufSizeInSamples)
|
||||
: BufferedSampleFormatter(bufSizeInSamples, formatStringSize){};
|
||||
protected:
|
||||
static constexpr char formatString[] = "%05zd: %013.6f\n";
|
||||
static constexpr size_t formatStringSize = 22;
|
||||
size_t sampleCnt;
|
||||
};
|
||||
|
||||
virtual void format(float value) override {
|
||||
size_t chars;
|
||||
if ((chars = std::snprintf(nextBufPos(), formatStringSize + 1,
|
||||
formatString, value)) >
|
||||
(int)formatStringSize) {
|
||||
throw RuntimeError("Output buffer too small. Expected " +
|
||||
std::to_string(formatStringSize) +
|
||||
" characters, got " + std::to_string(chars));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
static constexpr char formatString[] = "%013.6f\n";
|
||||
static constexpr size_t formatStringSize = 14;
|
||||
};
|
||||
|
||||
class BufferedSampleFormatterLong : public BufferedSampleFormatter {
|
||||
public:
|
||||
BufferedSampleFormatterLong(size_t bufSizeInSamples)
|
||||
: BufferedSampleFormatter(bufSizeInSamples, formatStringSize),
|
||||
sampleCnt(0){};
|
||||
|
||||
virtual void format(float value) override {
|
||||
if (std::snprintf(nextBufPos(), formatStringSize + 1, formatString,
|
||||
sampleCnt, value) > (int)formatStringSize) {
|
||||
throw RuntimeError("Output buffer too small");
|
||||
}
|
||||
sampleCnt = (sampleCnt + 1) % 100000;
|
||||
}
|
||||
|
||||
protected:
|
||||
static constexpr char formatString[] = "%05zd: %013.6f\n";
|
||||
static constexpr size_t formatStringSize = 22;
|
||||
size_t sampleCnt;
|
||||
};
|
||||
|
||||
std::unique_ptr<BufferedSampleFormatter>
|
||||
getBufferedSampleFormatter(const std::string &format,
|
||||
size_t bufSizeInSamples);
|
||||
std::unique_ptr<BufferedSampleFormatter>
|
||||
getBufferedSampleFormatter(const std::string &format, size_t bufSizeInSamples);
|
||||
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <fmt/ostream.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <villas/config.hpp>
|
||||
|
||||
namespace villas {
|
||||
|
@ -18,56 +18,34 @@ namespace fpga {
|
|||
|
||||
class Vlnv {
|
||||
public:
|
||||
static constexpr char delimiter = ':';
|
||||
static constexpr char delimiter = ':';
|
||||
|
||||
Vlnv() :
|
||||
vendor(""),
|
||||
library(""),
|
||||
name(""),
|
||||
version("")
|
||||
{ }
|
||||
Vlnv() : vendor(""), library(""), name(""), version("") {}
|
||||
|
||||
Vlnv(const std::string &s)
|
||||
{
|
||||
parseFromString(s);
|
||||
}
|
||||
Vlnv(const std::string &s) { parseFromString(s); }
|
||||
|
||||
static Vlnv
|
||||
getWildcard()
|
||||
{
|
||||
return Vlnv();
|
||||
}
|
||||
static Vlnv getWildcard() { return Vlnv(); }
|
||||
|
||||
std::string
|
||||
toString() const;
|
||||
std::string toString() const;
|
||||
|
||||
bool
|
||||
operator==(const Vlnv &other) const;
|
||||
bool operator==(const Vlnv &other) const;
|
||||
|
||||
bool
|
||||
operator!=(const Vlnv &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
bool operator!=(const Vlnv &other) const { return !(*this == other); }
|
||||
|
||||
friend std::ostream&
|
||||
operator<< (std::ostream &stream, const Vlnv &vlnv)
|
||||
{
|
||||
return stream
|
||||
<< (vlnv.vendor.empty() ? "*" : vlnv.vendor) << ":"
|
||||
<< (vlnv.library.empty() ? "*" : vlnv.library) << ":"
|
||||
<< (vlnv.name.empty() ? "*" : vlnv.name) << ":"
|
||||
<< (vlnv.version.empty() ? "*" : vlnv.version);
|
||||
}
|
||||
friend std::ostream &operator<<(std::ostream &stream, const Vlnv &vlnv) {
|
||||
return stream << (vlnv.vendor.empty() ? "*" : vlnv.vendor) << ":"
|
||||
<< (vlnv.library.empty() ? "*" : vlnv.library) << ":"
|
||||
<< (vlnv.name.empty() ? "*" : vlnv.name) << ":"
|
||||
<< (vlnv.version.empty() ? "*" : vlnv.version);
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
parseFromString(std::string vlnv);
|
||||
void parseFromString(std::string vlnv);
|
||||
|
||||
std::string vendor;
|
||||
std::string library;
|
||||
std::string name;
|
||||
std::string version;
|
||||
std::string vendor;
|
||||
std::string library;
|
||||
std::string name;
|
||||
std::string version;
|
||||
};
|
||||
|
||||
} // namespace fpga
|
||||
|
@ -75,6 +53,5 @@ private:
|
|||
|
||||
#ifndef FMT_LEGACY_OSTREAM_FORMATTER
|
||||
template <>
|
||||
class fmt::formatter<villas::fpga::Vlnv>
|
||||
: public fmt::ostream_formatter {};
|
||||
class fmt::formatter<villas::fpga::Vlnv> : public fmt::ostream_formatter {};
|
||||
#endif
|
||||
|
|
|
@ -13,9 +13,8 @@
|
|||
using namespace villas;
|
||||
using namespace villas::fpga;
|
||||
|
||||
Card::~Card()
|
||||
{
|
||||
for (auto ip = ips.rbegin(); ip != ips.rend(); ++ip){
|
||||
Card::~Card() {
|
||||
for (auto ip = ips.rbegin(); ip != ips.rend(); ++ip) {
|
||||
(*ip)->stop();
|
||||
}
|
||||
// Ensure IP destructors are called before memory is unmapped
|
||||
|
@ -34,100 +33,95 @@ Card::~Card()
|
|||
logger->debug("Unmap block {} at IOVA {:#x} of size {:#x}",
|
||||
mappedMemoryBlock.first, iova, size);
|
||||
vfioContainer->memoryUnmap(iova, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<ip::Core> Card::lookupIp(const std::string &name) const
|
||||
{
|
||||
for(auto &ip : ips) {
|
||||
if(*ip == name) {
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
std::shared_ptr<ip::Core> Card::lookupIp(const std::string &name) const {
|
||||
for (auto &ip : ips) {
|
||||
if (*ip == name) {
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<ip::Core> Card::lookupIp(const Vlnv &vlnv) const
|
||||
{
|
||||
for(auto &ip : ips) {
|
||||
if(*ip == vlnv) {
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
std::shared_ptr<ip::Core> Card::lookupIp(const Vlnv &vlnv) const {
|
||||
for (auto &ip : ips) {
|
||||
if (*ip == vlnv) {
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<ip::Core> Card::lookupIp(const ip::IpIdentifier &id) const
|
||||
{
|
||||
for (auto &ip : ips) {
|
||||
if (*ip == id) {
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
std::shared_ptr<ip::Core> Card::lookupIp(const ip::IpIdentifier &id) const {
|
||||
for (auto &ip : ips) {
|
||||
if (*ip == id) {
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Card::unmapMemoryBlock(const MemoryBlock& block)
|
||||
{
|
||||
if (memoryBlocksMapped.find(block.getAddrSpaceId()) == memoryBlocksMapped.end()) {
|
||||
throw std::runtime_error("Block " + std::to_string(block.getAddrSpaceId()) + " is not mapped but was requested to be unmapped.");
|
||||
}
|
||||
bool Card::unmapMemoryBlock(const MemoryBlock &block) {
|
||||
if (memoryBlocksMapped.find(block.getAddrSpaceId()) ==
|
||||
memoryBlocksMapped.end()) {
|
||||
throw std::runtime_error(
|
||||
"Block " + std::to_string(block.getAddrSpaceId()) +
|
||||
" is not mapped but was requested to be unmapped.");
|
||||
}
|
||||
|
||||
auto &mm = MemoryManager::get();
|
||||
auto &mm = MemoryManager::get();
|
||||
|
||||
auto translation = mm.getTranslation(addrSpaceIdDeviceToHost, block.getAddrSpaceId());
|
||||
auto translation =
|
||||
mm.getTranslation(addrSpaceIdDeviceToHost, block.getAddrSpaceId());
|
||||
|
||||
const uintptr_t iova = translation.getLocalAddr(0);
|
||||
const size_t size = translation.getSize();
|
||||
const uintptr_t iova = translation.getLocalAddr(0);
|
||||
const size_t size = translation.getSize();
|
||||
|
||||
logger->debug("Unmap block {} at IOVA {:#x} of size {:#x}",
|
||||
block.getAddrSpaceId(), iova, size);
|
||||
vfioContainer->memoryUnmap(iova, size);
|
||||
logger->debug("Unmap block {} at IOVA {:#x} of size {:#x}",
|
||||
block.getAddrSpaceId(), iova, size);
|
||||
vfioContainer->memoryUnmap(iova, size);
|
||||
|
||||
memoryBlocksMapped.erase(block.getAddrSpaceId());
|
||||
memoryBlocksMapped.erase(block.getAddrSpaceId());
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Card::mapMemoryBlock(const std::shared_ptr<MemoryBlock> block) {
|
||||
if (not vfioContainer->isIommuEnabled()) {
|
||||
logger->warn("VFIO mapping not supported without IOMMU");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Card::mapMemoryBlock(const std::shared_ptr<MemoryBlock> block)
|
||||
{
|
||||
if (not vfioContainer->isIommuEnabled()) {
|
||||
logger->warn("VFIO mapping not supported without IOMMU");
|
||||
return false;
|
||||
}
|
||||
auto &mm = MemoryManager::get();
|
||||
const auto &addrSpaceId = block->getAddrSpaceId();
|
||||
|
||||
auto &mm = MemoryManager::get();
|
||||
const auto &addrSpaceId = block->getAddrSpaceId();
|
||||
if (memoryBlocksMapped.find(addrSpaceId) != memoryBlocksMapped.end())
|
||||
// Block already mapped
|
||||
return true;
|
||||
else
|
||||
logger->debug("Create VFIO mapping for {}", addrSpaceId);
|
||||
|
||||
if (memoryBlocksMapped.find(addrSpaceId) != memoryBlocksMapped.end())
|
||||
// Block already mapped
|
||||
return true;
|
||||
else
|
||||
logger->debug("Create VFIO mapping for {}", addrSpaceId);
|
||||
auto translationFromProcess = mm.getTranslationFromProcess(addrSpaceId);
|
||||
uintptr_t processBaseAddr = translationFromProcess.getLocalAddr(0);
|
||||
uintptr_t iovaAddr =
|
||||
vfioContainer->memoryMap(processBaseAddr, UINTPTR_MAX, block->getSize());
|
||||
|
||||
auto translationFromProcess = mm.getTranslationFromProcess(addrSpaceId);
|
||||
uintptr_t processBaseAddr = translationFromProcess.getLocalAddr(0);
|
||||
uintptr_t iovaAddr = vfioContainer->memoryMap(processBaseAddr,
|
||||
UINTPTR_MAX,
|
||||
block->getSize());
|
||||
if (iovaAddr == UINTPTR_MAX) {
|
||||
logger->error("Cannot map memory at {:#x} of size {:#x}", processBaseAddr,
|
||||
block->getSize());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (iovaAddr == UINTPTR_MAX) {
|
||||
logger->error("Cannot map memory at {:#x} of size {:#x}",
|
||||
processBaseAddr, block->getSize());
|
||||
return false;
|
||||
}
|
||||
mm.createMapping(iovaAddr, 0, block->getSize(), "VFIO-D2H",
|
||||
this->addrSpaceIdDeviceToHost, addrSpaceId);
|
||||
|
||||
mm.createMapping(iovaAddr, 0, block->getSize(),
|
||||
"VFIO-D2H",
|
||||
this->addrSpaceIdDeviceToHost,
|
||||
addrSpaceId);
|
||||
// Remember that this block has already been mapped for later
|
||||
memoryBlocksMapped.insert({addrSpaceId, block});
|
||||
|
||||
// Remember that this block has already been mapped for later
|
||||
memoryBlocksMapped.insert({addrSpaceId, block});
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
|
180
fpga/lib/dma.cpp
180
fpga/lib/dma.cpp
|
@ -9,18 +9,18 @@
|
|||
|
||||
#include <csignal>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
#include <villas/fpga/core.hpp>
|
||||
#include <villas/fpga/card.hpp>
|
||||
#include <villas/fpga/vlnv.hpp>
|
||||
#include <villas/fpga/core.hpp>
|
||||
#include <villas/fpga/ips/dma.hpp>
|
||||
#include <villas/fpga/utils.hpp>
|
||||
#include <villas/fpga/vlnv.hpp>
|
||||
|
||||
using namespace villas;
|
||||
|
||||
|
@ -28,15 +28,14 @@ static std::shared_ptr<kernel::pci::DeviceList> pciDevices;
|
|||
static auto logger = villas::logging.get("villasfpga_dma");
|
||||
|
||||
struct villasfpga_handle_t {
|
||||
std::shared_ptr<villas::fpga::Card> card;
|
||||
std::shared_ptr<villas::fpga::ip::Dma> dma;
|
||||
std::shared_ptr<villas::fpga::Card> card;
|
||||
std::shared_ptr<villas::fpga::ip::Dma> dma;
|
||||
};
|
||||
struct villasfpga_memory_t {
|
||||
std::shared_ptr<villas::MemoryBlock> block;
|
||||
std::shared_ptr<villas::MemoryBlock> block;
|
||||
};
|
||||
|
||||
villasfpga_handle villasfpga_init(const char *configFile)
|
||||
{
|
||||
villasfpga_handle villasfpga_init(const char *configFile) {
|
||||
std::string fpgaName = "vc707";
|
||||
std::string connectStr = "3<->pipe";
|
||||
std::string outputFormat = "short";
|
||||
|
@ -84,100 +83,93 @@ villasfpga_handle villasfpga_init(const char *configFile)
|
|||
}
|
||||
}
|
||||
|
||||
void villasfpga_destroy(villasfpga_handle handle)
|
||||
{
|
||||
delete handle;
|
||||
void villasfpga_destroy(villasfpga_handle handle) { delete handle; }
|
||||
|
||||
int villasfpga_alloc(villasfpga_handle handle, villasfpga_memory *mem,
|
||||
size_t size) {
|
||||
try {
|
||||
auto &alloc = villas::HostRam::getAllocator();
|
||||
*mem = new villasfpga_memory_t;
|
||||
(*mem)->block = alloc.allocateBlock(size);
|
||||
return villasfpga_register(handle, mem);
|
||||
} catch (const RuntimeError &e) {
|
||||
logger->error("Failed to allocate memory: {}", e.what());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
int villasfpga_register(villasfpga_handle handle, villasfpga_memory *mem) {
|
||||
try {
|
||||
handle->dma->makeAccesibleFromVA((*mem)->block);
|
||||
return 0;
|
||||
} catch (const RuntimeError &e) {
|
||||
logger->error("Failed to register memory: {}", e.what());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
int villasfpga_free(villasfpga_memory mem) {
|
||||
try {
|
||||
delete mem;
|
||||
return 0;
|
||||
} catch (const RuntimeError &e) {
|
||||
logger->error("Failed to free memory: {}", e.what());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int villasfpga_alloc(villasfpga_handle handle, villasfpga_memory *mem, size_t size)
|
||||
{
|
||||
try {
|
||||
auto &alloc = villas::HostRam::getAllocator();
|
||||
*mem = new villasfpga_memory_t;
|
||||
(*mem)->block = alloc.allocateBlock(size);
|
||||
return villasfpga_register(handle, mem);
|
||||
} catch (const RuntimeError &e) {
|
||||
logger->error("Failed to allocate memory: {}", e.what());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
int villasfpga_register(villasfpga_handle handle, villasfpga_memory *mem)
|
||||
{
|
||||
try {
|
||||
handle->dma->makeAccesibleFromVA((*mem)->block);
|
||||
return 0;
|
||||
} catch (const RuntimeError &e) {
|
||||
logger->error("Failed to register memory: {}", e.what());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
int villasfpga_free(villasfpga_memory mem)
|
||||
{
|
||||
try {
|
||||
delete mem;
|
||||
return 0;
|
||||
} catch (const RuntimeError &e) {
|
||||
logger->error("Failed to free memory: {}", e.what());
|
||||
return -1;
|
||||
}
|
||||
int villasfpga_read(villasfpga_handle handle, villasfpga_memory mem,
|
||||
size_t size) {
|
||||
try {
|
||||
if (!handle->dma->read(*mem->block, size)) {
|
||||
logger->error("Failed to read from device");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
} catch (const RuntimeError &e) {
|
||||
logger->error("Failed to read memory: {}", e.what());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int villasfpga_read(villasfpga_handle handle, villasfpga_memory mem, size_t size)
|
||||
{
|
||||
try {
|
||||
if (!handle->dma->read(*mem->block, size)) {
|
||||
logger->error("Failed to read from device");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
} catch (const RuntimeError &e) {
|
||||
logger->error("Failed to read memory: {}", e.what());
|
||||
return -1;
|
||||
}
|
||||
int villasfpga_read_complete(villasfpga_handle handle, size_t *size) {
|
||||
try {
|
||||
auto readComp = handle->dma->readComplete();
|
||||
logger->debug("Read {} bytes", readComp.bytes);
|
||||
*size = readComp.bytes;
|
||||
return 0;
|
||||
} catch (const RuntimeError &e) {
|
||||
logger->error("Failed to read memory: {}", e.what());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int villasfpga_read_complete(villasfpga_handle handle, size_t *size)
|
||||
{
|
||||
try {
|
||||
auto readComp = handle->dma->readComplete();
|
||||
logger->debug("Read {} bytes", readComp.bytes);
|
||||
*size = readComp.bytes;
|
||||
return 0;
|
||||
} catch (const RuntimeError &e) {
|
||||
logger->error("Failed to read memory: {}", e.what());
|
||||
return -1;
|
||||
}
|
||||
int villasfpga_write(villasfpga_handle handle, villasfpga_memory mem,
|
||||
size_t size) {
|
||||
try {
|
||||
if (!handle->dma->write(*mem->block, size)) {
|
||||
logger->error("Failed to write to device");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
} catch (const RuntimeError &e) {
|
||||
logger->error("Failed to write memory: {}", e.what());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int villasfpga_write(villasfpga_handle handle, villasfpga_memory mem, size_t size)
|
||||
{
|
||||
try {
|
||||
if (!handle->dma->write(*mem->block, size)) {
|
||||
logger->error("Failed to write to device");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
} catch (const RuntimeError &e) {
|
||||
logger->error("Failed to write memory: {}", e.what());
|
||||
return -1;
|
||||
}
|
||||
int villasfpga_write_complete(villasfpga_handle handle, size_t *size) {
|
||||
try {
|
||||
auto writeComp = handle->dma->writeComplete();
|
||||
logger->debug("Wrote {} bytes", writeComp.bytes);
|
||||
*size = writeComp.bytes;
|
||||
return 0;
|
||||
} catch (const RuntimeError &e) {
|
||||
logger->error("Failed to write memory: {}", e.what());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int villasfpga_write_complete(villasfpga_handle handle, size_t *size)
|
||||
{
|
||||
try {
|
||||
auto writeComp = handle->dma->writeComplete();
|
||||
logger->debug("Wrote {} bytes", writeComp.bytes);
|
||||
*size = writeComp.bytes;
|
||||
return 0;
|
||||
} catch (const RuntimeError &e) {
|
||||
logger->error("Failed to write memory: {}", e.what());
|
||||
return -1;
|
||||
}
|
||||
void *villasfpga_get_ptr(villasfpga_memory mem) {
|
||||
return (void *)MemoryManager::get()
|
||||
.getTranslationFromProcess(mem->block->getAddrSpaceId())
|
||||
.getLocalAddr(0);
|
||||
}
|
||||
|
||||
void* villasfpga_get_ptr(villasfpga_memory mem)
|
||||
{
|
||||
return (void*)MemoryManager::get().getTranslationFromProcess(mem->block->getAddrSpaceId()).getLocalAddr(0);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,91 +12,106 @@
|
|||
#include <villas/fpga/ips/aurora.hpp>
|
||||
|
||||
// Register offsets
|
||||
#define AURORA_AXIS_SR_OFFSET 0x00 // Status Register (read-only)
|
||||
#define AURORA_AXIS_CR_OFFSET 0x04 // Control Register (read/write)
|
||||
#define AURORA_AXIS_CNTR_IN_HIGH_OFFSET 0x0C // Higher 32-bits of incoming frame counter
|
||||
#define AURORA_AXIS_CNTR_IN_LOW_OFFSET 0x08 // Lower 32-bits of incoming frame counter
|
||||
#define AURORA_AXIS_CNTR_OUT_HIGH_OFFSET 0x18 // Higher 32-bits of outgoing frame counter
|
||||
#define AURORA_AXIS_CNTR_OUT_LOW_OFFSET 0x1C // Lower 32-bits of outgoing frame counter
|
||||
#define AURORA_AXIS_SR_OFFSET 0x00 // Status Register (read-only)
|
||||
#define AURORA_AXIS_CR_OFFSET 0x04 // Control Register (read/write)
|
||||
#define AURORA_AXIS_CNTR_IN_HIGH_OFFSET \
|
||||
0x0C // Higher 32-bits of incoming frame counter
|
||||
#define AURORA_AXIS_CNTR_IN_LOW_OFFSET \
|
||||
0x08 // Lower 32-bits of incoming frame counter
|
||||
#define AURORA_AXIS_CNTR_OUT_HIGH_OFFSET \
|
||||
0x18 // Higher 32-bits of outgoing frame counter
|
||||
#define AURORA_AXIS_CNTR_OUT_LOW_OFFSET \
|
||||
0x1C // Lower 32-bits of outgoing frame counter
|
||||
|
||||
// Status register bits
|
||||
#define AURORA_AXIS_SR_CHAN_UP (1 << 0) // 1-bit, asserted when channel initialisation is complete and is ready for data transfer
|
||||
#define AURORA_AXIS_SR_LANE_UP (1 << 1) // 1-bit, asserted for each lane upon successful lane initialisation
|
||||
#define AURORA_AXIS_SR_HARD_ERR (1 << 2) // 1-bit hard rror status
|
||||
#define AURORA_AXIS_SR_SOFT_ERR (1 << 3) // 1-bit soft error status
|
||||
#define AURORA_AXIS_SR_FRAME_ERR (1 << 4) // 1-bit frame error status
|
||||
#define AURORA_AXIS_SR_CHAN_UP \
|
||||
(1 \
|
||||
<< 0) // 1-bit, asserted when channel initialisation is complete and is ready for data transfer
|
||||
#define AURORA_AXIS_SR_LANE_UP \
|
||||
(1 << 1) // 1-bit, asserted for each lane upon successful lane initialisation
|
||||
#define AURORA_AXIS_SR_HARD_ERR (1 << 2) // 1-bit hard rror status
|
||||
#define AURORA_AXIS_SR_SOFT_ERR (1 << 3) // 1-bit soft error status
|
||||
#define AURORA_AXIS_SR_FRAME_ERR (1 << 4) // 1-bit frame error status
|
||||
|
||||
// Control register bits
|
||||
// 1-bit, assert to put Aurora IP in loopback mode.
|
||||
#define AURORA_AXIS_CR_LOOPBACK (1 << 0)
|
||||
#define AURORA_AXIS_CR_LOOPBACK (1 << 0)
|
||||
|
||||
// 1-bit, assert to reset counters, incoming and outgoing frame counters.
|
||||
#define AURORA_AXIS_CR_RST_CTRS (1 << 1)
|
||||
#define AURORA_AXIS_CR_RST_CTRS (1 << 1)
|
||||
|
||||
// 1-bit, assert to turn off any sequence number handling by Aurora IP
|
||||
// Sequence number must be handled in software then.
|
||||
#define AURORA_AXIS_CR_SEQ_MODE (1 << 2)
|
||||
#define AURORA_AXIS_CR_SEQ_MODE (1 << 2)
|
||||
|
||||
/* 1-bit, assert to strip the received frame of the trailing sequence
|
||||
* number. Sequence number mode must be set to handled by Aurora IP,
|
||||
* otherwise this bit is ignored. */
|
||||
#define AURORA_AXIS_CR_SEQ_STRIP (1 << 3)
|
||||
#define AURORA_AXIS_CR_SEQ_STRIP (1 << 3)
|
||||
|
||||
/* 1-bit, assert to use the same sequence number in the outgoing
|
||||
* NovaCor-bound frames as the sequence number received from the
|
||||
* incoming frames from NovaCor. Sequence number mode must be set to
|
||||
* handled by Aurora IP, otherwise this bit is ignored.*/
|
||||
#define AURORA_AXIS_CR_SEQ_ECHO (1 << 4)
|
||||
#define AURORA_AXIS_CR_SEQ_ECHO (1 << 4)
|
||||
|
||||
using namespace villas::fpga::ip;
|
||||
|
||||
void Aurora::dump()
|
||||
{
|
||||
// Check Aurora AXI4 registers
|
||||
const uint32_t sr = readMemory<uint32_t>(registerMemory, AURORA_AXIS_SR_OFFSET);
|
||||
void Aurora::dump() {
|
||||
// Check Aurora AXI4 registers
|
||||
const uint32_t sr =
|
||||
readMemory<uint32_t>(registerMemory, AURORA_AXIS_SR_OFFSET);
|
||||
|
||||
logger->info("Aurora-NovaCor AXI-Stream interface details:");
|
||||
logger->info("Aurora status: {:#x}", sr);
|
||||
logger->info(" Channel up: {}", sr & AURORA_AXIS_SR_CHAN_UP ? CLR_GRN("yes") : CLR_RED("no"));
|
||||
logger->info(" Lane up: {}", sr & AURORA_AXIS_SR_LANE_UP ? CLR_GRN("yes") : CLR_RED("no"));
|
||||
logger->info(" Hard error: {}", sr & AURORA_AXIS_SR_HARD_ERR ? CLR_RED("yes") : CLR_GRN("no"));
|
||||
logger->info(" Soft error: {}", sr & AURORA_AXIS_SR_SOFT_ERR ? CLR_RED("yes") : CLR_GRN("no"));
|
||||
logger->info(" Frame error: {}", sr & AURORA_AXIS_SR_FRAME_ERR ? CLR_RED("yes") : CLR_GRN("no"));
|
||||
logger->info("Aurora-NovaCor AXI-Stream interface details:");
|
||||
logger->info("Aurora status: {:#x}", sr);
|
||||
logger->info(" Channel up: {}",
|
||||
sr & AURORA_AXIS_SR_CHAN_UP ? CLR_GRN("yes") : CLR_RED("no"));
|
||||
logger->info(" Lane up: {}",
|
||||
sr & AURORA_AXIS_SR_LANE_UP ? CLR_GRN("yes") : CLR_RED("no"));
|
||||
logger->info(" Hard error: {}",
|
||||
sr & AURORA_AXIS_SR_HARD_ERR ? CLR_RED("yes") : CLR_GRN("no"));
|
||||
logger->info(" Soft error: {}",
|
||||
sr & AURORA_AXIS_SR_SOFT_ERR ? CLR_RED("yes") : CLR_GRN("no"));
|
||||
logger->info(" Frame error: {}",
|
||||
sr & AURORA_AXIS_SR_FRAME_ERR ? CLR_RED("yes") : CLR_GRN("no"));
|
||||
|
||||
const uint64_t inCntLow = readMemory<uint32_t>(registerMemory, AURORA_AXIS_CNTR_IN_LOW_OFFSET);
|
||||
const uint64_t inCntHigh = readMemory<uint32_t>(registerMemory, AURORA_AXIS_CNTR_IN_HIGH_OFFSET);
|
||||
const uint64_t inCnt = (inCntHigh << 32) | inCntLow;
|
||||
const uint64_t inCntLow =
|
||||
readMemory<uint32_t>(registerMemory, AURORA_AXIS_CNTR_IN_LOW_OFFSET);
|
||||
const uint64_t inCntHigh =
|
||||
readMemory<uint32_t>(registerMemory, AURORA_AXIS_CNTR_IN_HIGH_OFFSET);
|
||||
const uint64_t inCnt = (inCntHigh << 32) | inCntLow;
|
||||
|
||||
const uint64_t outCntLow = readMemory<uint32_t>(registerMemory, AURORA_AXIS_CNTR_OUT_LOW_OFFSET);
|
||||
const uint64_t outCntHigh = readMemory<uint32_t>(registerMemory, AURORA_AXIS_CNTR_OUT_HIGH_OFFSET);
|
||||
const uint64_t outCnt = (outCntHigh << 32) | outCntLow;
|
||||
const uint64_t outCntLow =
|
||||
readMemory<uint32_t>(registerMemory, AURORA_AXIS_CNTR_OUT_LOW_OFFSET);
|
||||
const uint64_t outCntHigh =
|
||||
readMemory<uint32_t>(registerMemory, AURORA_AXIS_CNTR_OUT_HIGH_OFFSET);
|
||||
const uint64_t outCnt = (outCntHigh << 32) | outCntLow;
|
||||
|
||||
logger->info("Aurora frames received: {}", inCnt);
|
||||
logger->info("Aurora frames sent: {}", outCnt);
|
||||
logger->info("Aurora frames received: {}", inCnt);
|
||||
logger->info("Aurora frames sent: {}", outCnt);
|
||||
}
|
||||
|
||||
void Aurora::setLoopback(bool state)
|
||||
{
|
||||
auto cr = readMemory<uint32_t>(registerMemory, AURORA_AXIS_CR_OFFSET);
|
||||
void Aurora::setLoopback(bool state) {
|
||||
auto cr = readMemory<uint32_t>(registerMemory, AURORA_AXIS_CR_OFFSET);
|
||||
|
||||
if (state)
|
||||
cr |= AURORA_AXIS_CR_LOOPBACK;
|
||||
else
|
||||
cr &= ~AURORA_AXIS_CR_LOOPBACK;
|
||||
if (state)
|
||||
cr |= AURORA_AXIS_CR_LOOPBACK;
|
||||
else
|
||||
cr &= ~AURORA_AXIS_CR_LOOPBACK;
|
||||
|
||||
writeMemory<uint32_t>(registerMemory, AURORA_AXIS_CR_OFFSET, cr);
|
||||
writeMemory<uint32_t>(registerMemory, AURORA_AXIS_CR_OFFSET, cr);
|
||||
}
|
||||
|
||||
void Aurora::resetFrameCounters()
|
||||
{
|
||||
auto cr = readMemory<uint32_t>(registerMemory, AURORA_AXIS_CR_OFFSET);
|
||||
void Aurora::resetFrameCounters() {
|
||||
auto cr = readMemory<uint32_t>(registerMemory, AURORA_AXIS_CR_OFFSET);
|
||||
|
||||
cr |= AURORA_AXIS_CR_RST_CTRS;
|
||||
cr |= AURORA_AXIS_CR_RST_CTRS;
|
||||
|
||||
writeMemory<uint32_t>(registerMemory, AURORA_AXIS_CR_OFFSET, cr);
|
||||
writeMemory<uint32_t>(registerMemory, AURORA_AXIS_CR_OFFSET, cr);
|
||||
}
|
||||
|
||||
static char n[] = "aurora";
|
||||
static char d[] = "Aurora 8B/10B and additional support modules, like an AXI4-Lite register interface.";
|
||||
static char d[] = "Aurora 8B/10B and additional support modules, like an "
|
||||
"AXI4-Lite register interface.";
|
||||
static char v[] = "acs.eonerc.rwth-aachen.de:user:aurora_axis:";
|
||||
static NodePlugin<Aurora, n, d, v> f;
|
||||
|
|
|
@ -11,26 +11,22 @@
|
|||
using namespace villas;
|
||||
using namespace villas::fpga::ip;
|
||||
|
||||
void BramFactory::parse(Core &ip, json_t* cfg)
|
||||
{
|
||||
CoreFactory::parse(ip, cfg);
|
||||
void BramFactory::parse(Core &ip, json_t *cfg) {
|
||||
CoreFactory::parse(ip, cfg);
|
||||
|
||||
auto &bram = dynamic_cast<Bram&>(ip);
|
||||
auto &bram = dynamic_cast<Bram &>(ip);
|
||||
|
||||
json_error_t err;
|
||||
int ret = json_unpack_ex(cfg, &err, 0, "{ s: i }",
|
||||
"size", &bram.size
|
||||
);
|
||||
if (ret != 0)
|
||||
throw ConfigError(cfg, err, "", "Cannot parse BRAM config");
|
||||
json_error_t err;
|
||||
int ret = json_unpack_ex(cfg, &err, 0, "{ s: i }", "size", &bram.size);
|
||||
if (ret != 0)
|
||||
throw ConfigError(cfg, err, "", "Cannot parse BRAM config");
|
||||
}
|
||||
|
||||
bool Bram::init()
|
||||
{
|
||||
allocator = std::make_unique<LinearAllocator>
|
||||
(getAddressSpaceId(memoryBlock), this->size, 0);
|
||||
bool Bram::init() {
|
||||
allocator = std::make_unique<LinearAllocator>(getAddressSpaceId(memoryBlock),
|
||||
this->size, 0);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static BramFactory f;
|
||||
|
|
|
@ -7,140 +7,136 @@
|
|||
|
||||
#include <iostream>
|
||||
|
||||
#include <villas/plugin.hpp>
|
||||
#include <villas/fpga/ips/emc.hpp>
|
||||
#include <villas/plugin.hpp>
|
||||
|
||||
using namespace villas::fpga::ip;
|
||||
|
||||
bool EMC::init()
|
||||
{
|
||||
int ret;
|
||||
const uintptr_t base = getBaseAddr(registerMemory);
|
||||
bool EMC::init() {
|
||||
int ret;
|
||||
const uintptr_t base = getBaseAddr(registerMemory);
|
||||
|
||||
const int busWidth = 2;
|
||||
const int busWidth = 2;
|
||||
|
||||
#if defined(XPAR_XFL_DEVICE_FAMILY_INTEL) && XFL_TO_ASYNCMODE
|
||||
// Set Flash to Async mode.
|
||||
if (busWidth == 1) {
|
||||
WRITE_FLASH_8(base + ASYNC_ADDR, 0x60);
|
||||
WRITE_FLASH_8(base + ASYNC_ADDR, 0x03);
|
||||
}
|
||||
else if (busWidth == 2) {
|
||||
WRITE_FLASH_16(base + ASYNC_ADDR, INTEL_CMD_CONFIG_REG_SETUP);
|
||||
WRITE_FLASH_16(base + ASYNC_ADDR, INTEL_CMD_CONFIG_REG_CONFIRM);
|
||||
}
|
||||
// Set Flash to Async mode.
|
||||
if (busWidth == 1) {
|
||||
WRITE_FLASH_8(base + ASYNC_ADDR, 0x60);
|
||||
WRITE_FLASH_8(base + ASYNC_ADDR, 0x03);
|
||||
} else if (busWidth == 2) {
|
||||
WRITE_FLASH_16(base + ASYNC_ADDR, INTEL_CMD_CONFIG_REG_SETUP);
|
||||
WRITE_FLASH_16(base + ASYNC_ADDR, INTEL_CMD_CONFIG_REG_CONFIRM);
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = XFlash_Initialize(&xflash, base, busWidth, 0);
|
||||
if (ret != XST_SUCCESS)
|
||||
return false;
|
||||
ret = XFlash_Initialize(&xflash, base, busWidth, 0);
|
||||
if (ret != XST_SUCCESS)
|
||||
return false;
|
||||
|
||||
return XFlash_IsReady(&xflash);
|
||||
return XFlash_IsReady(&xflash);
|
||||
}
|
||||
|
||||
bool EMC::read(uint32_t offset, uint32_t length, uint8_t *data)
|
||||
{
|
||||
int ret;
|
||||
bool EMC::read(uint32_t offset, uint32_t length, uint8_t *data) {
|
||||
int ret;
|
||||
|
||||
/* Reset the Flash Device. This clears the ret registers and puts
|
||||
/* Reset the Flash Device. This clears the ret registers and puts
|
||||
* the device in Read mode.
|
||||
*/
|
||||
ret = XFlash_Reset(&xflash);
|
||||
if (ret != XST_SUCCESS)
|
||||
return false;
|
||||
ret = XFlash_Reset(&xflash);
|
||||
if (ret != XST_SUCCESS)
|
||||
return false;
|
||||
|
||||
// Perform the read operation.
|
||||
ret = XFlash_Read(&xflash, offset, length, data);
|
||||
if (ret != XST_SUCCESS)
|
||||
return false;
|
||||
// Perform the read operation.
|
||||
ret = XFlash_Read(&xflash, offset, length, data);
|
||||
if (ret != XST_SUCCESS)
|
||||
return false;
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// objcopy -I ihex -O binary somefile.mcs somefile.bin
|
||||
|
||||
bool EMC::flash(uint32_t offset, const std::string &filename)
|
||||
{
|
||||
bool result;
|
||||
uint32_t length;
|
||||
uint8_t *buffer;
|
||||
bool EMC::flash(uint32_t offset, const std::string &filename) {
|
||||
bool result;
|
||||
uint32_t length;
|
||||
uint8_t *buffer;
|
||||
|
||||
std::ifstream is(filename, std::ios::binary);
|
||||
std::ifstream is(filename, std::ios::binary);
|
||||
|
||||
// Get length of file:
|
||||
is.seekg(0, std::ios::end);
|
||||
length = is.tellg();
|
||||
// Get length of file:
|
||||
is.seekg(0, std::ios::end);
|
||||
length = is.tellg();
|
||||
|
||||
is.seekg (0, std::ios::beg);
|
||||
// Allocate memory:
|
||||
is.seekg(0, std::ios::beg);
|
||||
// Allocate memory:
|
||||
|
||||
buffer = new uint8_t[length];
|
||||
is.read(reinterpret_cast<char *>(buffer), length);
|
||||
is.close();
|
||||
buffer = new uint8_t[length];
|
||||
is.read(reinterpret_cast<char *>(buffer), length);
|
||||
is.close();
|
||||
|
||||
result = flash(offset, length, buffer);
|
||||
result = flash(offset, length, buffer);
|
||||
|
||||
delete[] buffer;
|
||||
delete[] buffer;
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Based on xilflash_readwrite_example.c
|
||||
bool EMC::flash(uint32_t offset, uint32_t length, uint8_t *data)
|
||||
{
|
||||
int ret = XST_FAILURE;
|
||||
uint32_t start = offset;
|
||||
bool EMC::flash(uint32_t offset, uint32_t length, uint8_t *data) {
|
||||
int ret = XST_FAILURE;
|
||||
uint32_t start = offset;
|
||||
|
||||
/* Reset the Flash Device. This clears the ret registers and puts
|
||||
/* Reset the Flash Device. This clears the ret registers and puts
|
||||
* the device in Read mode. */
|
||||
ret = XFlash_Reset(&xflash);
|
||||
if (ret != XST_SUCCESS){
|
||||
return false;
|
||||
}
|
||||
ret = XFlash_Reset(&xflash);
|
||||
if (ret != XST_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Perform an unlock operation before the erase operation for the Intel
|
||||
/* Perform an unlock operation before the erase operation for the Intel
|
||||
* Flash. The erase operation will result in an error if the block is
|
||||
* locked. */
|
||||
if ((xflash.CommandSet == XFL_CMDSET_INTEL_STANDARD) ||
|
||||
(xflash.CommandSet == XFL_CMDSET_INTEL_EXTENDED) ||
|
||||
(xflash.CommandSet == XFL_CMDSET_INTEL_G18)) {
|
||||
ret = XFlash_Unlock(&xflash, offset, 0);
|
||||
if(ret != XST_SUCCESS){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ((xflash.CommandSet == XFL_CMDSET_INTEL_STANDARD) ||
|
||||
(xflash.CommandSet == XFL_CMDSET_INTEL_EXTENDED) ||
|
||||
(xflash.CommandSet == XFL_CMDSET_INTEL_G18)) {
|
||||
ret = XFlash_Unlock(&xflash, offset, 0);
|
||||
if (ret != XST_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the Erase operation.
|
||||
ret = XFlash_Erase(&xflash, start, length);
|
||||
if (ret != XST_SUCCESS){;
|
||||
return false;
|
||||
}
|
||||
// Perform the Erase operation.
|
||||
ret = XFlash_Erase(&xflash, start, length);
|
||||
if (ret != XST_SUCCESS) {
|
||||
;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform the Write operation.
|
||||
ret = XFlash_Write(&xflash, start, length, data);
|
||||
if (ret != XST_SUCCESS){
|
||||
return false;
|
||||
}
|
||||
// Perform the Write operation.
|
||||
ret = XFlash_Write(&xflash, start, length, data);
|
||||
if (ret != XST_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform the read operation.
|
||||
uint8_t *verify_data = new uint8_t[length];
|
||||
ret = XFlash_Read(&xflash, start, length, verify_data);
|
||||
if(ret != XST_SUCCESS) {
|
||||
delete[] verify_data;
|
||||
return false;
|
||||
}
|
||||
// Perform the read operation.
|
||||
uint8_t *verify_data = new uint8_t[length];
|
||||
ret = XFlash_Read(&xflash, start, length, verify_data);
|
||||
if (ret != XST_SUCCESS) {
|
||||
delete[] verify_data;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare the data read against the data Written.
|
||||
for (unsigned i = 0; i < length; i++) {
|
||||
if (verify_data[i] != data[i]){
|
||||
delete[] verify_data;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Compare the data read against the data Written.
|
||||
for (unsigned i = 0; i < length; i++) {
|
||||
if (verify_data[i] != data[i]) {
|
||||
delete[] verify_data;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] verify_data;
|
||||
delete[] verify_data;
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static char n[] = "emc";
|
||||
|
|
|
@ -10,84 +10,81 @@
|
|||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <xilinx/xstatus.h>
|
||||
#include <xilinx/xllfifo.h>
|
||||
#include <xilinx/xstatus.h>
|
||||
|
||||
#include <villas/fpga/ips/fifo.hpp>
|
||||
#include <villas/fpga/ips/intc.hpp>
|
||||
|
||||
using namespace villas::fpga::ip;
|
||||
|
||||
bool Fifo::init()
|
||||
{
|
||||
XLlFifo_Config fifo_cfg;
|
||||
bool Fifo::init() {
|
||||
XLlFifo_Config fifo_cfg;
|
||||
|
||||
try {
|
||||
// If this throws an exception, then there's no AXI4 data interface
|
||||
fifo_cfg.Axi4BaseAddress = getBaseAddr(axi4Memory);
|
||||
fifo_cfg.Datainterface = 1;
|
||||
} catch (const std::out_of_range&) {
|
||||
fifo_cfg.Datainterface = 0;
|
||||
}
|
||||
try {
|
||||
// If this throws an exception, then there's no AXI4 data interface
|
||||
fifo_cfg.Axi4BaseAddress = getBaseAddr(axi4Memory);
|
||||
fifo_cfg.Datainterface = 1;
|
||||
} catch (const std::out_of_range &) {
|
||||
fifo_cfg.Datainterface = 0;
|
||||
}
|
||||
|
||||
if (XLlFifo_CfgInitialize(&xFifo, &fifo_cfg, getBaseAddr(registerMemory)) != XST_SUCCESS)
|
||||
return false;
|
||||
if (XLlFifo_CfgInitialize(&xFifo, &fifo_cfg, getBaseAddr(registerMemory)) !=
|
||||
XST_SUCCESS)
|
||||
return false;
|
||||
|
||||
if (irqs.find(irqName) == irqs.end()) {
|
||||
logger->error("IRQ '{}' not found but required", irqName);
|
||||
return false;
|
||||
}
|
||||
if (irqs.find(irqName) == irqs.end()) {
|
||||
logger->error("IRQ '{}' not found but required", irqName);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Receive complete IRQ
|
||||
XLlFifo_IntEnable(&xFifo, XLLF_INT_RC_MASK);
|
||||
irqs[irqName].irqController->enableInterrupt(irqs[irqName], false);
|
||||
// Receive complete IRQ
|
||||
XLlFifo_IntEnable(&xFifo, XLLF_INT_RC_MASK);
|
||||
irqs[irqName].irqController->enableInterrupt(irqs[irqName], false);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Fifo::stop()
|
||||
{
|
||||
// Receive complete IRQ
|
||||
XLlFifo_IntDisable(&xFifo, XLLF_INT_RC_MASK);
|
||||
irqs[irqName].irqController->disableInterrupt(irqs[irqName]);
|
||||
bool Fifo::stop() {
|
||||
// Receive complete IRQ
|
||||
XLlFifo_IntDisable(&xFifo, XLLF_INT_RC_MASK);
|
||||
irqs[irqName].irqController->disableInterrupt(irqs[irqName]);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Fifo::write(const void *buf, size_t len)
|
||||
{
|
||||
size_t Fifo::write(const void *buf, size_t len) {
|
||||
|
||||
uint32_t tdfv;
|
||||
uint32_t tdfv;
|
||||
|
||||
tdfv = XLlFifo_TxVacancy(&xFifo);
|
||||
if (tdfv < len)
|
||||
return -1;
|
||||
tdfv = XLlFifo_TxVacancy(&xFifo);
|
||||
if (tdfv < len)
|
||||
return -1;
|
||||
|
||||
// Buf has to be re-casted because Xilinx driver doesn't use const
|
||||
XLlFifo_Write(&xFifo, (void*) buf, len);
|
||||
XLlFifo_TxSetLen(&xFifo, len);
|
||||
// Buf has to be re-casted because Xilinx driver doesn't use const
|
||||
XLlFifo_Write(&xFifo, (void *)buf, len);
|
||||
XLlFifo_TxSetLen(&xFifo, len);
|
||||
|
||||
return len;
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t Fifo::read(void *buf, size_t len)
|
||||
{
|
||||
size_t nextlen = 0;
|
||||
size_t rxlen;
|
||||
size_t Fifo::read(void *buf, size_t len) {
|
||||
size_t nextlen = 0;
|
||||
size_t rxlen;
|
||||
|
||||
while (!XLlFifo_IsRxDone(&xFifo))
|
||||
irqs[irqName].irqController->waitForInterrupt(irqs[irqName]);
|
||||
while (!XLlFifo_IsRxDone(&xFifo))
|
||||
irqs[irqName].irqController->waitForInterrupt(irqs[irqName]);
|
||||
|
||||
XLlFifo_IntClear(&xFifo, XLLF_INT_RC_MASK);
|
||||
XLlFifo_IntClear(&xFifo, XLLF_INT_RC_MASK);
|
||||
|
||||
// Get length of next frame
|
||||
rxlen = XLlFifo_RxGetLen(&xFifo);
|
||||
nextlen = std::min(rxlen, len);
|
||||
// Get length of next frame
|
||||
rxlen = XLlFifo_RxGetLen(&xFifo);
|
||||
nextlen = std::min(rxlen, len);
|
||||
|
||||
// Read from FIFO
|
||||
XLlFifo_Read(&xFifo, buf, nextlen);
|
||||
// Read from FIFO
|
||||
XLlFifo_Read(&xFifo, buf, nextlen);
|
||||
|
||||
return nextlen;
|
||||
return nextlen;
|
||||
}
|
||||
|
||||
static char n1[] = "fifo";
|
||||
|
|
|
@ -11,12 +11,10 @@
|
|||
|
||||
using namespace villas::fpga::ip;
|
||||
|
||||
bool
|
||||
Gpio::init()
|
||||
{
|
||||
//const uintptr_t base = getBaseAddr(registerMemory);
|
||||
bool Gpio::init() {
|
||||
//const uintptr_t base = getBaseAddr(registerMemory);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static char n[] = "gpio";
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <villas/config.hpp>
|
||||
#include <villas/plugin.hpp>
|
||||
|
@ -25,97 +25,96 @@ bool InterruptController::stop() {
|
|||
return card->vfioDevice->pciMsiDeinit(this->efds) > 0;
|
||||
}
|
||||
|
||||
bool
|
||||
InterruptController::init()
|
||||
{
|
||||
const uintptr_t base = getBaseAddr(registerMemory);
|
||||
bool InterruptController::init() {
|
||||
const uintptr_t base = getBaseAddr(registerMemory);
|
||||
|
||||
num_irqs = card->vfioDevice->pciMsiInit(efds);
|
||||
if (num_irqs < 0)
|
||||
return false;
|
||||
num_irqs = card->vfioDevice->pciMsiInit(efds);
|
||||
if (num_irqs < 0)
|
||||
return false;
|
||||
|
||||
if (not card->vfioDevice->pciMsiFind(nos)) {
|
||||
return false;
|
||||
}
|
||||
if (not card->vfioDevice->pciMsiFind(nos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// For each IRQ
|
||||
for (int i = 0; i < num_irqs; i++) {
|
||||
// For each IRQ
|
||||
for (int i = 0; i < num_irqs; i++) {
|
||||
|
||||
// Try pinning to core
|
||||
PCIeCard* pciecard = dynamic_cast<PCIeCard*>(card);
|
||||
int ret = kernel::setIRQAffinity(nos[i], pciecard->affinity, nullptr);
|
||||
// Try pinning to core
|
||||
PCIeCard *pciecard = dynamic_cast<PCIeCard *>(card);
|
||||
int ret = kernel::setIRQAffinity(nos[i], pciecard->affinity, nullptr);
|
||||
|
||||
switch(ret) {
|
||||
case 0:
|
||||
// Everything is fine
|
||||
break;
|
||||
case EACCES:
|
||||
logger->warn("No permission to change affinity of VFIO-MSI interrupt, "
|
||||
"performance may be degraded!");
|
||||
break;
|
||||
default:
|
||||
logger->error("Failed to change affinity of VFIO-MSI interrupt");
|
||||
return false;
|
||||
}
|
||||
switch (ret) {
|
||||
case 0:
|
||||
// Everything is fine
|
||||
break;
|
||||
case EACCES:
|
||||
logger->warn("No permission to change affinity of VFIO-MSI interrupt, "
|
||||
"performance may be degraded!");
|
||||
break;
|
||||
default:
|
||||
logger->error("Failed to change affinity of VFIO-MSI interrupt");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Setup vector
|
||||
XIntc_Out32(base + XIN_IVAR_OFFSET + i * 4, i);
|
||||
}
|
||||
// Setup vector
|
||||
XIntc_Out32(base + XIN_IVAR_OFFSET + i * 4, i);
|
||||
}
|
||||
|
||||
XIntc_Out32(base + XIN_IMR_OFFSET, 0x00000000); // Use manual acknowlegement for all IRQs
|
||||
XIntc_Out32(base + XIN_IAR_OFFSET, 0xFFFFFFFF); // Acknowlege all pending IRQs manually
|
||||
XIntc_Out32(base + XIN_IMR_OFFSET, 0xFFFFFFFF); // Use fast acknowlegement for all IRQs
|
||||
XIntc_Out32(base + XIN_IER_OFFSET, 0x00000000); // Disable all IRQs by default
|
||||
XIntc_Out32(base + XIN_MER_OFFSET, XIN_INT_HARDWARE_ENABLE_MASK | XIN_INT_MASTER_ENABLE_MASK);
|
||||
XIntc_Out32(base + XIN_IMR_OFFSET,
|
||||
0x00000000); // Use manual acknowlegement for all IRQs
|
||||
XIntc_Out32(base + XIN_IAR_OFFSET,
|
||||
0xFFFFFFFF); // Acknowlege all pending IRQs manually
|
||||
XIntc_Out32(base + XIN_IMR_OFFSET,
|
||||
0xFFFFFFFF); // Use fast acknowlegement for all IRQs
|
||||
XIntc_Out32(base + XIN_IER_OFFSET, 0x00000000); // Disable all IRQs by default
|
||||
XIntc_Out32(base + XIN_MER_OFFSET,
|
||||
XIN_INT_HARDWARE_ENABLE_MASK | XIN_INT_MASTER_ENABLE_MASK);
|
||||
|
||||
logger->debug("enabled interrupts");
|
||||
logger->debug("enabled interrupts");
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
InterruptController::enableInterrupt(InterruptController::IrqMaskType mask, bool polling)
|
||||
{
|
||||
const uintptr_t base = getBaseAddr(registerMemory);
|
||||
bool InterruptController::enableInterrupt(InterruptController::IrqMaskType mask,
|
||||
bool polling) {
|
||||
const uintptr_t base = getBaseAddr(registerMemory);
|
||||
|
||||
// Current state of INTC
|
||||
const uint32_t ier = XIntc_In32(base + XIN_IER_OFFSET);
|
||||
const uint32_t imr = XIntc_In32(base + XIN_IMR_OFFSET);
|
||||
// Current state of INTC
|
||||
const uint32_t ier = XIntc_In32(base + XIN_IER_OFFSET);
|
||||
const uint32_t imr = XIntc_In32(base + XIN_IMR_OFFSET);
|
||||
|
||||
// Clear pending IRQs
|
||||
XIntc_Out32(base + XIN_IAR_OFFSET, mask);
|
||||
// Clear pending IRQs
|
||||
XIntc_Out32(base + XIN_IAR_OFFSET, mask);
|
||||
|
||||
for (int i = 0; i < num_irqs; i++) {
|
||||
if (mask & (1 << i))
|
||||
this->polling[i] = polling;
|
||||
}
|
||||
for (int i = 0; i < num_irqs; i++) {
|
||||
if (mask & (1 << i))
|
||||
this->polling[i] = polling;
|
||||
}
|
||||
|
||||
if (polling) {
|
||||
XIntc_Out32(base + XIN_IMR_OFFSET, imr & ~mask);
|
||||
XIntc_Out32(base + XIN_IER_OFFSET, ier & ~mask);
|
||||
}
|
||||
else {
|
||||
XIntc_Out32(base + XIN_IER_OFFSET, ier | mask);
|
||||
XIntc_Out32(base + XIN_IMR_OFFSET, imr | mask);
|
||||
}
|
||||
if (polling) {
|
||||
XIntc_Out32(base + XIN_IMR_OFFSET, imr & ~mask);
|
||||
XIntc_Out32(base + XIN_IER_OFFSET, ier & ~mask);
|
||||
} else {
|
||||
XIntc_Out32(base + XIN_IER_OFFSET, ier | mask);
|
||||
XIntc_Out32(base + XIN_IMR_OFFSET, imr | mask);
|
||||
}
|
||||
|
||||
logger->debug("New ier = {:x}", XIntc_In32(base + XIN_IER_OFFSET));
|
||||
logger->debug("New imr = {:x}", XIntc_In32(base + XIN_IMR_OFFSET));
|
||||
logger->debug("New isr = {:x}", XIntc_In32(base + XIN_ISR_OFFSET));
|
||||
logger->debug("Interupts enabled: mask={:x} polling={:d}", mask, polling);
|
||||
logger->debug("New ier = {:x}", XIntc_In32(base + XIN_IER_OFFSET));
|
||||
logger->debug("New imr = {:x}", XIntc_In32(base + XIN_IMR_OFFSET));
|
||||
logger->debug("New isr = {:x}", XIntc_In32(base + XIN_ISR_OFFSET));
|
||||
logger->debug("Interupts enabled: mask={:x} polling={:d}", mask, polling);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
InterruptController::disableInterrupt(InterruptController::IrqMaskType mask)
|
||||
{
|
||||
const uintptr_t base = getBaseAddr(registerMemory);
|
||||
uint32_t ier = XIntc_In32(base + XIN_IER_OFFSET);
|
||||
bool InterruptController::disableInterrupt(
|
||||
InterruptController::IrqMaskType mask) {
|
||||
const uintptr_t base = getBaseAddr(registerMemory);
|
||||
uint32_t ier = XIntc_In32(base + XIN_IER_OFFSET);
|
||||
|
||||
XIntc_Out32(base + XIN_IER_OFFSET, ier & ~mask);
|
||||
XIntc_Out32(base + XIN_IER_OFFSET, ier & ~mask);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
ssize_t InterruptController::waitForInterrupt(int irq) {
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <limits>
|
||||
#include <jansson.h>
|
||||
#include <limits>
|
||||
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/memory.hpp>
|
||||
|
@ -17,134 +17,125 @@
|
|||
|
||||
using namespace villas::fpga::ip;
|
||||
|
||||
bool
|
||||
AxiPciExpressBridge::init()
|
||||
{
|
||||
auto &mm = MemoryManager::get();
|
||||
bool AxiPciExpressBridge::init() {
|
||||
auto &mm = MemoryManager::get();
|
||||
|
||||
// Throw an exception if the is no bus master interface and thus no
|
||||
// address space we can use for translation -> error
|
||||
card->addrSpaceIdHostToDevice = busMasterInterfaces.at(axiInterface);
|
||||
// Throw an exception if the is no bus master interface and thus no
|
||||
// address space we can use for translation -> error
|
||||
card->addrSpaceIdHostToDevice = busMasterInterfaces.at(axiInterface);
|
||||
|
||||
// Map PCIe BAR0 via VFIO
|
||||
const void* bar0_mapped = card->vfioDevice->regionMap(VFIO_PCI_BAR0_REGION_INDEX);
|
||||
if (bar0_mapped == MAP_FAILED) {
|
||||
logger->error("Failed to mmap() BAR0");
|
||||
return false;
|
||||
}
|
||||
// Map PCIe BAR0 via VFIO
|
||||
const void *bar0_mapped =
|
||||
card->vfioDevice->regionMap(VFIO_PCI_BAR0_REGION_INDEX);
|
||||
if (bar0_mapped == MAP_FAILED) {
|
||||
logger->error("Failed to mmap() BAR0");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine size of BAR0 region
|
||||
const size_t bar0_size = card->vfioDevice->regionGetSize(VFIO_PCI_BAR0_REGION_INDEX);
|
||||
// Determine size of BAR0 region
|
||||
const size_t bar0_size =
|
||||
card->vfioDevice->regionGetSize(VFIO_PCI_BAR0_REGION_INDEX);
|
||||
|
||||
// Create a mapping from process address space to the FPGA card via vfio
|
||||
mm.createMapping(reinterpret_cast<uintptr_t>(bar0_mapped),
|
||||
0, bar0_size, "vfio-h2d",
|
||||
mm.getProcessAddressSpace(),
|
||||
card->addrSpaceIdHostToDevice);
|
||||
// Create a mapping from process address space to the FPGA card via vfio
|
||||
mm.createMapping(reinterpret_cast<uintptr_t>(bar0_mapped), 0, bar0_size,
|
||||
"vfio-h2d", mm.getProcessAddressSpace(),
|
||||
card->addrSpaceIdHostToDevice);
|
||||
|
||||
// Make PCIe (IOVA) address space available to FPGA via BAR0
|
||||
// Make PCIe (IOVA) address space available to FPGA via BAR0
|
||||
|
||||
// IPs that can access this address space will know it via their memory view
|
||||
const auto addrSpaceNameDeviceToHost =
|
||||
mm.getSlaveAddrSpaceName(getInstanceName(), pcieMemory);
|
||||
// IPs that can access this address space will know it via their memory view
|
||||
const auto addrSpaceNameDeviceToHost =
|
||||
mm.getSlaveAddrSpaceName(getInstanceName(), pcieMemory);
|
||||
|
||||
// Save ID in card so we can create mappings later when needed (e.g. when
|
||||
// allocating DMA memory in host RAM)
|
||||
card->addrSpaceIdDeviceToHost =
|
||||
mm.getOrCreateAddressSpace(addrSpaceNameDeviceToHost);
|
||||
// Save ID in card so we can create mappings later when needed (e.g. when
|
||||
// allocating DMA memory in host RAM)
|
||||
card->addrSpaceIdDeviceToHost =
|
||||
mm.getOrCreateAddressSpace(addrSpaceNameDeviceToHost);
|
||||
|
||||
auto pciAddrSpaceId = mm.getPciAddressSpace();
|
||||
auto pciAddrSpaceId = mm.getPciAddressSpace();
|
||||
|
||||
auto regions = dynamic_cast<PCIeCard*>(card)->pdev->getRegions();
|
||||
auto regions = dynamic_cast<PCIeCard *>(card)->pdev->getRegions();
|
||||
|
||||
int i = 0;
|
||||
for (auto region : regions) {
|
||||
const size_t region_size = region.end - region.start + 1;
|
||||
int i = 0;
|
||||
for (auto region : regions) {
|
||||
const size_t region_size = region.end - region.start + 1;
|
||||
|
||||
char barName[] = "BARx";
|
||||
barName[3] = '0' + region.num;
|
||||
auto pciBar = pcieToAxiTranslations.at(barName);
|
||||
char barName[] = "BARx";
|
||||
barName[3] = '0' + region.num;
|
||||
auto pciBar = pcieToAxiTranslations.at(barName);
|
||||
|
||||
logger->info("PCI-BAR{}: bus addr={:#x} size={:#x}",
|
||||
region.num, region.start, region_size);
|
||||
logger->info("PCI-BAR{}: AXI translation offset {:#x}",
|
||||
i, pciBar.translation);
|
||||
logger->info("PCI-BAR{}: bus addr={:#x} size={:#x}", region.num,
|
||||
region.start, region_size);
|
||||
logger->info("PCI-BAR{}: AXI translation offset {:#x}", i,
|
||||
pciBar.translation);
|
||||
|
||||
mm.createMapping(region.start, pciBar.translation, region_size,
|
||||
std::string("PCI-") + barName,
|
||||
pciAddrSpaceId, card->addrSpaceIdHostToDevice);
|
||||
}
|
||||
mm.createMapping(region.start, pciBar.translation, region_size,
|
||||
std::string("PCI-") + barName, pciAddrSpaceId,
|
||||
card->addrSpaceIdHostToDevice);
|
||||
}
|
||||
|
||||
for (auto& [barName, axiBar] : axiToPcieTranslations) {
|
||||
logger->info("AXI-{}: bus addr={:#x} size={:#x}",
|
||||
barName, axiBar.base, axiBar.size);
|
||||
logger->info("AXI-{}: PCI translation offset: {:#x}",
|
||||
barName, axiBar.translation);
|
||||
for (auto &[barName, axiBar] : axiToPcieTranslations) {
|
||||
logger->info("AXI-{}: bus addr={:#x} size={:#x}", barName, axiBar.base,
|
||||
axiBar.size);
|
||||
logger->info("AXI-{}: PCI translation offset: {:#x}", barName,
|
||||
axiBar.translation);
|
||||
|
||||
auto barXAddrSpaceName = mm.getSlaveAddrSpaceName(getInstanceName(), barName);
|
||||
auto barXAddrSpaceId = mm.getOrCreateAddressSpace(barXAddrSpaceName);
|
||||
auto barXAddrSpaceName =
|
||||
mm.getSlaveAddrSpaceName(getInstanceName(), barName);
|
||||
auto barXAddrSpaceId = mm.getOrCreateAddressSpace(barXAddrSpaceName);
|
||||
|
||||
// Base is already incorporated into mapping of each IP by Vivado, so
|
||||
// the mapping src has to be 0
|
||||
mm.createMapping(0, axiBar.translation, axiBar.size,
|
||||
std::string("AXI-") + barName,
|
||||
barXAddrSpaceId, pciAddrSpaceId);
|
||||
// Base is already incorporated into mapping of each IP by Vivado, so
|
||||
// the mapping src has to be 0
|
||||
mm.createMapping(0, axiBar.translation, axiBar.size,
|
||||
std::string("AXI-") + barName, barXAddrSpaceId,
|
||||
pciAddrSpaceId);
|
||||
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
AxiPciExpressBridgeFactory::parse(Core &ip, json_t *cfg)
|
||||
{
|
||||
CoreFactory::parse(ip, cfg);
|
||||
void AxiPciExpressBridgeFactory::parse(Core &ip, json_t *cfg) {
|
||||
CoreFactory::parse(ip, cfg);
|
||||
|
||||
auto logger = getLogger();
|
||||
auto &pcie = dynamic_cast<AxiPciExpressBridge&>(ip);
|
||||
auto logger = getLogger();
|
||||
auto &pcie = dynamic_cast<AxiPciExpressBridge &>(ip);
|
||||
|
||||
for (auto barType : std::list<std::string>{
|
||||
"axi_bars",
|
||||
"pcie_bars"
|
||||
}) {
|
||||
json_t *json_bars = json_object_get(cfg, barType.c_str());
|
||||
if (not json_is_object(json_bars))
|
||||
throw ConfigError(cfg, "", "Missing BAR config: {}", barType);
|
||||
for (auto barType : std::list<std::string>{"axi_bars", "pcie_bars"}) {
|
||||
json_t *json_bars = json_object_get(cfg, barType.c_str());
|
||||
if (not json_is_object(json_bars))
|
||||
throw ConfigError(cfg, "", "Missing BAR config: {}", barType);
|
||||
|
||||
json_t* json_bar;
|
||||
const char* bar_name;
|
||||
json_object_foreach(json_bars, bar_name, json_bar) {
|
||||
unsigned int translation;
|
||||
json_t *json_bar;
|
||||
const char *bar_name;
|
||||
json_object_foreach(json_bars, bar_name, json_bar) {
|
||||
unsigned int translation;
|
||||
|
||||
json_error_t err;
|
||||
int ret = json_unpack_ex(json_bar, &err, 0, "{ s: i }",
|
||||
"translation", &translation
|
||||
);
|
||||
if (ret != 0)
|
||||
throw ConfigError(json_bar, err, "", "Cannot parse {}/{}", barType, bar_name);
|
||||
json_error_t err;
|
||||
int ret = json_unpack_ex(json_bar, &err, 0, "{ s: i }", "translation",
|
||||
&translation);
|
||||
if (ret != 0)
|
||||
throw ConfigError(json_bar, err, "", "Cannot parse {}/{}", barType,
|
||||
bar_name);
|
||||
|
||||
if (barType == "axi_bars") {
|
||||
json_int_t base, high, size;
|
||||
int ret = json_unpack_ex(json_bar, &err, 0, "{ s: I, s: I, s: I }",
|
||||
"baseaddr", &base,
|
||||
"highaddr", &high,
|
||||
"size", &size
|
||||
);
|
||||
if (ret != 0)
|
||||
throw ConfigError(json_bar, err, "", "Cannot parse {}/{}", barType, bar_name);
|
||||
if (barType == "axi_bars") {
|
||||
json_int_t base, high, size;
|
||||
int ret =
|
||||
json_unpack_ex(json_bar, &err, 0, "{ s: I, s: I, s: I }",
|
||||
"baseaddr", &base, "highaddr", &high, "size", &size);
|
||||
if (ret != 0)
|
||||
throw ConfigError(json_bar, err, "", "Cannot parse {}/{}", barType,
|
||||
bar_name);
|
||||
|
||||
pcie.axiToPcieTranslations[bar_name] = {
|
||||
.base = static_cast<uintptr_t>(base),
|
||||
.size = static_cast<size_t>(size),
|
||||
.translation = translation
|
||||
};
|
||||
} else
|
||||
pcie.pcieToAxiTranslations[bar_name] = {
|
||||
.translation = translation
|
||||
};
|
||||
}
|
||||
}
|
||||
pcie.axiToPcieTranslations[bar_name] = {
|
||||
.base = static_cast<uintptr_t>(base),
|
||||
.size = static_cast<size_t>(size),
|
||||
.translation = translation};
|
||||
} else
|
||||
pcie.pcieToAxiTranslations[bar_name] = {.translation = translation};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static AxiPciExpressBridgeFactory p;
|
||||
|
|
|
@ -11,63 +11,86 @@
|
|||
|
||||
#include <villas/fpga/ips/rtds.hpp>
|
||||
|
||||
#define RTDS_HZ 100000000 // 100 MHz
|
||||
#define RTDS_HZ 100000000 // 100 MHz
|
||||
|
||||
#define RTDS_AXIS_MAX_TX 64 // The amount of values which is supported by the VIILASfpga card
|
||||
#define RTDS_AXIS_MAX_RX 64 // The amount of values which is supported by the VIILASfpga card
|
||||
#define RTDS_AXIS_MAX_TX \
|
||||
64 // The amount of values which is supported by the VIILASfpga card
|
||||
#define RTDS_AXIS_MAX_RX \
|
||||
64 // The amount of values which is supported by the VIILASfpga card
|
||||
|
||||
// Register offsets
|
||||
#define RTDS_AXIS_SR_OFFSET 0x00 // Status Register (read-only). See RTDS_AXIS_SR_* constant.
|
||||
#define RTDS_AXIS_CR_OFFSET 0x04 // Control Register (read/write)
|
||||
#define RTDS_AXIS_TSCNT_LOW_OFFSET 0x08 // Lower 32 bits of timestep counter (read-only).
|
||||
#define RTDS_AXIS_TSCNT_HIGH_OFFSET 0x0C // Higher 32 bits of timestep counter (read-only).
|
||||
#define RTDS_AXIS_TS_PERIOD_OFFSET 0x10 // Period in clock cycles of previous timestep (read-only).
|
||||
#define RTDS_AXIS_COALESC_OFFSET 0x14 // IRQ Coalescing register (read/write).
|
||||
#define RTDS_AXIS_VERSION_OFFSET 0x18 // 16 bit version field passed back to the rack for version reporting (visible from “status” command, read/write).
|
||||
#define RTDS_AXIS_MRATE 0x1C // Multi-rate register
|
||||
#define RTDS_AXIS_SR_OFFSET \
|
||||
0x00 // Status Register (read-only). See RTDS_AXIS_SR_* constant.
|
||||
#define RTDS_AXIS_CR_OFFSET 0x04 // Control Register (read/write)
|
||||
#define RTDS_AXIS_TSCNT_LOW_OFFSET \
|
||||
0x08 // Lower 32 bits of timestep counter (read-only).
|
||||
#define RTDS_AXIS_TSCNT_HIGH_OFFSET \
|
||||
0x0C // Higher 32 bits of timestep counter (read-only).
|
||||
#define RTDS_AXIS_TS_PERIOD_OFFSET \
|
||||
0x10 // Period in clock cycles of previous timestep (read-only).
|
||||
#define RTDS_AXIS_COALESC_OFFSET 0x14 // IRQ Coalescing register (read/write).
|
||||
#define RTDS_AXIS_VERSION_OFFSET \
|
||||
0x18 // 16 bit version field passed back to the rack for version reporting (visible from “status” command, read/write).
|
||||
#define RTDS_AXIS_MRATE 0x1C // Multi-rate register
|
||||
|
||||
// Status register bits
|
||||
#define RTDS_AXIS_SR_CARDDETECTED (1 << 0) // ‘1’ when RTDS software has detected and configured card.
|
||||
#define RTDS_AXIS_SR_LINKUP (1 << 1) // ‘1’ when RTDS communication link has been negotiated.
|
||||
#define RTDS_AXIS_SR_TX_FULL (1 << 2) // Tx buffer is full, writes that happen when UserTxFull=’1’ will be dropped (Throttling / buffering is performed by hardware).
|
||||
#define RTDS_AXIS_SR_TX_INPROGRESS (1 << 3) // Indicates when data is being put on link.
|
||||
#define RTDS_AXIS_SR_CASE_RUNNING (1 << 4) // There is currently a simulation running.
|
||||
#define RTDS_AXIS_SR_CARDDETECTED \
|
||||
(1 << 0) // ‘1’ when RTDS software has detected and configured card.
|
||||
#define RTDS_AXIS_SR_LINKUP \
|
||||
(1 << 1) // ‘1’ when RTDS communication link has been negotiated.
|
||||
#define RTDS_AXIS_SR_TX_FULL \
|
||||
(1 \
|
||||
<< 2) // Tx buffer is full, writes that happen when UserTxFull=’1’ will be dropped (Throttling / buffering is performed by hardware).
|
||||
#define RTDS_AXIS_SR_TX_INPROGRESS \
|
||||
(1 << 3) // Indicates when data is being put on link.
|
||||
#define RTDS_AXIS_SR_CASE_RUNNING \
|
||||
(1 << 4) // There is currently a simulation running.
|
||||
|
||||
// Control register bits
|
||||
#define RTDS_AXIS_CR_DISABLE_LINK 0 // Disable SFP TX when set
|
||||
#define RTDS_AXIS_CR_DISABLE_LINK 0 // Disable SFP TX when set
|
||||
|
||||
using namespace villas::fpga::ip;
|
||||
|
||||
void RtdsGtfpga::dump()
|
||||
{
|
||||
// Check RTDS_Axis registers
|
||||
const uint32_t sr = readMemory<uint32_t>(registerMemory, RTDS_AXIS_SR_OFFSET);
|
||||
void RtdsGtfpga::dump() {
|
||||
// Check RTDS_Axis registers
|
||||
const uint32_t sr = readMemory<uint32_t>(registerMemory, RTDS_AXIS_SR_OFFSET);
|
||||
|
||||
logger->info("RTDS AXI Stream interface details:");
|
||||
logger->info("RTDS status: {:#x}", sr);
|
||||
logger->info(" Card detected: {}", sr & RTDS_AXIS_SR_CARDDETECTED ? CLR_GRN("yes") : CLR_RED("no"));
|
||||
logger->info(" Link up: {}", sr & RTDS_AXIS_SR_LINKUP ? CLR_GRN("yes") : CLR_RED("no"));
|
||||
logger->info(" TX queue full: {}", sr & RTDS_AXIS_SR_TX_FULL ? CLR_RED("yes") : CLR_GRN("no"));
|
||||
logger->info(" TX in progress: {}", sr & RTDS_AXIS_SR_TX_INPROGRESS ? CLR_YEL("yes") : "no");
|
||||
logger->info(" Case running: {}", sr & RTDS_AXIS_SR_CASE_RUNNING ? CLR_GRN("yes") : CLR_RED("no"));
|
||||
logger->info("RTDS AXI Stream interface details:");
|
||||
logger->info("RTDS status: {:#x}", sr);
|
||||
logger->info(" Card detected: {}",
|
||||
sr & RTDS_AXIS_SR_CARDDETECTED ? CLR_GRN("yes") : CLR_RED("no"));
|
||||
logger->info(" Link up: {}",
|
||||
sr & RTDS_AXIS_SR_LINKUP ? CLR_GRN("yes") : CLR_RED("no"));
|
||||
logger->info(" TX queue full: {}",
|
||||
sr & RTDS_AXIS_SR_TX_FULL ? CLR_RED("yes") : CLR_GRN("no"));
|
||||
logger->info(" TX in progress: {}",
|
||||
sr & RTDS_AXIS_SR_TX_INPROGRESS ? CLR_YEL("yes") : "no");
|
||||
logger->info(" Case running: {}",
|
||||
sr & RTDS_AXIS_SR_CASE_RUNNING ? CLR_GRN("yes") : CLR_RED("no"));
|
||||
|
||||
logger->info("RTDS control: {:#x}", readMemory<uint32_t>(registerMemory, RTDS_AXIS_CR_OFFSET));
|
||||
logger->info("RTDS IRQ coalesc: {}", readMemory<uint32_t>(registerMemory, RTDS_AXIS_COALESC_OFFSET));
|
||||
logger->info("RTDS IRQ version: {:#x}", readMemory<uint32_t>(registerMemory, RTDS_AXIS_VERSION_OFFSET));
|
||||
logger->info("RTDS IRQ multi-rate: {}", readMemory<uint32_t>(registerMemory, RTDS_AXIS_MRATE));
|
||||
logger->info("RTDS control: {:#x}",
|
||||
readMemory<uint32_t>(registerMemory, RTDS_AXIS_CR_OFFSET));
|
||||
logger->info("RTDS IRQ coalesc: {}",
|
||||
readMemory<uint32_t>(registerMemory, RTDS_AXIS_COALESC_OFFSET));
|
||||
logger->info("RTDS IRQ version: {:#x}",
|
||||
readMemory<uint32_t>(registerMemory, RTDS_AXIS_VERSION_OFFSET));
|
||||
logger->info("RTDS IRQ multi-rate: {}",
|
||||
readMemory<uint32_t>(registerMemory, RTDS_AXIS_MRATE));
|
||||
|
||||
const uint64_t timestepLow = readMemory<uint32_t>(registerMemory, RTDS_AXIS_TSCNT_LOW_OFFSET);
|
||||
const uint64_t timestepHigh = readMemory<uint32_t>(registerMemory, RTDS_AXIS_TSCNT_HIGH_OFFSET);
|
||||
const uint64_t timestep = (timestepHigh << 32) | timestepLow;
|
||||
const uint64_t timestepLow =
|
||||
readMemory<uint32_t>(registerMemory, RTDS_AXIS_TSCNT_LOW_OFFSET);
|
||||
const uint64_t timestepHigh =
|
||||
readMemory<uint32_t>(registerMemory, RTDS_AXIS_TSCNT_HIGH_OFFSET);
|
||||
const uint64_t timestep = (timestepHigh << 32) | timestepLow;
|
||||
|
||||
logger->info("RTDS timestep counter: {}", timestep);
|
||||
logger->info("RTDS timestep period: {:.3f} us", getDt() * 1e6);
|
||||
logger->info("RTDS timestep counter: {}", timestep);
|
||||
logger->info("RTDS timestep period: {:.3f} us", getDt() * 1e6);
|
||||
}
|
||||
|
||||
double RtdsGtfpga::getDt()
|
||||
{
|
||||
const auto dt = readMemory<uint16_t>(registerMemory, RTDS_AXIS_TS_PERIOD_OFFSET);
|
||||
return (dt == 0xFFFF) ? 0.0 : (double) dt / RTDS_HZ;
|
||||
double RtdsGtfpga::getDt() {
|
||||
const auto dt =
|
||||
readMemory<uint16_t>(registerMemory, RTDS_AXIS_TS_PERIOD_OFFSET);
|
||||
return (dt == 0xFFFF) ? 0.0 : (double)dt / RTDS_HZ;
|
||||
}
|
||||
|
||||
static char n[] = "rtds_gtfpga";
|
||||
|
|
|
@ -5,57 +5,62 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <villas/fpga/ips/gpu2rtds.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/memory_manager.hpp>
|
||||
#include <villas/fpga/ips/gpu2rtds.hpp>
|
||||
|
||||
using namespace villas::fpga::ip;
|
||||
|
||||
bool Gpu2Rtds::init()
|
||||
{
|
||||
Hls::init();
|
||||
bool Gpu2Rtds::init() {
|
||||
Hls::init();
|
||||
|
||||
auto ®isters = addressTranslations.at(registerMemory);
|
||||
auto ®isters = addressTranslations.at(registerMemory);
|
||||
|
||||
registerStatus = reinterpret_cast<StatusRegister*>(registers.getLocalAddr(registerStatusOffset));
|
||||
registerStatusCtrl = reinterpret_cast<StatusControlRegister*>(registers.getLocalAddr(registerStatusCtrlOffset));
|
||||
registerFrameSize = reinterpret_cast<uint32_t*>(registers.getLocalAddr(registerFrameSizeOffset));
|
||||
registerFrames = reinterpret_cast<uint32_t*>(registers.getLocalAddr(registerFrameOffset));
|
||||
registerStatus = reinterpret_cast<StatusRegister *>(
|
||||
registers.getLocalAddr(registerStatusOffset));
|
||||
registerStatusCtrl = reinterpret_cast<StatusControlRegister *>(
|
||||
registers.getLocalAddr(registerStatusCtrlOffset));
|
||||
registerFrameSize = reinterpret_cast<uint32_t *>(
|
||||
registers.getLocalAddr(registerFrameSizeOffset));
|
||||
registerFrames =
|
||||
reinterpret_cast<uint32_t *>(registers.getLocalAddr(registerFrameOffset));
|
||||
|
||||
maxFrameSize = getMaxFrameSize();
|
||||
logger->info("Max. frame size supported: {}", maxFrameSize);
|
||||
maxFrameSize = getMaxFrameSize();
|
||||
logger->info("Max. frame size supported: {}", maxFrameSize);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Gpu2Rtds::startOnce(size_t frameSize)
|
||||
{
|
||||
*registerFrameSize = frameSize;
|
||||
bool Gpu2Rtds::startOnce(size_t frameSize) {
|
||||
*registerFrameSize = frameSize;
|
||||
|
||||
start();
|
||||
start();
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Gpu2Rtds::dump(spdlog::level::level_enum logLevel)
|
||||
{
|
||||
const auto frame_size = *registerFrameSize;
|
||||
auto status = *registerStatus;
|
||||
void Gpu2Rtds::dump(spdlog::level::level_enum logLevel) {
|
||||
const auto frame_size = *registerFrameSize;
|
||||
auto status = *registerStatus;
|
||||
|
||||
logger->log(logLevel, "Gpu2Rtds registers:");
|
||||
logger->log(logLevel, " Frame size (words): {:#x}", frame_size);
|
||||
logger->log(logLevel, " Status: {:#x}", status.value);
|
||||
logger->log(logLevel, " Running: {}", (status.is_running ? "yes" : "no"));
|
||||
logger->log(logLevel, " Frame too short: {}", (status.frame_too_short ? "yes" : "no"));
|
||||
logger->log(logLevel, " Frame too long: {}", (status.frame_too_long ? "yes" : "no"));
|
||||
logger->log(logLevel, " Frame size invalid: {}", (status.invalid_frame_size ? "yes" : "no"));
|
||||
logger->log(logLevel, " Last count: {}", (int) status.last_count);
|
||||
logger->log(logLevel, " Last seq. number: {}", (int) status.last_seq_nr);
|
||||
logger->log(logLevel, " Max. frame size: {}", (int) status.max_frame_size);
|
||||
logger->log(logLevel, "Gpu2Rtds registers:");
|
||||
logger->log(logLevel, " Frame size (words): {:#x}", frame_size);
|
||||
logger->log(logLevel, " Status: {:#x}", status.value);
|
||||
logger->log(logLevel, " Running: {}",
|
||||
(status.is_running ? "yes" : "no"));
|
||||
logger->log(logLevel, " Frame too short: {}",
|
||||
(status.frame_too_short ? "yes" : "no"));
|
||||
logger->log(logLevel, " Frame too long: {}",
|
||||
(status.frame_too_long ? "yes" : "no"));
|
||||
logger->log(logLevel, " Frame size invalid: {}",
|
||||
(status.invalid_frame_size ? "yes" : "no"));
|
||||
logger->log(logLevel, " Last count: {}", (int)status.last_count);
|
||||
logger->log(logLevel, " Last seq. number: {}", (int)status.last_seq_nr);
|
||||
logger->log(logLevel, " Max. frame size: {}",
|
||||
(int)status.max_frame_size);
|
||||
}
|
||||
|
||||
//bool Gpu2Rtds::startOnce(const MemoryBlock &mem, size_t frameSize, size_t dataOffset, size_t doorbellOffset)
|
||||
|
@ -99,24 +104,24 @@ void Gpu2Rtds::dump(spdlog::level::level_enum logLevel)
|
|||
// return true;
|
||||
//}
|
||||
|
||||
size_t
|
||||
Gpu2Rtds::getMaxFrameSize()
|
||||
{
|
||||
*registerFrameSize = 0;
|
||||
size_t Gpu2Rtds::getMaxFrameSize() {
|
||||
*registerFrameSize = 0;
|
||||
|
||||
start();
|
||||
while (not isFinished());
|
||||
start();
|
||||
while (not isFinished())
|
||||
;
|
||||
|
||||
while (not registerStatusCtrl->status_ap_vld);
|
||||
while (not registerStatusCtrl->status_ap_vld)
|
||||
;
|
||||
|
||||
axilite_reg_status_t status = *registerStatus;
|
||||
axilite_reg_status_t status = *registerStatus;
|
||||
|
||||
// logger->debug("(*registerStatus).max_frame_size: {}", (*registerStatus).max_frame_size);
|
||||
// logger->debug("status.max_frame_size: {}", status.max_frame_size);
|
||||
// logger->debug("(*registerStatus).max_frame_size: {}", (*registerStatus).max_frame_size);
|
||||
// logger->debug("status.max_frame_size: {}", status.max_frame_size);
|
||||
|
||||
// assert(status.max_frame_size == (*registerStatus).max_frame_size);
|
||||
// assert(status.max_frame_size == (*registerStatus).max_frame_size);
|
||||
|
||||
return status.max_frame_size;
|
||||
return status.max_frame_size;
|
||||
}
|
||||
|
||||
//void
|
||||
|
|
|
@ -5,113 +5,116 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <villas/fpga/ips/rtds2gpu.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/memory_manager.hpp>
|
||||
#include <villas/fpga/ips/rtds2gpu.hpp>
|
||||
|
||||
using namespace villas::fpga::ip;
|
||||
|
||||
bool Rtds2Gpu::init()
|
||||
{
|
||||
Hls::init();
|
||||
bool Rtds2Gpu::init() {
|
||||
Hls::init();
|
||||
|
||||
xInstance.IsReady = XIL_COMPONENT_IS_READY;
|
||||
xInstance.Ctrl_BaseAddress = getBaseAddr(registerMemory);
|
||||
xInstance.IsReady = XIL_COMPONENT_IS_READY;
|
||||
xInstance.Ctrl_BaseAddress = getBaseAddr(registerMemory);
|
||||
|
||||
status.value = 0;
|
||||
started = false;
|
||||
status.value = 0;
|
||||
started = false;
|
||||
|
||||
// maxFrameSize = getMaxFrameSize();
|
||||
maxFrameSize = 16;
|
||||
logger->info("Max. frame size supported: {}", maxFrameSize);
|
||||
// maxFrameSize = getMaxFrameSize();
|
||||
maxFrameSize = 16;
|
||||
logger->info("Max. frame size supported: {}", maxFrameSize);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Rtds2Gpu::dump(spdlog::level::level_enum logLevel)
|
||||
{
|
||||
const auto baseaddr = XRtds2gpu_Get_baseaddr(&xInstance);
|
||||
const auto data_offset = XRtds2gpu_Get_data_offset(&xInstance);
|
||||
const auto doorbell_offset = XRtds2gpu_Get_doorbell_offset(&xInstance);
|
||||
const auto frame_size = XRtds2gpu_Get_frame_size(&xInstance);
|
||||
void Rtds2Gpu::dump(spdlog::level::level_enum logLevel) {
|
||||
const auto baseaddr = XRtds2gpu_Get_baseaddr(&xInstance);
|
||||
const auto data_offset = XRtds2gpu_Get_data_offset(&xInstance);
|
||||
const auto doorbell_offset = XRtds2gpu_Get_doorbell_offset(&xInstance);
|
||||
const auto frame_size = XRtds2gpu_Get_frame_size(&xInstance);
|
||||
|
||||
logger->log(logLevel, "Rtds2Gpu registers (IP base {:#x}):", xInstance.Ctrl_BaseAddress);
|
||||
logger->log(logLevel, " Base address (bytes): {:#x}", baseaddr);
|
||||
logger->log(logLevel, " Doorbell offset (bytes): {:#x}", doorbell_offset);
|
||||
logger->log(logLevel, " Data offset (bytes): {:#x}", data_offset);
|
||||
logger->log(logLevel, " Frame size (words): {:#x}", frame_size);
|
||||
logger->log(logLevel, " Status: {:#x}", status.value);
|
||||
logger->log(logLevel, " Running: {}", (status.is_running ? "yes" : "no"));
|
||||
logger->log(logLevel, " Frame too short: {}", (status.frame_too_short ? "yes" : "no"));
|
||||
logger->log(logLevel, " Frame too long: {}", (status.frame_too_long ? "yes" : "no"));
|
||||
logger->log(logLevel, " Frame size invalid: {}", (status.invalid_frame_size ? "yes" : "no"));
|
||||
logger->log(logLevel, " Last count: {}", (int) status.last_count);
|
||||
logger->log(logLevel, " Last seq. number: {}", (int) status.last_seq_nr);
|
||||
logger->log(logLevel, " Max. frame size: {}", (int) status.max_frame_size);
|
||||
logger->log(logLevel, "Rtds2Gpu registers (IP base {:#x}):",
|
||||
xInstance.Ctrl_BaseAddress);
|
||||
logger->log(logLevel, " Base address (bytes): {:#x}", baseaddr);
|
||||
logger->log(logLevel, " Doorbell offset (bytes): {:#x}", doorbell_offset);
|
||||
logger->log(logLevel, " Data offset (bytes): {:#x}", data_offset);
|
||||
logger->log(logLevel, " Frame size (words): {:#x}", frame_size);
|
||||
logger->log(logLevel, " Status: {:#x}", status.value);
|
||||
logger->log(logLevel, " Running: {}",
|
||||
(status.is_running ? "yes" : "no"));
|
||||
logger->log(logLevel, " Frame too short: {}",
|
||||
(status.frame_too_short ? "yes" : "no"));
|
||||
logger->log(logLevel, " Frame too long: {}",
|
||||
(status.frame_too_long ? "yes" : "no"));
|
||||
logger->log(logLevel, " Frame size invalid: {}",
|
||||
(status.invalid_frame_size ? "yes" : "no"));
|
||||
logger->log(logLevel, " Last count: {}", (int)status.last_count);
|
||||
logger->log(logLevel, " Last seq. number: {}", (int)status.last_seq_nr);
|
||||
logger->log(logLevel, " Max. frame size: {}",
|
||||
(int)status.max_frame_size);
|
||||
}
|
||||
|
||||
bool Rtds2Gpu::startOnce(const MemoryBlock &mem, size_t frameSize, size_t dataOffset, size_t doorbellOffset)
|
||||
{
|
||||
auto &mm = MemoryManager::get();
|
||||
bool Rtds2Gpu::startOnce(const MemoryBlock &mem, size_t frameSize,
|
||||
size_t dataOffset, size_t doorbellOffset) {
|
||||
auto &mm = MemoryManager::get();
|
||||
|
||||
if (frameSize > maxFrameSize) {
|
||||
logger->error("Requested frame size of {} exceeds max. frame size of {}",
|
||||
frameSize, maxFrameSize);
|
||||
return false;
|
||||
}
|
||||
if (frameSize > maxFrameSize) {
|
||||
logger->error("Requested frame size of {} exceeds max. frame size of {}",
|
||||
frameSize, maxFrameSize);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto translationFromIp = mm.getTranslation(
|
||||
getMasterAddrSpaceByInterface(axiInterface),
|
||||
mem.getAddrSpaceId());
|
||||
auto translationFromIp = mm.getTranslation(
|
||||
getMasterAddrSpaceByInterface(axiInterface), mem.getAddrSpaceId());
|
||||
|
||||
// Set address of memory block in HLS IP
|
||||
XRtds2gpu_Set_baseaddr(&xInstance, translationFromIp.getLocalAddr(0));
|
||||
// Set address of memory block in HLS IP
|
||||
XRtds2gpu_Set_baseaddr(&xInstance, translationFromIp.getLocalAddr(0));
|
||||
|
||||
XRtds2gpu_Set_doorbell_offset(&xInstance, doorbellOffset);
|
||||
XRtds2gpu_Set_data_offset(&xInstance, dataOffset);
|
||||
XRtds2gpu_Set_frame_size(&xInstance, frameSize);
|
||||
XRtds2gpu_Set_doorbell_offset(&xInstance, doorbellOffset);
|
||||
XRtds2gpu_Set_data_offset(&xInstance, dataOffset);
|
||||
XRtds2gpu_Set_frame_size(&xInstance, frameSize);
|
||||
|
||||
// Prepare memory with all zeroes
|
||||
auto translationFromProcess = mm.getTranslationFromProcess(mem.getAddrSpaceId());
|
||||
auto memory = reinterpret_cast<void*>(translationFromProcess.getLocalAddr(0));
|
||||
memset(memory, 0, mem.getSize());
|
||||
// Prepare memory with all zeroes
|
||||
auto translationFromProcess =
|
||||
mm.getTranslationFromProcess(mem.getAddrSpaceId());
|
||||
auto memory =
|
||||
reinterpret_cast<void *>(translationFromProcess.getLocalAddr(0));
|
||||
memset(memory, 0, mem.getSize());
|
||||
|
||||
// Start IP
|
||||
return start();
|
||||
// Start IP
|
||||
return start();
|
||||
}
|
||||
|
||||
bool Rtds2Gpu::updateStatus()
|
||||
{
|
||||
if (not XRtds2gpu_Get_status_vld(&xInstance))
|
||||
return false;
|
||||
bool Rtds2Gpu::updateStatus() {
|
||||
if (not XRtds2gpu_Get_status_vld(&xInstance))
|
||||
return false;
|
||||
|
||||
status.value = XRtds2gpu_Get_status(&xInstance);
|
||||
status.value = XRtds2gpu_Get_status(&xInstance);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Rtds2Gpu::getMaxFrameSize()
|
||||
{
|
||||
XRtds2gpu_Set_frame_size(&xInstance, 0);
|
||||
size_t Rtds2Gpu::getMaxFrameSize() {
|
||||
XRtds2gpu_Set_frame_size(&xInstance, 0);
|
||||
|
||||
start();
|
||||
while (not isFinished());
|
||||
updateStatus();
|
||||
start();
|
||||
while (not isFinished())
|
||||
;
|
||||
updateStatus();
|
||||
|
||||
return status.max_frame_size;
|
||||
return status.max_frame_size;
|
||||
}
|
||||
|
||||
void Rtds2Gpu::dumpDoorbell(uint32_t doorbellRegister) const
|
||||
{
|
||||
auto &doorbell = reinterpret_cast<reg_doorbell_t&>(doorbellRegister);
|
||||
void Rtds2Gpu::dumpDoorbell(uint32_t doorbellRegister) const {
|
||||
auto &doorbell = reinterpret_cast<reg_doorbell_t &>(doorbellRegister);
|
||||
|
||||
logger->info("Doorbell register: {:#08x}", doorbell.value);
|
||||
logger->info(" Valid: {}", doorbell.is_valid ? "yes" : "no");
|
||||
logger->info(" Count: {}", (int) doorbell.count);
|
||||
logger->info(" Seq. number: {}", (int) doorbell.seq_nr);
|
||||
logger->info("Doorbell register: {:#08x}", doorbell.value);
|
||||
logger->info(" Valid: {}", doorbell.is_valid ? "yes" : "no");
|
||||
logger->info(" Count: {}", (int)doorbell.count);
|
||||
logger->info(" Seq. number: {}", (int)doorbell.seq_nr);
|
||||
}
|
||||
|
||||
static char n[] = "Rtds2Gpu";
|
||||
|
|
|
@ -10,212 +10,237 @@
|
|||
|
||||
/************************** Function Implementation *************************/
|
||||
#ifndef __linux__
|
||||
int XRtds2gpu_CfgInitialize(XRtds2gpu *InstancePtr, XRtds2gpu_Config *ConfigPtr) {
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(ConfigPtr != NULL);
|
||||
int XRtds2gpu_CfgInitialize(XRtds2gpu *InstancePtr,
|
||||
XRtds2gpu_Config *ConfigPtr) {
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(ConfigPtr != NULL);
|
||||
|
||||
InstancePtr->Ctrl_BaseAddress = ConfigPtr->Ctrl_BaseAddress;
|
||||
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
|
||||
InstancePtr->Ctrl_BaseAddress = ConfigPtr->Ctrl_BaseAddress;
|
||||
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
|
||||
|
||||
return XST_SUCCESS;
|
||||
return XST_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
void XRtds2gpu_Start(XRtds2gpu *InstancePtr) {
|
||||
u32 Data;
|
||||
u32 Data;
|
||||
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_AP_CTRL) & 0x80;
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_AP_CTRL, Data | 0x01);
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress,
|
||||
XRTDS2GPU_CTRL_ADDR_AP_CTRL) &
|
||||
0x80;
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_AP_CTRL,
|
||||
Data | 0x01);
|
||||
}
|
||||
|
||||
u32 XRtds2gpu_IsDone(XRtds2gpu *InstancePtr) {
|
||||
u32 Data;
|
||||
u32 Data;
|
||||
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_AP_CTRL);
|
||||
return (Data >> 1) & 0x1;
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress,
|
||||
XRTDS2GPU_CTRL_ADDR_AP_CTRL);
|
||||
return (Data >> 1) & 0x1;
|
||||
}
|
||||
|
||||
u32 XRtds2gpu_IsIdle(XRtds2gpu *InstancePtr) {
|
||||
u32 Data;
|
||||
u32 Data;
|
||||
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_AP_CTRL);
|
||||
return (Data >> 2) & 0x1;
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress,
|
||||
XRTDS2GPU_CTRL_ADDR_AP_CTRL);
|
||||
return (Data >> 2) & 0x1;
|
||||
}
|
||||
|
||||
u32 XRtds2gpu_IsReady(XRtds2gpu *InstancePtr) {
|
||||
u32 Data;
|
||||
u32 Data;
|
||||
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_AP_CTRL);
|
||||
// Check ap_start to see if the pcore is ready for next input
|
||||
return !(Data & 0x1);
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress,
|
||||
XRTDS2GPU_CTRL_ADDR_AP_CTRL);
|
||||
// Check ap_start to see if the pcore is ready for next input
|
||||
return !(Data & 0x1);
|
||||
}
|
||||
|
||||
void XRtds2gpu_EnableAutoRestart(XRtds2gpu *InstancePtr) {
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_AP_CTRL, 0x80);
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_AP_CTRL,
|
||||
0x80);
|
||||
}
|
||||
|
||||
void XRtds2gpu_DisableAutoRestart(XRtds2gpu *InstancePtr) {
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_AP_CTRL, 0);
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_AP_CTRL,
|
||||
0);
|
||||
}
|
||||
|
||||
void XRtds2gpu_Set_baseaddr(XRtds2gpu *InstancePtr, u32 Data) {
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_BASEADDR_DATA, Data);
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress,
|
||||
XRTDS2GPU_CTRL_ADDR_BASEADDR_DATA, Data);
|
||||
}
|
||||
|
||||
u32 XRtds2gpu_Get_baseaddr(XRtds2gpu *InstancePtr) {
|
||||
u32 Data;
|
||||
u32 Data;
|
||||
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_BASEADDR_DATA);
|
||||
return Data;
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress,
|
||||
XRTDS2GPU_CTRL_ADDR_BASEADDR_DATA);
|
||||
return Data;
|
||||
}
|
||||
|
||||
void XRtds2gpu_Set_data_offset(XRtds2gpu *InstancePtr, u32 Data) {
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_DATA_OFFSET_DATA, Data);
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress,
|
||||
XRTDS2GPU_CTRL_ADDR_DATA_OFFSET_DATA, Data);
|
||||
}
|
||||
|
||||
u32 XRtds2gpu_Get_data_offset(XRtds2gpu *InstancePtr) {
|
||||
u32 Data;
|
||||
u32 Data;
|
||||
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_DATA_OFFSET_DATA);
|
||||
return Data;
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress,
|
||||
XRTDS2GPU_CTRL_ADDR_DATA_OFFSET_DATA);
|
||||
return Data;
|
||||
}
|
||||
|
||||
void XRtds2gpu_Set_doorbell_offset(XRtds2gpu *InstancePtr, u32 Data) {
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_DOORBELL_OFFSET_DATA, Data);
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress,
|
||||
XRTDS2GPU_CTRL_ADDR_DOORBELL_OFFSET_DATA, Data);
|
||||
}
|
||||
|
||||
u32 XRtds2gpu_Get_doorbell_offset(XRtds2gpu *InstancePtr) {
|
||||
u32 Data;
|
||||
u32 Data;
|
||||
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_DOORBELL_OFFSET_DATA);
|
||||
return Data;
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress,
|
||||
XRTDS2GPU_CTRL_ADDR_DOORBELL_OFFSET_DATA);
|
||||
return Data;
|
||||
}
|
||||
|
||||
void XRtds2gpu_Set_frame_size(XRtds2gpu *InstancePtr, u32 Data) {
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_FRAME_SIZE_DATA, Data);
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress,
|
||||
XRTDS2GPU_CTRL_ADDR_FRAME_SIZE_DATA, Data);
|
||||
}
|
||||
|
||||
u32 XRtds2gpu_Get_frame_size(XRtds2gpu *InstancePtr) {
|
||||
u32 Data;
|
||||
u32 Data;
|
||||
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_FRAME_SIZE_DATA);
|
||||
return Data;
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress,
|
||||
XRTDS2GPU_CTRL_ADDR_FRAME_SIZE_DATA);
|
||||
return Data;
|
||||
}
|
||||
|
||||
u32 XRtds2gpu_Get_status(XRtds2gpu *InstancePtr) {
|
||||
u32 Data;
|
||||
u32 Data;
|
||||
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_STATUS_DATA);
|
||||
return Data;
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress,
|
||||
XRTDS2GPU_CTRL_ADDR_STATUS_DATA);
|
||||
return Data;
|
||||
}
|
||||
|
||||
u32 XRtds2gpu_Get_status_vld(XRtds2gpu *InstancePtr) {
|
||||
u32 Data;
|
||||
u32 Data;
|
||||
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_STATUS_CTRL);
|
||||
return Data & 0x1;
|
||||
Data = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress,
|
||||
XRTDS2GPU_CTRL_ADDR_STATUS_CTRL);
|
||||
return Data & 0x1;
|
||||
}
|
||||
|
||||
void XRtds2gpu_InterruptGlobalEnable(XRtds2gpu *InstancePtr) {
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_GIE, 1);
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_GIE, 1);
|
||||
}
|
||||
|
||||
void XRtds2gpu_InterruptGlobalDisable(XRtds2gpu *InstancePtr) {
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_GIE, 0);
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_GIE, 0);
|
||||
}
|
||||
|
||||
void XRtds2gpu_InterruptEnable(XRtds2gpu *InstancePtr, u32 Mask) {
|
||||
u32 Register;
|
||||
u32 Register;
|
||||
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
Register = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_IER);
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_IER, Register | Mask);
|
||||
Register =
|
||||
XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_IER);
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_IER,
|
||||
Register | Mask);
|
||||
}
|
||||
|
||||
void XRtds2gpu_InterruptDisable(XRtds2gpu *InstancePtr, u32 Mask) {
|
||||
u32 Register;
|
||||
u32 Register;
|
||||
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
Register = XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_IER);
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_IER, Register & (~Mask));
|
||||
Register =
|
||||
XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_IER);
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_IER,
|
||||
Register & (~Mask));
|
||||
}
|
||||
|
||||
void XRtds2gpu_InterruptClear(XRtds2gpu *InstancePtr, u32 Mask) {
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertVoid(InstancePtr != NULL);
|
||||
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_ISR, Mask);
|
||||
XRtds2gpu_WriteReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_ISR,
|
||||
Mask);
|
||||
}
|
||||
|
||||
u32 XRtds2gpu_InterruptGetEnabled(XRtds2gpu *InstancePtr) {
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
return XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_IER);
|
||||
return XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress,
|
||||
XRTDS2GPU_CTRL_ADDR_IER);
|
||||
}
|
||||
|
||||
u32 XRtds2gpu_InterruptGetStatus(XRtds2gpu *InstancePtr) {
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
Xil_AssertNonvoid(InstancePtr != NULL);
|
||||
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
||||
|
||||
return XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress, XRTDS2GPU_CTRL_ADDR_ISR);
|
||||
return XRtds2gpu_ReadReg(InstancePtr->Ctrl_BaseAddress,
|
||||
XRTDS2GPU_CTRL_ADDR_ISR);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,33 +18,33 @@ namespace villas {
|
|||
namespace fpga {
|
||||
namespace ip {
|
||||
|
||||
bool AxiStreamSwitch::init()
|
||||
{
|
||||
if (XAxisScr_CfgInitialize(&xSwitch, &xConfig, getBaseAddr(registerMemory)) != XST_SUCCESS) {
|
||||
logger->error("Cannot initialize switch");
|
||||
return false;
|
||||
}
|
||||
bool AxiStreamSwitch::init() {
|
||||
if (XAxisScr_CfgInitialize(&xSwitch, &xConfig, getBaseAddr(registerMemory)) !=
|
||||
XST_SUCCESS) {
|
||||
logger->error("Cannot initialize switch");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disable all masters
|
||||
XAxisScr_RegUpdateDisable(&xSwitch);
|
||||
XAxisScr_MiPortDisableAll(&xSwitch);
|
||||
XAxisScr_RegUpdateEnable(&xSwitch);
|
||||
// Disable all masters
|
||||
XAxisScr_RegUpdateDisable(&xSwitch);
|
||||
XAxisScr_MiPortDisableAll(&xSwitch);
|
||||
XAxisScr_RegUpdateEnable(&xSwitch);
|
||||
|
||||
for (auto& [masterName, masterPort] : portsMaster) {
|
||||
for (auto &[masterName, masterPort] : portsMaster) {
|
||||
|
||||
// Initialize internal mapping
|
||||
portMapping[masterName] = PORT_DISABLED;
|
||||
// Initialize internal mapping
|
||||
portMapping[masterName] = PORT_DISABLED;
|
||||
|
||||
// Each slave port may be internally routed to a master port
|
||||
for (auto& [slaveName, slavePort] : portsSlave) {
|
||||
(void) slaveName;
|
||||
// Each slave port may be internally routed to a master port
|
||||
for (auto &[slaveName, slavePort] : portsSlave) {
|
||||
(void)slaveName;
|
||||
|
||||
streamGraph.addDefaultEdge(slavePort->getIdentifier(),
|
||||
masterPort->getIdentifier());
|
||||
}
|
||||
}
|
||||
streamGraph.addDefaultEdge(slavePort->getIdentifier(),
|
||||
masterPort->getIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AxiStreamSwitch::printConfig() const {
|
||||
|
@ -73,89 +73,83 @@ void AxiStreamSwitch::printConfig() const {
|
|||
}
|
||||
|
||||
bool AxiStreamSwitch::connectInternal(const std::string &portSlave,
|
||||
const std::string &portMaster)
|
||||
{
|
||||
// Check if slave port exists
|
||||
try {
|
||||
getSlavePort(portSlave);
|
||||
} catch (const std::out_of_range&) {
|
||||
logger->error("Switch doesn't have a slave port named '{}'", portSlave);
|
||||
return false;
|
||||
}
|
||||
const std::string &portMaster) {
|
||||
// Check if slave port exists
|
||||
try {
|
||||
getSlavePort(portSlave);
|
||||
} catch (const std::out_of_range &) {
|
||||
logger->error("Switch doesn't have a slave port named '{}'", portSlave);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if master port exists
|
||||
try {
|
||||
getMasterPort(portMaster);
|
||||
} catch (const std::out_of_range&) {
|
||||
logger->error("Switch doesn't have a master port named '{}'", portMaster);
|
||||
return false;
|
||||
}
|
||||
// Check if master port exists
|
||||
try {
|
||||
getMasterPort(portMaster);
|
||||
} catch (const std::out_of_range &) {
|
||||
logger->error("Switch doesn't have a master port named '{}'", portMaster);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (portSlave.substr(0, 1) != "S" or
|
||||
portMaster.substr(0, 1) != "M") {
|
||||
logger->error("sanity check failed: master {} slave {}",
|
||||
portMaster, portSlave);
|
||||
return false;
|
||||
}
|
||||
if (portSlave.substr(0, 1) != "S" or portMaster.substr(0, 1) != "M") {
|
||||
logger->error("sanity check failed: master {} slave {}", portMaster,
|
||||
portSlave);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (portMapping[portMaster] == portSlave) {
|
||||
logger->debug("Ports already connected (slave {} to master {}",
|
||||
portSlave, portMaster);
|
||||
return true;
|
||||
}
|
||||
if (portMapping[portMaster] == portSlave) {
|
||||
logger->debug("Ports already connected (slave {} to master {}", portSlave,
|
||||
portMaster);
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto [master, slave] : portMapping) {
|
||||
if (slave == portSlave) {
|
||||
logger->warn("Slave {} has already been connected to master {}. "
|
||||
"Disabling master {}.",
|
||||
slave, master, master);
|
||||
for (auto [master, slave] : portMapping) {
|
||||
if (slave == portSlave) {
|
||||
logger->warn("Slave {} has already been connected to master {}. "
|
||||
"Disabling master {}.",
|
||||
slave, master, master);
|
||||
|
||||
XAxisScr_RegUpdateDisable(&xSwitch);
|
||||
XAxisScr_MiPortDisable(&xSwitch, portNameToNum(master));
|
||||
XAxisScr_RegUpdateEnable(&xSwitch);
|
||||
XAxisScr_RegUpdateDisable(&xSwitch);
|
||||
XAxisScr_MiPortDisable(&xSwitch, portNameToNum(master));
|
||||
XAxisScr_RegUpdateEnable(&xSwitch);
|
||||
|
||||
portMapping[master] = PORT_DISABLED;
|
||||
}
|
||||
}
|
||||
portMapping[master] = PORT_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
// Reconfigure switch
|
||||
XAxisScr_RegUpdateDisable(&xSwitch);
|
||||
XAxisScr_MiPortEnable(&xSwitch, portNameToNum(portMaster), portNameToNum(portSlave));
|
||||
XAxisScr_RegUpdateEnable(&xSwitch);
|
||||
// Reconfigure switch
|
||||
XAxisScr_RegUpdateDisable(&xSwitch);
|
||||
XAxisScr_MiPortEnable(&xSwitch, portNameToNum(portMaster),
|
||||
portNameToNum(portSlave));
|
||||
XAxisScr_RegUpdateEnable(&xSwitch);
|
||||
|
||||
portMapping[portMaster] = portSlave;
|
||||
portMapping[portMaster] = portSlave;
|
||||
|
||||
logger->debug("Connect slave {} to master {}", portSlave, portMaster);
|
||||
logger->debug("Connect slave {} to master {}", portSlave, portMaster);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
int AxiStreamSwitch::portNameToNum(const std::string &portName)
|
||||
{
|
||||
const std::string number = portName.substr(1, 2);
|
||||
return std::stoi(number);
|
||||
int AxiStreamSwitch::portNameToNum(const std::string &portName) {
|
||||
const std::string number = portName.substr(1, 2);
|
||||
return std::stoi(number);
|
||||
}
|
||||
|
||||
void AxiStreamSwitchFactory::parse(Core &ip, json_t *cfg)
|
||||
{
|
||||
NodeFactory::parse(ip, cfg);
|
||||
void AxiStreamSwitchFactory::parse(Core &ip, json_t *cfg) {
|
||||
NodeFactory::parse(ip, cfg);
|
||||
|
||||
auto logger = getLogger();
|
||||
auto logger = getLogger();
|
||||
|
||||
auto &axiSwitch = dynamic_cast<AxiStreamSwitch&>(ip);
|
||||
auto &axiSwitch = dynamic_cast<AxiStreamSwitch &>(ip);
|
||||
|
||||
int num_si, num_mi;
|
||||
json_error_t err;
|
||||
auto ret = json_unpack_ex(cfg, &err, 0, "{ s: { s: i, s: i } }",
|
||||
"parameters",
|
||||
"num_si", &num_si,
|
||||
"num_mi", &num_mi
|
||||
);
|
||||
if (ret != 0)
|
||||
throw ConfigError(cfg, err, "", "Cannot parse switch config");
|
||||
int num_si, num_mi;
|
||||
json_error_t err;
|
||||
auto ret = json_unpack_ex(cfg, &err, 0, "{ s: { s: i, s: i } }", "parameters",
|
||||
"num_si", &num_si, "num_mi", &num_mi);
|
||||
if (ret != 0)
|
||||
throw ConfigError(cfg, err, "", "Cannot parse switch config");
|
||||
|
||||
axiSwitch.xConfig.MaxNumMI = num_mi;
|
||||
axiSwitch.xConfig.MaxNumSI = num_si;
|
||||
axiSwitch.xConfig.MaxNumMI = num_mi;
|
||||
axiSwitch.xConfig.MaxNumSI = num_si;
|
||||
}
|
||||
|
||||
static AxiStreamSwitchFactory f;
|
||||
|
|
|
@ -12,53 +12,47 @@
|
|||
|
||||
#include <xilinx/xtmrctr.h>
|
||||
|
||||
#include <villas/fpga/ips/timer.hpp>
|
||||
#include <villas/fpga/ips/intc.hpp>
|
||||
#include <villas/fpga/ips/timer.hpp>
|
||||
|
||||
using namespace villas::fpga::ip;
|
||||
|
||||
bool Timer::init()
|
||||
{
|
||||
XTmrCtr_Config xtmr_cfg;
|
||||
xtmr_cfg.SysClockFreqHz = getFrequency();
|
||||
bool Timer::init() {
|
||||
XTmrCtr_Config xtmr_cfg;
|
||||
xtmr_cfg.SysClockFreqHz = getFrequency();
|
||||
|
||||
XTmrCtr_CfgInitialize(&xTmr, &xtmr_cfg, getBaseAddr(registerMemory));
|
||||
XTmrCtr_InitHw(&xTmr);
|
||||
XTmrCtr_CfgInitialize(&xTmr, &xtmr_cfg, getBaseAddr(registerMemory));
|
||||
XTmrCtr_InitHw(&xTmr);
|
||||
|
||||
if (irqs.find(irqName) == irqs.end()) {
|
||||
logger->error("IRQ '{}' not found but required", irqName);
|
||||
return false;
|
||||
}
|
||||
if (irqs.find(irqName) == irqs.end()) {
|
||||
logger->error("IRQ '{}' not found but required", irqName);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disable so we don't receive any stray interrupts
|
||||
irqs[irqName].irqController->disableInterrupt(irqs[irqName]);
|
||||
// Disable so we don't receive any stray interrupts
|
||||
irqs[irqName].irqController->disableInterrupt(irqs[irqName]);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Timer::start(uint32_t ticks)
|
||||
{
|
||||
irqs[irqName].irqController->enableInterrupt(irqs[irqName], false);
|
||||
bool Timer::start(uint32_t ticks) {
|
||||
irqs[irqName].irqController->enableInterrupt(irqs[irqName], false);
|
||||
|
||||
XTmrCtr_SetOptions(&xTmr, 0, XTC_EXT_COMPARE_OPTION | XTC_DOWN_COUNT_OPTION);
|
||||
XTmrCtr_SetResetValue(&xTmr, 0, ticks);
|
||||
XTmrCtr_Start(&xTmr, 0);
|
||||
XTmrCtr_SetOptions(&xTmr, 0, XTC_EXT_COMPARE_OPTION | XTC_DOWN_COUNT_OPTION);
|
||||
XTmrCtr_SetResetValue(&xTmr, 0, ticks);
|
||||
XTmrCtr_Start(&xTmr, 0);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Timer::wait()
|
||||
{
|
||||
int count = irqs[irqName].irqController->waitForInterrupt(irqs[irqName]);
|
||||
irqs[irqName].irqController->disableInterrupt(irqs[irqName]);
|
||||
bool Timer::wait() {
|
||||
int count = irqs[irqName].irqController->waitForInterrupt(irqs[irqName]);
|
||||
irqs[irqName].irqController->disableInterrupt(irqs[irqName]);
|
||||
|
||||
return (count == 1);
|
||||
return (count == 1);
|
||||
}
|
||||
|
||||
uint32_t Timer::remaining()
|
||||
{
|
||||
return XTmrCtr_GetValue(&xTmr, 0);
|
||||
}
|
||||
uint32_t Timer::remaining() { return XTmrCtr_GetValue(&xTmr, 0); }
|
||||
|
||||
static char n[] = "timer";
|
||||
static char d[] = "Xilinx's programmable timer / counter";
|
||||
|
|
|
@ -12,17 +12,13 @@
|
|||
|
||||
using namespace villas;
|
||||
|
||||
bool
|
||||
HostRam::free(void* addr, size_t length)
|
||||
{
|
||||
return munmap(addr, length) == 0;
|
||||
bool HostRam::free(void *addr, size_t length) {
|
||||
return munmap(addr, length) == 0;
|
||||
}
|
||||
|
||||
void*
|
||||
HostRam::allocate(size_t length, int flags)
|
||||
{
|
||||
const int mmap_flags = flags | MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT;
|
||||
const int mmap_protection = PROT_READ | PROT_WRITE;
|
||||
void *HostRam::allocate(size_t length, int flags) {
|
||||
const int mmap_flags = flags | MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT;
|
||||
const int mmap_protection = PROT_READ | PROT_WRITE;
|
||||
|
||||
return mmap(nullptr, length, mmap_protection, mmap_flags, 0, 0);
|
||||
return mmap(nullptr, length, mmap_protection, mmap_flags, 0, 0);
|
||||
}
|
||||
|
|
|
@ -5,17 +5,17 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <fmt/ostream.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/memory.hpp>
|
||||
#include <villas/kernel/pci.hpp>
|
||||
#include <villas/kernel/vfio_container.hpp>
|
||||
#include <villas/fpga/core.hpp>
|
||||
#include <villas/fpga/node.hpp>
|
||||
#include <villas/fpga/pcie_card.hpp>
|
||||
#include <villas/kernel/pci.hpp>
|
||||
#include <villas/kernel/vfio_container.hpp>
|
||||
#include <villas/memory.hpp>
|
||||
|
||||
using namespace villas;
|
||||
using namespace villas::fpga;
|
||||
|
@ -23,7 +23,8 @@ using namespace villas::fpga;
|
|||
// Instantiate factory to register
|
||||
static PCIeCardFactory PCIeCardFactoryInstance;
|
||||
|
||||
static const kernel::pci::Device defaultFilter((kernel::pci::Id(FPGA_PCI_VID_XILINX, FPGA_PCI_PID_VFPGA)));
|
||||
static const kernel::pci::Device
|
||||
defaultFilter((kernel::pci::Id(FPGA_PCI_VID_XILINX, FPGA_PCI_PID_VFPGA)));
|
||||
|
||||
std::shared_ptr<PCIeCard>
|
||||
PCIeCardFactory::make(json_t *json_card, std::string card_name,
|
||||
|
@ -155,39 +156,35 @@ PCIeCardFactory::make(json_t *json_card, std::string card_name,
|
|||
return card;
|
||||
}
|
||||
|
||||
PCIeCard::~PCIeCard()
|
||||
{
|
||||
PCIeCard::~PCIeCard() {}
|
||||
|
||||
}
|
||||
|
||||
bool PCIeCard::init()
|
||||
{
|
||||
logger = getLogger();
|
||||
|
||||
logger->info("Initializing FPGA card {}", name);
|
||||
|
||||
// Attach PCIe card to VFIO container
|
||||
vfioDevice = vfioContainer->attachDevice(*pdev);
|
||||
|
||||
// Enable memory access and PCI bus mastering for DMA
|
||||
if (not vfioDevice->pciEnable()) {
|
||||
logger->error("Failed to enable PCI device");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reset system?
|
||||
if (doReset) {
|
||||
// Reset / detect PCI device
|
||||
if (not vfioDevice->pciHotReset()) {
|
||||
logger->error("Failed to reset PCI device");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (not reset()) {
|
||||
logger->error("Failed to reset FGPA card");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
bool PCIeCard::init() {
|
||||
logger = getLogger();
|
||||
|
||||
logger->info("Initializing FPGA card {}", name);
|
||||
|
||||
// Attach PCIe card to VFIO container
|
||||
vfioDevice = vfioContainer->attachDevice(*pdev);
|
||||
|
||||
// Enable memory access and PCI bus mastering for DMA
|
||||
if (not vfioDevice->pciEnable()) {
|
||||
logger->error("Failed to enable PCI device");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reset system?
|
||||
if (doReset) {
|
||||
// Reset / detect PCI device
|
||||
if (not vfioDevice->pciHotReset()) {
|
||||
logger->error("Failed to reset PCI device");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (not reset()) {
|
||||
logger->error("Failed to reset FGPA card");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <csignal>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <csignal>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <jansson.h>
|
||||
#include <regex>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <CLI11.hpp>
|
||||
#include <rang.hpp>
|
||||
|
@ -21,7 +21,6 @@
|
|||
#include <villas/exceptions.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
#include <villas/fpga/card.hpp>
|
||||
#include <villas/fpga/core.hpp>
|
||||
|
@ -61,8 +60,7 @@ fpga::ConnectString::ConnectString(std::string &connectString, int maxPortNum)
|
|||
parseString(connectString);
|
||||
}
|
||||
|
||||
void fpga::ConnectString::parseString(std::string& connectString)
|
||||
{
|
||||
void fpga::ConnectString::parseString(std::string &connectString) {
|
||||
if (connectString.empty())
|
||||
return;
|
||||
|
||||
|
@ -119,8 +117,7 @@ void fpga::ConnectString::parseString(std::string& connectString)
|
|||
}
|
||||
}
|
||||
|
||||
int fpga::ConnectString::portStringToInt(std::string &str) const
|
||||
{
|
||||
int fpga::ConnectString::portStringToInt(std::string &str) const {
|
||||
if (str == "stdin" || str == "stdout" || str == "pipe" || str == "dma" ||
|
||||
str == "dino") {
|
||||
return -1;
|
||||
|
@ -134,7 +131,6 @@ int fpga::ConnectString::portStringToInt(std::string &str) const
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// parses a string like "1->2" or "1<->stdout" and configures the crossbar accordingly
|
||||
void fpga::ConnectString::configCrossBar(
|
||||
std::shared_ptr<villas::fpga::Card> card) const {
|
||||
|
@ -205,24 +201,23 @@ void fpga::ConnectString::configCrossBar(
|
|||
}
|
||||
}
|
||||
|
||||
void fpga::setupColorHandling()
|
||||
{
|
||||
// Handle Control-C nicely
|
||||
struct sigaction sigIntHandler;
|
||||
sigIntHandler.sa_handler = [](int){
|
||||
std::cout << std::endl << rang::style::reset << rang::fgB::red;
|
||||
std::cout << "Control-C detected, exiting..." << rang::style::reset << std::endl;
|
||||
std::exit(1); // Will call the correct exit func, no unwinding of the stack though
|
||||
};
|
||||
void fpga::setupColorHandling() {
|
||||
// Handle Control-C nicely
|
||||
struct sigaction sigIntHandler;
|
||||
sigIntHandler.sa_handler = [](int) {
|
||||
std::cout << std::endl << rang::style::reset << rang::fgB::red;
|
||||
std::cout << "Control-C detected, exiting..." << rang::style::reset
|
||||
<< std::endl;
|
||||
std::exit(
|
||||
1); // Will call the correct exit func, no unwinding of the stack though
|
||||
};
|
||||
|
||||
sigemptyset(&sigIntHandler.sa_mask);
|
||||
sigIntHandler.sa_flags = 0;
|
||||
sigaction(SIGINT, &sigIntHandler, nullptr);
|
||||
sigemptyset(&sigIntHandler.sa_mask);
|
||||
sigIntHandler.sa_flags = 0;
|
||||
sigaction(SIGINT, &sigIntHandler, nullptr);
|
||||
|
||||
// Reset color if exiting not by signal
|
||||
std::atexit([](){
|
||||
std::cout << rang::style::reset;
|
||||
});
|
||||
// Reset color if exiting not by signal
|
||||
std::atexit([]() { std::cout << rang::style::reset; });
|
||||
}
|
||||
|
||||
std::shared_ptr<fpga::Card>
|
||||
|
@ -331,15 +326,16 @@ std::shared_ptr<fpga::Card> fpga::setupFpgaCard(const std::string &configFile,
|
|||
return card;
|
||||
}
|
||||
|
||||
std::unique_ptr<fpga::BufferedSampleFormatter> fpga::getBufferedSampleFormatter(
|
||||
const std::string &format,
|
||||
size_t bufSizeInSamples)
|
||||
{
|
||||
if (format == "long") {
|
||||
return std::make_unique<fpga::BufferedSampleFormatterLong>(bufSizeInSamples);
|
||||
} else if (format == "short") {
|
||||
return std::make_unique<fpga::BufferedSampleFormatterShort>(bufSizeInSamples);
|
||||
} else {
|
||||
throw RuntimeError("Unknown output format '{}'", format);
|
||||
}
|
||||
std::unique_ptr<fpga::BufferedSampleFormatter>
|
||||
fpga::getBufferedSampleFormatter(const std::string &format,
|
||||
size_t bufSizeInSamples) {
|
||||
if (format == "long") {
|
||||
return std::make_unique<fpga::BufferedSampleFormatterLong>(
|
||||
bufSizeInSamples);
|
||||
} else if (format == "short") {
|
||||
return std::make_unique<fpga::BufferedSampleFormatterShort>(
|
||||
bufSizeInSamples);
|
||||
} else {
|
||||
throw RuntimeError("Unknown output format '{}'", format);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,56 +5,53 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <villas/fpga/vlnv.hpp>
|
||||
|
||||
using namespace villas::fpga;
|
||||
|
||||
bool
|
||||
Vlnv::operator==(const Vlnv &other) const
|
||||
{
|
||||
// If a field is empty, it means wildcard matching everything
|
||||
const bool vendorWildcard = vendor.empty() or other.vendor.empty();
|
||||
const bool libraryWildcard = library.empty() or other.library.empty();
|
||||
const bool nameWildcard = name.empty() or other.name.empty();
|
||||
const bool versionWildcard = version.empty() or other.version.empty();
|
||||
bool Vlnv::operator==(const Vlnv &other) const {
|
||||
// If a field is empty, it means wildcard matching everything
|
||||
const bool vendorWildcard = vendor.empty() or other.vendor.empty();
|
||||
const bool libraryWildcard = library.empty() or other.library.empty();
|
||||
const bool nameWildcard = name.empty() or other.name.empty();
|
||||
const bool versionWildcard = version.empty() or other.version.empty();
|
||||
|
||||
const bool vendorMatch = vendorWildcard or vendor == other.vendor;
|
||||
const bool libraryMatch = libraryWildcard or library == other.library;
|
||||
const bool nameMatch = nameWildcard or name == other.name;
|
||||
const bool versionMatch = versionWildcard or version == other.version;
|
||||
const bool vendorMatch = vendorWildcard or vendor == other.vendor;
|
||||
const bool libraryMatch = libraryWildcard or library == other.library;
|
||||
const bool nameMatch = nameWildcard or name == other.name;
|
||||
const bool versionMatch = versionWildcard or version == other.version;
|
||||
|
||||
return vendorMatch and libraryMatch and nameMatch and versionMatch;
|
||||
return vendorMatch and libraryMatch and nameMatch and versionMatch;
|
||||
}
|
||||
|
||||
void
|
||||
Vlnv::parseFromString(std::string vlnv)
|
||||
{
|
||||
// Tokenize by delimiter
|
||||
std::stringstream sstream(vlnv);
|
||||
std::getline(sstream, vendor, delimiter);
|
||||
std::getline(sstream, library, delimiter);
|
||||
std::getline(sstream, name, delimiter);
|
||||
std::getline(sstream, version, delimiter);
|
||||
void Vlnv::parseFromString(std::string vlnv) {
|
||||
// Tokenize by delimiter
|
||||
std::stringstream sstream(vlnv);
|
||||
std::getline(sstream, vendor, delimiter);
|
||||
std::getline(sstream, library, delimiter);
|
||||
std::getline(sstream, name, delimiter);
|
||||
std::getline(sstream, version, delimiter);
|
||||
|
||||
// Represent wildcard internally as empty string
|
||||
if (vendor == "*") vendor = "";
|
||||
if (library == "*") library = "";
|
||||
if (name == "*") name = "";
|
||||
if (version == "*") version = "";
|
||||
// Represent wildcard internally as empty string
|
||||
if (vendor == "*")
|
||||
vendor = "";
|
||||
if (library == "*")
|
||||
library = "";
|
||||
if (name == "*")
|
||||
name = "";
|
||||
if (version == "*")
|
||||
version = "";
|
||||
}
|
||||
|
||||
std::string
|
||||
Vlnv::toString() const
|
||||
{
|
||||
std::stringstream stream;
|
||||
std::string string;
|
||||
std::string Vlnv::toString() const {
|
||||
std::stringstream stream;
|
||||
std::string string;
|
||||
|
||||
stream << *this;
|
||||
stream >> string;
|
||||
stream << *this;
|
||||
stream >> string;
|
||||
|
||||
return string;
|
||||
return string;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,121 +9,124 @@
|
|||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <termios.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define PRINT_ERROR \
|
||||
do { \
|
||||
fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \
|
||||
__LINE__, __FILE__, errno, strerror(errno)); exit(1); \
|
||||
} while (0)
|
||||
#define PRINT_ERROR \
|
||||
do { \
|
||||
fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", __LINE__, \
|
||||
__FILE__, errno, strerror(errno)); \
|
||||
exit(1); \
|
||||
} while (0)
|
||||
|
||||
#define MAP_SIZE 4096UL
|
||||
#define MAP_MASK (MAP_SIZE - 1)
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int fd;
|
||||
void *map_base, *virt_addr;
|
||||
unsigned read_result, writeval;
|
||||
char *filename;
|
||||
off_t target;
|
||||
int access_type = 'w';
|
||||
int fd;
|
||||
void *map_base, *virt_addr;
|
||||
unsigned read_result, writeval;
|
||||
char *filename;
|
||||
off_t target;
|
||||
int access_type = 'w';
|
||||
|
||||
if (argc < 3) {
|
||||
// pcimem /sys/bus/pci/devices/0001\:00\:07.0/resource0 0x100 w 0x00
|
||||
// argv[0] [1] [2] [3] [4]
|
||||
fprintf(stderr, "\nUsage:\t%s { sys file } { offset } [ type [ data ] ]\n"
|
||||
"\tsys file: sysfs file for the pci resource to act on\n"
|
||||
"\toffset : offset into pci memory region to act upon\n"
|
||||
"\ttype : access operation type : [b]yte, [h]alfword, [w]ord\n"
|
||||
"\tdata : data to be written\n\n",
|
||||
argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
if (argc < 3) {
|
||||
// pcimem /sys/bus/pci/devices/0001\:00\:07.0/resource0 0x100 w 0x00
|
||||
// argv[0] [1] [2] [3] [4]
|
||||
fprintf(stderr,
|
||||
"\nUsage:\t%s { sys file } { offset } [ type [ data ] ]\n"
|
||||
"\tsys file: sysfs file for the pci resource to act on\n"
|
||||
"\toffset : offset into pci memory region to act upon\n"
|
||||
"\ttype : access operation type : [b]yte, [h]alfword, [w]ord\n"
|
||||
"\tdata : data to be written\n\n",
|
||||
argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
filename = argv[1];
|
||||
target = strtoul(argv[2], 0, 0);
|
||||
filename = argv[1];
|
||||
target = strtoul(argv[2], 0, 0);
|
||||
|
||||
if (argc > 3)
|
||||
access_type = tolower(argv[3][0]);
|
||||
if (argc > 3)
|
||||
access_type = tolower(argv[3][0]);
|
||||
|
||||
fd = open(filename, O_RDWR | O_SYNC);
|
||||
if (fd < 0)
|
||||
PRINT_ERROR;
|
||||
fd = open(filename, O_RDWR | O_SYNC);
|
||||
if (fd < 0)
|
||||
PRINT_ERROR;
|
||||
|
||||
printf("%s opened.\n", filename);
|
||||
printf("Target offset is %#jx, page size is %lu\n", (uintmax_t) target, sysconf(_SC_PAGE_SIZE));
|
||||
printf("%s opened.\n", filename);
|
||||
printf("Target offset is %#jx, page size is %lu\n", (uintmax_t)target,
|
||||
sysconf(_SC_PAGE_SIZE));
|
||||
|
||||
fflush(stdout);
|
||||
fflush(stdout);
|
||||
|
||||
// Map one page
|
||||
printf("mmap(%d, %lu, %#x, %#x, %d, %#jx)\n", 0,
|
||||
MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||
fd, (uintmax_t) target);
|
||||
// Map one page
|
||||
printf("mmap(%d, %lu, %#x, %#x, %d, %#jx)\n", 0, MAP_SIZE,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED, fd, (uintmax_t)target);
|
||||
|
||||
map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target & ~MAP_MASK);
|
||||
map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
|
||||
target & ~MAP_MASK);
|
||||
|
||||
if (map_base == (void *) -1)
|
||||
PRINT_ERROR;
|
||||
if (map_base == (void *)-1)
|
||||
PRINT_ERROR;
|
||||
|
||||
printf("PCI Memory mapped to address %p.\n", map_base);
|
||||
fflush(stdout);
|
||||
printf("PCI Memory mapped to address %p.\n", map_base);
|
||||
fflush(stdout);
|
||||
|
||||
virt_addr = (uint8_t*) map_base + (target & MAP_MASK);
|
||||
virt_addr = (uint8_t *)map_base + (target & MAP_MASK);
|
||||
|
||||
switch(access_type) {
|
||||
case 'b':
|
||||
read_result = *((unsigned char *) virt_addr);
|
||||
break;
|
||||
case 'h':
|
||||
read_result = *((unsigned short *) virt_addr);
|
||||
break;
|
||||
case 'w':
|
||||
read_result = *((unsigned int *) virt_addr);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Illegal data type '%c'.\n", access_type);
|
||||
exit(2);
|
||||
}
|
||||
switch (access_type) {
|
||||
case 'b':
|
||||
read_result = *((unsigned char *)virt_addr);
|
||||
break;
|
||||
case 'h':
|
||||
read_result = *((unsigned short *)virt_addr);
|
||||
break;
|
||||
case 'w':
|
||||
read_result = *((unsigned int *)virt_addr);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Illegal data type '%c'.\n", access_type);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
printf("Value at offset %#jx (%p): %#x\n", (uintmax_t) target, virt_addr, read_result);
|
||||
fflush(stdout);
|
||||
printf("Value at offset %#jx (%p): %#x\n", (uintmax_t)target, virt_addr,
|
||||
read_result);
|
||||
fflush(stdout);
|
||||
|
||||
if (argc > 4) {
|
||||
writeval = strtoul(argv[4], 0, 0);
|
||||
switch(access_type) {
|
||||
case 'b':
|
||||
*((unsigned char *) virt_addr) = writeval;
|
||||
read_result = *((unsigned char *) virt_addr);
|
||||
break;
|
||||
case 'h':
|
||||
*((unsigned short *) virt_addr) = writeval;
|
||||
read_result = *((unsigned short *) virt_addr);
|
||||
break;
|
||||
case 'w':
|
||||
*((unsigned int *) virt_addr) = writeval;
|
||||
read_result = *((unsigned int *) virt_addr);
|
||||
break;
|
||||
}
|
||||
printf("Written %#x; readback %#x\n", writeval, read_result);
|
||||
fflush(stdout);
|
||||
}
|
||||
if (argc > 4) {
|
||||
writeval = strtoul(argv[4], 0, 0);
|
||||
switch (access_type) {
|
||||
case 'b':
|
||||
*((unsigned char *)virt_addr) = writeval;
|
||||
read_result = *((unsigned char *)virt_addr);
|
||||
break;
|
||||
case 'h':
|
||||
*((unsigned short *)virt_addr) = writeval;
|
||||
read_result = *((unsigned short *)virt_addr);
|
||||
break;
|
||||
case 'w':
|
||||
*((unsigned int *)virt_addr) = writeval;
|
||||
read_result = *((unsigned int *)virt_addr);
|
||||
break;
|
||||
}
|
||||
printf("Written %#x; readback %#x\n", writeval, read_result);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
if (munmap(map_base, MAP_SIZE) == -1)
|
||||
PRINT_ERROR;
|
||||
if (munmap(map_base, MAP_SIZE) == -1)
|
||||
PRINT_ERROR;
|
||||
|
||||
close(fd);
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <csignal>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <jansson.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <CLI11.hpp>
|
||||
#include <rang.hpp>
|
||||
|
@ -18,128 +18,131 @@
|
|||
#include <villas/exceptions.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
#include <villas/fpga/core.hpp>
|
||||
#include <villas/fpga/card.hpp>
|
||||
#include <villas/fpga/vlnv.hpp>
|
||||
#include <villas/fpga/core.hpp>
|
||||
#include <villas/fpga/ips/aurora_xilinx.hpp>
|
||||
#include <villas/fpga/ips/dma.hpp>
|
||||
#include <villas/fpga/ips/rtds.hpp>
|
||||
#include <villas/fpga/ips/aurora_xilinx.hpp>
|
||||
#include <villas/fpga/utils.hpp>
|
||||
#include <villas/fpga/vlnv.hpp>
|
||||
|
||||
using namespace villas;
|
||||
|
||||
static std::shared_ptr<kernel::pci::DeviceList> pciDevices;
|
||||
static auto logger = villas::logging.get("streamer");
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
// Command Line Parser
|
||||
CLI::App app{"VILLASfpga data streamer"};
|
||||
int main(int argc, char *argv[]) {
|
||||
// Command Line Parser
|
||||
CLI::App app{"VILLASfpga data streamer"};
|
||||
|
||||
try {
|
||||
std::string configFile;
|
||||
app.add_option("-c,--config", configFile, "Configuration file")
|
||||
->check(CLI::ExistingFile);
|
||||
try {
|
||||
std::string configFile;
|
||||
app.add_option("-c,--config", configFile, "Configuration file")
|
||||
->check(CLI::ExistingFile);
|
||||
|
||||
std::string fpgaName = "vc707";
|
||||
app.add_option("--fpga", fpgaName, "Which FPGA to use");
|
||||
app.parse(argc, argv);
|
||||
std::string fpgaName = "vc707";
|
||||
app.add_option("--fpga", fpgaName, "Which FPGA to use");
|
||||
app.parse(argc, argv);
|
||||
|
||||
// Logging setup
|
||||
spdlog::set_level(spdlog::level::debug);
|
||||
fpga::setupColorHandling();
|
||||
// Logging setup
|
||||
spdlog::set_level(spdlog::level::debug);
|
||||
fpga::setupColorHandling();
|
||||
|
||||
if (configFile.empty()) {
|
||||
logger->error("No configuration file provided/ Please use -c/--config argument");
|
||||
return 1;
|
||||
}
|
||||
if (configFile.empty()) {
|
||||
logger->error(
|
||||
"No configuration file provided/ Please use -c/--config argument");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto card = fpga::setupFpgaCard(configFile, fpgaName);
|
||||
auto card = fpga::setupFpgaCard(configFile, fpgaName);
|
||||
|
||||
std::vector<std::shared_ptr<fpga::ip::AuroraXilinx>> aurora_channels;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
auto name = fmt::format("aurora_8b10b_ch{}", i);
|
||||
auto id = fpga::ip::IpIdentifier("xilinx.com:ip:aurora_8b10b:", name);
|
||||
auto aurora = std::dynamic_pointer_cast<fpga::ip::AuroraXilinx>(card->lookupIp(id));
|
||||
if (aurora == nullptr) {
|
||||
logger->error("No Aurora interface found on FPGA");
|
||||
return 1;
|
||||
}
|
||||
std::vector<std::shared_ptr<fpga::ip::AuroraXilinx>> aurora_channels;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
auto name = fmt::format("aurora_8b10b_ch{}", i);
|
||||
auto id = fpga::ip::IpIdentifier("xilinx.com:ip:aurora_8b10b:", name);
|
||||
auto aurora =
|
||||
std::dynamic_pointer_cast<fpga::ip::AuroraXilinx>(card->lookupIp(id));
|
||||
if (aurora == nullptr) {
|
||||
logger->error("No Aurora interface found on FPGA");
|
||||
return 1;
|
||||
}
|
||||
|
||||
aurora_channels.push_back(aurora);
|
||||
}
|
||||
aurora_channels.push_back(aurora);
|
||||
}
|
||||
|
||||
auto dma = std::dynamic_pointer_cast<fpga::ip::Dma>
|
||||
(card->lookupIp(fpga::Vlnv("xilinx.com:ip:axi_dma:")));
|
||||
if (dma == nullptr) {
|
||||
logger->error("No DMA found on FPGA ");
|
||||
return 1;
|
||||
}
|
||||
auto dma = std::dynamic_pointer_cast<fpga::ip::Dma>(
|
||||
card->lookupIp(fpga::Vlnv("xilinx.com:ip:axi_dma:")));
|
||||
if (dma == nullptr) {
|
||||
logger->error("No DMA found on FPGA ");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (auto aurora : aurora_channels)
|
||||
aurora->dump();
|
||||
for (auto aurora : aurora_channels)
|
||||
aurora->dump();
|
||||
|
||||
// Configure Crossbar switch
|
||||
// Configure Crossbar switch
|
||||
#if 1
|
||||
aurora_channels[3]->connect(aurora_channels[3]->getDefaultMasterPort(), dma->getDefaultSlavePort());
|
||||
dma->connect(dma->getDefaultMasterPort(), aurora_channels[3]->getDefaultSlavePort());
|
||||
aurora_channels[3]->connect(aurora_channels[3]->getDefaultMasterPort(),
|
||||
dma->getDefaultSlavePort());
|
||||
dma->connect(dma->getDefaultMasterPort(),
|
||||
aurora_channels[3]->getDefaultSlavePort());
|
||||
#else
|
||||
dma->connectLoopback();
|
||||
dma->connectLoopback();
|
||||
#endif
|
||||
auto &alloc = villas::HostRam::getAllocator();
|
||||
const std::shared_ptr<villas::MemoryBlock> block[] = {
|
||||
alloc.allocateBlock(0x200 * sizeof(uint32_t)),
|
||||
alloc.allocateBlock(0x200 * sizeof(uint32_t))
|
||||
};
|
||||
villas::MemoryAccessor<int32_t> mem[] = {*block[0], *block[1]};
|
||||
auto &alloc = villas::HostRam::getAllocator();
|
||||
const std::shared_ptr<villas::MemoryBlock> block[] = {
|
||||
alloc.allocateBlock(0x200 * sizeof(uint32_t)),
|
||||
alloc.allocateBlock(0x200 * sizeof(uint32_t))};
|
||||
villas::MemoryAccessor<int32_t> mem[] = {*block[0], *block[1]};
|
||||
|
||||
for (auto b : block) {
|
||||
dma->makeAccesibleFromVA(b);
|
||||
}
|
||||
auto &mm = MemoryManager::get();
|
||||
mm.getGraph().dump("graph.dot");
|
||||
for (auto b : block) {
|
||||
dma->makeAccesibleFromVA(b);
|
||||
}
|
||||
auto &mm = MemoryManager::get();
|
||||
mm.getGraph().dump("graph.dot");
|
||||
|
||||
while (true) {
|
||||
// Setup read transfer
|
||||
dma->read(*block[0], block[0]->getSize());
|
||||
while (true) {
|
||||
// Setup read transfer
|
||||
dma->read(*block[0], block[0]->getSize());
|
||||
|
||||
// Read values from stdin
|
||||
std::string line;
|
||||
std::getline(std::cin, line);
|
||||
auto values = villas::utils::tokenize(line, ";");
|
||||
// Read values from stdin
|
||||
std::string line;
|
||||
std::getline(std::cin, line);
|
||||
auto values = villas::utils::tokenize(line, ";");
|
||||
|
||||
size_t i = 0;
|
||||
for (auto &value: values) {
|
||||
if (value.empty()) continue;
|
||||
size_t i = 0;
|
||||
for (auto &value : values) {
|
||||
if (value.empty())
|
||||
continue;
|
||||
|
||||
const int32_t number = std::stoi(value);
|
||||
mem[1][i++] = number;
|
||||
}
|
||||
const int32_t number = std::stoi(value);
|
||||
mem[1][i++] = number;
|
||||
}
|
||||
|
||||
// Initiate write transfer
|
||||
bool state = dma->write(*block[1], i * sizeof(int32_t));
|
||||
if (!state)
|
||||
logger->error("Failed to write to device");
|
||||
// Initiate write transfer
|
||||
bool state = dma->write(*block[1], i * sizeof(int32_t));
|
||||
if (!state)
|
||||
logger->error("Failed to write to device");
|
||||
|
||||
auto writeComp = dma->writeComplete();
|
||||
logger->info("Wrote {} bytes", writeComp.bytes);
|
||||
auto writeComp = dma->writeComplete();
|
||||
logger->info("Wrote {} bytes", writeComp.bytes);
|
||||
|
||||
auto readComp = dma->readComplete();
|
||||
auto valuesRead = readComp.bytes / sizeof(int32_t);
|
||||
logger->info("Read {} bytes, {} bds, {} interrupts", readComp.bytes, readComp.bds, readComp.interrupts);
|
||||
auto readComp = dma->readComplete();
|
||||
auto valuesRead = readComp.bytes / sizeof(int32_t);
|
||||
logger->info("Read {} bytes, {} bds, {} interrupts", readComp.bytes,
|
||||
readComp.bds, readComp.interrupts);
|
||||
|
||||
for (size_t i = 0; i < valuesRead; i++)
|
||||
std::cerr << mem[0][i] << ";";
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
} catch (const RuntimeError &e) {
|
||||
logger->error("Error: {}", e.what());
|
||||
return -1;
|
||||
} catch (const CLI::ParseError &e) {
|
||||
return app.exit(e);
|
||||
}
|
||||
for (size_t i = 0; i < valuesRead; i++)
|
||||
std::cerr << mem[0][i] << ";";
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
} catch (const RuntimeError &e) {
|
||||
logger->error("Error: {}", e.what());
|
||||
return -1;
|
||||
} catch (const CLI::ParseError &e) {
|
||||
return app.exit(e);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -5,97 +5,96 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <villas/fpga/dma.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
villasfpga_handle vh;
|
||||
villasfpga_memory mem1, mem2;
|
||||
void *mem1ptr, *mem2ptr;
|
||||
size_t size;
|
||||
int main(int argc, char *argv[]) {
|
||||
int ret;
|
||||
villasfpga_handle vh;
|
||||
villasfpga_memory mem1, mem2;
|
||||
void *mem1ptr, *mem2ptr;
|
||||
size_t size;
|
||||
|
||||
if (argv == NULL) {
|
||||
return 1;
|
||||
}
|
||||
if (argc != 2 || argv[1] == NULL) {
|
||||
fprintf(stderr, "Usage: %s <config file>\n", argv[0]);
|
||||
}
|
||||
if (argv == NULL) {
|
||||
return 1;
|
||||
}
|
||||
if (argc != 2 || argv[1] == NULL) {
|
||||
fprintf(stderr, "Usage: %s <config file>\n", argv[0]);
|
||||
}
|
||||
|
||||
if ((vh = villasfpga_init(argv[1])) == NULL) {
|
||||
fprintf(stderr, "Failed to initialize FPGA\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
if ((vh = villasfpga_init(argv[1])) == NULL) {
|
||||
fprintf(stderr, "Failed to initialize FPGA\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (villasfpga_alloc(vh, &mem1, 0x200 * sizeof(uint32_t)) != 0) {
|
||||
fprintf(stderr, "Failed to allocate DMA memory 1\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
if (villasfpga_alloc(vh, &mem1, 0x200 * sizeof(uint32_t)) != 0) {
|
||||
fprintf(stderr, "Failed to allocate DMA memory 1\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (villasfpga_alloc(vh, &mem2, 0x200 * sizeof(uint32_t)) != 0) {
|
||||
fprintf(stderr, "Failed to allocate DMA memory 2\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
if (villasfpga_alloc(vh, &mem2, 0x200 * sizeof(uint32_t)) != 0) {
|
||||
fprintf(stderr, "Failed to allocate DMA memory 2\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((mem1ptr = villasfpga_get_ptr(mem1)) == NULL) {
|
||||
fprintf(stderr, "Failed to get pointer to DMA memory 1\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
if ((mem1ptr = villasfpga_get_ptr(mem1)) == NULL) {
|
||||
fprintf(stderr, "Failed to get pointer to DMA memory 1\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((mem2ptr = villasfpga_get_ptr(mem2)) == NULL) {
|
||||
fprintf(stderr, "Failed to get pointer to DMA memory 2\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
if ((mem2ptr = villasfpga_get_ptr(mem2)) == NULL) {
|
||||
fprintf(stderr, "Failed to get pointer to DMA memory 2\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("DMA memory 1: %p, DMA memory 2: %p\n", mem1ptr, mem2ptr);
|
||||
printf("DMA memory 1: %p, DMA memory 2: %p\n", mem1ptr, mem2ptr);
|
||||
|
||||
while (1) {
|
||||
// Setup read transfer
|
||||
if ((ret = villasfpga_read(vh, mem1, 0x200 * sizeof(uint32_t))) != 0) {
|
||||
fprintf(stderr, "Failed to initiate read transfer\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
while (1) {
|
||||
// Setup read transfer
|
||||
if ((ret = villasfpga_read(vh, mem1, 0x200 * sizeof(uint32_t))) != 0) {
|
||||
fprintf(stderr, "Failed to initiate read transfer\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("Enter a float:\n");
|
||||
if ((ret = scanf("%f", (float*)mem2ptr)) != 1) {
|
||||
fprintf(stderr, "Failed to parse input: sscanf returned %d\n", ret);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
printf("sending %f (%zu bytes)\n", ((float*)mem2ptr)[0], sizeof(float));
|
||||
// Initiate write transfer
|
||||
if ((ret = villasfpga_write(vh, mem2, sizeof(float))) != 0) {
|
||||
fprintf(stderr, "Failed to initiate write transfer\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
printf("Enter a float:\n");
|
||||
if ((ret = scanf("%f", (float *)mem2ptr)) != 1) {
|
||||
fprintf(stderr, "Failed to parse input: sscanf returned %d\n", ret);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
printf("sending %f (%zu bytes)\n", ((float *)mem2ptr)[0], sizeof(float));
|
||||
// Initiate write transfer
|
||||
if ((ret = villasfpga_write(vh, mem2, sizeof(float))) != 0) {
|
||||
fprintf(stderr, "Failed to initiate write transfer\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((ret = villasfpga_write_complete(vh, &size)) != 0) {
|
||||
fprintf(stderr, "Failed to write complete\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
if ((ret = villasfpga_write_complete(vh, &size)) != 0) {
|
||||
fprintf(stderr, "Failed to write complete\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((ret = villasfpga_read_complete(vh, &size)) != 0) {
|
||||
fprintf(stderr, "Failed to write complete\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
if ((ret = villasfpga_read_complete(vh, &size)) != 0) {
|
||||
fprintf(stderr, "Failed to write complete\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("Read %f (%zu bytes)\n", ((float*)mem1ptr)[0], size);
|
||||
}
|
||||
printf("Read %f (%zu bytes)\n", ((float *)mem1ptr)[0], size);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -7,90 +7,92 @@
|
|||
|
||||
#include <criterion/criterion.h>
|
||||
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/memory.hpp>
|
||||
#include <villas/fpga/card.hpp>
|
||||
#include <villas/fpga/ips/dma.hpp>
|
||||
#include <villas/fpga/ips/bram.hpp>
|
||||
#include <villas/fpga/ips/dma.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/memory.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
#include "global.hpp"
|
||||
|
||||
using namespace villas;
|
||||
|
||||
// cppcheck-suppress unknownMacro
|
||||
Test(fpga, dma, .description = "DMA")
|
||||
{
|
||||
auto logger = logging.get("unit-test:dma");
|
||||
Test(fpga, dma, .description = "DMA") {
|
||||
auto logger = logging.get("unit-test:dma");
|
||||
|
||||
std::list<std::shared_ptr<fpga::ip::Dma>> dmaIps;
|
||||
std::list<std::shared_ptr<fpga::ip::Dma>> dmaIps;
|
||||
|
||||
for (auto &ip : state.cards.front()->ips) {
|
||||
if (*ip == fpga::Vlnv("xilinx.com:ip:axi_dma:")) {
|
||||
auto dma = std::dynamic_pointer_cast<fpga::ip::Dma>(ip);
|
||||
dmaIps.push_back(dma);
|
||||
}
|
||||
}
|
||||
for (auto &ip : state.cards.front()->ips) {
|
||||
if (*ip == fpga::Vlnv("xilinx.com:ip:axi_dma:")) {
|
||||
auto dma = std::dynamic_pointer_cast<fpga::ip::Dma>(ip);
|
||||
dmaIps.push_back(dma);
|
||||
}
|
||||
}
|
||||
|
||||
size_t count = 0;
|
||||
for (auto &dma : dmaIps) {
|
||||
logger->info("Testing {}", *dma);
|
||||
size_t count = 0;
|
||||
for (auto &dma : dmaIps) {
|
||||
logger->info("Testing {}", *dma);
|
||||
|
||||
if (not dma->loopbackPossible()) {
|
||||
logger->info("Loopback test not possible for {}", *dma);
|
||||
continue;
|
||||
}
|
||||
if (not dma->loopbackPossible()) {
|
||||
logger->info("Loopback test not possible for {}", *dma);
|
||||
continue;
|
||||
}
|
||||
|
||||
dma->connectLoopback();
|
||||
dma->connectLoopback();
|
||||
|
||||
// Simple DMA can only transfer up to 4 kb due to PCIe page size burst
|
||||
// limitation
|
||||
size_t len = 4 * (1 << 10);
|
||||
// Simple DMA can only transfer up to 4 kb due to PCIe page size burst
|
||||
// limitation
|
||||
size_t len = 4 * (1 << 10);
|
||||
|
||||
#if 0
|
||||
// Allocate memory to use with DMA
|
||||
auto src = HostDmaRam::getAllocator().allocate<char>(len);
|
||||
auto dst = HostDmaRam::getAllocator().allocate<char>(len);
|
||||
#elif 0
|
||||
// ... only works with IOMMU enabled currently
|
||||
// ... only works with IOMMU enabled currently
|
||||
|
||||
// Find a block RAM IP to write to
|
||||
auto bramIp = state.cards.front()->lookupIp(fpga::Vlnv("xilinx.com:ip:axi_bram_ctrl:"));
|
||||
auto bram = std::dynamic_pointer_cast<fpga::ip::Bram>(bramIp);
|
||||
cr_assert_not_null(bram, "Couldn't find BRAM");
|
||||
// Find a block RAM IP to write to
|
||||
auto bramIp = state.cards.front()->lookupIp(
|
||||
fpga::Vlnv("xilinx.com:ip:axi_bram_ctrl:"));
|
||||
auto bram = std::dynamic_pointer_cast<fpga::ip::Bram>(bramIp);
|
||||
cr_assert_not_null(bram, "Couldn't find BRAM");
|
||||
|
||||
auto src = bram->getAllocator().allocate<char>(len);
|
||||
auto dst = bram->getAllocator().allocate<char>(len);
|
||||
auto src = bram->getAllocator().allocate<char>(len);
|
||||
auto dst = bram->getAllocator().allocate<char>(len);
|
||||
#else
|
||||
// ... only works with IOMMU enabled currently
|
||||
auto &alloc = villas::HostRam::getAllocator();
|
||||
const std::shared_ptr<villas::MemoryBlock> srcBlock = alloc.allocateBlock(len);
|
||||
const std::shared_ptr<villas::MemoryBlock> dstBlock = alloc.allocateBlock(len);
|
||||
villas::MemoryAccessor<char> src(*srcBlock);
|
||||
villas::MemoryAccessor<char> dst(*dstBlock);
|
||||
// ... only works with IOMMU enabled currently
|
||||
auto &alloc = villas::HostRam::getAllocator();
|
||||
const std::shared_ptr<villas::MemoryBlock> srcBlock =
|
||||
alloc.allocateBlock(len);
|
||||
const std::shared_ptr<villas::MemoryBlock> dstBlock =
|
||||
alloc.allocateBlock(len);
|
||||
villas::MemoryAccessor<char> src(*srcBlock);
|
||||
villas::MemoryAccessor<char> dst(*dstBlock);
|
||||
#endif
|
||||
// Make sure memory is accessible for DMA
|
||||
dma->makeAccesibleFromVA(srcBlock);
|
||||
dma->makeAccesibleFromVA(dstBlock);
|
||||
// Make sure memory is accessible for DMA
|
||||
dma->makeAccesibleFromVA(srcBlock);
|
||||
dma->makeAccesibleFromVA(dstBlock);
|
||||
|
||||
// Get new random data
|
||||
const size_t lenRandom = utils::readRandom(&src, len);
|
||||
cr_assert(len == lenRandom, "Failed to get random data");
|
||||
// Get new random data
|
||||
const size_t lenRandom = utils::readRandom(&src, len);
|
||||
cr_assert(len == lenRandom, "Failed to get random data");
|
||||
|
||||
// Start transfer
|
||||
cr_assert(dma->memcpy(src.getMemoryBlock(), dst.getMemoryBlock(), len),
|
||||
"DMA ping pong failed");
|
||||
// Start transfer
|
||||
cr_assert(dma->memcpy(src.getMemoryBlock(), dst.getMemoryBlock(), len),
|
||||
"DMA ping pong failed");
|
||||
|
||||
// Compare data
|
||||
cr_assert(memcmp(&src, &dst, len) == 0, "Data not equal");
|
||||
// Compare data
|
||||
cr_assert(memcmp(&src, &dst, len) == 0, "Data not equal");
|
||||
|
||||
logger->info(CLR_GRN("Passed"));
|
||||
logger->info(CLR_GRN("Passed"));
|
||||
|
||||
count++;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
cr_assert(count > 0, "No DMA found");
|
||||
cr_assert(count > 0, "No DMA found");
|
||||
|
||||
MemoryManager::get().getGraph().dump();
|
||||
fpga::ip::Node::getGraph().dump();
|
||||
MemoryManager::get().getGraph().dump();
|
||||
fpga::ip::Node::getGraph().dump();
|
||||
}
|
||||
|
|
|
@ -18,59 +18,58 @@
|
|||
using namespace villas;
|
||||
|
||||
// cppcheck-suppress unknownMacro
|
||||
Test(fpga, fifo, .description = "FIFO")
|
||||
{
|
||||
ssize_t len;
|
||||
char src[255], dst[255];
|
||||
size_t count = 0;
|
||||
Test(fpga, fifo, .description = "FIFO") {
|
||||
ssize_t len;
|
||||
char src[255], dst[255];
|
||||
size_t count = 0;
|
||||
|
||||
auto logger = logging.get("unit-test:fifo");
|
||||
auto logger = logging.get("unit-test:fifo");
|
||||
|
||||
for (auto &ip : state.cards.front()->ips) {
|
||||
// Skip non-fifo IPs
|
||||
if (*ip != fpga::Vlnv("xilinx.com:ip:axi_fifo_mm_s:"))
|
||||
continue;
|
||||
for (auto &ip : state.cards.front()->ips) {
|
||||
// Skip non-fifo IPs
|
||||
if (*ip != fpga::Vlnv("xilinx.com:ip:axi_fifo_mm_s:"))
|
||||
continue;
|
||||
|
||||
logger->info("Testing {}", *ip);
|
||||
logger->info("Testing {}", *ip);
|
||||
|
||||
auto fifo = std::dynamic_pointer_cast<fpga::ip::Fifo>(ip);
|
||||
auto fifo = std::dynamic_pointer_cast<fpga::ip::Fifo>(ip);
|
||||
|
||||
if (not fifo->loopbackPossible()) {
|
||||
logger->info("Loopback test not possible for {}", *ip);
|
||||
continue;
|
||||
}
|
||||
if (not fifo->loopbackPossible()) {
|
||||
logger->info("Loopback test not possible for {}", *ip);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (not fifo->connectLoopback()) {
|
||||
continue;
|
||||
}
|
||||
if (not fifo->connectLoopback()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get some random data to compare
|
||||
memset(dst, 0, sizeof(dst));
|
||||
len = utils::readRandom((char *) src, sizeof(src));
|
||||
if (len != sizeof(src)) {
|
||||
logger->error("Failed to get random data");
|
||||
continue;
|
||||
}
|
||||
// Get some random data to compare
|
||||
memset(dst, 0, sizeof(dst));
|
||||
len = utils::readRandom((char *)src, sizeof(src));
|
||||
if (len != sizeof(src)) {
|
||||
logger->error("Failed to get random data");
|
||||
continue;
|
||||
}
|
||||
|
||||
len = fifo->write(src, sizeof(src));
|
||||
if (len != sizeof(src)) {
|
||||
logger->error("Failed to send to FIFO");
|
||||
continue;
|
||||
}
|
||||
len = fifo->write(src, sizeof(src));
|
||||
if (len != sizeof(src)) {
|
||||
logger->error("Failed to send to FIFO");
|
||||
continue;
|
||||
}
|
||||
|
||||
len = fifo->read(dst, sizeof(dst));
|
||||
if (len != sizeof(dst)) {
|
||||
logger->error("Failed to read from FIFO");
|
||||
continue;
|
||||
}
|
||||
len = fifo->read(dst, sizeof(dst));
|
||||
if (len != sizeof(dst)) {
|
||||
logger->error("Failed to read from FIFO");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compare data
|
||||
cr_assert_eq(memcmp(src, dst, sizeof(src)), 0, "Data not equal");
|
||||
// Compare data
|
||||
cr_assert_eq(memcmp(src, dst, sizeof(src)), 0, "Data not equal");
|
||||
|
||||
logger->info(CLR_GRN("Passed"));
|
||||
logger->info(CLR_GRN("Passed"));
|
||||
|
||||
count++;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
cr_assert(count > 0, "No FIFO found");
|
||||
cr_assert(count > 0, "No FIFO found");
|
||||
}
|
||||
|
|
|
@ -18,12 +18,12 @@
|
|||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#define FPGA_CARD "vc707"
|
||||
#define TEST_CONFIG "../etc/fpga.json"
|
||||
#define TEST_LEN 0x1000
|
||||
#define FPGA_CARD "vc707"
|
||||
#define TEST_CONFIG "../etc/fpga.json"
|
||||
#define TEST_LEN 0x1000
|
||||
|
||||
#define CPU_HZ 3392389000
|
||||
#define FPGA_AXI_HZ 125000000
|
||||
#define CPU_HZ 3392389000
|
||||
#define FPGA_AXI_HZ 125000000
|
||||
|
||||
using namespace villas;
|
||||
|
||||
|
@ -31,58 +31,53 @@ static kernel::pci::DeviceList *pciDevices;
|
|||
|
||||
FpgaState state;
|
||||
|
||||
static void init()
|
||||
{
|
||||
FILE *f;
|
||||
json_error_t err;
|
||||
static void init() {
|
||||
FILE *f;
|
||||
json_error_t err;
|
||||
|
||||
spdlog::set_level(spdlog::level::debug);
|
||||
spdlog::set_pattern("[%T] [%l] [%n] %v");
|
||||
spdlog::set_level(spdlog::level::debug);
|
||||
spdlog::set_pattern("[%T] [%l] [%n] %v");
|
||||
|
||||
plugin::registry->dump();
|
||||
plugin::registry->dump();
|
||||
|
||||
pciDevices = kernel::pci::DeviceList::getInstance();
|
||||
pciDevices = kernel::pci::DeviceList::getInstance();
|
||||
|
||||
auto vfioContainer = std::make_shared<kernel::vfio::Container>();
|
||||
auto vfioContainer = std::make_shared<kernel::vfio::Container>();
|
||||
|
||||
// Parse FPGA configuration
|
||||
char *fn = getenv("TEST_CONFIG");
|
||||
f = fopen(fn ? fn : TEST_CONFIG, "r");
|
||||
cr_assert_not_null(f, "Cannot open config file");
|
||||
// Parse FPGA configuration
|
||||
char *fn = getenv("TEST_CONFIG");
|
||||
f = fopen(fn ? fn : TEST_CONFIG, "r");
|
||||
cr_assert_not_null(f, "Cannot open config file");
|
||||
|
||||
json_t *json = json_loadf(f, 0, &err);
|
||||
cr_assert_not_null(json, "Cannot load JSON config");
|
||||
json_t *json = json_loadf(f, 0, &err);
|
||||
cr_assert_not_null(json, "Cannot load JSON config");
|
||||
|
||||
fclose(f);
|
||||
fclose(f);
|
||||
|
||||
json_t *fpgas = json_object_get(json, "fpgas");
|
||||
cr_assert_not_null(fpgas, "No section 'fpgas' found in config");
|
||||
cr_assert(json_object_size(json) > 0, "No FPGAs defined in config");
|
||||
json_t *fpgas = json_object_get(json, "fpgas");
|
||||
cr_assert_not_null(fpgas, "No section 'fpgas' found in config");
|
||||
cr_assert(json_object_size(json) > 0, "No FPGAs defined in config");
|
||||
|
||||
// Get the FPGA card plugin
|
||||
auto fpgaCardFactory = plugin::registry->lookup<fpga::PCIeCardFactory>("pcie");
|
||||
cr_assert_not_null(fpgaCardFactory, "No plugin for FPGA card found");
|
||||
// Get the FPGA card plugin
|
||||
auto fpgaCardFactory =
|
||||
plugin::registry->lookup<fpga::PCIeCardFactory>("pcie");
|
||||
cr_assert_not_null(fpgaCardFactory, "No plugin for FPGA card found");
|
||||
|
||||
// Create all FPGA card instances using the corresponding plugin
|
||||
auto configDir = std::filesystem::path(fn).parent_path();
|
||||
auto cards = std::list<std::shared_ptr<fpga::Card>>();
|
||||
fpga::createCards(json, cards, configDir);
|
||||
state.cards = cards;
|
||||
// Create all FPGA card instances using the corresponding plugin
|
||||
auto configDir = std::filesystem::path(fn).parent_path();
|
||||
auto cards = std::list<std::shared_ptr<fpga::Card>>();
|
||||
fpga::createCards(json, cards, configDir);
|
||||
state.cards = cards;
|
||||
|
||||
cr_assert(state.cards.size() != 0, "No FPGA cards found!");
|
||||
cr_assert(state.cards.size() != 0, "No FPGA cards found!");
|
||||
|
||||
json_decref(json);
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
static void fini()
|
||||
{
|
||||
// Release all cards
|
||||
state.cards.clear();
|
||||
static void fini() {
|
||||
// Release all cards
|
||||
state.cards.clear();
|
||||
}
|
||||
|
||||
// cppcheck-suppress unknownMacro
|
||||
TestSuite(fpga,
|
||||
.init = init,
|
||||
.fini = fini,
|
||||
.description = "VILLASfpga"
|
||||
);
|
||||
TestSuite(fpga, .init = init, .fini = fini, .description = "VILLASfpga");
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
class FpgaState {
|
||||
public:
|
||||
// List of all available FPGA cards, only first will be tested at the moment
|
||||
// List of all available FPGA cards, only first will be tested at the moment
|
||||
std::list<std::shared_ptr<villas::fpga::Card>> cards;
|
||||
};
|
||||
|
||||
|
|
|
@ -10,123 +10,126 @@
|
|||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/fpga/card.hpp>
|
||||
#include <villas/fpga/ips/dma.hpp>
|
||||
#include <villas/fpga/ips/bram.hpp>
|
||||
#include <villas/fpga/ips/dma.hpp>
|
||||
#include <villas/log.hpp>
|
||||
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
#include "global.hpp"
|
||||
|
||||
#include <villas/memory.hpp>
|
||||
#include <villas/gpu.hpp>
|
||||
#include <villas/memory.hpp>
|
||||
|
||||
using namespace villas;
|
||||
|
||||
// cppcheck-suppress unknownMacro
|
||||
Test(fpga, gpu_dma, .description = "GPU DMA tests")
|
||||
{
|
||||
auto logger = logging.get("unit-test:dma");
|
||||
Test(fpga, gpu_dma, .description = "GPU DMA tests") {
|
||||
auto logger = logging.get("unit-test:dma");
|
||||
|
||||
auto &card = state.cards.front();
|
||||
auto &card = state.cards.front();
|
||||
|
||||
auto gpuPlugin = Plugin::Registry<GpuFactory>("cuda");
|
||||
cr_assert_not_null(gpuPlugin, "No GPU plugin found");
|
||||
auto gpuPlugin = Plugin::Registry<GpuFactory>("cuda");
|
||||
cr_assert_not_null(gpuPlugin, "No GPU plugin found");
|
||||
|
||||
auto gpus = gpuPlugin->make();
|
||||
cr_assert(gpus.size() > 0, "No GPUs found");
|
||||
auto gpus = gpuPlugin->make();
|
||||
cr_assert(gpus.size() > 0, "No GPUs found");
|
||||
|
||||
// Just get first cpu
|
||||
auto &gpu = gpus.front();
|
||||
// Just get first cpu
|
||||
auto &gpu = gpus.front();
|
||||
|
||||
size_t count = 0;
|
||||
for (auto &ip : card->ips) {
|
||||
// Skip non-dma IPs
|
||||
if (*ip != fpga::Vlnv("xilinx.com:ip:axi_bram_ctrl:"))
|
||||
continue;
|
||||
size_t count = 0;
|
||||
for (auto &ip : card->ips) {
|
||||
// Skip non-dma IPs
|
||||
if (*ip != fpga::Vlnv("xilinx.com:ip:axi_bram_ctrl:"))
|
||||
continue;
|
||||
|
||||
logger->info("Testing {}", *ip);
|
||||
logger->info("Testing {}", *ip);
|
||||
|
||||
auto bram = dynamic_cast<fpga::ip::Bram*>(ip.get());
|
||||
cr_assert_not_null(bram, "Couldn't find BRAM");
|
||||
auto bram = dynamic_cast<fpga::ip::Bram *>(ip.get());
|
||||
cr_assert_not_null(bram, "Couldn't find BRAM");
|
||||
|
||||
count++;
|
||||
count++;
|
||||
|
||||
size_t len = 4 * (1 << 10);
|
||||
size_t len = 4 * (1 << 10);
|
||||
|
||||
// Allocate memory to use with DMA
|
||||
// Allocate memory to use with DMA
|
||||
|
||||
auto bram0 = bram->getAllocator().allocate<char>(len);
|
||||
auto bram1 = bram->getAllocator().allocate<char>(len);
|
||||
auto bram0 = bram->getAllocator().allocate<char>(len);
|
||||
auto bram1 = bram->getAllocator().allocate<char>(len);
|
||||
|
||||
gpu->makeAccessibleFromPCIeOrHostRam(bram0.getMemoryBlock());
|
||||
gpu->makeAccessibleFromPCIeOrHostRam(bram1.getMemoryBlock());
|
||||
gpu->makeAccessibleFromPCIeOrHostRam(bram0.getMemoryBlock());
|
||||
gpu->makeAccessibleFromPCIeOrHostRam(bram1.getMemoryBlock());
|
||||
|
||||
auto hostRam0 = HostRam::getAllocator().allocate<char>(len);
|
||||
auto hostRam1 = HostRam::getAllocator().allocate<char>(len);
|
||||
auto hostRam0 = HostRam::getAllocator().allocate<char>(len);
|
||||
auto hostRam1 = HostRam::getAllocator().allocate<char>(len);
|
||||
|
||||
gpu->makeAccessibleFromPCIeOrHostRam(hostRam0.getMemoryBlock());
|
||||
gpu->makeAccessibleFromPCIeOrHostRam(hostRam1.getMemoryBlock());
|
||||
gpu->makeAccessibleFromPCIeOrHostRam(hostRam0.getMemoryBlock());
|
||||
gpu->makeAccessibleFromPCIeOrHostRam(hostRam1.getMemoryBlock());
|
||||
|
||||
auto dmaRam0 = HostDmaRam::getAllocator().allocate<char>(len);
|
||||
auto dmaRam1 = HostDmaRam::getAllocator().allocate<char>(len);
|
||||
auto dmaRam0 = HostDmaRam::getAllocator().allocate<char>(len);
|
||||
auto dmaRam1 = HostDmaRam::getAllocator().allocate<char>(len);
|
||||
|
||||
gpu->makeAccessibleFromPCIeOrHostRam(dmaRam0.getMemoryBlock());
|
||||
gpu->makeAccessibleFromPCIeOrHostRam(dmaRam1.getMemoryBlock());
|
||||
gpu->makeAccessibleFromPCIeOrHostRam(dmaRam0.getMemoryBlock());
|
||||
gpu->makeAccessibleFromPCIeOrHostRam(dmaRam1.getMemoryBlock());
|
||||
|
||||
auto gpuMem0 = gpu->getAllocator().allocate<char>(64 << 10);
|
||||
auto gpuMem1 = gpu->getAllocator().allocate<char>(64 << 10);
|
||||
auto gpuMem0 = gpu->getAllocator().allocate<char>(64 << 10);
|
||||
auto gpuMem1 = gpu->getAllocator().allocate<char>(64 << 10);
|
||||
|
||||
gpu->makeAccessibleToPCIeAndVA(gpuMem0.getMemoryBlock());
|
||||
gpu->makeAccessibleToPCIeAndVA(gpuMem1.getMemoryBlock());
|
||||
gpu->makeAccessibleToPCIeAndVA(gpuMem0.getMemoryBlock());
|
||||
gpu->makeAccessibleToPCIeAndVA(gpuMem1.getMemoryBlock());
|
||||
|
||||
// auto &src = bram0;
|
||||
// auto &dst = bram1;
|
||||
// auto &src = bram0;
|
||||
// auto &dst = bram1;
|
||||
|
||||
// auto &src = hostRam0;
|
||||
// auto &dst = hostRam1;
|
||||
// auto &src = hostRam0;
|
||||
// auto &dst = hostRam1;
|
||||
|
||||
auto &src = dmaRam0;
|
||||
// auto &dst = dmaRam1;
|
||||
auto &src = dmaRam0;
|
||||
// auto &dst = dmaRam1;
|
||||
|
||||
// auto &src = gpuMem0;
|
||||
auto &dst = gpuMem1;
|
||||
// auto &src = gpuMem0;
|
||||
auto &dst = gpuMem1;
|
||||
|
||||
std::list<std::pair<std::string, std::function<void()>>> memcpyFuncs = {
|
||||
{"cudaMemcpy", [&]() {gpu->memcpySync(src.getMemoryBlock(), dst.getMemoryBlock(), len);}},
|
||||
{"CUDA kernel", [&]() {gpu->memcpyKernel(src.getMemoryBlock(), dst.getMemoryBlock(), len);}},
|
||||
};
|
||||
std::list<std::pair<std::string, std::function<void()>>> memcpyFuncs = {
|
||||
{"cudaMemcpy",
|
||||
[&]() {
|
||||
gpu->memcpySync(src.getMemoryBlock(), dst.getMemoryBlock(), len);
|
||||
}},
|
||||
{"CUDA kernel",
|
||||
[&]() {
|
||||
gpu->memcpyKernel(src.getMemoryBlock(), dst.getMemoryBlock(), len);
|
||||
}},
|
||||
};
|
||||
|
||||
auto dmaIp = card->lookupIp(fpga::Vlnv("xilinx.com:ip:axi_dma:"));
|
||||
auto dma = std::dynamic_pointer_cast<fpga::ip::Dma>(dmaIp);
|
||||
auto dmaIp = card->lookupIp(fpga::Vlnv("xilinx.com:ip:axi_dma:"));
|
||||
auto dma = std::dynamic_pointer_cast<fpga::ip::Dma>(dmaIp);
|
||||
|
||||
if (dma != nullptr and dma->connectLoopback()) {
|
||||
memcpyFuncs.push_back({
|
||||
"DMA memcpy", [&] (){
|
||||
dma->makeAccesibleFromVA(src.getMemoryBlock());
|
||||
dma->makeAccesibleFromVA(dst.getMemoryBlock());
|
||||
dma->memcpy(src.getMemoryBlock(), dst.getMemoryBlock(), len);
|
||||
}});
|
||||
}
|
||||
if (dma != nullptr and dma->connectLoopback()) {
|
||||
memcpyFuncs.push_back({"DMA memcpy", [&]() {
|
||||
dma->makeAccesibleFromVA(src.getMemoryBlock());
|
||||
dma->makeAccesibleFromVA(dst.getMemoryBlock());
|
||||
dma->memcpy(src.getMemoryBlock(),
|
||||
dst.getMemoryBlock(), len);
|
||||
}});
|
||||
}
|
||||
|
||||
for (auto& [name, memcpyFunc] : memcpyFuncs) {
|
||||
logger->info("Testing {}", name);
|
||||
for (auto &[name, memcpyFunc] : memcpyFuncs) {
|
||||
logger->info("Testing {}", name);
|
||||
|
||||
// Get new random data
|
||||
const size_t lenRandom = utils::read_random(&src, len);
|
||||
cr_assert(len == lenRandom, "Failed to get random data");
|
||||
// Get new random data
|
||||
const size_t lenRandom = utils::read_random(&src, len);
|
||||
cr_assert(len == lenRandom, "Failed to get random data");
|
||||
|
||||
memcpyFunc();
|
||||
const bool success = memcmp(&src, &dst, len) == 0;
|
||||
memcpyFunc();
|
||||
const bool success = memcmp(&src, &dst, len) == 0;
|
||||
|
||||
logger->info(" {}", success ?
|
||||
CLR_GRN("Passed") :
|
||||
CLR_RED("Failed"));
|
||||
}
|
||||
logger->info(" {}", success ? CLR_GRN("Passed") : CLR_RED("Failed"));
|
||||
}
|
||||
|
||||
MemoryManager::getGraph().dump();
|
||||
}
|
||||
MemoryManager::getGraph().dump();
|
||||
}
|
||||
|
||||
cr_assert(count > 0, "No BRAM found");
|
||||
cr_assert(count > 0, "No BRAM found");
|
||||
}
|
||||
|
|
|
@ -11,100 +11,104 @@
|
|||
#include <criterion/logging.h>
|
||||
#include <criterion/options.h>
|
||||
|
||||
#include <villas/log.hpp>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <villas/log.hpp>
|
||||
|
||||
extern "C" {
|
||||
// We override criterions function here
|
||||
void criterion_log_noformat(enum criterion_severity severity, const char *msg);
|
||||
void criterion_plog(enum criterion_logging_level level, const struct criterion_prefix_data *prefix, const char *msg, ...);
|
||||
void criterion_vlog(enum criterion_logging_level level, const char *msg, va_list args);
|
||||
// We override criterions function here
|
||||
void criterion_log_noformat(enum criterion_severity severity, const char *msg);
|
||||
void criterion_plog(enum criterion_logging_level level,
|
||||
const struct criterion_prefix_data *prefix, const char *msg,
|
||||
...);
|
||||
void criterion_vlog(enum criterion_logging_level level, const char *msg,
|
||||
va_list args);
|
||||
}
|
||||
|
||||
struct criterion_prefix_data {
|
||||
const char *prefix;
|
||||
const char *color;
|
||||
const char *prefix;
|
||||
const char *color;
|
||||
};
|
||||
|
||||
static int format_msg(char *buf, size_t buflen, const char *msg, va_list args)
|
||||
{
|
||||
int len = vsnprintf(buf, buflen, msg, args);
|
||||
static int format_msg(char *buf, size_t buflen, const char *msg, va_list args) {
|
||||
int len = vsnprintf(buf, buflen, msg, args);
|
||||
|
||||
// Strip new line
|
||||
char *nl = strchr(buf, '\n');
|
||||
if (nl)
|
||||
*nl = 0;
|
||||
// Strip new line
|
||||
char *nl = strchr(buf, '\n');
|
||||
if (nl)
|
||||
*nl = 0;
|
||||
|
||||
return len;
|
||||
return len;
|
||||
}
|
||||
|
||||
void criterion_log_noformat(enum criterion_severity severity, const char *msg)
|
||||
{
|
||||
auto logger = villas::logging.get("criterion");
|
||||
void criterion_log_noformat(enum criterion_severity severity, const char *msg) {
|
||||
auto logger = villas::logging.get("criterion");
|
||||
|
||||
switch (severity) {
|
||||
case CR_LOG_INFO:
|
||||
logger->info(msg);
|
||||
break;
|
||||
switch (severity) {
|
||||
case CR_LOG_INFO:
|
||||
logger->info(msg);
|
||||
break;
|
||||
|
||||
case CR_LOG_WARNING:
|
||||
logger->warn(msg);
|
||||
break;
|
||||
case CR_LOG_WARNING:
|
||||
logger->warn(msg);
|
||||
break;
|
||||
|
||||
case CR_LOG_ERROR:
|
||||
logger->error(msg);
|
||||
break;
|
||||
}
|
||||
case CR_LOG_ERROR:
|
||||
logger->error(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void criterion_vlog(enum criterion_logging_level level, const char *msg, va_list args)
|
||||
{
|
||||
char formatted_msg[1024];
|
||||
void criterion_vlog(enum criterion_logging_level level, const char *msg,
|
||||
va_list args) {
|
||||
char formatted_msg[1024];
|
||||
|
||||
if (level < criterion_options.logging_threshold)
|
||||
return;
|
||||
if (level < criterion_options.logging_threshold)
|
||||
return;
|
||||
|
||||
format_msg(formatted_msg, sizeof(formatted_msg), msg, args);
|
||||
format_msg(formatted_msg, sizeof(formatted_msg), msg, args);
|
||||
|
||||
auto logger = villas::logging.get("criterion");
|
||||
logger->info(formatted_msg);
|
||||
auto logger = villas::logging.get("criterion");
|
||||
logger->info(formatted_msg);
|
||||
}
|
||||
|
||||
void criterion_plog(enum criterion_logging_level level, const struct criterion_prefix_data *prefix, const char *msg, ...)
|
||||
{
|
||||
char formatted_msg[1024];
|
||||
void criterion_plog(enum criterion_logging_level level,
|
||||
const struct criterion_prefix_data *prefix, const char *msg,
|
||||
...) {
|
||||
char formatted_msg[1024];
|
||||
|
||||
va_list args;
|
||||
va_list args;
|
||||
|
||||
if (level < criterion_options.logging_threshold)
|
||||
return;
|
||||
if (level < criterion_options.logging_threshold)
|
||||
return;
|
||||
|
||||
va_start(args, msg);
|
||||
format_msg(formatted_msg, sizeof(formatted_msg), msg, args);
|
||||
va_end(args);
|
||||
va_start(args, msg);
|
||||
format_msg(formatted_msg, sizeof(formatted_msg), msg, args);
|
||||
va_end(args);
|
||||
|
||||
auto logger = villas::logging.get("criterion");
|
||||
auto logger = villas::logging.get("criterion");
|
||||
|
||||
if (strstr(formatted_msg, "Warning"))
|
||||
logger->warn(formatted_msg);
|
||||
else if (strstr(formatted_msg, "Failed"))
|
||||
logger->error(formatted_msg);
|
||||
else if (!strcmp(prefix->prefix, "----") && !strcmp(prefix->color, "\33[0;34m"))
|
||||
logger->info(formatted_msg);
|
||||
else if (!strcmp(prefix->prefix, "----") && !strcmp(prefix->color, "\33[1;30m"))
|
||||
logger->debug(formatted_msg);
|
||||
else if (!strcmp(prefix->prefix, "===="))
|
||||
logger->info(formatted_msg);
|
||||
else if (!strcmp(prefix->prefix, "RUN "))
|
||||
logger->info("Run: {}", formatted_msg);
|
||||
else if (!strcmp(prefix->prefix, "SKIP"))
|
||||
logger->info("Skip: {}", formatted_msg);
|
||||
else if (!strcmp(prefix->prefix, "PASS"))
|
||||
logger->info("Pass: {}", formatted_msg);
|
||||
else if (!strcmp(prefix->prefix, "FAIL"))
|
||||
logger->error("Fail: {}", formatted_msg);
|
||||
else if (!strcmp(prefix->prefix, "WARN"))
|
||||
logger->warn(formatted_msg);
|
||||
else if (!strcmp(prefix->prefix, "ERR "))
|
||||
logger->error(formatted_msg);
|
||||
if (strstr(formatted_msg, "Warning"))
|
||||
logger->warn(formatted_msg);
|
||||
else if (strstr(formatted_msg, "Failed"))
|
||||
logger->error(formatted_msg);
|
||||
else if (!strcmp(prefix->prefix, "----") &&
|
||||
!strcmp(prefix->color, "\33[0;34m"))
|
||||
logger->info(formatted_msg);
|
||||
else if (!strcmp(prefix->prefix, "----") &&
|
||||
!strcmp(prefix->color, "\33[1;30m"))
|
||||
logger->debug(formatted_msg);
|
||||
else if (!strcmp(prefix->prefix, "===="))
|
||||
logger->info(formatted_msg);
|
||||
else if (!strcmp(prefix->prefix, "RUN "))
|
||||
logger->info("Run: {}", formatted_msg);
|
||||
else if (!strcmp(prefix->prefix, "SKIP"))
|
||||
logger->info("Skip: {}", formatted_msg);
|
||||
else if (!strcmp(prefix->prefix, "PASS"))
|
||||
logger->info("Pass: {}", formatted_msg);
|
||||
else if (!strcmp(prefix->prefix, "FAIL"))
|
||||
logger->error("Fail: {}", formatted_msg);
|
||||
else if (!strcmp(prefix->prefix, "WARN"))
|
||||
logger->warn(formatted_msg);
|
||||
else if (!strcmp(prefix->prefix, "ERR "))
|
||||
logger->error(formatted_msg);
|
||||
}
|
||||
|
|
|
@ -6,59 +6,56 @@
|
|||
*/
|
||||
|
||||
#include <criterion/criterion.h>
|
||||
#include <criterion/options.h>
|
||||
#include <criterion/hooks.h>
|
||||
#include <criterion/internal/ordered-set.h>
|
||||
#include <criterion/options.h>
|
||||
|
||||
#include <villas/log.hpp>
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
// Returns true if there is at least one enabled test in this suite
|
||||
static bool suite_enabled(struct criterion_test_set *tests, const char *name)
|
||||
{
|
||||
FOREACH_SET(void *suite_ptr, tests->suites) {
|
||||
struct criterion_suite_set *suite = (struct criterion_suite_set *) suite_ptr;
|
||||
static bool suite_enabled(struct criterion_test_set *tests, const char *name) {
|
||||
FOREACH_SET(void *suite_ptr, tests->suites) {
|
||||
struct criterion_suite_set *suite = (struct criterion_suite_set *)suite_ptr;
|
||||
|
||||
if (!strcmp(suite->suite.name, name)) {
|
||||
FOREACH_SET(void *test_ptr, suite->tests) {
|
||||
struct criterion_test *test = (struct criterion_test *) test_ptr;
|
||||
if (!strcmp(suite->suite.name, name)) {
|
||||
FOREACH_SET(void *test_ptr, suite->tests) {
|
||||
struct criterion_test *test = (struct criterion_test *)test_ptr;
|
||||
|
||||
if (!test->data->disabled)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!test->data->disabled)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Limit number of parallel jobs to 1 in case we use the FPGA
|
||||
ReportHook(PRE_ALL)(struct criterion_test_set *tests)
|
||||
{
|
||||
if (suite_enabled(tests, "fpga")) {
|
||||
auto logger = villas::logging.get("unittest");
|
||||
ReportHook(PRE_ALL)(struct criterion_test_set *tests) {
|
||||
if (suite_enabled(tests, "fpga")) {
|
||||
auto logger = villas::logging.get("unittest");
|
||||
|
||||
logger->info("FPGA tests enabled. Only 1 job is executed in parallel!.");
|
||||
criterion_options.jobs = 1;
|
||||
}
|
||||
logger->info("FPGA tests enabled. Only 1 job is executed in parallel!.");
|
||||
criterion_options.jobs = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
int main(int argc, char *argv[]) {
|
||||
int ret;
|
||||
|
||||
spdlog::set_level(spdlog::level::debug);
|
||||
spdlog::set_pattern("[%T] [%l] [%n] %v");
|
||||
spdlog::set_level(spdlog::level::debug);
|
||||
spdlog::set_pattern("[%T] [%l] [%n] %v");
|
||||
|
||||
// Run criterion tests
|
||||
auto tests = criterion_initialize();
|
||||
// Run criterion tests
|
||||
auto tests = criterion_initialize();
|
||||
|
||||
ret = criterion_handle_args(argc, argv, true);
|
||||
if (ret)
|
||||
ret = !criterion_run_all_tests(tests);
|
||||
ret = criterion_handle_args(argc, argv, true);
|
||||
if (ret)
|
||||
ret = !criterion_run_all_tests(tests);
|
||||
|
||||
criterion_finalize(tests);
|
||||
criterion_finalize(tests);
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
#include <criterion/criterion.h>
|
||||
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/memory.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
#include <villas/fpga/card.hpp>
|
||||
#include <villas/fpga/ips/dma.hpp>
|
||||
|
@ -28,92 +28,93 @@
|
|||
using namespace villas::fpga::ip;
|
||||
|
||||
// cppcheck-suppress unknownMacro
|
||||
Test(fpga, rtds, .description = "RTDS")
|
||||
{
|
||||
auto logger = villas::logging.get("unit-test:rtds");
|
||||
Test(fpga, rtds, .description = "RTDS") {
|
||||
auto logger = villas::logging.get("unit-test:rtds");
|
||||
|
||||
std::list<villas::fpga::ip::RtdsGtfpga*> rtdsIps;
|
||||
std::list<villas::fpga::ip::Dma*> dmaIps;
|
||||
std::list<villas::fpga::ip::RtdsGtfpga *> rtdsIps;
|
||||
std::list<villas::fpga::ip::Dma *> dmaIps;
|
||||
|
||||
for (auto &ip : state.cards.front()->ips) {
|
||||
if (*ip == villas::fpga::Vlnv("acs.eonerc.rwth-aachen.de:user:rtds_axis:")) {
|
||||
auto rtds = reinterpret_cast<villas::fpga::ip::RtdsGtfpga*>(ip.get());
|
||||
rtdsIps.push_back(rtds);
|
||||
}
|
||||
for (auto &ip : state.cards.front()->ips) {
|
||||
if (*ip ==
|
||||
villas::fpga::Vlnv("acs.eonerc.rwth-aachen.de:user:rtds_axis:")) {
|
||||
auto rtds = reinterpret_cast<villas::fpga::ip::RtdsGtfpga *>(ip.get());
|
||||
rtdsIps.push_back(rtds);
|
||||
}
|
||||
|
||||
if (*ip == villas::fpga::Vlnv("xilinx.com:ip:axi_dma:")) {
|
||||
auto dma = reinterpret_cast<villas::fpga::ip::Dma*>(ip.get());
|
||||
dmaIps.push_back(dma);
|
||||
}
|
||||
}
|
||||
if (*ip == villas::fpga::Vlnv("xilinx.com:ip:axi_dma:")) {
|
||||
auto dma = reinterpret_cast<villas::fpga::ip::Dma *>(ip.get());
|
||||
dmaIps.push_back(dma);
|
||||
}
|
||||
}
|
||||
|
||||
cr_assert(rtdsIps.size() > 0, "No RTDS IPs available to test");
|
||||
cr_assert(dmaIps.size() > 0, "No DMA IPs available to test RTDS with");
|
||||
cr_assert(rtdsIps.size() > 0, "No RTDS IPs available to test");
|
||||
cr_assert(dmaIps.size() > 0, "No DMA IPs available to test RTDS with");
|
||||
|
||||
for (auto rtds : rtdsIps) {
|
||||
for (auto dma : dmaIps) {
|
||||
logger->info("Testing {} with DMA {}", *rtds, *dma);
|
||||
for (auto rtds : rtdsIps) {
|
||||
for (auto dma : dmaIps) {
|
||||
logger->info("Testing {} with DMA {}", *rtds, *dma);
|
||||
|
||||
rtds->dump();
|
||||
rtds->dump();
|
||||
|
||||
auto rtdsMaster = rtds->getMasterPort(rtds->masterPort);
|
||||
auto rtdsSlave = rtds->getSlavePort(rtds->slavePort);
|
||||
auto rtdsMaster = rtds->getMasterPort(rtds->masterPort);
|
||||
auto rtdsSlave = rtds->getSlavePort(rtds->slavePort);
|
||||
|
||||
auto dmaMaster = dma->getMasterPort(dma->mm2sPort);
|
||||
auto dmaSlave = dma->getSlavePort(dma->s2mmPort);
|
||||
auto dmaMaster = dma->getMasterPort(dma->mm2sPort);
|
||||
auto dmaSlave = dma->getSlavePort(dma->s2mmPort);
|
||||
|
||||
// rtds->connect(*rtds);
|
||||
// logger->info("loopback");
|
||||
// while (1);
|
||||
// rtds->connect(*rtds);
|
||||
// logger->info("loopback");
|
||||
// while (1);
|
||||
|
||||
// rtds->connect(rtdsMaster, dmaSlave);
|
||||
// dma->connect(dmaMaster, rtdsSlave);
|
||||
// rtds->connect(rtdsMaster, dmaSlave);
|
||||
// dma->connect(dmaMaster, rtdsSlave);
|
||||
|
||||
auto mem = villas::HostRam::getAllocator().allocate<int32_t>(0x100 / sizeof(int32_t));
|
||||
auto mem = villas::HostRam::getAllocator().allocate<int32_t>(
|
||||
0x100 / sizeof(int32_t));
|
||||
|
||||
// auto start = std::chrono::high_resolution_clock::now();
|
||||
// auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (int i = 1; i < 5; i++) {
|
||||
logger->info("RTT iteration {}", i);
|
||||
for (int i = 1; i < 5; i++) {
|
||||
logger->info("RTT iteration {}", i);
|
||||
|
||||
// logger->info("Prepare read");
|
||||
cr_assert(dma->read(mem.getMemoryBlock(), mem.getMemoryBlock().getSize()),
|
||||
"Failed to initiate DMA read");
|
||||
// logger->info("Prepare read");
|
||||
cr_assert(
|
||||
dma->read(mem.getMemoryBlock(), mem.getMemoryBlock().getSize()),
|
||||
"Failed to initiate DMA read");
|
||||
|
||||
// logger->info("Wait read");
|
||||
const size_t bytesRead = dma->readComplete().bytes;
|
||||
cr_assert(bytesRead > 0,
|
||||
"Failed to complete DMA read");
|
||||
// logger->info("Wait read");
|
||||
const size_t bytesRead = dma->readComplete().bytes;
|
||||
cr_assert(bytesRead > 0, "Failed to complete DMA read");
|
||||
|
||||
// logger->info("Bytes received: {}", bytesRead);
|
||||
// logger->info("Prepare write");
|
||||
cr_assert(dma->write(mem.getMemoryBlock(), bytesRead),
|
||||
"Failed to initiate DMA write");
|
||||
// logger->info("Bytes received: {}", bytesRead);
|
||||
// logger->info("Prepare write");
|
||||
cr_assert(dma->write(mem.getMemoryBlock(), bytesRead),
|
||||
"Failed to initiate DMA write");
|
||||
|
||||
// logger->info("Wait write");
|
||||
// const size_t bytesWritten = dma->writeComplete();
|
||||
// cr_assert(bytesWritten > 0,
|
||||
// "Failed to complete DMA write");
|
||||
// logger->info("Wait write");
|
||||
// const size_t bytesWritten = dma->writeComplete();
|
||||
// cr_assert(bytesWritten > 0,
|
||||
// "Failed to complete DMA write");
|
||||
|
||||
// usleep(5);
|
||||
// sched_yield();
|
||||
// usleep(5);
|
||||
// sched_yield();
|
||||
|
||||
// for (int i = 0;)
|
||||
// rdtsc_sleep();
|
||||
// for (int i = 0;)
|
||||
// rdtsc_sleep();
|
||||
|
||||
// static constexpr int loopCount = 10000;
|
||||
// if (i % loopCount == 0) {
|
||||
// const auto end = std::chrono::high_resolution_clock::now();
|
||||
// static constexpr int loopCount = 10000;
|
||||
// if (i % loopCount == 0) {
|
||||
// const auto end = std::chrono::high_resolution_clock::now();
|
||||
|
||||
// auto durationUs = std::chrono::duration_cast<std::chrono::microseconds>(end - start) / loopCount;
|
||||
// auto durationUs = std::chrono::duration_cast<std::chrono::microseconds>(end - start) / loopCount;
|
||||
|
||||
// logger->info("Avg. loop duration: {} us", durationUs.count());
|
||||
// logger->info("Avg. loop duration: {} us", durationUs.count());
|
||||
|
||||
// start = std::chrono::high_resolution_clock::now();
|
||||
// }
|
||||
}
|
||||
// start = std::chrono::high_resolution_clock::now();
|
||||
// }
|
||||
}
|
||||
|
||||
logger->info(CLR_GRN("Passed"));
|
||||
}
|
||||
}
|
||||
logger->info(CLR_GRN("Passed"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,308 +9,326 @@
|
|||
|
||||
#include <iostream>
|
||||
|
||||
#include <villas/fpga/card.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/memory.hpp>
|
||||
#include <villas/fpga/card.hpp>
|
||||
|
||||
#include <villas/fpga/ips/rtds2gpu.hpp>
|
||||
#include <villas/fpga/ips/gpu2rtds.hpp>
|
||||
#include <villas/fpga/ips/switch.hpp>
|
||||
#include <villas/fpga/ips/dma.hpp>
|
||||
#include <villas/fpga/ips/gpu2rtds.hpp>
|
||||
#include <villas/fpga/ips/rtds.hpp>
|
||||
#include <villas/fpga/ips/rtds2gpu.hpp>
|
||||
#include <villas/fpga/ips/switch.hpp>
|
||||
#include <villas/gpu.hpp>
|
||||
|
||||
#include "global.hpp"
|
||||
|
||||
using namespace villas;
|
||||
|
||||
static constexpr size_t SAMPLE_SIZE = 4;
|
||||
static constexpr size_t SAMPLE_COUNT = 1;
|
||||
static constexpr size_t FRAME_SIZE = SAMPLE_COUNT * SAMPLE_SIZE;
|
||||
static constexpr size_t SAMPLE_SIZE = 4;
|
||||
static constexpr size_t SAMPLE_COUNT = 1;
|
||||
static constexpr size_t FRAME_SIZE = SAMPLE_COUNT * SAMPLE_SIZE;
|
||||
|
||||
static constexpr size_t DOORBELL_OFFSET = SAMPLE_COUNT;
|
||||
static constexpr size_t DATA_OFFSET = 0;
|
||||
|
||||
static void dumpMem(const uint32_t* addr, size_t len)
|
||||
{
|
||||
const size_t bytesPerLine = 16;
|
||||
const size_t lines = (len) / bytesPerLine + 1;
|
||||
const uint8_t* buf = reinterpret_cast<const uint8_t*>(addr);
|
||||
static void dumpMem(const uint32_t *addr, size_t len) {
|
||||
const size_t bytesPerLine = 16;
|
||||
const size_t lines = (len) / bytesPerLine + 1;
|
||||
const uint8_t *buf = reinterpret_cast<const uint8_t *>(addr);
|
||||
|
||||
size_t bytesRead = 0;
|
||||
size_t bytesRead = 0;
|
||||
|
||||
for (size_t line = 0; line < lines; line++) {
|
||||
const unsigned base = line * bytesPerLine;
|
||||
printf("0x%04x: ", base);
|
||||
for (size_t line = 0; line < lines; line++) {
|
||||
const unsigned base = line * bytesPerLine;
|
||||
printf("0x%04x: ", base);
|
||||
|
||||
for (size_t i = 0; i < bytesPerLine && bytesRead < len; i++) {
|
||||
printf("0x%02x ", buf[base + i]);
|
||||
bytesRead++;
|
||||
}
|
||||
puts("");
|
||||
}
|
||||
for (size_t i = 0; i < bytesPerLine && bytesRead < len; i++) {
|
||||
printf("0x%02x ", buf[base + i]);
|
||||
bytesRead++;
|
||||
}
|
||||
puts("");
|
||||
}
|
||||
}
|
||||
|
||||
// cppcheck-suppress unknownMacro
|
||||
Test(fpga, rtds2gpu_loopback_dma, .description = "Rtds2Gpu")
|
||||
{
|
||||
auto logger = logging.get("unit-test:rtds2gpu");
|
||||
Test(fpga, rtds2gpu_loopback_dma, .description = "Rtds2Gpu") {
|
||||
auto logger = logging.get("unit-test:rtds2gpu");
|
||||
|
||||
for (auto &ip : state.cards.front()->ips) {
|
||||
if (*ip != fpga::Vlnv("acs.eonerc.rwth-aachen.de:hls:rtds2gpu:"))
|
||||
continue;
|
||||
for (auto &ip : state.cards.front()->ips) {
|
||||
if (*ip != fpga::Vlnv("acs.eonerc.rwth-aachen.de:hls:rtds2gpu:"))
|
||||
continue;
|
||||
|
||||
logger->info("Testing {}", *ip);
|
||||
logger->info("Testing {}", *ip);
|
||||
|
||||
// Collect neccessary IPs
|
||||
auto rtds2gpu = std::dynamic_pointer_cast<fpga::ip::Rtds2Gpu>(ip);
|
||||
// Collect neccessary IPs
|
||||
auto rtds2gpu = std::dynamic_pointer_cast<fpga::ip::Rtds2Gpu>(ip);
|
||||
|
||||
auto axiSwitch = std::dynamic_pointer_cast<fpga::ip::AxiStreamSwitch>(
|
||||
state.cards.front()->lookupIp(fpga::Vlnv("xilinx.com:ip:axis_switch:")));
|
||||
auto axiSwitch = std::dynamic_pointer_cast<fpga::ip::AxiStreamSwitch>(
|
||||
state.cards.front()->lookupIp(
|
||||
fpga::Vlnv("xilinx.com:ip:axis_switch:")));
|
||||
|
||||
auto dma = std::dynamic_pointer_cast<fpga::ip::Dma>(
|
||||
state.cards.front()->lookupIp(fpga::Vlnv("xilinx.com:ip:axi_dma:")));
|
||||
auto dma = std::dynamic_pointer_cast<fpga::ip::Dma>(
|
||||
state.cards.front()->lookupIp(fpga::Vlnv("xilinx.com:ip:axi_dma:")));
|
||||
|
||||
auto gpu2rtds = std::dynamic_pointer_cast<fpga::ip::Gpu2Rtds>(
|
||||
state.cards.front()->lookupIp(fpga::Vlnv("acs.eonerc.rwth-aachen.de:hls:gpu2rtds:")));
|
||||
auto gpu2rtds = std::dynamic_pointer_cast<fpga::ip::Gpu2Rtds>(
|
||||
state.cards.front()->lookupIp(
|
||||
fpga::Vlnv("acs.eonerc.rwth-aachen.de:hls:gpu2rtds:")));
|
||||
|
||||
auto rtds = std::dynamic_pointer_cast<fpga::ip::Rtds>(
|
||||
state.cards.front()->lookupIp(fpga::Vlnv("acs.eonerc.rwth-aachen.de:user:rtds_axis:")));
|
||||
auto rtds =
|
||||
std::dynamic_pointer_cast<fpga::ip::Rtds>(state.cards.front()->lookupIp(
|
||||
fpga::Vlnv("acs.eonerc.rwth-aachen.de:user:rtds_axis:")));
|
||||
|
||||
cr_assert_not_null(axiSwitch, "No AXI switch IP found");
|
||||
cr_assert_not_null(dma, "No DMA IP found");
|
||||
cr_assert_not_null(gpu2rtds, "No Gpu2Rtds IP found");
|
||||
cr_assert_not_null(rtds, "RTDS IP not found");
|
||||
cr_assert_not_null(axiSwitch, "No AXI switch IP found");
|
||||
cr_assert_not_null(dma, "No DMA IP found");
|
||||
cr_assert_not_null(gpu2rtds, "No Gpu2Rtds IP found");
|
||||
cr_assert_not_null(rtds, "RTDS IP not found");
|
||||
|
||||
rtds2gpu.dump(spdlog::level::debug);
|
||||
gpu2rtds->dump(spdlog::level::debug);
|
||||
rtds2gpu.dump(spdlog::level::debug);
|
||||
gpu2rtds->dump(spdlog::level::debug);
|
||||
|
||||
// Allocate and prepare memory
|
||||
// Allocate and prepare memory
|
||||
|
||||
// Allocate space for all samples and doorbell register
|
||||
auto dmaMemSrc = HostDmaRam::getAllocator(0).allocate<uint32_t>(SAMPLE_COUNT + 1);
|
||||
auto dmaMemDst = HostDmaRam::getAllocator(0).allocate<uint32_t>(SAMPLE_COUNT + 1);
|
||||
auto dmaMemDst2 = HostDmaRam::getAllocator(0).allocate<uint32_t>(SAMPLE_COUNT + 1);
|
||||
// Allocate space for all samples and doorbell register
|
||||
auto dmaMemSrc =
|
||||
HostDmaRam::getAllocator(0).allocate<uint32_t>(SAMPLE_COUNT + 1);
|
||||
auto dmaMemDst =
|
||||
HostDmaRam::getAllocator(0).allocate<uint32_t>(SAMPLE_COUNT + 1);
|
||||
auto dmaMemDst2 =
|
||||
HostDmaRam::getAllocator(0).allocate<uint32_t>(SAMPLE_COUNT + 1);
|
||||
|
||||
memset(&dmaMemSrc, 0x11, dmaMemSrc.getMemoryBlock().getSize());
|
||||
memset(&dmaMemDst, 0x55, dmaMemDst.getMemoryBlock().getSize());
|
||||
memset(&dmaMemDst2, 0x77, dmaMemDst2.getMemoryBlock().getSize());
|
||||
memset(&dmaMemSrc, 0x11, dmaMemSrc.getMemoryBlock().getSize());
|
||||
memset(&dmaMemDst, 0x55, dmaMemDst.getMemoryBlock().getSize());
|
||||
memset(&dmaMemDst2, 0x77, dmaMemDst2.getMemoryBlock().getSize());
|
||||
|
||||
const uint32_t* dataSrc = &dmaMemSrc[DATA_OFFSET];
|
||||
const uint32_t* dataDst = &dmaMemDst[DATA_OFFSET];
|
||||
const uint32_t* dataDst2 = &dmaMemDst2[0];
|
||||
const uint32_t *dataSrc = &dmaMemSrc[DATA_OFFSET];
|
||||
const uint32_t *dataDst = &dmaMemDst[DATA_OFFSET];
|
||||
const uint32_t *dataDst2 = &dmaMemDst2[0];
|
||||
|
||||
dumpMem(dataSrc, dmaMemSrc.getMemoryBlock().getSize());
|
||||
dumpMem(dataDst, dmaMemDst.getMemoryBlock().getSize());
|
||||
dumpMem(dataDst2, dmaMemDst2.getMemoryBlock().getSize());
|
||||
dumpMem(dataSrc, dmaMemSrc.getMemoryBlock().getSize());
|
||||
dumpMem(dataDst, dmaMemDst.getMemoryBlock().getSize());
|
||||
dumpMem(dataDst2, dmaMemDst2.getMemoryBlock().getSize());
|
||||
|
||||
// Connect AXI Stream from DMA to Rtds2Gpu IP
|
||||
cr_assert(dma->connect(rtds2gpu));
|
||||
// Connect AXI Stream from DMA to Rtds2Gpu IP
|
||||
cr_assert(dma->connect(rtds2gpu));
|
||||
|
||||
cr_assert(rtds2gpu.startOnce(dmaMemDst.getMemoryBlock(), SAMPLE_COUNT, DATA_OFFSET * 4, DOORBELL_OFFSET * 4),
|
||||
"Preparing Rtds2Gpu IP failed");
|
||||
cr_assert(rtds2gpu.startOnce(dmaMemDst.getMemoryBlock(), SAMPLE_COUNT,
|
||||
DATA_OFFSET * 4, DOORBELL_OFFSET * 4),
|
||||
"Preparing Rtds2Gpu IP failed");
|
||||
|
||||
cr_assert(dma->write(dmaMemSrc.getMemoryBlock(), FRAME_SIZE),
|
||||
"Starting DMA MM2S transfer failed");
|
||||
cr_assert(dma->write(dmaMemSrc.getMemoryBlock(), FRAME_SIZE),
|
||||
"Starting DMA MM2S transfer failed");
|
||||
|
||||
cr_assert(dma->writeComplete(),
|
||||
"DMA failed");
|
||||
cr_assert(dma->writeComplete(), "DMA failed");
|
||||
|
||||
while (not rtds2gpu.isFinished());
|
||||
while (not rtds2gpu.isFinished())
|
||||
;
|
||||
|
||||
const uint32_t* doorbellDst = &dmaMemDst[DOORBELL_OFFSET];
|
||||
rtds2gpu.dump(spdlog::level::info);
|
||||
rtds2gpu.dumpDoorbell(*doorbellDst);
|
||||
const uint32_t *doorbellDst = &dmaMemDst[DOORBELL_OFFSET];
|
||||
rtds2gpu.dump(spdlog::level::info);
|
||||
rtds2gpu.dumpDoorbell(*doorbellDst);
|
||||
|
||||
cr_assert(memcmp(dataSrc, dataDst, FRAME_SIZE) == 0, "Memory not equal");
|
||||
cr_assert(memcmp(dataSrc, dataDst, FRAME_SIZE) == 0, "Memory not equal");
|
||||
|
||||
for (size_t i = 0; i < SAMPLE_COUNT; i++)
|
||||
gpu2rtds->registerFrames[i] = dmaMemDst[i];
|
||||
for (size_t i = 0; i < SAMPLE_COUNT; i++)
|
||||
gpu2rtds->registerFrames[i] = dmaMemDst[i];
|
||||
|
||||
// Connect AXI Stream from Gpu2Rtds IP to DMA
|
||||
cr_assert(gpu2rtds->connect(*dma));
|
||||
// Connect AXI Stream from Gpu2Rtds IP to DMA
|
||||
cr_assert(gpu2rtds->connect(*dma));
|
||||
|
||||
cr_assert(dma->read(dmaMemDst2.getMemoryBlock(), FRAME_SIZE),
|
||||
"Starting DMA S2MM transfer failed");
|
||||
cr_assert(dma->read(dmaMemDst2.getMemoryBlock(), FRAME_SIZE),
|
||||
"Starting DMA S2MM transfer failed");
|
||||
|
||||
cr_assert(gpu2rtds->startOnce(SAMPLE_COUNT),
|
||||
"Preparing Gpu2Rtds IP failed");
|
||||
cr_assert(gpu2rtds->startOnce(SAMPLE_COUNT),
|
||||
"Preparing Gpu2Rtds IP failed");
|
||||
|
||||
cr_assert(dma->readComplete(),
|
||||
"DMA failed");
|
||||
cr_assert(dma->readComplete(), "DMA failed");
|
||||
|
||||
while (not gpu2rtds->isFinished());
|
||||
while (not gpu2rtds->isFinished())
|
||||
;
|
||||
|
||||
cr_assert(memcmp(dataSrc, dataDst2, FRAME_SIZE) == 0, "Memory not equal");
|
||||
cr_assert(memcmp(dataSrc, dataDst2, FRAME_SIZE) == 0, "Memory not equal");
|
||||
|
||||
dumpMem(dataSrc, dmaMemSrc.getMemoryBlock().getSize());
|
||||
dumpMem(dataDst, dmaMemDst.getMemoryBlock().getSize());
|
||||
dumpMem(dataDst2, dmaMemDst2.getMemoryBlock().getSize());
|
||||
dumpMem(dataSrc, dmaMemSrc.getMemoryBlock().getSize());
|
||||
dumpMem(dataDst, dmaMemDst.getMemoryBlock().getSize());
|
||||
dumpMem(dataDst2, dmaMemDst2.getMemoryBlock().getSize());
|
||||
|
||||
logger->info(CLR_GRN("Passed"));
|
||||
}
|
||||
logger->info(CLR_GRN("Passed"));
|
||||
}
|
||||
}
|
||||
|
||||
// cppcheck-suppress unknownMacro
|
||||
Test(fpga, rtds2gpu_rtt_cpu, .description = "Rtds2Gpu RTT via CPU")
|
||||
{
|
||||
auto logger = logging.get("unit-test:rtds2gpu");
|
||||
Test(fpga, rtds2gpu_rtt_cpu, .description = "Rtds2Gpu RTT via CPU") {
|
||||
auto logger = logging.get("unit-test:rtds2gpu");
|
||||
|
||||
// Collect neccessary IPs
|
||||
auto gpu2rtds = std::dynamic_pointer_cast<fpga::ip::Gpu2Rtds>(
|
||||
state.cards.front()->lookupIp(fpga::Vlnv("acs.eonerc.rwth-aachen.de:hls:gpu2rtds:")));
|
||||
// Collect neccessary IPs
|
||||
auto gpu2rtds = std::dynamic_pointer_cast<fpga::ip::Gpu2Rtds>(
|
||||
state.cards.front()->lookupIp(
|
||||
fpga::Vlnv("acs.eonerc.rwth-aachen.de:hls:gpu2rtds:")));
|
||||
|
||||
auto rtds2gpu = std::dynamic_pointer_cast<fpga::ip::Rtds2Gpu>(
|
||||
state.cards.front()->lookupIp(fpga::Vlnv("acs.eonerc.rwth-aachen.de:hls:rtds2gpu:")));
|
||||
auto rtds2gpu = std::dynamic_pointer_cast<fpga::ip::Rtds2Gpu>(
|
||||
state.cards.front()->lookupIp(
|
||||
fpga::Vlnv("acs.eonerc.rwth-aachen.de:hls:rtds2gpu:")));
|
||||
|
||||
cr_assert_not_null(gpu2rtds, "No Gpu2Rtds IP found");
|
||||
cr_assert_not_null(rtds2gpu, "No Rtds2Gpu IP not found");
|
||||
cr_assert_not_null(gpu2rtds, "No Gpu2Rtds IP found");
|
||||
cr_assert_not_null(rtds2gpu, "No Rtds2Gpu IP not found");
|
||||
|
||||
for (auto &ip : state.cards.front()->ips) {
|
||||
if (*ip != fpga::Vlnv("acs.eonerc.rwth-aachen.de:user:rtds_axis:"))
|
||||
continue;
|
||||
for (auto &ip : state.cards.front()->ips) {
|
||||
if (*ip != fpga::Vlnv("acs.eonerc.rwth-aachen.de:user:rtds_axis:"))
|
||||
continue;
|
||||
|
||||
auto &rtds = dynamic_cast<fpga::ip::Rtds&>(*ip);
|
||||
logger->info("Testing {}", rtds);
|
||||
auto &rtds = dynamic_cast<fpga::ip::Rtds &>(*ip);
|
||||
logger->info("Testing {}", rtds);
|
||||
|
||||
auto dmaRam = HostDmaRam::getAllocator().allocate<uint32_t>(SAMPLE_COUNT + 1);
|
||||
uint32_t* data = &dmaRam[DATA_OFFSET];
|
||||
uint32_t* doorbell = &dmaRam[DOORBELL_OFFSET];
|
||||
auto dmaRam =
|
||||
HostDmaRam::getAllocator().allocate<uint32_t>(SAMPLE_COUNT + 1);
|
||||
uint32_t *data = &dmaRam[DATA_OFFSET];
|
||||
uint32_t *doorbell = &dmaRam[DOORBELL_OFFSET];
|
||||
|
||||
// TEST: rtds loopback via switch, this should always work and have RTT=1
|
||||
//cr_assert(rtds.connect(rtds));
|
||||
//logger->info("loopback");
|
||||
//while (1);
|
||||
// TEST: rtds loopback via switch, this should always work and have RTT=1
|
||||
//cr_assert(rtds.connect(rtds));
|
||||
//logger->info("loopback");
|
||||
//while (1);
|
||||
|
||||
cr_assert(rtds.connect(*rtds2gpu));
|
||||
cr_assert(gpu2rtds->connect(rtds));
|
||||
cr_assert(rtds.connect(*rtds2gpu));
|
||||
cr_assert(gpu2rtds->connect(rtds));
|
||||
|
||||
for (size_t i = 1; i <= 10000; ) {
|
||||
rtds2gpu->doorbellReset(*doorbell);
|
||||
rtds2gpu->startOnce(dmaRam.getMemoryBlock(), SAMPLE_COUNT, DATA_OFFSET * 4, DOORBELL_OFFSET * 4);
|
||||
for (size_t i = 1; i <= 10000;) {
|
||||
rtds2gpu->doorbellReset(*doorbell);
|
||||
rtds2gpu->startOnce(dmaRam.getMemoryBlock(), SAMPLE_COUNT,
|
||||
DATA_OFFSET * 4, DOORBELL_OFFSET * 4);
|
||||
|
||||
// Wait by polling rtds2gpu IP or ...
|
||||
// while (not rtds2gpu->isFinished());
|
||||
// Wait by polling rtds2gpu IP or ...
|
||||
// while (not rtds2gpu->isFinished());
|
||||
|
||||
// Wait by polling (local) doorbell register (= just memory)
|
||||
while (not rtds2gpu->doorbellIsValid(*doorbell));
|
||||
// Wait by polling (local) doorbell register (= just memory)
|
||||
while (not rtds2gpu->doorbellIsValid(*doorbell))
|
||||
;
|
||||
|
||||
// Copy samples to gpu2rtds IP
|
||||
for (size_t i = 0; i < SAMPLE_COUNT; i++) {
|
||||
gpu2rtds->registerFrames[i] = data[i];
|
||||
}
|
||||
// Copy samples to gpu2rtds IP
|
||||
for (size_t i = 0; i < SAMPLE_COUNT; i++) {
|
||||
gpu2rtds->registerFrames[i] = data[i];
|
||||
}
|
||||
|
||||
// Waiting for gpu2rtds is not strictly required
|
||||
gpu2rtds->startOnce(SAMPLE_COUNT);
|
||||
//while (not gpu2rtds->isFinished());
|
||||
// Waiting for gpu2rtds is not strictly required
|
||||
gpu2rtds->startOnce(SAMPLE_COUNT);
|
||||
//while (not gpu2rtds->isFinished());
|
||||
|
||||
if (i % 1000 == 0) {
|
||||
logger->info("Successful iterations {}, data {}", i, data[0]);
|
||||
rtds2gpu->dump();
|
||||
rtds2gpu->dumpDoorbell(data[1]);
|
||||
}
|
||||
}
|
||||
if (i % 1000 == 0) {
|
||||
logger->info("Successful iterations {}, data {}", i, data[0]);
|
||||
rtds2gpu->dump();
|
||||
rtds2gpu->dumpDoorbell(data[1]);
|
||||
}
|
||||
}
|
||||
|
||||
logger->info(CLR_GRN("Passed"));
|
||||
}
|
||||
logger->info(CLR_GRN("Passed"));
|
||||
}
|
||||
}
|
||||
|
||||
void gpu_rtds_rtt_start(volatile uint32_t* dataIn, volatile reg_doorbell_t* doorbellIn,
|
||||
volatile uint32_t* dataOut, volatile fpga::ip::ControlRegister* controlRegister);
|
||||
void gpu_rtds_rtt_start(volatile uint32_t *dataIn,
|
||||
volatile reg_doorbell_t *doorbellIn,
|
||||
volatile uint32_t *dataOut,
|
||||
volatile fpga::ip::ControlRegister *controlRegister);
|
||||
|
||||
void gpu_rtds_rtt_stop();
|
||||
|
||||
// cppcheck-suppress unknownMacro
|
||||
Test(fpga, rtds2gpu_rtt_gpu, .description = "Rtds2Gpu RTT via GPU")
|
||||
{
|
||||
auto logger = logging.get("unit-test:rtds2gpu");
|
||||
Test(fpga, rtds2gpu_rtt_gpu, .description = "Rtds2Gpu RTT via GPU") {
|
||||
auto logger = logging.get("unit-test:rtds2gpu");
|
||||
|
||||
// Collect neccessary IPs
|
||||
auto gpu2rtds = std::dynamic_pointer_cast<fpga::ip::Gpu2Rtds>(
|
||||
state.cards.front()->lookupIp(fpga::Vlnv("acs.eonerc.rwth-aachen.de:hls:gpu2rtds:")));
|
||||
// Collect neccessary IPs
|
||||
auto gpu2rtds = std::dynamic_pointer_cast<fpga::ip::Gpu2Rtds>(
|
||||
state.cards.front()->lookupIp(
|
||||
fpga::Vlnv("acs.eonerc.rwth-aachen.de:hls:gpu2rtds:")));
|
||||
|
||||
auto rtds2gpu = std::dynamic_pointer_cast<fpga::ip::Rtds2Gpu>(
|
||||
state.cards.front()->lookupIp(fpga::Vlnv("acs.eonerc.rwth-aachen.de:hls:rtds2gpu:")));
|
||||
auto rtds2gpu = std::dynamic_pointer_cast<fpga::ip::Rtds2Gpu>(
|
||||
state.cards.front()->lookupIp(
|
||||
fpga::Vlnv("acs.eonerc.rwth-aachen.de:hls:rtds2gpu:")));
|
||||
|
||||
cr_assert_not_null(gpu2rtds, "No Gpu2Rtds IP found");
|
||||
cr_assert_not_null(rtds2gpu, "No Rtds2Gpu IP not found");
|
||||
cr_assert_not_null(gpu2rtds, "No Gpu2Rtds IP found");
|
||||
cr_assert_not_null(rtds2gpu, "No Rtds2Gpu IP not found");
|
||||
|
||||
auto gpuPlugin = Registry::lookup<GpuFactory>("cuda");
|
||||
cr_assert_not_null(gpuPlugin, "No GPU plugin found");
|
||||
auto gpuPlugin = Registry::lookup<GpuFactory>("cuda");
|
||||
cr_assert_not_null(gpuPlugin, "No GPU plugin found");
|
||||
|
||||
auto gpus = gpuPlugin->make();
|
||||
cr_assert(gpus.size() > 0, "No GPUs found");
|
||||
auto gpus = gpuPlugin->make();
|
||||
cr_assert(gpus.size() > 0, "No GPUs found");
|
||||
|
||||
// Just get first cpu
|
||||
auto &gpu = gpus.front();
|
||||
// Just get first cpu
|
||||
auto &gpu = gpus.front();
|
||||
|
||||
// Allocate memory on GPU and make accessible by to PCIe/FPGA
|
||||
auto gpuRam = gpu->getAllocator().allocate<uint32_t>(SAMPLE_COUNT + 1);
|
||||
cr_assert(gpu->makeAccessibleToPCIeAndVA(gpuRam.getMemoryBlock()));
|
||||
// Allocate memory on GPU and make accessible by to PCIe/FPGA
|
||||
auto gpuRam = gpu->getAllocator().allocate<uint32_t>(SAMPLE_COUNT + 1);
|
||||
cr_assert(gpu->makeAccessibleToPCIeAndVA(gpuRam.getMemoryBlock()));
|
||||
|
||||
// Make Gpu2Rtds IP register memory on FPGA accessible to GPU
|
||||
cr_assert(gpu->makeAccessibleFromPCIeOrHostRam(gpu2rtds->getRegisterMemory()));
|
||||
// Make Gpu2Rtds IP register memory on FPGA accessible to GPU
|
||||
cr_assert(
|
||||
gpu->makeAccessibleFromPCIeOrHostRam(gpu2rtds->getRegisterMemory()));
|
||||
|
||||
auto tr = gpu->translate(gpuRam.getMemoryBlock());
|
||||
auto tr = gpu->translate(gpuRam.getMemoryBlock());
|
||||
|
||||
auto dataIn = reinterpret_cast<uint32_t*>(tr.getLocalAddr(DATA_OFFSET * sizeof(uint32_t)));
|
||||
auto doorbellIn = reinterpret_cast<reg_doorbell_t*>(tr.getLocalAddr(DOORBELL_OFFSET * sizeof(uint32_t)));
|
||||
auto dataIn = reinterpret_cast<uint32_t *>(
|
||||
tr.getLocalAddr(DATA_OFFSET * sizeof(uint32_t)));
|
||||
auto doorbellIn = reinterpret_cast<reg_doorbell_t *>(
|
||||
tr.getLocalAddr(DOORBELL_OFFSET * sizeof(uint32_t)));
|
||||
|
||||
auto gpu2rtdsRegisters = gpu->translate(gpu2rtds->getRegisterMemory());
|
||||
auto gpu2rtdsRegisters = gpu->translate(gpu2rtds->getRegisterMemory());
|
||||
|
||||
auto frameRegister = reinterpret_cast<uint32_t*>(gpu2rtdsRegisters.getLocalAddr(gpu2rtds->registerFrameOffset));
|
||||
auto controlRegister = reinterpret_cast<fpga::ip::ControlRegister*>(gpu2rtdsRegisters.getLocalAddr(gpu2rtds->registerControlAddr));
|
||||
auto frameRegister = reinterpret_cast<uint32_t *>(
|
||||
gpu2rtdsRegisters.getLocalAddr(gpu2rtds->registerFrameOffset));
|
||||
auto controlRegister = reinterpret_cast<fpga::ip::ControlRegister *>(
|
||||
gpu2rtdsRegisters.getLocalAddr(gpu2rtds->registerControlAddr));
|
||||
|
||||
// auto doorbellInCpu = reinterpret_cast<reg_doorbell_t*>(&gpuRam[DOORBELL_OFFSET]);
|
||||
// auto doorbellInCpu = reinterpret_cast<reg_doorbell_t*>(&gpuRam[DOORBELL_OFFSET]);
|
||||
|
||||
for (auto &ip : state.cards.front()->ips) {
|
||||
if (*ip != fpga::Vlnv("acs.eonerc.rwth-aachen.de:user:rtds_axis:"))
|
||||
continue;
|
||||
for (auto &ip : state.cards.front()->ips) {
|
||||
if (*ip != fpga::Vlnv("acs.eonerc.rwth-aachen.de:user:rtds_axis:"))
|
||||
continue;
|
||||
|
||||
auto &rtds = dynamic_cast<fpga::ip::Rtds&>(*ip);
|
||||
logger->info("Testing {}", rtds);
|
||||
auto &rtds = dynamic_cast<fpga::ip::Rtds &>(*ip);
|
||||
logger->info("Testing {}", rtds);
|
||||
|
||||
// TEST: rtds loopback via switch, this should always work and have RTT=1
|
||||
//cr_assert(rtds.connect(rtds));
|
||||
//logger->info("loopback");
|
||||
//while (1);
|
||||
// TEST: rtds loopback via switch, this should always work and have RTT=1
|
||||
//cr_assert(rtds.connect(rtds));
|
||||
//logger->info("loopback");
|
||||
//while (1);
|
||||
|
||||
cr_assert(rtds.connect(*rtds2gpu));
|
||||
cr_assert(gpu2rtds->connect(rtds));
|
||||
cr_assert(rtds.connect(*rtds2gpu));
|
||||
cr_assert(gpu2rtds->connect(rtds));
|
||||
|
||||
// Launch once so they are configured
|
||||
cr_assert(rtds2gpu->startOnce(gpuRam.getMemoryBlock(), SAMPLE_COUNT, DATA_OFFSET * 4, DOORBELL_OFFSET * 4));
|
||||
cr_assert(gpu2rtds->startOnce(SAMPLE_COUNT));
|
||||
// Launch once so they are configured
|
||||
cr_assert(rtds2gpu->startOnce(gpuRam.getMemoryBlock(), SAMPLE_COUNT,
|
||||
DATA_OFFSET * 4, DOORBELL_OFFSET * 4));
|
||||
cr_assert(gpu2rtds->startOnce(SAMPLE_COUNT));
|
||||
|
||||
rtds2gpu->setAutoRestart(true);
|
||||
rtds2gpu->start();
|
||||
rtds2gpu->setAutoRestart(true);
|
||||
rtds2gpu->start();
|
||||
|
||||
logger->info("GPU RTT RTDS");
|
||||
logger->info("GPU RTT RTDS");
|
||||
|
||||
std::string dummy;
|
||||
std::string dummy;
|
||||
|
||||
gpu_rtds_rtt_start(dataIn, doorbellIn, frameRegister, controlRegister);
|
||||
gpu_rtds_rtt_start(dataIn, doorbellIn, frameRegister, controlRegister);
|
||||
|
||||
// while (1) {
|
||||
// cr_assert(rtds2gpu->startOnce(gpuRam.getMemoryBlock(), SAMPLE_COUNT, DATA_OFFSET * 4, DOORBELL_OFFSET * 4));
|
||||
// }
|
||||
// while (1) {
|
||||
// cr_assert(rtds2gpu->startOnce(gpuRam.getMemoryBlock(), SAMPLE_COUNT, DATA_OFFSET * 4, DOORBELL_OFFSET * 4));
|
||||
// }
|
||||
|
||||
// for (int i = 0; i < 10000; i++) {
|
||||
// while (not doorbellInCpu->is_valid);
|
||||
// logger->debug("received data");
|
||||
// }
|
||||
// for (int i = 0; i < 10000; i++) {
|
||||
// while (not doorbellInCpu->is_valid);
|
||||
// logger->debug("received data");
|
||||
// }
|
||||
|
||||
// logger->info("Press enter to cancel");
|
||||
// std::cin >> dummy;
|
||||
// logger->info("Press enter to cancel");
|
||||
// std::cin >> dummy;
|
||||
|
||||
while (1) {
|
||||
sleep(1);
|
||||
// logger->debug("Current sequence number: {}", doorbellInCpu->seq_nr);
|
||||
logger->debug("Still running");
|
||||
}
|
||||
while (1) {
|
||||
sleep(1);
|
||||
// logger->debug("Current sequence number: {}", doorbellInCpu->seq_nr);
|
||||
logger->debug("Still running");
|
||||
}
|
||||
|
||||
gpu_rtds_rtt_stop();
|
||||
gpu_rtds_rtt_stop();
|
||||
|
||||
logger->info(CLR_GRN("Passed"));
|
||||
}
|
||||
logger->info(CLR_GRN("Passed"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,73 +11,72 @@
|
|||
#include <villas/fpga/vlnv.hpp>
|
||||
|
||||
#include <villas/fpga/ips/dma.hpp>
|
||||
#include <villas/fpga/ips/switch.hpp>
|
||||
#include <villas/fpga/ips/rtds.hpp>
|
||||
#include <villas/fpga/ips/switch.hpp>
|
||||
|
||||
extern struct fpga_card *card;
|
||||
|
||||
// cppcheck-suppress unknownMacro
|
||||
Test(fpga, rtds_rtt, .description = "RTDS: tight rtt")
|
||||
{
|
||||
int ret;
|
||||
struct fpga_ip *ip, *rtds;
|
||||
struct dma_mem buf;
|
||||
size_t recvlen;
|
||||
Test(fpga, rtds_rtt, .description = "RTDS: tight rtt") {
|
||||
int ret;
|
||||
struct fpga_ip *ip, *rtds;
|
||||
struct dma_mem buf;
|
||||
size_t recvlen;
|
||||
|
||||
std::list<villas::fpga::ip::Rtds*> rtdsIps;
|
||||
std::list<villas::fpga::ip::Dma*> dmaIps;
|
||||
std::list<villas::fpga::ip::Rtds *> rtdsIps;
|
||||
std::list<villas::fpga::ip::Dma *> dmaIps;
|
||||
|
||||
// Get IP cores
|
||||
for (auto &ip : state.cards.front()->ips) {
|
||||
if (*ip == villas::fpga::Vlnv("acs.eonerc.rwth-aachen.de:user:rtds_axis:")) {
|
||||
auto rtds = reinterpret_cast<villas::fpga::ip::Rtds*>(ip.get());
|
||||
rtdsIps.push_back(rtds);
|
||||
}
|
||||
// Get IP cores
|
||||
for (auto &ip : state.cards.front()->ips) {
|
||||
if (*ip ==
|
||||
villas::fpga::Vlnv("acs.eonerc.rwth-aachen.de:user:rtds_axis:")) {
|
||||
auto rtds = reinterpret_cast<villas::fpga::ip::Rtds *>(ip.get());
|
||||
rtdsIps.push_back(rtds);
|
||||
}
|
||||
|
||||
if (*ip == villas::fpga::Vlnv("xilinx.com:ip:axi_dma:")) {
|
||||
auto dma = reinterpret_cast<villas::fpga::ip::Dma*>(ip.get());
|
||||
dmaIps.push_back(dma);
|
||||
}
|
||||
}
|
||||
if (*ip == villas::fpga::Vlnv("xilinx.com:ip:axi_dma:")) {
|
||||
auto dma = reinterpret_cast<villas::fpga::ip::Dma *>(ip.get());
|
||||
dmaIps.push_back(dma);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto rtds : rtdsIps) {
|
||||
for (auto dma : dmaIps) {
|
||||
for (auto rtds : rtdsIps) {
|
||||
for (auto dma : dmaIps) {
|
||||
|
||||
rtds->connect
|
||||
rtds->connect
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
ret = switch_connect(card->sw, rtds, ip);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
ret = switch_connect(card->sw, rtds, ip);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
ret = switch_connect(card->sw, ip, rtds);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
ret = switch_connect(card->sw, ip, rtds);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
ret = dma_alloc(ip, &buf, 0x100, 0);
|
||||
cr_assert_eq(ret, 0, "Failed to allocate DMA memory");
|
||||
|
||||
ret = dma_alloc(ip, &buf, 0x100, 0);
|
||||
cr_assert_eq(ret, 0, "Failed to allocate DMA memory");
|
||||
while (1) {
|
||||
|
||||
while (1) {
|
||||
ret = dma_read(ip, buf.base_phys, buf.len);
|
||||
cr_assert_eq(ret, 0, "Failed to start DMA read: %d", ret);
|
||||
|
||||
ret = dma_read(ip, buf.base_phys, buf.len);
|
||||
cr_assert_eq(ret, 0, "Failed to start DMA read: %d", ret);
|
||||
ret = dma_read_complete(ip, NULL, &recvlen);
|
||||
cr_assert_eq(ret, 0, "Failed to complete DMA read: %d", ret);
|
||||
|
||||
ret = dma_read_complete(ip, NULL, &recvlen);
|
||||
cr_assert_eq(ret, 0, "Failed to complete DMA read: %d", ret);
|
||||
ret = dma_write(ip, buf.base_phys, recvlen);
|
||||
cr_assert_eq(ret, 0, "Failed to start DMA write: %d", ret);
|
||||
|
||||
ret = dma_write(ip, buf.base_phys, recvlen);
|
||||
cr_assert_eq(ret, 0, "Failed to start DMA write: %d", ret);
|
||||
ret = dma_write_complete(ip, NULL, NULL);
|
||||
cr_assert_eq(ret, 0, "Failed to complete DMA write: %d", ret);
|
||||
}
|
||||
|
||||
ret = dma_write_complete(ip, NULL, NULL);
|
||||
cr_assert_eq(ret, 0, "Failed to complete DMA write: %d", ret);
|
||||
}
|
||||
ret = switch_disconnect(card->sw, rtds, ip);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
ret = switch_disconnect(card->sw, rtds, ip);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
ret = switch_disconnect(card->sw, ip, rtds);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
ret = switch_disconnect(card->sw, ip, rtds);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
ret = dma_free(ip, &buf);
|
||||
cr_assert_eq(ret, 0, "Failed to release DMA memory");
|
||||
ret = dma_free(ip, &buf);
|
||||
cr_assert_eq(ret, 0, "Failed to release DMA memory");
|
||||
}
|
||||
|
|
|
@ -7,52 +7,53 @@
|
|||
|
||||
#include <chrono>
|
||||
#include <criterion/criterion.h>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/fpga/card.hpp>
|
||||
#include <villas/fpga/ips/timer.hpp>
|
||||
#include <villas/log.hpp>
|
||||
|
||||
#include <villas/config.hpp>
|
||||
#include "global.hpp"
|
||||
#include <villas/config.hpp>
|
||||
|
||||
// cppcheck-suppress unknownMacro
|
||||
Test(fpga, timer, .description = "Timer Counter")
|
||||
{
|
||||
auto logger = villas::logging.get("unit-test:timer");
|
||||
Test(fpga, timer, .description = "Timer Counter") {
|
||||
auto logger = villas::logging.get("unit-test:timer");
|
||||
|
||||
size_t count = 0;
|
||||
for (auto &ip : state.cards.front()->ips) {
|
||||
// Skip non-timer IPs
|
||||
if (*ip != villas::fpga::Vlnv("xilinx.com:ip:axi_timer:"))
|
||||
continue;
|
||||
size_t count = 0;
|
||||
for (auto &ip : state.cards.front()->ips) {
|
||||
// Skip non-timer IPs
|
||||
if (*ip != villas::fpga::Vlnv("xilinx.com:ip:axi_timer:"))
|
||||
continue;
|
||||
|
||||
logger->info("Testing {}", *ip);
|
||||
logger->info("Testing {}", *ip);
|
||||
|
||||
auto timer = dynamic_cast<villas::fpga::ip::Timer&>(*ip);
|
||||
auto timer = dynamic_cast<villas::fpga::ip::Timer &>(*ip);
|
||||
|
||||
logger->info("Test simple waiting");
|
||||
timer.start(timer.getFrequency() / 10);
|
||||
cr_assert(timer.wait(), "Waiting failed");
|
||||
logger->info("Test simple waiting");
|
||||
timer.start(timer.getFrequency() / 10);
|
||||
cr_assert(timer.wait(), "Waiting failed");
|
||||
|
||||
logger->info(CLR_GRN("Passed"));
|
||||
logger->info(CLR_GRN("Passed"));
|
||||
|
||||
logger->info("Measure waiting time (1s)");
|
||||
logger->info("Measure waiting time (1s)");
|
||||
|
||||
timer.start(timer.getFrequency());
|
||||
const auto start = std::chrono::high_resolution_clock::now();
|
||||
timer.start(timer.getFrequency());
|
||||
const auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
timer.wait();
|
||||
const auto stop = std::chrono::high_resolution_clock::now();
|
||||
timer.wait();
|
||||
const auto stop = std::chrono::high_resolution_clock::now();
|
||||
|
||||
const int oneSecondInUs = 1000000;
|
||||
const auto duration = stop - start;
|
||||
const auto durationUs = std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
|
||||
const int oneSecondInUs = 1000000;
|
||||
const auto duration = stop - start;
|
||||
const auto durationUs =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
|
||||
|
||||
cr_assert(std::abs(durationUs - oneSecondInUs) < 0.01 * oneSecondInUs, "Timer deviation > 1%%");
|
||||
cr_assert(std::abs(durationUs - oneSecondInUs) < 0.01 * oneSecondInUs,
|
||||
"Timer deviation > 1%%");
|
||||
|
||||
logger->info(CLR_GRN("Passed:") " Time passed: {} us", durationUs);
|
||||
logger->info(CLR_GRN("Passed:") " Time passed: {} us", durationUs);
|
||||
|
||||
count++;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
cr_assert(count > 0, "No timer found");
|
||||
cr_assert(count > 0, "No timer found");
|
||||
}
|
||||
|
|
4788
fpga/thirdparty/CLI11/CLI11.hpp
vendored
4788
fpga/thirdparty/CLI11/CLI11.hpp
vendored
File diff suppressed because it is too large
Load diff
723
fpga/thirdparty/rang/rang.hpp
vendored
723
fpga/thirdparty/rang/rang.hpp
vendored
|
@ -6,7 +6,6 @@
|
|||
* SPDX-License-Identifier: Unlicense
|
||||
*********************************************************************************/
|
||||
|
||||
|
||||
#ifndef RANG_DOT_HPP
|
||||
#define RANG_DOT_HPP
|
||||
|
||||
|
@ -27,14 +26,14 @@
|
|||
|
||||
#if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x0600)
|
||||
#error \
|
||||
"Please include rang.hpp before any windows system headers or set _WIN32_WINNT at least to _WIN32_WINNT_VISTA"
|
||||
"Please include rang.hpp before any windows system headers or set _WIN32_WINNT at least to _WIN32_WINNT_VISTA"
|
||||
#elif !defined(_WIN32_WINNT)
|
||||
#define _WIN32_WINNT _WIN32_WINNT_VISTA
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <memory>
|
||||
#include <windows.h>
|
||||
|
||||
// Only defined in windows 10 onwards, redefining in lower windows since it
|
||||
// doesn't gets used in lower versions
|
||||
|
@ -58,451 +57,433 @@ namespace rang {
|
|||
* Note that on Windows terminals bold style is same as fgB color.
|
||||
*/
|
||||
enum class style {
|
||||
reset = 0,
|
||||
bold = 1,
|
||||
dim = 2,
|
||||
italic = 3,
|
||||
underline = 4,
|
||||
blink = 5,
|
||||
rblink = 6,
|
||||
reversed = 7,
|
||||
conceal = 8,
|
||||
crossed = 9
|
||||
reset = 0,
|
||||
bold = 1,
|
||||
dim = 2,
|
||||
italic = 3,
|
||||
underline = 4,
|
||||
blink = 5,
|
||||
rblink = 6,
|
||||
reversed = 7,
|
||||
conceal = 8,
|
||||
crossed = 9
|
||||
};
|
||||
|
||||
enum class fg {
|
||||
black = 30,
|
||||
red = 31,
|
||||
green = 32,
|
||||
yellow = 33,
|
||||
blue = 34,
|
||||
magenta = 35,
|
||||
cyan = 36,
|
||||
gray = 37,
|
||||
reset = 39
|
||||
black = 30,
|
||||
red = 31,
|
||||
green = 32,
|
||||
yellow = 33,
|
||||
blue = 34,
|
||||
magenta = 35,
|
||||
cyan = 36,
|
||||
gray = 37,
|
||||
reset = 39
|
||||
};
|
||||
|
||||
enum class bg {
|
||||
black = 40,
|
||||
red = 41,
|
||||
green = 42,
|
||||
yellow = 43,
|
||||
blue = 44,
|
||||
magenta = 45,
|
||||
cyan = 46,
|
||||
gray = 47,
|
||||
reset = 49
|
||||
black = 40,
|
||||
red = 41,
|
||||
green = 42,
|
||||
yellow = 43,
|
||||
blue = 44,
|
||||
magenta = 45,
|
||||
cyan = 46,
|
||||
gray = 47,
|
||||
reset = 49
|
||||
};
|
||||
|
||||
enum class fgB {
|
||||
black = 90,
|
||||
red = 91,
|
||||
green = 92,
|
||||
yellow = 93,
|
||||
blue = 94,
|
||||
magenta = 95,
|
||||
cyan = 96,
|
||||
gray = 97
|
||||
black = 90,
|
||||
red = 91,
|
||||
green = 92,
|
||||
yellow = 93,
|
||||
blue = 94,
|
||||
magenta = 95,
|
||||
cyan = 96,
|
||||
gray = 97
|
||||
};
|
||||
|
||||
enum class bgB {
|
||||
black = 100,
|
||||
red = 101,
|
||||
green = 102,
|
||||
yellow = 103,
|
||||
blue = 104,
|
||||
magenta = 105,
|
||||
cyan = 106,
|
||||
gray = 107
|
||||
black = 100,
|
||||
red = 101,
|
||||
green = 102,
|
||||
yellow = 103,
|
||||
blue = 104,
|
||||
magenta = 105,
|
||||
cyan = 106,
|
||||
gray = 107
|
||||
};
|
||||
|
||||
enum class control { // Behaviour of rang function calls
|
||||
Off = 0, // toggle off rang style/color calls
|
||||
Auto = 1, // (Default) autodect terminal and colorize if needed
|
||||
Force = 2 // force ansi color output to non terminal streams
|
||||
enum class control { // Behaviour of rang function calls
|
||||
Off = 0, // toggle off rang style/color calls
|
||||
Auto = 1, // (Default) autodect terminal and colorize if needed
|
||||
Force = 2 // force ansi color output to non terminal streams
|
||||
};
|
||||
// Use rang::setControlMode to set rang control mode
|
||||
|
||||
enum class winTerm { // Windows Terminal Mode
|
||||
Auto = 0, // (Default) automatically detects wheter Ansi or Native API
|
||||
Ansi = 1, // Force use Ansi API
|
||||
Native = 2 // Force use Native API
|
||||
enum class winTerm { // Windows Terminal Mode
|
||||
Auto = 0, // (Default) automatically detects wheter Ansi or Native API
|
||||
Ansi = 1, // Force use Ansi API
|
||||
Native = 2 // Force use Native API
|
||||
};
|
||||
// Use rang::setWinTermMode to explicitly set terminal API for Windows
|
||||
// Calling rang::setWinTermMode have no effect on other OS
|
||||
|
||||
namespace rang_implementation {
|
||||
|
||||
inline std::atomic<control> &controlMode() noexcept
|
||||
{
|
||||
static std::atomic<control> value(control::Auto);
|
||||
return value;
|
||||
}
|
||||
inline std::atomic<control> &controlMode() noexcept {
|
||||
static std::atomic<control> value(control::Auto);
|
||||
return value;
|
||||
}
|
||||
|
||||
inline std::atomic<winTerm> &winTermMode() noexcept
|
||||
{
|
||||
static std::atomic<winTerm> termMode(winTerm::Auto);
|
||||
return termMode;
|
||||
}
|
||||
inline std::atomic<winTerm> &winTermMode() noexcept {
|
||||
static std::atomic<winTerm> termMode(winTerm::Auto);
|
||||
return termMode;
|
||||
}
|
||||
|
||||
inline bool supportsColor() noexcept
|
||||
{
|
||||
inline bool supportsColor() noexcept {
|
||||
#if defined(OS_LINUX) || defined(OS_MAC)
|
||||
|
||||
static const bool result = [] {
|
||||
const char *Terms[]
|
||||
= { "ansi", "color", "console", "cygwin", "gnome",
|
||||
"konsole", "kterm", "linux", "msys", "putty",
|
||||
"rxvt", "screen", "vt100", "xterm" };
|
||||
static const bool result = [] {
|
||||
const char *Terms[] = {"ansi", "color", "console", "cygwin", "gnome",
|
||||
"konsole", "kterm", "linux", "msys", "putty",
|
||||
"rxvt", "screen", "vt100", "xterm"};
|
||||
|
||||
const char *env_p = std::getenv("TERM");
|
||||
if (env_p == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return std::any_of(std::begin(Terms), std::end(Terms),
|
||||
[&](const char *term) {
|
||||
return std::strstr(env_p, term) != nullptr;
|
||||
});
|
||||
}();
|
||||
const char *env_p = std::getenv("TERM");
|
||||
if (env_p == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return std::any_of(
|
||||
std::begin(Terms), std::end(Terms),
|
||||
[&](const char *term) { return std::strstr(env_p, term) != nullptr; });
|
||||
}();
|
||||
|
||||
#elif defined(OS_WIN)
|
||||
// All windows versions support colors through native console methods
|
||||
static constexpr bool result = true;
|
||||
// All windows versions support colors through native console methods
|
||||
static constexpr bool result = true;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef OS_WIN
|
||||
|
||||
inline bool isMsysPty(int fd) noexcept {
|
||||
// Dynamic load for binary compability with old Windows
|
||||
const auto ptrGetFileInformationByHandleEx =
|
||||
reinterpret_cast<decltype(&GetFileInformationByHandleEx)>(
|
||||
GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
|
||||
"GetFileInformationByHandleEx"));
|
||||
if (!ptrGetFileInformationByHandleEx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool isMsysPty(int fd) noexcept
|
||||
{
|
||||
// Dynamic load for binary compability with old Windows
|
||||
const auto ptrGetFileInformationByHandleEx
|
||||
= reinterpret_cast<decltype(&GetFileInformationByHandleEx)>(
|
||||
GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
|
||||
"GetFileInformationByHandleEx"));
|
||||
if (!ptrGetFileInformationByHandleEx) {
|
||||
return false;
|
||||
}
|
||||
HANDLE h = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HANDLE h = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
// Check that it's a pipe:
|
||||
if (GetFileType(h) != FILE_TYPE_PIPE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that it's a pipe:
|
||||
if (GetFileType(h) != FILE_TYPE_PIPE) {
|
||||
return false;
|
||||
}
|
||||
// POD type is binary compatible with FILE_NAME_INFO from WinBase.h
|
||||
// It have the same alignment and used to avoid UB in caller code
|
||||
struct MY_FILE_NAME_INFO {
|
||||
DWORD FileNameLength;
|
||||
WCHAR FileName[MAX_PATH];
|
||||
};
|
||||
|
||||
// POD type is binary compatible with FILE_NAME_INFO from WinBase.h
|
||||
// It have the same alignment and used to avoid UB in caller code
|
||||
struct MY_FILE_NAME_INFO {
|
||||
DWORD FileNameLength;
|
||||
WCHAR FileName[MAX_PATH];
|
||||
};
|
||||
auto pNameInfo = std::unique_ptr<MY_FILE_NAME_INFO>(new (std::nothrow)
|
||||
MY_FILE_NAME_INFO());
|
||||
if (!pNameInfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto pNameInfo = std::unique_ptr<MY_FILE_NAME_INFO>(
|
||||
new (std::nothrow) MY_FILE_NAME_INFO());
|
||||
if (!pNameInfo) {
|
||||
return false;
|
||||
}
|
||||
// Check pipe name is template of
|
||||
// {"cygwin-","msys-"}XXXXXXXXXXXXXXX-ptyX-XX
|
||||
if (!ptrGetFileInformationByHandleEx(h, FileNameInfo, pNameInfo.get(),
|
||||
sizeof(MY_FILE_NAME_INFO))) {
|
||||
return false;
|
||||
}
|
||||
std::wstring name(pNameInfo->FileName,
|
||||
pNameInfo->FileNameLength / sizeof(WCHAR));
|
||||
if ((name.find(L"msys-") == std::wstring::npos &&
|
||||
name.find(L"cygwin-") == std::wstring::npos) ||
|
||||
name.find(L"-pty") == std::wstring::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check pipe name is template of
|
||||
// {"cygwin-","msys-"}XXXXXXXXXXXXXXX-ptyX-XX
|
||||
if (!ptrGetFileInformationByHandleEx(h, FileNameInfo, pNameInfo.get(),
|
||||
sizeof(MY_FILE_NAME_INFO))) {
|
||||
return false;
|
||||
}
|
||||
std::wstring name(pNameInfo->FileName, pNameInfo->FileNameLength / sizeof(WCHAR));
|
||||
if ((name.find(L"msys-") == std::wstring::npos
|
||||
&& name.find(L"cygwin-") == std::wstring::npos)
|
||||
|| name.find(L"-pty") == std::wstring::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
inline bool isTerminal(const std::streambuf *osbuf) noexcept
|
||||
{
|
||||
using std::cerr;
|
||||
using std::clog;
|
||||
using std::cout;
|
||||
inline bool isTerminal(const std::streambuf *osbuf) noexcept {
|
||||
using std::cerr;
|
||||
using std::clog;
|
||||
using std::cout;
|
||||
#if defined(OS_LINUX) || defined(OS_MAC)
|
||||
if (osbuf == cout.rdbuf()) {
|
||||
static const bool cout_term = isatty(fileno(stdout)) != 0;
|
||||
return cout_term;
|
||||
} else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) {
|
||||
static const bool cerr_term = isatty(fileno(stderr)) != 0;
|
||||
return cerr_term;
|
||||
}
|
||||
if (osbuf == cout.rdbuf()) {
|
||||
static const bool cout_term = isatty(fileno(stdout)) != 0;
|
||||
return cout_term;
|
||||
} else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) {
|
||||
static const bool cerr_term = isatty(fileno(stderr)) != 0;
|
||||
return cerr_term;
|
||||
}
|
||||
#elif defined(OS_WIN)
|
||||
if (osbuf == cout.rdbuf()) {
|
||||
static const bool cout_term
|
||||
= (_isatty(_fileno(stdout)) || isMsysPty(_fileno(stdout)));
|
||||
return cout_term;
|
||||
} else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) {
|
||||
static const bool cerr_term
|
||||
= (_isatty(_fileno(stderr)) || isMsysPty(_fileno(stderr)));
|
||||
return cerr_term;
|
||||
}
|
||||
if (osbuf == cout.rdbuf()) {
|
||||
static const bool cout_term =
|
||||
(_isatty(_fileno(stdout)) || isMsysPty(_fileno(stdout)));
|
||||
return cout_term;
|
||||
} else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) {
|
||||
static const bool cerr_term =
|
||||
(_isatty(_fileno(stderr)) || isMsysPty(_fileno(stderr)));
|
||||
return cerr_term;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using enableStd = typename std::enable_if<
|
||||
std::is_same<T, rang::style>::value || std::is_same<T, rang::fg>::value
|
||||
|| std::is_same<T, rang::bg>::value || std::is_same<T, rang::fgB>::value
|
||||
|| std::is_same<T, rang::bgB>::value,
|
||||
std::ostream &>::type;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using enableStd = typename std::enable_if<
|
||||
std::is_same<T, rang::style>::value || std::is_same<T, rang::fg>::value ||
|
||||
std::is_same<T, rang::bg>::value || std::is_same<T, rang::fgB>::value ||
|
||||
std::is_same<T, rang::bgB>::value,
|
||||
std::ostream &>::type;
|
||||
|
||||
#ifdef OS_WIN
|
||||
|
||||
struct SGR { // Select Graphic Rendition parameters for Windows console
|
||||
BYTE fgColor; // foreground color (0-15) lower 3 rgb bits + intense bit
|
||||
BYTE bgColor; // background color (0-15) lower 3 rgb bits + intense bit
|
||||
BYTE bold; // emulated as FOREGROUND_INTENSITY bit
|
||||
BYTE underline; // emulated as BACKGROUND_INTENSITY bit
|
||||
BOOLEAN inverse; // swap foreground/bold & background/underline
|
||||
BOOLEAN conceal; // set foreground/bold to background/underline
|
||||
};
|
||||
struct SGR { // Select Graphic Rendition parameters for Windows console
|
||||
BYTE fgColor; // foreground color (0-15) lower 3 rgb bits + intense bit
|
||||
BYTE bgColor; // background color (0-15) lower 3 rgb bits + intense bit
|
||||
BYTE bold; // emulated as FOREGROUND_INTENSITY bit
|
||||
BYTE underline; // emulated as BACKGROUND_INTENSITY bit
|
||||
BOOLEAN inverse; // swap foreground/bold & background/underline
|
||||
BOOLEAN conceal; // set foreground/bold to background/underline
|
||||
};
|
||||
|
||||
enum class AttrColor : BYTE { // Color attributes for console screen buffer
|
||||
black = 0,
|
||||
red = 4,
|
||||
green = 2,
|
||||
yellow = 6,
|
||||
blue = 1,
|
||||
magenta = 5,
|
||||
cyan = 3,
|
||||
gray = 7
|
||||
};
|
||||
enum class AttrColor : BYTE { // Color attributes for console screen buffer
|
||||
black = 0,
|
||||
red = 4,
|
||||
green = 2,
|
||||
yellow = 6,
|
||||
blue = 1,
|
||||
magenta = 5,
|
||||
cyan = 3,
|
||||
gray = 7
|
||||
};
|
||||
|
||||
inline HANDLE getConsoleHandle(const std::streambuf *osbuf) noexcept
|
||||
{
|
||||
if (osbuf == std::cout.rdbuf()) {
|
||||
static const HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
return hStdout;
|
||||
} else if (osbuf == std::cerr.rdbuf() || osbuf == std::clog.rdbuf()) {
|
||||
static const HANDLE hStderr = GetStdHandle(STD_ERROR_HANDLE);
|
||||
return hStderr;
|
||||
}
|
||||
return INVALID_HANDLE_VALUE;
|
||||
inline HANDLE getConsoleHandle(const std::streambuf *osbuf) noexcept {
|
||||
if (osbuf == std::cout.rdbuf()) {
|
||||
static const HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
return hStdout;
|
||||
} else if (osbuf == std::cerr.rdbuf() || osbuf == std::clog.rdbuf()) {
|
||||
static const HANDLE hStderr = GetStdHandle(STD_ERROR_HANDLE);
|
||||
return hStderr;
|
||||
}
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
inline bool setWinTermAnsiColors(const std::streambuf *osbuf) noexcept {
|
||||
HANDLE h = getConsoleHandle(osbuf);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
DWORD dwMode = 0;
|
||||
if (!GetConsoleMode(h, &dwMode)) {
|
||||
return false;
|
||||
}
|
||||
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
if (!SetConsoleMode(h, dwMode)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool supportsAnsi(const std::streambuf *osbuf) noexcept {
|
||||
using std::cerr;
|
||||
using std::clog;
|
||||
using std::cout;
|
||||
if (osbuf == cout.rdbuf()) {
|
||||
static const bool cout_ansi =
|
||||
(isMsysPty(_fileno(stdout)) || setWinTermAnsiColors(osbuf));
|
||||
return cout_ansi;
|
||||
} else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) {
|
||||
static const bool cerr_ansi =
|
||||
(isMsysPty(_fileno(stderr)) || setWinTermAnsiColors(osbuf));
|
||||
return cerr_ansi;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline const SGR &defaultState() noexcept {
|
||||
static const SGR defaultSgr = []() -> SGR {
|
||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||
WORD attrib = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info) ||
|
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &info)) {
|
||||
attrib = info.wAttributes;
|
||||
}
|
||||
SGR sgr = {0, 0, 0, 0, FALSE, FALSE};
|
||||
sgr.fgColor = attrib & 0x0F;
|
||||
sgr.bgColor = (attrib & 0xF0) >> 4;
|
||||
return sgr;
|
||||
}();
|
||||
return defaultSgr;
|
||||
}
|
||||
|
||||
inline bool setWinTermAnsiColors(const std::streambuf *osbuf) noexcept
|
||||
{
|
||||
HANDLE h = getConsoleHandle(osbuf);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
DWORD dwMode = 0;
|
||||
if (!GetConsoleMode(h, &dwMode)) {
|
||||
return false;
|
||||
}
|
||||
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
if (!SetConsoleMode(h, dwMode)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
inline BYTE ansi2attr(BYTE rgb) noexcept {
|
||||
static const AttrColor rev[8] = {
|
||||
AttrColor::black, AttrColor::red, AttrColor::green, AttrColor::yellow,
|
||||
AttrColor::blue, AttrColor::magenta, AttrColor::cyan, AttrColor::gray};
|
||||
return static_cast<BYTE>(rev[rgb]);
|
||||
}
|
||||
|
||||
inline bool supportsAnsi(const std::streambuf *osbuf) noexcept
|
||||
{
|
||||
using std::cerr;
|
||||
using std::clog;
|
||||
using std::cout;
|
||||
if (osbuf == cout.rdbuf()) {
|
||||
static const bool cout_ansi
|
||||
= (isMsysPty(_fileno(stdout)) || setWinTermAnsiColors(osbuf));
|
||||
return cout_ansi;
|
||||
} else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) {
|
||||
static const bool cerr_ansi
|
||||
= (isMsysPty(_fileno(stderr)) || setWinTermAnsiColors(osbuf));
|
||||
return cerr_ansi;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline void setWinSGR(rang::bg col, SGR &state) noexcept {
|
||||
if (col != rang::bg::reset) {
|
||||
state.bgColor = ansi2attr(static_cast<BYTE>(col) - 40);
|
||||
} else {
|
||||
state.bgColor = defaultState().bgColor;
|
||||
}
|
||||
}
|
||||
|
||||
inline const SGR &defaultState() noexcept
|
||||
{
|
||||
static const SGR defaultSgr = []() -> SGR {
|
||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||
WORD attrib = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),
|
||||
&info)
|
||||
|| GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE),
|
||||
&info)) {
|
||||
attrib = info.wAttributes;
|
||||
}
|
||||
SGR sgr = { 0, 0, 0, 0, FALSE, FALSE };
|
||||
sgr.fgColor = attrib & 0x0F;
|
||||
sgr.bgColor = (attrib & 0xF0) >> 4;
|
||||
return sgr;
|
||||
}();
|
||||
return defaultSgr;
|
||||
}
|
||||
inline void setWinSGR(rang::fg col, SGR &state) noexcept {
|
||||
if (col != rang::fg::reset) {
|
||||
state.fgColor = ansi2attr(static_cast<BYTE>(col) - 30);
|
||||
} else {
|
||||
state.fgColor = defaultState().fgColor;
|
||||
}
|
||||
}
|
||||
|
||||
inline BYTE ansi2attr(BYTE rgb) noexcept
|
||||
{
|
||||
static const AttrColor rev[8]
|
||||
= { AttrColor::black, AttrColor::red, AttrColor::green,
|
||||
AttrColor::yellow, AttrColor::blue, AttrColor::magenta,
|
||||
AttrColor::cyan, AttrColor::gray };
|
||||
return static_cast<BYTE>(rev[rgb]);
|
||||
}
|
||||
inline void setWinSGR(rang::bgB col, SGR &state) noexcept {
|
||||
state.bgColor =
|
||||
(BACKGROUND_INTENSITY >> 4) | ansi2attr(static_cast<BYTE>(col) - 100);
|
||||
}
|
||||
|
||||
inline void setWinSGR(rang::bg col, SGR &state) noexcept
|
||||
{
|
||||
if (col != rang::bg::reset) {
|
||||
state.bgColor = ansi2attr(static_cast<BYTE>(col) - 40);
|
||||
} else {
|
||||
state.bgColor = defaultState().bgColor;
|
||||
}
|
||||
}
|
||||
inline void setWinSGR(rang::fgB col, SGR &state) noexcept {
|
||||
state.fgColor = FOREGROUND_INTENSITY | ansi2attr(static_cast<BYTE>(col) - 90);
|
||||
}
|
||||
|
||||
inline void setWinSGR(rang::fg col, SGR &state) noexcept
|
||||
{
|
||||
if (col != rang::fg::reset) {
|
||||
state.fgColor = ansi2attr(static_cast<BYTE>(col) - 30);
|
||||
} else {
|
||||
state.fgColor = defaultState().fgColor;
|
||||
}
|
||||
}
|
||||
inline void setWinSGR(rang::style style, SGR &state) noexcept {
|
||||
switch (style) {
|
||||
case rang::style::reset:
|
||||
state = defaultState();
|
||||
break;
|
||||
case rang::style::bold:
|
||||
state.bold = FOREGROUND_INTENSITY;
|
||||
break;
|
||||
case rang::style::underline:
|
||||
case rang::style::blink:
|
||||
state.underline = BACKGROUND_INTENSITY;
|
||||
break;
|
||||
case rang::style::reversed:
|
||||
state.inverse = TRUE;
|
||||
break;
|
||||
case rang::style::conceal:
|
||||
state.conceal = TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline void setWinSGR(rang::bgB col, SGR &state) noexcept
|
||||
{
|
||||
state.bgColor = (BACKGROUND_INTENSITY >> 4)
|
||||
| ansi2attr(static_cast<BYTE>(col) - 100);
|
||||
}
|
||||
inline SGR ¤t_state() noexcept {
|
||||
static SGR state = defaultState();
|
||||
return state;
|
||||
}
|
||||
|
||||
inline void setWinSGR(rang::fgB col, SGR &state) noexcept
|
||||
{
|
||||
state.fgColor
|
||||
= FOREGROUND_INTENSITY | ansi2attr(static_cast<BYTE>(col) - 90);
|
||||
inline WORD SGR2Attr(const SGR &state) noexcept {
|
||||
WORD attrib = 0;
|
||||
if (state.conceal) {
|
||||
if (state.inverse) {
|
||||
attrib = (state.fgColor << 4) | state.fgColor;
|
||||
if (state.bold)
|
||||
attrib |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
|
||||
} else {
|
||||
attrib = (state.bgColor << 4) | state.bgColor;
|
||||
if (state.underline)
|
||||
attrib |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
|
||||
}
|
||||
} else if (state.inverse) {
|
||||
attrib = (state.fgColor << 4) | state.bgColor;
|
||||
if (state.bold)
|
||||
attrib |= BACKGROUND_INTENSITY;
|
||||
if (state.underline)
|
||||
attrib |= FOREGROUND_INTENSITY;
|
||||
} else {
|
||||
attrib =
|
||||
state.fgColor | (state.bgColor << 4) | state.bold | state.underline;
|
||||
}
|
||||
return attrib;
|
||||
}
|
||||
|
||||
inline void setWinSGR(rang::style style, SGR &state) noexcept
|
||||
{
|
||||
switch (style) {
|
||||
case rang::style::reset: state = defaultState(); break;
|
||||
case rang::style::bold: state.bold = FOREGROUND_INTENSITY; break;
|
||||
case rang::style::underline:
|
||||
case rang::style::blink:
|
||||
state.underline = BACKGROUND_INTENSITY;
|
||||
break;
|
||||
case rang::style::reversed: state.inverse = TRUE; break;
|
||||
case rang::style::conceal: state.conceal = TRUE; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
inline void setWinColorAnsi(std::ostream &os, T const value) {
|
||||
os << "\033[" << static_cast<int>(value) << "m";
|
||||
}
|
||||
|
||||
inline SGR ¤t_state() noexcept
|
||||
{
|
||||
static SGR state = defaultState();
|
||||
return state;
|
||||
}
|
||||
template <typename T>
|
||||
inline void setWinColorNative(std::ostream &os, T const value) {
|
||||
const HANDLE h = getConsoleHandle(os.rdbuf());
|
||||
if (h != INVALID_HANDLE_VALUE) {
|
||||
setWinSGR(value, current_state());
|
||||
// Out all buffered text to console with previous settings:
|
||||
os.flush();
|
||||
SetConsoleTextAttribute(h, SGR2Attr(current_state()));
|
||||
}
|
||||
}
|
||||
|
||||
inline WORD SGR2Attr(const SGR &state) noexcept
|
||||
{
|
||||
WORD attrib = 0;
|
||||
if (state.conceal) {
|
||||
if (state.inverse) {
|
||||
attrib = (state.fgColor << 4) | state.fgColor;
|
||||
if (state.bold)
|
||||
attrib |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
|
||||
} else {
|
||||
attrib = (state.bgColor << 4) | state.bgColor;
|
||||
if (state.underline)
|
||||
attrib |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
|
||||
}
|
||||
} else if (state.inverse) {
|
||||
attrib = (state.fgColor << 4) | state.bgColor;
|
||||
if (state.bold) attrib |= BACKGROUND_INTENSITY;
|
||||
if (state.underline) attrib |= FOREGROUND_INTENSITY;
|
||||
} else {
|
||||
attrib = state.fgColor | (state.bgColor << 4) | state.bold
|
||||
| state.underline;
|
||||
}
|
||||
return attrib;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void setWinColorAnsi(std::ostream &os, T const value)
|
||||
{
|
||||
os << "\033[" << static_cast<int>(value) << "m";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void setWinColorNative(std::ostream &os, T const value)
|
||||
{
|
||||
const HANDLE h = getConsoleHandle(os.rdbuf());
|
||||
if (h != INVALID_HANDLE_VALUE) {
|
||||
setWinSGR(value, current_state());
|
||||
// Out all buffered text to console with previous settings:
|
||||
os.flush();
|
||||
SetConsoleTextAttribute(h, SGR2Attr(current_state()));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline enableStd<T> setColor(std::ostream &os, T const value)
|
||||
{
|
||||
if (winTermMode() == winTerm::Auto) {
|
||||
if (supportsAnsi(os.rdbuf())) {
|
||||
setWinColorAnsi(os, value);
|
||||
} else {
|
||||
setWinColorNative(os, value);
|
||||
}
|
||||
} else if (winTermMode() == winTerm::Ansi) {
|
||||
setWinColorAnsi(os, value);
|
||||
} else {
|
||||
setWinColorNative(os, value);
|
||||
}
|
||||
return os;
|
||||
template <typename T>
|
||||
inline enableStd<T> setColor(std::ostream &os, T const value) {
|
||||
if (winTermMode() == winTerm::Auto) {
|
||||
if (supportsAnsi(os.rdbuf())) {
|
||||
setWinColorAnsi(os, value);
|
||||
} else {
|
||||
setWinColorNative(os, value);
|
||||
}
|
||||
} else if (winTermMode() == winTerm::Ansi) {
|
||||
setWinColorAnsi(os, value);
|
||||
} else {
|
||||
setWinColorNative(os, value);
|
||||
}
|
||||
return os;
|
||||
}
|
||||
#else
|
||||
template <typename T>
|
||||
inline enableStd<T> setColor(std::ostream &os, T const value)
|
||||
{
|
||||
return os << "\033[" << static_cast<int>(value) << "m";
|
||||
}
|
||||
template <typename T>
|
||||
inline enableStd<T> setColor(std::ostream &os, T const value) {
|
||||
return os << "\033[" << static_cast<int>(value) << "m";
|
||||
}
|
||||
#endif
|
||||
} // namespace rang_implementation
|
||||
} // namespace rang_implementation
|
||||
|
||||
template <typename T>
|
||||
inline rang_implementation::enableStd<T> operator<<(std::ostream &os,
|
||||
const T value)
|
||||
{
|
||||
const control option = rang_implementation::controlMode();
|
||||
switch (option) {
|
||||
case control::Auto:
|
||||
return rang_implementation::supportsColor()
|
||||
&& rang_implementation::isTerminal(os.rdbuf())
|
||||
? rang_implementation::setColor(os, value)
|
||||
: os;
|
||||
case control::Force: return rang_implementation::setColor(os, value);
|
||||
default: return os;
|
||||
}
|
||||
const T value) {
|
||||
const control option = rang_implementation::controlMode();
|
||||
switch (option) {
|
||||
case control::Auto:
|
||||
return rang_implementation::supportsColor() &&
|
||||
rang_implementation::isTerminal(os.rdbuf())
|
||||
? rang_implementation::setColor(os, value)
|
||||
: os;
|
||||
case control::Force:
|
||||
return rang_implementation::setColor(os, value);
|
||||
default:
|
||||
return os;
|
||||
}
|
||||
}
|
||||
|
||||
inline void setWinTermMode(const rang::winTerm value) noexcept
|
||||
{
|
||||
rang_implementation::winTermMode() = value;
|
||||
inline void setWinTermMode(const rang::winTerm value) noexcept {
|
||||
rang_implementation::winTermMode() = value;
|
||||
}
|
||||
|
||||
inline void setControlMode(const control value) noexcept
|
||||
{
|
||||
rang_implementation::controlMode() = value;
|
||||
inline void setControlMode(const control value) noexcept {
|
||||
rang_implementation::controlMode() = value;
|
||||
}
|
||||
|
||||
} // namespace rang
|
||||
} // namespace rang
|
||||
|
||||
#undef OS_LINUX
|
||||
#undef OS_WIN
|
||||
|
|
|
@ -90,7 +90,7 @@ public:
|
|||
json_t *load(const std::string &u, bool resolveIncludes = true,
|
||||
bool resolveEnvVars = true);
|
||||
|
||||
std::string const & getConfigPath() const { return configPath; }
|
||||
std::string const &getConfigPath() const { return configPath; }
|
||||
};
|
||||
|
||||
} // namespace node
|
||||
|
|
Loading…
Add table
Reference in a new issue