From 010e0c3681729bc4beef140a50175171006081ed Mon Sep 17 00:00:00 2001
From: Daniel Krebs <github@daniel-krebs.net>
Date: Wed, 30 May 2018 13:58:26 +0200
Subject: [PATCH] hls: add base HLS IP and enable virtual multi-inheritance

Virtual inheritance is required because (for example) the Rtds2Gpu
IP inherits from Hls and IpNode who both inherit from IpCore.
---
 fpga/include/villas/fpga/ip_node.hpp      |   2 +-
 fpga/include/villas/fpga/ips/hls.hpp      | 137 ++++++++++++++++++++++
 fpga/include/villas/fpga/ips/rtds2gpu.hpp |   9 +-
 fpga/include/villas/memory.hpp            |   2 +
 fpga/lib/ip_node.cpp                      |   4 +-
 fpga/lib/ips/rtds2gpu/rtds2gpu.cpp        |  29 +----
 fpga/tests/rtds2gpu.cpp                   |  10 +-
 7 files changed, 153 insertions(+), 40 deletions(-)
 create mode 100644 fpga/include/villas/fpga/ips/hls.hpp

diff --git a/fpga/include/villas/fpga/ip_node.hpp b/fpga/include/villas/fpga/ip_node.hpp
index 964070991..1257529c2 100644
--- a/fpga/include/villas/fpga/ip_node.hpp
+++ b/fpga/include/villas/fpga/ip_node.hpp
@@ -86,7 +86,7 @@ public:
 };
 
 
