mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-30 00:00:11 +01:00
lib/ip: adapt to fit new config layout provided by hwdef-parse
This commit is contained in:
parent
92aea92f19
commit
f14df8aa32
9 changed files with 136 additions and 109 deletions
|
@ -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<int, IrqPort> irqs; ///< Interrupts 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
|
||||
};
|
||||
|
||||
|
|
|
@ -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<int, int> getLoopbackPorts() const;
|
||||
std::pair<std::string, std::string> getLoopbackPorts() const;
|
||||
|
||||
protected:
|
||||
std::map<int, StreamPort> portsMaster;
|
||||
std::map<int, StreamPort> portsSlave;
|
||||
std::map<std::string, StreamPort> portsMaster;
|
||||
std::map<std::string, StreamPort> portsSlave;
|
||||
};
|
||||
|
||||
class IpNodeFactory : public IpCoreFactory {
|
||||
|
|
|
@ -61,6 +61,7 @@ private:
|
|||
IpCore* slaveIn;
|
||||
};
|
||||
|
||||
int num_ports;
|
||||
XAxis_Switch xSwitch;
|
||||
std::map<int, int> 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
|
||||
|
|
|
@ -42,7 +42,7 @@ using DependencyGraph = villas::utils::DependencyGraph<std::string>;
|
|||
|
||||
static
|
||||
std::list<std::string>
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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<int, IpNode::StreamPort>& 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<int, int>
|
||||
std::pair<std::string, std::string>
|
||||
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<ip::AxiStreamSwitch*>(
|
||||
card->lookupIp(portMaster.nodeName));
|
||||
|
||||
if(axiStreamSwitch == nullptr) {
|
||||
logger->error("Cannot find IP {}", *axiStreamSwitch);
|
||||
logger->error("Cannot find switch");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,8 +51,8 @@ FifoFactory::configureJson(IpCore &ip, json_t *json_ip)
|
|||
}
|
||||
|
||||
auto& fifo = reinterpret_cast<Fifo&>(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<InterruptController*>(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<InterruptController*>(dependencies["intc"]);
|
||||
|
||||
while (!XLlFifo_IsRxDone(&xFifo))
|
||||
intc->waitForInterrupt(irqs[0].num);
|
||||
intc->waitForInterrupt(irqs["interrupt"].num);
|
||||
|
||||
XLlFifo_IntClear(&xFifo, XLLF_INT_RC_MASK);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#include <jansson.h>
|
||||
#include <xilinx/xaxis_switch.h>
|
||||
|
||||
#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<AxiStreamSwitch&>(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
|
||||
|
|
|
@ -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<InterruptController*>(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);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue