/* API for interacting with the FPGA DMA Controller. * * Author: Niklas Eiling * SPDX-FileCopyrightText: 2023 Niklas Eiling * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include using namespace villas; static std::shared_ptr pciDevices; static auto logger = villas::Log::get("villasfpga_dma"); struct villasfpga_handle_t { std::shared_ptr card; std::shared_ptr dma; }; struct villasfpga_memory_t { std::shared_ptr block; }; villasfpga_handle villasfpga_init(const char *configFile) { std::string fpgaName = "vc707"; std::string connectStr = "3<->pipe"; std::string outputFormat = "short"; bool dumpGraph = false; bool dumpAuroraChannels = true; try { // Logging setup Log::getInstance().setLevel(spdlog::level::debug); fpga::setupColorHandling(); if (configFile == nullptr || configFile[0] == '\0') { logger->error( "No configuration file provided/ Please use -c/--config argument"); return nullptr; } auto handle = new villasfpga_handle_t; handle->card = fpga::setupFpgaCard(configFile, fpgaName); if (dumpGraph) { auto &mm = MemoryManager::get(); mm.getGraph().dump("graph.dot"); } if (dumpAuroraChannels) { auto aurora_channels = getAuroraChannels(handle->card); for (auto aurora : *aurora_channels) aurora->dump(); } // Configure Crossbar switch const fpga::ConnectString parsedConnectString(connectStr); if (!parsedConnectString.configCrossBar(handle->card)) { logger->error("Failed to configure crossbar"); return nullptr; } return handle; } catch (const RuntimeError &e) { logger->error("Error: {}", e.what()); return nullptr; } catch (const std::exception &e) { logger->error("Error: {}", e.what()); return nullptr; } catch (...) { logger->error("Unknown error"); return nullptr; } } 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_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_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; } } void *villasfpga_get_ptr(villasfpga_memory mem) { return (void *)MemoryManager::get() .getTranslationFromProcess(mem->block->getAddrSpaceId()) .getLocalAddr(0); }