-class IpNode : public IpCore {
+class IpNode : public virtual IpCore {
 public:
 
 	friend class IpNodeFactory;
diff --git a/fpga/include/villas/fpga/ips/hls.hpp b/fpga/include/villas/fpga/ips/hls.hpp
new file mode 100644
index 000000000..685af9050
--- /dev/null
+++ b/fpga/include/villas/fpga/ips/hls.hpp
@@ -0,0 +1,137 @@
+#pragma once
+
+#include <villas/memory.hpp>
+#include <villas/fpga/ip_node.hpp>
+
+namespace villas {
+namespace fpga {
+namespace ip {
+
+
+class Hls : public virtual IpCore
+{
+public:
+	virtual bool init()
+	{
+		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));
+
+		setAutoRestart(false);
+		setGlobalInterrupt(false);
+
+		return true;
+	}
+
+	bool start()
+	{
+		controlRegister->ap_start = true;
+		running = true;
+
+		return true;
+	}
+
+	virtual bool isFinished()
+	{ updateRunningStatus(); return !running; }
+
+
+	bool isRunning()
+	{ updateRunningStatus(); return running; }
+
+
+	void setAutoRestart(bool enabled) const
+	{ controlRegister->auto_restart = enabled; }
+
+
+	void setGlobalInterrupt(bool enabled) const
+	{ globalIntRegister->globalInterruptEnable = enabled; }
+
+
+	void setReadyInterrupt(bool enabled) const
+	{ ipIntEnableRegister->ap_ready = enabled; }
+
+
+	void setDoneInterrupt(bool enabled) const
+	{ ipIntEnableRegister->ap_done = enabled; }
+
+
+	bool isIdleBit() const
+	{ return controlRegister->ap_idle; }
+
+
+	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; }
+
+
+	bool isAutoRestartBit() const
+	{ return controlRegister->auto_restart; }
+
+private:
+	void updateRunningStatus()
+	{
+		if(running and isIdleBit())
+			running = false;
+	}
+
+protected:
+	/* Memory block handling */
+
+	static constexpr const char* registerMemory = "Reg";
+
+	virtual std::list<MemoryBlockName> getMemoryBlocks() const
+	{ return { registerMemory }; }
+
+
+private:
+	/* 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;
+
+	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 IpIntRegister { uint32_t
+		ap_done : 1,
+		ap_ready : 1,
+		_res : 30;
+	};
+protected:
+	ControlRegister*	controlRegister;
+	GlobalIntRegister*	globalIntRegister;
+	IpIntRegister*		ipIntEnableRegister;
+	IpIntRegister*		ipIntStatusRegister;
+
+	bool running;
+};
+
+} // namespace ip
+} // namespace fpga
+} // namespace villas
diff --git a/fpga/include/villas/fpga/ips/rtds2gpu.hpp b/fpga/include/villas/fpga/ips/rtds2gpu.hpp
index 9c5b24bb3..8a64d44a3 100644
--- a/fpga/include/villas/fpga/ips/rtds2gpu.hpp
+++ b/fpga/include/villas/fpga/ips/rtds2gpu.hpp
@@ -2,6 +2,7 @@
 
 #include <villas/memory.hpp>
 #include <villas/fpga/ip_node.hpp>
+#include <villas/fpga/ips/hls.hpp>
 
 #include "rtds2gpu/xrtds2gpu.h"
 #include "rtds2gpu/register_types.hpp"
@@ -11,23 +12,17 @@ namespace fpga {
 namespace ip {
 
 
-class Rtds2Gpu : public IpNode
+class Rtds2Gpu : public IpNode, public Hls
 {
 public:
 	friend class Rtds2GpuFactory;
 
 	bool init();
 
-	bool start();
-
 	void dump(spdlog::level::level_enum logLevel = spdlog::level::info);
 
 	bool startOnce(const MemoryBlock& mem, size_t frameSize, size_t dataOffset, size_t doorbellOffset);
 
-	bool isFinished();
-
-	bool isReady();
-
 	size_t getMaxFrameSize();
 
 	void dumpDoorbell(uint32_t doorbellRegister) const;
diff --git a/fpga/include/villas/memory.hpp b/fpga/include/villas/memory.hpp
index ce7a9190c..d06e3c1d6 100644
--- a/fpga/include/villas/memory.hpp
+++ b/fpga/include/villas/memory.hpp
@@ -62,6 +62,8 @@ public:
 	MemoryAccessor(const MemoryBlock& mem) :
 	    translation(MemoryManager::get().getTranslationFromProcess(mem.getAddrSpaceId())) {}
 
+	MemoryAccessor(const MemoryTranslation& translation) :
+	    translation(translation) {}
 
 	T& operator*() const {
 		return *reinterpret_cast<T*>(translation.getLocalAddr(0));
diff --git a/fpga/lib/ip_node.cpp b/fpga/lib/ip_node.cpp
index 9435e24a3..016bdf784 100644
--- a/fpga/lib/ip_node.cpp
+++ b/fpga/lib/ip_node.cpp
@@ -19,7 +19,7 @@ IpNode::streamGraph;
 bool
 IpNodeFactory::configureJson(IpCore& ip, json_t* json_ip)
 {
-	auto& ipNode = reinterpret_cast<IpNode&>(ip);
+	auto& ipNode = dynamic_cast<IpNode&>(ip);
 	auto logger = getLogger();
 
 	json_t* json_ports = json_object_get(json_ip, "ports");
@@ -194,7 +194,7 @@ IpNode::connectLoopback()
 	logger->debug("switch at: {}", portMaster->nodeName);
 
 	// TODO: verify this is really a switch!
-	auto axiStreamSwitch = reinterpret_cast<ip::AxiStreamSwitch*>(
+	auto axiStreamSwitch = dynamic_cast<ip::AxiStreamSwitch*>(
 	                            card->lookupIp(portMaster->nodeName));
 
 	if(axiStreamSwitch == nullptr) {
diff --git a/fpga/lib/ips/rtds2gpu/rtds2gpu.cpp b/fpga/lib/ips/rtds2gpu/rtds2gpu.cpp
index b9ce32d13..a39d45061 100644
--- a/fpga/lib/ips/rtds2gpu/rtds2gpu.cpp
+++ b/fpga/lib/ips/rtds2gpu/rtds2gpu.cpp
@@ -14,12 +14,11 @@ static Rtds2GpuFactory factory;
 
 bool Rtds2Gpu::init()
 {
+	Hls::init();
+
 	xInstance.IsReady = XIL_COMPONENT_IS_READY;
 	xInstance.Ctrl_BaseAddress = getBaseAddr(registerMemory);
 
-	// make sure IP is stopped for now
-	XRtds2gpu_DisableAutoRestart(&xInstance);
-
 	status.value = 0;
 	started = false;
 
@@ -29,13 +28,7 @@ bool Rtds2Gpu::init()
 	return true;
 }
 
-bool Rtds2Gpu::start()
-{
-	XRtds2gpu_Start(&xInstance);
-	started = true;
 
-	return true;
-}
 
 void Rtds2Gpu::dump(spdlog::level::level_enum logLevel)
 {
@@ -89,26 +82,9 @@ bool Rtds2Gpu::startOnce(const MemoryBlock& mem, size_t frameSize, size_t dataOf
 	return start();
 }
 
-bool Rtds2Gpu::isFinished()
-{
-	if(started and isReady()) {
-		started = false;
 
-		if(not updateStatus()) {
-			throw "IP is finished but status register invalid";
-		}
-	}
 
-	return !started;
-}
 
-bool
-Rtds2Gpu::isReady()
-{
-	// use the idle bit to indicate readiness, we don't care about the difference
-	// here
-	return XRtds2gpu_IsIdle(&xInstance);
-}
 
 bool
 Rtds2Gpu::updateStatus()
@@ -128,6 +104,7 @@ Rtds2Gpu::getMaxFrameSize()
 
 	start();
 	while(not isFinished());
+	updateStatus();
 
 	return status.max_frame_size;
 }
diff --git a/fpga/tests/rtds2gpu.cpp b/fpga/tests/rtds2gpu.cpp
index 59cb6a415..4c5e301c5 100644
--- a/fpga/tests/rtds2gpu.cpp
+++ b/fpga/tests/rtds2gpu.cpp
@@ -55,12 +55,14 @@ Test(fpga, rtds2gpu, .description = "Rtds2Gpu")
 
 		/* Collect neccessary IPs */
 
-		auto rtds2gpu = reinterpret_cast<villas::fpga::ip::Rtds2Gpu&>(*ip);
+		auto rtds2gpu = dynamic_cast<villas::fpga::ip::Rtds2Gpu&>(*ip);
 
-		auto axiSwitch = reinterpret_cast<villas::fpga::ip::AxiStreamSwitch*>(
-		                     state.cards.front()->lookupIp(villas::fpga::Vlnv("xilinx.com:ip:axis_switch:")));
+		auto axiSwitchPtr = state.cards.front()->lookupIp(villas::fpga::Vlnv("xilinx.com:ip:axis_switch:"));
+		auto axiSwitch = dynamic_cast<villas::fpga::ip::AxiStreamSwitch*>(axiSwitchPtr);
 
-		auto dma = reinterpret_cast<villas::fpga::ip::Dma*>(
+		cr_assert_not_null(axiSwitchPtr);
+
+		auto dma = dynamic_cast<villas::fpga::ip::Dma*>(
 		                     state.cards.front()->lookupIp(villas::fpga::Vlnv("xilinx.com:ip:axi_dma:")));
 
 		rtds2gpu.dump(spdlog::level::debug);