From 2f0a10c49b9386a8021124fa3f6219e63071c675 Mon Sep 17 00:00:00 2001
From: Niklas Eiling <niklas.eiling@eonerc.rwth-aachen.de>
Date: Thu, 14 Mar 2024 11:49:51 +0100
Subject: [PATCH] fpga: enable using Xilinx xdma IP as DMA to AXI bridge as
 required for Ultrascale+ FPGAs

Signed-off-by: Niklas Eiling <niklas.eiling@eonerc.rwth-aachen.de>
---
 fpga/include/villas/fpga/ips/pcie.hpp | 23 +++++++++++++++++++++--
 fpga/lib/core.cpp                     |  1 +
 fpga/lib/ips/pcie.cpp                 |  3 ++-
 tools/hwdef-parse.py                  |  8 ++++++--
 4 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/fpga/include/villas/fpga/ips/pcie.hpp b/fpga/include/villas/fpga/ips/pcie.hpp
index 94c091825..85960bf8d 100644
--- a/fpga/include/villas/fpga/ips/pcie.hpp
+++ b/fpga/include/villas/fpga/ips/pcie.hpp
@@ -24,8 +24,8 @@ public:
 
   virtual bool init() override;
 
-private:
-  static constexpr char axiInterface[] = "M_AXI";
+protected:
+  virtual const char *getAxiInterfaceName() { return "M_AXI"; };
   static constexpr char pcieMemory[] = "BAR0";
 
   struct AxiBar {
@@ -42,6 +42,11 @@ private:
   std::map<std::string, PciBar> pcieToAxiTranslations;
 };
 
+class XDmaBridge : public AxiPciExpressBridge {
+protected:
+  virtual const char *getAxiInterfaceName() { return "M_AXI_B"; };
+};
+
 class AxiPciExpressBridgeFactory : CoreFactory {
 
 public:
@@ -63,6 +68,20 @@ protected:
   virtual void parse(Core &, json_t *) override;
 };
 
+class XDmaBridgeFactory : public AxiPciExpressBridgeFactory {
+
+public:
+  virtual std::string getDescription() const {
+    return "Xilinx's XDMA IP configured as AXI-PCIe Bridge";
+  }
+
+private:
+  virtual Vlnv getCompatibleVlnv() const { return Vlnv("xilinx.com:ip:xdma:"); }
+
+  // Create a concrete IP instance
+  Core *make() const { return new XDmaBridge; };
+};
+
 } // namespace ip
 } // namespace fpga
 } // namespace villas
diff --git a/fpga/lib/core.cpp b/fpga/lib/core.cpp
index 2af5a1752..2e3235c5d 100644
--- a/fpga/lib/core.cpp
+++ b/fpga/lib/core.cpp
@@ -31,6 +31,7 @@ using namespace villas::fpga::ip;
 // first.
 static std::list<Vlnv> vlnvInitializationOrder = {
     Vlnv("xilinx.com:ip:axi_pcie:"),
+    Vlnv("xilinx.com:ip:xdma:"),
     Vlnv("xilinx.com:module_ref:axi_pcie_intc:"),
     Vlnv("xilinx.com:ip:axis_switch:"),
     Vlnv("xilinx.com:ip:axi_iic:"),
diff --git a/fpga/lib/ips/pcie.cpp b/fpga/lib/ips/pcie.cpp
index c6b12025d..17529a41b 100644
--- a/fpga/lib/ips/pcie.cpp
+++ b/fpga/lib/ips/pcie.cpp
@@ -22,7 +22,7 @@ bool AxiPciExpressBridge::init() {
 
   // 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);
+  card->addrSpaceIdHostToDevice = busMasterInterfaces.at(getAxiInterfaceName());
 
   // Map PCIe BAR0 via VFIO
   const void *bar0_mapped =
@@ -139,3 +139,4 @@ void AxiPciExpressBridgeFactory::parse(Core &ip, json_t *cfg) {
 }
 
 static AxiPciExpressBridgeFactory p;
+static XDmaBridgeFactory p2;
diff --git a/tools/hwdef-parse.py b/tools/hwdef-parse.py
index 13981aa06..aa87c6619 100755
--- a/tools/hwdef-parse.py
+++ b/tools/hwdef-parse.py
@@ -43,6 +43,7 @@ whitelist = [
     ["xilinx.com", "ip", "axi_gpio"],
     ["xilinx.com", "ip", "axi_bram_ctrl"],
     ["xilinx.com", "ip", "axi_pcie"],
+    ["xilinx.com", "ip", "xdma"],
     ["xilinx.com", "ip", "axi_iic"],
     ["xilinx.com", "module_ref", "dinoif_fast"],
     ["xilinx.com", "module_ref", "dinoif_dac"],
@@ -322,7 +323,7 @@ for bram in brams:
     if instance in ips:
         ips[instance]["size"] = int(size)
 
-pcies = root.xpath('.//MODULE[@MODTYPE="axi_pcie"]')
+pcies = root.xpath('.//MODULE[@MODTYPE="axi_pcie" or @MODTYPE="xdma"]')
 for pcie in pcies:
     instance = pcie.get("INSTANCE")
     axi_bars = ips[instance].setdefault("axi_bars", {})
@@ -332,8 +333,11 @@ for pcie in pcies:
         ("AXIBAR", "PCIEBAR", axi_bars),
         ("PCIEBAR", "AXIBAR", pcie_bars),
     ):
+        barnum = pcie.find('.//PARAMETER[@NAME="C_{}_NUM"]'.format(from_bar))
+        if barnum == None:
+            barnum = pcie.find('.//PARAMETER[@NAME="{}_NUM"]'.format(from_bar))
         from_bar_num = int(
-            pcie.find('.//PARAMETER[@NAME="C_{}_NUM"]'.format(from_bar)).get("VALUE")
+            barnum.get("VALUE")
         )
 
         for i in range(0, from_bar_num):