diff --git a/fpga/include/villas/fpga/ip.hpp b/fpga/include/villas/fpga/ip.hpp index 258bfaade..53ac244ad 100644 --- a/fpga/include/villas/fpga/ip.hpp +++ b/fpga/include/villas/fpga/ip.hpp @@ -134,7 +134,7 @@ protected: 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 irqs; ///< Interrupts of this IP component + std::map irqs; ///< Interrupts of this IP component std::map dependencies; ///< dependencies on other IPs }; diff --git a/fpga/include/villas/fpga/ip_node.hpp b/fpga/include/villas/fpga/ip_node.hpp index 6f7f92486..6dfe12011 100644 --- a/fpga/include/villas/fpga/ip_node.hpp +++ b/fpga/include/villas/fpga/ip_node.hpp @@ -54,18 +54,18 @@ public: std::string nodeName; }; - bool connect(int port, const StreamPort& to); - bool disconnect(int port); + bool connect(std::string portName, const StreamPort& to); + bool disconnect(std::string portName); bool loopbackPossible() const; bool connectLoopback(); private: - std::pair getLoopbackPorts() const; + std::pair getLoopbackPorts() const; protected: - std::map portsMaster; - std::map portsSlave; + std::map portsMaster; + std::map portsSlave; }; class IpNodeFactory : public IpCoreFactory { diff --git a/fpga/include/villas/fpga/ips/switch.hpp b/fpga/include/villas/fpga/ips/switch.hpp index 64767346b..b4418f5dd 100644 --- a/fpga/include/villas/fpga/ips/switch.hpp +++ b/fpga/include/villas/fpga/ips/switch.hpp @@ -61,6 +61,7 @@ private: IpCore* slaveIn; }; + int num_ports; XAxis_Switch xSwitch; std::map portMapping; }; @@ -71,6 +72,8 @@ public: AxiStreamSwitchFactory() : IpNodeFactory(getName()) {} + bool configureJson(IpCore& ip, json_t *json_ip); + IpCore* create() { return new AxiStreamSwitch; } @@ -81,7 +84,7 @@ public: { return "Xilinx's AXI4-Stream switch"; } Vlnv getCompatibleVlnv() const - { return Vlnv("xilinx.com:ip:axis_interconnect:"); } + { return Vlnv("xilinx.com:ip:axis_switch:"); } }; } // namespace ip diff --git a/fpga/lib/ip.cpp b/fpga/lib/ip.cpp index bd3dcda95..4daac1309 100644 --- a/fpga/lib/ip.cpp +++ b/fpga/lib/ip.cpp @@ -42,7 +42,7 @@ using DependencyGraph = villas::utils::DependencyGraph; static std::list -dependencyTokens = {"irq", "port", "memory"}; +dependencyTokens = {"irqs"}; static bool @@ -73,41 +73,45 @@ buildDependencyGraph(DependencyGraph& dependencyGraph, json_t* json_ips, std::st continue; } - const char* value = json_string_value(json_dependency); - if(value == nullptr) { - logger->warn("Property {} of {} is invalid", - dependencyToken, TXT_BOLD(name)); - continue; - } + const char* irq_name; + json_t* json_irq; + json_object_foreach(json_dependency, irq_name, json_irq) { + const char* value = json_string_value(json_irq); + if(value == nullptr) { + logger->warn("Property {} of {} is invalid", + dependencyToken, TXT_BOLD(name)); + continue; + } - auto mapping = villas::utils::tokenize(value, ":"); + auto mapping = villas::utils::tokenize(value, ":"); - if(mapping.size() != 2) { - logger->error("Invalid {} mapping of {}", - dependencyToken, TXT_BOLD(name)); + if(mapping.size() != 2) { + logger->error("Invalid {} mapping of {}", + dependencyToken, TXT_BOLD(name)); - dependencyGraph.removeNode(name); - return false; - } + dependencyGraph.removeNode(name); + return false; + } - if(name == mapping[0]) { - logger->error("IP {} cannot depend on itself", TXT_BOLD(name)); + if(name == mapping[0]) { + logger->error("IP {} cannot depend on itself", TXT_BOLD(name)); - dependencyGraph.removeNode(name); - return false; - } + dependencyGraph.removeNode(name); + return false; + } - // already add dependency, if adding it fails, removing the dependency - // will also remove the current one - dependencyGraph.addDependency(name, mapping[0]); + // already add dependency, if adding it fails, removing the dependency + // will also remove the current one + dependencyGraph.addDependency(name, mapping[0]); - if(not buildDependencyGraph(dependencyGraph, json_ips, mapping[0])) { - logger->error("Dependency {} of {} not satisfied", - mapping[0], TXT_BOLD(name)); + if(not buildDependencyGraph(dependencyGraph, json_ips, mapping[0])) { + logger->error("Dependency {} of {} not satisfied", + mapping[0], TXT_BOLD(name)); - dependencyGraph.removeNode(mapping[0]); - return false; + dependencyGraph.removeNode(mapping[0]); + return false; + } } } @@ -228,11 +232,13 @@ IpCoreFactory::make(PCIeCard* card, json_t *json_ips) } json_t* json_irqs = json_object_get(json_ip, "irqs"); - if(json_is_array(json_irqs)) { - size_t index; + if(json_is_object(json_irqs)) { + const char* irq_name; json_t* json_irq; - json_array_foreach(json_irqs, index, json_irq) { + json_object_foreach(json_irqs, irq_name, json_irq) { const char* irq = json_string_value(json_irq); + + auto tokens = utils::tokenize(irq, ":"); if(tokens.size() != 2) { logger->warn("Cannot parse IRQ '{}' of {}", @@ -247,9 +253,11 @@ IpCoreFactory::make(PCIeCard* card, json_t *json_ips) logger->warn("IRQ number is not an integer: '{}'", irq); continue; } - - ip->irqs[index] = {num, tokens[0], ""}; + logger->debug("IRQ: {} -> {}:{}", irq_name, tokens[0], num); + ip->irqs[irq_name] = {num, tokens[0], ""}; } + } else { + logger->debug("IP has no interrupts"); } diff --git a/fpga/lib/ip_node.cpp b/fpga/lib/ip_node.cpp index 6c2b5df04..6f25ac7ca 100644 --- a/fpga/lib/ip_node.cpp +++ b/fpga/lib/ip_node.cpp @@ -19,101 +19,79 @@ IpNodeFactory::configureJson(IpCore& ip, json_t* json_ip) auto logger = getLogger(); json_t* json_ports = json_object_get(json_ip, "ports"); - if(json_ports == nullptr) { - logger->error("IpNode {} has no ports property", ip); - return false; + if(not json_is_array(json_ports)) { + logger->debug("IP has no ports"); + return true; } - json_t* json_master = json_object_get(json_ports, "master"); - json_t* json_slave = json_object_get(json_ports, "slave"); - - const bool hasMasterPorts = json_is_array(json_master); - const bool hasSlavePorts = json_is_array(json_slave); - - if( (not hasMasterPorts) and (not hasSlavePorts)) { - logger->error("IpNode {} has no ports", ip); - return false; - } - - // intentionally use short-circuit evaluation to only call populatePorts - // if the property exists - bool masterPortsSuccess = - (hasMasterPorts and populatePorts(ipNode.portsMaster, json_master)) - or (not hasMasterPorts); - - bool slavePortsSuccess = - (hasSlavePorts and populatePorts(ipNode.portsSlave, json_slave)) - or (not hasSlavePorts); - - return (masterPortsSuccess and slavePortsSuccess); -} - -bool -IpNodeFactory::populatePorts(std::map& portMap, json_t* json) -{ - auto logger = getLogger(); - size_t index; json_t* json_port; - json_array_foreach(json, index, json_port) { - int myPortNum; - const char* to = nullptr; + json_array_foreach(json_ports, index, json_port) { + if(not json_is_object(json_port)) { + logger->error("Port {} is not an object", index); + return false; + } - int ret = json_unpack(json_port, "{ s : i, s? : s}", - "num", &myPortNum, - "to", &to); + const char *role_raw, *target_raw, *name_raw; + int ret = json_unpack(json_port, "{ s: s, s: s, s: s }", + "role", &role_raw, + "target", &target_raw, + "name", &name_raw); if(ret != 0) { - logger->error("Port definition required field 'num'"); + logger->error("Cannot parse port {}", index); return false; } - if(to == nullptr) { - logger->debug("Nothing connected to port {}", myPortNum); - portMap[myPortNum] = {}; - continue; - } - - const auto tokens = utils::tokenize(to, ":"); + const auto tokens = utils::tokenize(target_raw, ":"); if(tokens.size() != 2) { - logger->error("Too many tokens in property 'other'"); + logger->error("Cannot parse 'target' of port {}", index); return false; } - int otherPortNum; + int port_num; try { - otherPortNum = std::stoi(tokens[1]); + port_num = std::stoi(tokens[1]); } catch(const std::invalid_argument&) { - logger->error("Other port number is not an integral number"); + logger->error("Target port number is not an integer: '{}'", target_raw); return false; } - logger->debug("Adding port mapping: {}:{}", myPortNum, to); - portMap[myPortNum] = { otherPortNum, tokens[0] }; + IpNode::StreamPort port; + port.nodeName = tokens[0]; + port.portNumber = port_num; + + const std::string role(role_raw); + if(role == "master" or role == "initiator") { + ipNode.portsMaster[name_raw] = port; + } else /* slave */ { + ipNode.portsSlave[name_raw] = port; + } + } return true; } -std::pair +std::pair IpNode::getLoopbackPorts() const { - for(auto& [masterNum, masterTo] : portsMaster) { - for(auto& [slaveNum, slaveTo] : portsSlave) { + for(auto& [masterName, masterTo] : portsMaster) { + for(auto& [slaveName, slaveTo] : portsSlave) { // TODO: should we also check which IP both ports are connected to? if(masterTo.nodeName == slaveTo.nodeName) { - return { masterNum, slaveNum }; + return { masterName, slaveName }; } } } - return { -1, -1 }; + return { "", "" }; } bool IpNode::loopbackPossible() const { auto ports = getLoopbackPorts(); - return (ports.first != -1) and (ports.second != -1); + return (not ports.first.empty()) and (not ports.second.empty()); } bool @@ -125,12 +103,17 @@ IpNode::connectLoopback() const auto& portMaster = portsMaster[ports.first]; const auto& portSlave = portsSlave[ports.second]; + logger->debug("master port: {}", ports.first); + logger->debug("slave port: {}", ports.second); + + logger->debug("switch at: {}", portMaster.nodeName); + // TODO: verify this is really a switch! auto axiStreamSwitch = reinterpret_cast( card->lookupIp(portMaster.nodeName)); if(axiStreamSwitch == nullptr) { - logger->error("Cannot find IP {}", *axiStreamSwitch); + logger->error("Cannot find switch"); return false; } diff --git a/fpga/lib/ips/fifo.cpp b/fpga/lib/ips/fifo.cpp index eb7dc4669..087d3ad8f 100644 --- a/fpga/lib/ips/fifo.cpp +++ b/fpga/lib/ips/fifo.cpp @@ -51,8 +51,8 @@ FifoFactory::configureJson(IpCore &ip, json_t *json_ip) } auto& fifo = reinterpret_cast(ip); - if(json_unpack(json_ip, "{ s: i }", "baseaddr_axi4", &fifo.baseaddr_axi4) != 0) { - logger->warn("Cannot parse property 'baseaddr_axi4'"); + if(json_unpack(json_ip, "{ s: i }", "axi4_baseaddr", &fifo.baseaddr_axi4) != 0) { + logger->warn("Cannot parse property 'axi4_baseaddr'"); return false; } @@ -76,7 +76,7 @@ bool Fifo::init() XLlFifo_IntEnable(&xFifo, XLLF_INT_RC_MASK); auto intc = reinterpret_cast(dependencies["intc"]); - intc->enableInterrupt(irqs[0], false); + intc->enableInterrupt(irqs["interrupt"], false); return true; } @@ -113,7 +113,7 @@ size_t Fifo::read(void *buf, size_t len) auto intc = reinterpret_cast(dependencies["intc"]); while (!XLlFifo_IsRxDone(&xFifo)) - intc->waitForInterrupt(irqs[0].num); + intc->waitForInterrupt(irqs["interrupt"].num); XLlFifo_IntClear(&xFifo, XLLF_INT_RC_MASK); diff --git a/fpga/lib/ips/intc.cpp b/fpga/lib/ips/intc.cpp index e7716c1ff..90cbd7144 100644 --- a/fpga/lib/ips/intc.cpp +++ b/fpga/lib/ips/intc.cpp @@ -76,7 +76,8 @@ InterruptController::init() XIntc_Out32(base + XIN_IER_OFFSET, 0x00000000); /* Disable all IRQs by default */ XIntc_Out32(base + XIN_MER_OFFSET, XIN_INT_HARDWARE_ENABLE_MASK | XIN_INT_MASTER_ENABLE_MASK); - logger->debug("enabled interrupts");; + logger->debug("enabled interrupts"); + return true; } diff --git a/fpga/lib/ips/switch.cpp b/fpga/lib/ips/switch.cpp index 81110a7f7..9f5a73470 100644 --- a/fpga/lib/ips/switch.cpp +++ b/fpga/lib/ips/switch.cpp @@ -23,6 +23,7 @@ * along with this program. If not, see . *********************************************************************************/ +#include #include #include "log.hpp" @@ -39,8 +40,8 @@ AxiStreamSwitch::init() { /* Setup AXI-stream switch */ XAxis_Switch_Config sw_cfg; - sw_cfg.MaxNumMI = portsMaster.size(); - sw_cfg.MaxNumSI = portsSlave.size(); + sw_cfg.MaxNumMI = num_ports; + sw_cfg.MaxNumSI = num_ports; if(XAxisScr_CfgInitialize(&xSwitch, &sw_cfg, getBaseaddr()) != XST_SUCCESS) { return false; @@ -122,6 +123,25 @@ AxiStreamSwitch::disconnectSlave(int port) return true; } +bool +AxiStreamSwitchFactory::configureJson(IpCore& ip, json_t* json_ip) +{ + if(not IpNodeFactory::configureJson(ip, json_ip)) + return false; + + auto logger = getLogger(); + + auto& axiSwitch = reinterpret_cast(ip); + + if(json_unpack(json_ip, "{ s: i }", "num_ports", &axiSwitch.num_ports) != 0) { + logger->error("Cannot parse 'num_ports'"); + return false; + } + + return true; +} + + } // namespace ip } // namespace fpga } // namespace villas diff --git a/fpga/lib/ips/timer.cpp b/fpga/lib/ips/timer.cpp index 5e0c56e4c..a6e19e257 100644 --- a/fpga/lib/ips/timer.cpp +++ b/fpga/lib/ips/timer.cpp @@ -39,21 +39,33 @@ static TimerFactory factory; bool Timer::init() { + auto logger = getLogger(); + XTmrCtr_Config xtmr_cfg; xtmr_cfg.SysClockFreqHz = getFrequency(); XTmrCtr_CfgInitialize(&xTmr, &xtmr_cfg, getBaseaddr()); XTmrCtr_InitHw(&xTmr); + if(dependencies.find("intc") == dependencies.end()) { + logger->error("No intc"); + return false; + } + + if(irqs.find("generateout0") == irqs.end()) { + logger->error("no irq"); + return false; + } + intc = reinterpret_cast(dependencies["intc"]); - intc->disableInterrupt(irqs[0]); + intc->disableInterrupt(irqs["generateout0"]); return true; } bool Timer::start(uint32_t ticks) { - intc->enableInterrupt(irqs[0], false); + intc->enableInterrupt(irqs["generateout0"], false); XTmrCtr_SetOptions(&xTmr, 0, XTC_EXT_COMPARE_OPTION | XTC_DOWN_COUNT_OPTION); XTmrCtr_SetResetValue(&xTmr, 0, ticks); @@ -64,8 +76,8 @@ bool Timer::start(uint32_t ticks) bool Timer::wait() { - int count = intc->waitForInterrupt(irqs[0]); - intc->disableInterrupt(irqs[0]); + int count = intc->waitForInterrupt(irqs["generateout0"]); + intc->disableInterrupt(irqs["generateout0"]); return (count == 1); }