Commit 3de60605 authored by Daniel Krebs's avatar Daniel Krebs Committed by Steffen Vogel

lib/ips: make use of MemoryManager and new config layout

parent e2d1d1fe
......@@ -41,14 +41,23 @@
#include <jansson.h>
#include "memory_manager.hpp"
namespace villas {
namespace fpga {
// forward declaration
class PCIeCard;
namespace ip {
// forward declarations
class IpCore;
class IpCoreFactory;
class InterruptController;
using IpCoreList = std::list<std::unique_ptr<IpCore>>;
class IpIdentifier {
public:
......@@ -58,25 +67,30 @@ public:
IpIdentifier(std::string vlnvString, std::string name = "") :
vlnv(vlnvString), name(name) {}
const std::string&
getName() const
{ return name; }
const Vlnv&
getVlnv() const
{ return vlnv; }
friend std::ostream&
operator<< (std::ostream& stream, const IpIdentifier& id)
{ return stream << TXT_BOLD(id.name) << " vlnv=" << id.vlnv; }
private:
Vlnv vlnv;
std::string name;
};
using IpDependency = std::pair<std::string, Vlnv>;
// forward declarations
class IpCoreFactory;
class IpCore {
public:
friend IpCoreFactory;
IpCore() : card(nullptr), baseaddr(0) {}
IpCore() : card(nullptr) {}
virtual ~IpCore() {}
// IPs can implement this interface
......@@ -88,10 +102,10 @@ public:
bool
operator== (const IpIdentifier& otherId) {
const bool vlnvMatch = id.vlnv == otherId.vlnv;
const bool nameWildcard = id.name.empty() or otherId.name.empty();
const bool vlnvMatch = id.getVlnv() == otherId.getVlnv();
const bool nameWildcard = id.getName().empty() or otherId.getName().empty();
return vlnvMatch and (nameWildcard or id.name == otherId.name);
return vlnvMatch and (nameWildcard or id.getName() == otherId.getName());
}
bool
......@@ -101,45 +115,55 @@ public:
bool
operator== (const Vlnv& otherVlnv)
{ return id.vlnv == otherVlnv; }
{ return id.getVlnv() == otherVlnv; }
bool
operator== (const std::string& otherName)
{ return id.name == otherName; }
{ return id.getName() == otherName; }
friend std::ostream&
operator<< (std::ostream& stream, const IpCore& ip)
{ return stream << ip.id; }
const std::string&
getInstanceName()
{ return id.getName(); }
protected:
uintptr_t
getBaseaddr() const
{ return getAddrMapped(this->baseaddr); }
getBaseAddr(const std::string& block) const;
uintptr_t
getAddrMapped(uintptr_t address) const;
getLocalAddr(const std::string& block, uintptr_t address) const;
SpdLogger
getLogger() { return loggerGetOrCreate(id.name); }
getLogger() { return loggerGetOrCreate(id.getName()); }
struct IrqPort {
int num;
std::string controllerName;
InterruptController* irqController;
std::string description;
};
InterruptController*
getInterruptController(const std::string& interruptName);
protected:
virtual std::list<std::string> getMemoryBlocks() const { return {}; }
// populated by FpgaIpFactory
PCIeCard* card; ///< FPGA card this IP is instantiated on
IpIdentifier id; ///< VLNV and name defined in JSON config
uintptr_t baseaddr; ///< The baseadress of this IP component
std::map<std::string, IrqPort> irqs; ///< Interrupts of this IP component
std::map<std::string, IpCore*> dependencies; ///< dependencies on other IPs
/// Cached translations from the process address space to each memory block
std::map<std::string, MemoryTranslation> addressTranslations;
};
using IpCoreList = std::list<std::unique_ptr<IpCore>>;
class IpCoreFactory : public Plugin {
......@@ -164,11 +188,9 @@ private:
virtual bool configureJson(IpCore& /* ip */, json_t* /* json */)
{ return true; }
virtual Vlnv getCompatibleVlnv() const = 0;
virtual std::string getName() const = 0;
virtual std::string getDescription() const = 0;
virtual std::list<IpDependency> getDependencies() const { return {}; }
protected:
static SpdLogger
......
......@@ -50,8 +50,14 @@ public:
size_t read(void* buf, size_t len);
private:
static constexpr char registerMemory[] = "Mem0";
static constexpr char axi4Memory[] = "Mem1";
static constexpr char irqName[] = "interrupt";
std::list<std::string> getMemoryBlocks() const
{ return { registerMemory, axi4Memory }; }
XLlFifo xFifo;
uintptr_t baseaddr_axi4;
};
......@@ -77,9 +83,6 @@ public:
Vlnv getCompatibleVlnv() const
{ return {"xilinx.com:ip:axi_fifo_mm_s:"}; }
std::list<IpDependency> getDependencies() const
{ return { {"intc", Vlnv("acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:") } }; }
};
} // namespace ip
......
......@@ -61,6 +61,13 @@ public:
{ return waitForInterrupt(irq.num); }
private:
static constexpr char registerMemory[] = "Reg";
std::list<std::string> getMemoryBlocks() const
{ return { registerMemory }; }
struct Interrupt {
int eventFd; /**< Event file descriptor */
int number; /**< Interrupt number from /proc/interrupts */
......@@ -82,6 +89,10 @@ public:
IpCoreFactory(getName())
{}
static constexpr const char*
getCompatibleVlnvString()
{ return "acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:"; }
IpCore* create()
{ return new InterruptController; }
......@@ -94,7 +105,10 @@ public:
{ return "Xilinx's programmable interrupt controller"; }
Vlnv getCompatibleVlnv() const
{ return Vlnv("acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:"); }
{ return Vlnv(getCompatibleVlnvString()); }
// std::list<IpDependency> getDependencies() const
// { return { {"pcie", Vlnv("xilinx.com:ip:axi_pcie:") } }; }
};
} // namespace ip
......
/** AXI Stream interconnect related helper functions
*
* These functions present a simpler interface to Xilinx' AXI Stream switch driver (XAxis_Switch_*)
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @author Daniel Krebs <github@daniel-krebs.net>
* @copyright 2017, Steffen Vogel
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
/** @addtogroup fpga VILLASfpga
* @{
*/
#pragma once
#include <string>
#include <map>
#include <jansson.h>
#include <xilinx/xaxis_switch.h>
#include "fpga/ip_node.hpp"
#include "fpga/vlnv.hpp"
namespace villas {
namespace fpga {
namespace ip {
class AxiPciExpressBridge : public IpCore {
public:
friend class AxiPciExpressBridgeFactory;
bool init();
};
class AxiPciExpressBridgeFactory : public IpCoreFactory {
public:
AxiPciExpressBridgeFactory() :
IpCoreFactory(getName()) {}
static constexpr const char*
getCompatibleVlnvString()
{ return "xilinx.com:ip:axi_pcie:"; }
IpCore* create()
{ return new AxiPciExpressBridge; }
std::string getName() const
{ return "AxiPciExpressBridge"; }
std::string getDescription() const
{ return "Xilinx's AXI-PCIe Bridge"; }
Vlnv getCompatibleVlnv() const
{ return Vlnv(getCompatibleVlnvString()); }
};
} // namespace ip
} // namespace fpga
} // namespace villas
/** @} */
......@@ -56,6 +56,11 @@ public:
private:
static constexpr int PORT_DISABLED = -1;
static constexpr char registerMemory[] = "Reg";
std::list<std::string> getMemoryBlocks() const
{ return { registerMemory }; }
struct Path {
IpCore* masterOut;
IpCore* slaveIn;
......@@ -72,6 +77,10 @@ public:
AxiStreamSwitchFactory() :
IpNodeFactory(getName()) {}
static constexpr const char*
getCompatibleVlnvString()
{ return "xilinx.com:ip:axis_switch:"; }
bool configureJson(IpCore& ip, json_t *json_ip);
IpCore* create()
......@@ -84,7 +93,7 @@ public:
{ return "Xilinx's AXI4-Stream switch"; }
Vlnv getCompatibleVlnv() const
{ return Vlnv("xilinx.com:ip:axis_switch:"); }
{ return Vlnv(getCompatibleVlnvString()); }
};
} // namespace ip
......
......@@ -43,10 +43,10 @@ namespace ip {
class Timer : public IpCore
{
friend class TimerFactory;
public:
bool init();
bool start(uint32_t ticks);
bool wait();
uint32_t remaining();
......@@ -62,8 +62,14 @@ public:
{ return FPGA_AXI_HZ; }
private:
std::list<std::string> getMemoryBlocks() const
{ return { registerMemory }; }
static constexpr char irqName[] = "generateout0";
static constexpr char registerMemory[] = "Reg";
XTmrCtr xTmr;
InterruptController* intc;
};
......@@ -88,9 +94,6 @@ public:
Vlnv getCompatibleVlnv() const
{ return {"xilinx.com:ip:axi_timer:"}; }
std::list<IpDependency> getDependencies() const
{ return { {"intc", Vlnv("acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:") } }; }
};
} // namespace ip
......
set(SOURCES
ip.cpp
ip.c
ip_node.cpp
vlnv.cpp
vlnv.c
card.c
vlnv.cpp
card.cpp
ip.cpp
ip_node.cpp
ips/timer.c
ips/timer.cpp
ips/model.c
ips/switch.c
ips/switch.cpp
ips/dft.c
ips/fifo.c
ips/fifo.cpp
ips/dma.c
ips/intc.cpp
ips/intc.c
ips/gpio.c
ips/rtds_axis.c
ips/timer.cpp
ips/switch.cpp
ips/fifo.cpp
ips/intc.cpp
ips/pcie.cpp
kernel/kernel.c
kernel/pci.c
kernel/vfio.c
memory_manager.cpp
plugin.c
plugin.cpp
utils.c
utils.cpp
list.c
log.c
log_config.c
log_helper.c
plugin.cpp
utils.cpp
memory_manager.cpp
)
include(FindPkgConfig)
......
This diff is collapsed.
......@@ -50,33 +50,32 @@ FifoFactory::configureJson(IpCore &ip, json_t *json_ip)
return false;
}
auto& fifo = reinterpret_cast<Fifo&>(ip);
if(json_unpack(json_ip, "{ s: i }", "axi4_baseaddr", &fifo.baseaddr_axi4) != 0) {
logger->warn("Cannot parse property 'axi4_baseaddr'");
return false;
}
return true;
}
bool Fifo::init()
{
auto logger = getLogger();
XLlFifo_Config fifo_cfg;
fifo_cfg.Axi4BaseAddress = getAddrMapped(this->baseaddr_axi4);
fifo_cfg.Axi4BaseAddress = getBaseAddr(axi4Memory);
// use AXI4 for Data, AXI4-Lite for control
fifo_cfg.Datainterface = (this->baseaddr_axi4 != static_cast<size_t>(-1)) ? 1 : 0;
fifo_cfg.Datainterface = (fifo_cfg.Axi4BaseAddress != -1) ? 1 : 0;
if (XLlFifo_CfgInitialize(&xFifo, &fifo_cfg, getBaseaddr()) != XST_SUCCESS)
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;
}
// Receive complete IRQ
XLlFifo_IntEnable(&xFifo, XLLF_INT_RC_MASK);
auto intc = reinterpret_cast<InterruptController*>(dependencies["intc"]);
intc->enableInterrupt(irqs["interrupt"], false);
irqs[irqName].irqController->enableInterrupt(irqs[irqName], false);
return true;
}
......@@ -85,6 +84,7 @@ bool Fifo::stop()
{
// Receive complete IRQ
XLlFifo_IntDisable(&xFifo, XLLF_INT_RC_MASK);
irqs[irqName].irqController->disableInterrupt(irqs[irqName]);
return true;
}
......@@ -110,10 +110,8 @@ size_t Fifo::read(void *buf, size_t len)
size_t nextlen = 0;
size_t rxlen;
auto intc = reinterpret_cast<InterruptController*>(dependencies["intc"]);
while (!XLlFifo_IsRxDone(&xFifo))
intc->waitForInterrupt(irqs["interrupt"].num);
irqs[irqName].irqController->waitForInterrupt(irqs[irqName]);
XLlFifo_IntClear(&xFifo, XLLF_INT_RC_MASK);
......
......@@ -48,7 +48,7 @@ InterruptController::~InterruptController()
bool
InterruptController::init()
{
const uintptr_t base = getBaseaddr();
const uintptr_t base = getBaseAddr(registerMemory);
auto logger = getLogger();
num_irqs = vfio_pci_msi_init(&card->vfio_device, efds);
......@@ -86,7 +86,7 @@ bool
InterruptController::enableInterrupt(InterruptController::IrqMaskType mask, bool polling)
{
auto logger = getLogger();
const uintptr_t base = getBaseaddr();
const uintptr_t base = getBaseAddr(registerMemory);
/* Current state of INTC */
const uint32_t ier = XIntc_In32(base + XIN_IER_OFFSET);
......@@ -120,7 +120,7 @@ InterruptController::enableInterrupt(InterruptController::IrqMaskType mask, bool
bool
InterruptController::disableInterrupt(InterruptController::IrqMaskType mask)
{
const uintptr_t base = getBaseaddr();
const uintptr_t base = getBaseAddr(registerMemory);
uint32_t ier = XIntc_In32(base + XIN_IER_OFFSET);
XIntc_Out32(base + XIN_IER_OFFSET, ier & ~mask);
......@@ -133,7 +133,7 @@ InterruptController::waitForInterrupt(int irq)
{
assert(irq < maxIrqs);
const uintptr_t base = getBaseaddr();
const uintptr_t base = getBaseAddr(registerMemory);
if (this->polling[irq]) {
uint32_t isr, mask = 1 << irq;
......
/** AXI PCIe bridge
*
* @author Daniel Krebs <github@daniel-krebs.net>
* @copyright 2018, RWTH Institute for Automation of Complex Power Systems (ACS)
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <limits>
#include <jansson.h>
#include "fpga/ips/pcie.hpp"
#include "fpga/card.hpp"
#include "log.hpp"
#include "memory_manager.hpp"
namespace villas {
namespace fpga {
namespace ip {
static AxiPciExpressBridgeFactory factory;
bool
AxiPciExpressBridge::init()
{
// Create an identity mapping from the FPGA card to this IP as an entry
// point to all other IPs in the FPGA, because Vivado will generate a
// memory view for this bridge that can see all others.
auto addrSpace = MemoryManager::get().findAddressSpace(getInstanceName());
MemoryManager::get().createMapping(0x00, 0x00, SIZE_MAX, "PCIeBridge",
card->addrSpaceId, addrSpace);
return true;
}
} // namespace ip
} // namespace fpga
} // namespace villas
......@@ -37,13 +37,16 @@ static AxiStreamSwitchFactory factory;
bool
AxiStreamSwitch::init()
{
{
auto logger = getLogger();
/* Setup AXI-stream switch */
XAxis_Switch_Config sw_cfg;
sw_cfg.MaxNumMI = num_ports;
sw_cfg.MaxNumSI = num_ports;
if(XAxisScr_CfgInitialize(&xSwitch, &sw_cfg, getBaseaddr()) != XST_SUCCESS) {
if(XAxisScr_CfgInitialize(&xSwitch, &sw_cfg, getBaseAddr(registerMemory)) != XST_SUCCESS) {
logger->error("Cannot initialize switch");
return false;
}
......@@ -57,7 +60,6 @@ AxiStreamSwitch::init()
portMapping[portMaster] = PORT_DISABLED;
}
return true;
}
......
......@@ -44,28 +44,23 @@ bool Timer::init()
XTmrCtr_Config xtmr_cfg;
xtmr_cfg.SysClockFreqHz = getFrequency();
XTmrCtr_CfgInitialize(&xTmr, &xtmr_cfg, getBaseaddr());
XTmrCtr_CfgInitialize(&xTmr, &xtmr_cfg, getBaseAddr(registerMemory));
XTmrCtr_InitHw(&xTmr);
if(dependencies.find("intc") == dependencies.end()) {
logger->error("No intc");
if(irqs.find(irqName) == irqs.end()) {
logger->error("IRQ '{}' not found but required", irqName);
return false;
}
if(irqs.find("generateout0") == irqs.end()) {
logger->error("no irq");
return false;
}
intc = reinterpret_cast<InterruptController*>(dependencies["intc"]);
intc->disableInterrupt(irqs["generateout0"]);
// disable so we don't receive any stray interrupts
irqs[irqName].irqController->disableInterrupt(irqs[irqName]);
return true;
}
bool Timer::start(uint32_t ticks)
{
intc->enableInterrupt(irqs["generateout0"], false);
irqs[irqName].irqController->enableInterrupt(irqs[irqName], false);
XTmrCtr_SetOptions(&xTmr, 0, XTC_EXT_COMPARE_OPTION | XTC_DOWN_COUNT_OPTION);
XTmrCtr_SetResetValue(&xTmr, 0, ticks);
......@@ -76,8 +71,8 @@ bool Timer::start(uint32_t ticks)
bool Timer::wait()
{
int count = intc->waitForInterrupt(irqs["generateout0"]);
intc->disableInterrupt(irqs["generateout0"]);
int count = irqs[irqName].irqController->waitForInterrupt(irqs[irqName]);
irqs[irqName].irqController->disableInterrupt(irqs[irqName]);
return (count == 1);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment