2023-09-08 11:35:18 +02:00
|
|
|
/* AXI PCIe bridge
|
2018-02-13 14:13:14 +01:00
|
|
|
*
|
2023-01-07 17:20:15 +01:00
|
|
|
* Author: Daniel Krebs <github@daniel-krebs.net>
|
2023-09-08 11:35:18 +02:00
|
|
|
* SPDX-FileCopyrightText: 2018 Institute for Automation of Complex Power Systems, RWTH Aachen University
|
2023-01-07 17:20:15 +01:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2023-09-08 11:35:18 +02:00
|
|
|
*/
|
2018-02-13 14:13:14 +01:00
|
|
|
|
|
|
|
#include <jansson.h>
|
2024-02-29 19:34:27 +01:00
|
|
|
#include <limits>
|
2018-02-13 14:13:14 +01:00
|
|
|
|
2022-11-11 06:36:20 -05:00
|
|
|
#include <villas/exceptions.hpp>
|
2018-06-04 12:17:23 +02:00
|
|
|
#include <villas/memory.hpp>
|
|
|
|
|
|
|
|
#include <villas/fpga/card.hpp>
|
|
|
|
#include <villas/fpga/ips/pcie.hpp>
|
2023-01-26 18:11:25 +01:00
|
|
|
#include <villas/fpga/pcie_card.hpp>
|
2018-02-13 14:13:14 +01:00
|
|
|
|
2020-06-14 22:11:15 +02:00
|
|
|
using namespace villas::fpga::ip;
|
2018-02-13 14:13:14 +01:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
bool AxiPciExpressBridge::init() {
|
|
|
|
auto &mm = MemoryManager::get();
|
2018-03-26 15:48:57 +02:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
// Throw an exception if the is no bus master interface and thus no
|
|
|
|
// address space we can use for translation -> error
|
2024-03-14 11:49:51 +01:00
|
|
|
card->addrSpaceIdHostToDevice = busMasterInterfaces.at(getAxiInterfaceName());
|
2018-02-13 19:58:22 +01:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
// 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;
|
|
|
|
}
|
2018-05-15 17:50:48 +02:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
// Determine size of BAR0 region
|
|
|
|
const size_t bar0_size =
|
|
|
|
card->vfioDevice->regionGetSize(VFIO_PCI_BAR0_REGION_INDEX);
|
2018-05-15 17:50:48 +02:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
// 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);
|
2018-03-26 15:48:57 +02:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
// Make PCIe (IOVA) address space available to FPGA via BAR0
|
2018-03-26 15:48:57 +02:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
// IPs that can access this address space will know it via their memory view
|
|
|
|
const auto addrSpaceNameDeviceToHost =
|
|
|
|
mm.getSlaveAddrSpaceName(getInstanceName(), pcieMemory);
|
2018-03-26 15:48:57 +02:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
// 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);
|
2018-02-13 14:13:14 +01:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
auto pciAddrSpaceId = mm.getPciAddressSpace();
|
2018-05-15 17:41:40 +02:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
auto regions = dynamic_cast<PCIeCard *>(card)->pdev->getRegions();
|
2018-05-15 17:41:40 +02:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
int i = 0;
|
|
|
|
for (auto region : regions) {
|
|
|
|
const size_t region_size = region.end - region.start + 1;
|
2018-05-15 17:41:40 +02:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
char barName[] = "BARx";
|
|
|
|
barName[3] = '0' + region.num;
|
|
|
|
auto pciBar = pcieToAxiTranslations.at(barName);
|
2018-05-15 17:41:40 +02:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
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);
|
2018-05-15 17:41:40 +02:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
mm.createMapping(region.start, pciBar.translation, region_size,
|
|
|
|
std::string("PCI-") + barName, pciAddrSpaceId,
|
|
|
|
card->addrSpaceIdHostToDevice);
|
|
|
|
}
|
2018-05-15 17:41:40 +02:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
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);
|
2018-05-15 17:41:40 +02:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
auto barXAddrSpaceName =
|
|
|
|
mm.getSlaveAddrSpaceName(getInstanceName(), barName);
|
|
|
|
auto barXAddrSpaceId = mm.getOrCreateAddressSpace(barXAddrSpaceName);
|
2018-05-15 17:41:40 +02:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
// 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);
|
2020-06-15 21:21:05 +02:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
i++;
|
|
|
|
}
|
2018-05-15 17:41:40 +02:00
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
return true;
|
2018-05-15 17:41:40 +02:00
|
|
|
}
|
|
|
|
|
2024-02-29 19:34:27 +01:00
|
|
|
void AxiPciExpressBridgeFactory::parse(Core &ip, json_t *cfg) {
|
|
|
|
CoreFactory::parse(ip, cfg);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
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};
|
|
|
|
}
|
|
|
|
}
|
2018-02-13 14:13:14 +01:00
|
|
|
}
|
2022-12-02 18:46:00 +01:00
|
|
|
|
|
|
|
static AxiPciExpressBridgeFactory p;
|
2024-03-14 11:49:51 +01:00
|
|
|
static XDmaBridgeFactory p2;
|