1
0
Fork 0
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:
Steffen Vogel 2024-02-29 19:34:27 +01:00
parent 29cf5540a0
commit 3d73c759ea
71 changed files with 6062 additions and 6285 deletions

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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>

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 &registers = addressTranslations.at(registerMemory);
virtual bool init() override {
auto &registers = 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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;
};

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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;

View file

@ -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";

View file

@ -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";

View file

@ -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";

View file

@ -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) {

View file

@ -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;

View file

@ -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";

View file

@ -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 &registers = addressTranslations.at(registerMemory);
auto &registers = 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

View file

@ -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";

View file

@ -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);
}

View file

@ -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;

View file

@ -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";

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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();
}

View file

@ -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");
}

View file

@ -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");

View file

@ -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;
};

View file

@ -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");
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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"));
}
}
}

View file

@ -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"));
}
}

View file

@ -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");
}

View file

@ -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");
}

File diff suppressed because it is too large Load diff

View file

@ -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 &current_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 &current_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

View file

@ -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