Skip to content

Commit

Permalink
Villas Interface: Improve FpgaExample (#299)
Browse files Browse the repository at this point in the history
This makes the FpgaExample a closed loop test of the communication via
VILLAS.
I also added the signal names configured in the example to the actual
signals and added a bit more debug output.
  • Loading branch information
m-mirz authored Jun 8, 2024
2 parents 518be00 + 0f913be commit da76744
Show file tree
Hide file tree
Showing 13 changed files with 226 additions and 86 deletions.
9 changes: 4 additions & 5 deletions dpsim-villas/examples/cxx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ list(APPEND LIBRARIES ${VILLASNODE_LIBRARIES})
list(APPEND LIBRARIES "jansson")
list(APPEND LIBRARIES "uuid")
list(APPEND LIBRARIES "villas-common")
list(APPEND LIBRARIES "villas-fpga")
list(APPEND LIBRARIES "xil")
list(APPEND INCLUDE_DIRS ${VILLASNODE_INCLUDE_DIRS})

set(SHMEM_SOURCES
FileExample.cpp
MqttExample.cpp
FpgaExample.cpp
SharedMemExample.cpp
#ShmemExample.cpp
#ShmemDistributedReference.cpp
Expand All @@ -27,11 +30,7 @@ set(SHMEM_SOURCES
)

if(WITH_CIM)
set(CIM_SHMEM_SOURCES
#Shmem_WSCC-9bus.cpp
#Shmem_WSCC-9bus_Ctrl.cpp
#Shmem_WSCC-9bus_CtrlDist.cpp
)
list(APPEND INCLUDE_DIRS ${CIMPP_INCLUDE_DIRS})
endif()

foreach(SOURCE ${SHMEM_SOURCES} ${CIM_SHMEM_SOURCES})
Expand Down
218 changes: 158 additions & 60 deletions dpsim-villas/examples/cxx/FpgaExample.cpp
Original file line number Diff line number Diff line change
@@ -1,91 +1,189 @@
// SPDX-License-Identifier: Apache-2.0
/* Simple test circuit for testing connection to a FPGA via VILLASnode
*
* Author: Niklas Eiling <[email protected]>
* SPDX-FileCopyrightText: 2024 Niklas Eiling <[email protected]>
* SPDX-License-Identifier: Apache-2.0
*/

#include <filesystem>
#include <fstream>

#include <DPsim.h>
#include <dpsim-models/DP/DP_Ph1_CurrentSource.h>
#include <dpsim-models/SimNode.h>
#include <dpsim-villas/InterfaceVillas.h>
#include <dpsim/Utils.h>

using namespace DPsim;
using namespace CPS::DP;
using namespace CPS::DP::Ph1;

int main(int argc, char *argv[]) {
// Very simple test circuit. Just a few resistors and an inductance.
// Voltage is read from VILLASnode and current through everything is written back.
String simName = "Fpga_example";
CPS::Logger::setLogDir("logs/" + simName);
Real timeStep = 0.01;
const std::string buildFpgaConfig(CommandLineArgs &args) {
std::filesystem::path fpgaIpPath =
"/usr/local/etc/villas/node/etc/fpga/vc707-xbar-pcie-dino/"
"vc707-xbar-pcie-dino.json";

if (args.options.find("ips") != args.options.end()) {
fpgaIpPath = std::filesystem::path(args.getOptionString("ips"));
}
std::string cardConfig = fmt::format(
R"STRING("card": {{
"interface": "pcie",
"id": "10ee:7021",
"slot": "0000:88:00.0",
"do_reset": true,
"ips": "{}",
"polling": true
}},
"connect": [
"0->3",
"dino<-dma",
"dino->dma"
],
"low_latency_mode": true,
"timestep": {})STRING",
fpgaIpPath.string(), args.timeStep);
std::string signalOutConfig = fmt::format(
R"STRING("out": {{
"signals": [{{
"name": "from_dpsim",
"type": "complex",
"unit": "V"
}}],
"hooks": [{{
"type": "dp",
"signal": "from_dpsim",
"f0": {},
"dt": {},
"harmonics": [0],
"inverse": true
}}]
}})STRING",
args.sysFreq, args.timeStep);
std::string signalInConfig = fmt::format(
R"STRING("in": {{
"signals": [{{
"name": "to_dpsim",
"type": "float",
"unit": "V",
"builtin": false
}}],
"hooks": ["print", {{
"type": "dp",
"signal": "to_dpsim",
"f0": {},
"dt": {},
"harmonics": [0],
"inverse": false
}}]
}})STRING",
args.sysFreq, args.timeStep);
const std::string config = fmt::format(
R"STRING({{
"type": "fpga",
{},
{},
{}
}})STRING",
cardConfig, signalOutConfig, signalInConfig);
DPsim::Logger::get("FpgaExample")->debug("Config for Node:\n{}", config);
return config;
}

SystemTopology loopbackTopology(CommandLineArgs &args,
std::shared_ptr<InterfaceVillas> intf,
std::shared_ptr<DataLogger> logger) {
// Nodes
auto n1 = SimNode::make("n1");

// Components
auto vs = VoltageSource::make("v_s");
vs->setParameters(Complex(10, 0), args.sysFreq);
auto rl = Resistor::make("r_l");
rl->setParameters(1);

// Topology
vs->connect({SimNode::GND, n1});
rl->connect({n1, SimNode::GND});

// Interface
intf->importAttribute(vs->mVoltageRef, 0, false, false, "from_dino", "A");
intf->exportAttribute(n1->mVoltage->deriveCoeff<Complex>(0, 0), 0, true,
"to_dino", "V");
intf->printVillasSignals();

// Logger
logger->logAttribute("v1", n1->mVoltage);
logger->logAttribute("rl_i", rl->mIntfCurrent);

return SystemTopology(args.sysFreq, SystemNodeList{SimNode::GND, n1},
SystemComponentList{vs, rl});
}

SystemTopology hilTopology(CommandLineArgs &args,
std::shared_ptr<InterfaceVillas> intf,
std::shared_ptr<DataLogger> logger) {
// Nodes
auto n1 = SimNode::make("n1");
auto n2 = SimNode::make("n2");
auto n3 = SimNode::make("n3");
auto n4 = SimNode::make("n4");

// Components
auto evs = VoltageSource::make("v_s");
evs->setParameters(Complex(5, 0));
auto vs = VoltageSource::make("v_s");
vs->setParameters(Complex(10, 0), args.sysFreq);
auto rs = Resistor::make("r_s");
rs->setParameters(1);
auto rl = Resistor::make("r_line");
rl->setParameters(1);
auto ll = Inductor::make("l_line");
ll->setParameters(1);
auto rL = Resistor::make("r_load");
rL->setParameters(1000);

auto cs = CurrentSource::make("i_l");
cs->setParameters(Complex(0, 0));

// Topology
evs->connect({SimNode::GND, n1});
vs->connect({SimNode::GND, n1});
rs->connect({n1, n2});
rl->connect({n2, n3});
ll->connect({n3, n4});
rL->connect({n4, SimNode::GND});

auto sys = SystemTopology(50, SystemNodeList{SimNode::GND, n1, n2, n3, n4},
SystemComponentList{evs, rs, rl, ll, rL});

RealTimeSimulation sim(simName);
sim.setSystem(sys);
sim.setTimeStep(timeStep);
sim.setFinalTime(10.0);

std::string fpgaConfig = R"STRING({
type = "fpga",
card = {
interface = "pcie",
id = "10ee:7021",
slot = "0000:88:00.0",
do_reset = true,
ips = "../../fpga/vc707-xbar-pcie-dino/vc707-xbar-pcie-dino-v2.json",
polling = true,
},
connect = ["0->3", "dino<-dma", "dino->dma"],
signals = ({ name = "dino", unit = "Volts", type = "float"}),
builtin = false,
lowLatencyMode = true,
})STRING";

auto intf = std::make_shared<InterfaceVillas>(fpgaConfig);
cs->connect({n2, SimNode::GND});

// Interface
sim.addInterface(intf);
intf->importAttribute(evs->mVoltageRef, 0, true, true);
intf->exportAttribute(evs->mIntfVoltage->deriveCoeff<Complex>(0, 0), 0, true,
"v_src");
intf->importAttribute(cs->mCurrentRef, 0, false, false, "from_dino", "A");
intf->exportAttribute(n2->mVoltage->deriveCoeff<Complex>(0, 0), 0, true,
"to_dino", "V");
intf->printVillasSignals();

// Logger
auto logger = DataLogger::make(simName);
logger->logAttribute("v1", n1->mVoltage);
logger->logAttribute("v2", n2->mVoltage);
logger->logAttribute("v3", n3->mVoltage);
logger->logAttribute("v4", n4->mVoltage);
logger->logAttribute("v_src", evs->mVoltageRef);
logger->logAttribute("i_r", rl->mIntfCurrent, 1, 1);
logger->logAttribute("i_evs", evs->mIntfCurrent, 1, 1);
logger->logAttribute("v_evs", evs->mIntfVoltage, 1, 1);
sim.addLogger(logger);
logger->logAttribute("cs_i", cs->mIntfCurrent);

return SystemTopology(args.sysFreq, SystemNodeList{SimNode::GND, n1, n2},
SystemComponentList{vs, rs, cs});
}

sim.run(1);
SystemTopology getTopology(CommandLineArgs &args,
std::shared_ptr<InterfaceVillas> intf,
std::shared_ptr<DataLogger> logger) {
if (args.options.find("topology") != args.options.end()) {
std::string topology = args.getOptionString("topology");
if (topology == "hil") {
return hilTopology(args, intf, logger);
} else if (topology == "loopback") {
return loopbackTopology(args, intf, logger);
}
}
return hilTopology(args, intf, logger);
}

int main(int argc, char *argv[]) {
CommandLineArgs args(argc, argv, "FpgaExample", 0.01, 10 * 60, 5.);
CPS::Logger::setLogDir("logs/" + args.name);

auto intf = std::make_shared<InterfaceVillas>(buildFpgaConfig(args));
auto logger = DataLogger::make(args.name);

auto sys = getTopology(args, intf, logger);

RealTimeSimulation sim(args.name, args);
sim.setSystem(sys);
sim.addInterface(intf);
sim.addLogger(logger);
sim.run();

//std::ofstream of("task_dependencies.svg");
//sim.dependencyGraph().render(of);
Expand Down
9 changes: 6 additions & 3 deletions dpsim-villas/include/dpsim-villas/InterfaceVillas.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
#include <dpsim-models/PtrFactory.h>
#include <dpsim/Interface.h>

#include <villas/exceptions.hpp>
#include <villas/kernel/rt.hpp>
#include <villas/memory.hpp>
#include <villas/node.hpp>
#include <villas/node/exceptions.hpp>
#include <villas/node/memory.hpp>
#include <villas/pool.hpp>
#include <villas/sample.hpp>
#include <villas/signal.hpp>
Expand Down Expand Up @@ -39,7 +39,8 @@ class InterfaceVillas : public Interface,
/// @param syncOnSimulationStart Whether the simulation should block before the first timestep until this attribute has been updated
void importAttribute(CPS::AttributeBase::Ptr attr, UInt idx,
Bool blockOnRead = false,
Bool syncOnSimulationStart = true);
Bool syncOnSimulationStart = true,
const String &name = "", const String &unit = "");

/// @brief configure an attribute export
/// @param attr the attribute which's value should be exported
Expand All @@ -50,5 +51,7 @@ class InterfaceVillas : public Interface,
void exportAttribute(CPS::AttributeBase::Ptr attr, UInt idx,
Bool waitForOnWrite, const String &name = "",
const String &unit = "");

void printVillasSignals() const;
};
} // namespace DPsim
9 changes: 6 additions & 3 deletions dpsim-villas/include/dpsim-villas/InterfaceWorkerVillas.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
#include <dpsim-models/PtrFactory.h>
#include <dpsim/InterfaceWorker.h>

#include <villas/exceptions.hpp>
#include <villas/kernel/rt.hpp>
#include <villas/memory.hpp>
#include <villas/node.hpp>
#include <villas/node/exceptions.hpp>
#include <villas/node/memory.hpp>
#include <villas/pool.hpp>
#include <villas/sample.hpp>
#include <villas/signal.hpp>
Expand Down Expand Up @@ -65,12 +65,15 @@ class InterfaceWorkerVillas : public InterfaceWorker,
std::vector<Interface::AttributePacket> &updatedAttrs) override;

virtual void configureImport(UInt attributeId, const std::type_info &type,
UInt idx);
UInt idx, const String &name = "",
const String &unit = "");
virtual void configureExport(UInt attributeId, const std::type_info &type,
UInt idx, Bool waitForOnWrite,
const String &name = "",
const String &unit = "");

void printSignals() const;

private:
void prepareNode();
void setupNodeSignals();
Expand Down
12 changes: 9 additions & 3 deletions dpsim-villas/src/InterfaceVillas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ InterfaceVillas::InterfaceVillas(const String &nodeConfig, UInt queueLength,

void InterfaceVillas::importAttribute(CPS::AttributeBase::Ptr attr, UInt idx,
Bool blockOnRead,
Bool syncOnSimulationStart) {
Bool syncOnSimulationStart,
const String &name, const String &unit) {
Interface::addImport(attr, blockOnRead, syncOnSimulationStart);
std::dynamic_pointer_cast<InterfaceWorkerVillas>(mInterfaceWorker)
->configureImport((UInt)mImportAttrsDpsim.size() - 1, attr->getType(),
idx);
idx, name, unit);
}

void InterfaceVillas::exportAttribute(CPS::AttributeBase::Ptr attr, UInt idx,
Expand All @@ -32,4 +33,9 @@ void InterfaceVillas::exportAttribute(CPS::AttributeBase::Ptr attr, UInt idx,
idx, waitForOnWrite, name, unit);
}

} // namespace DPsim
void InterfaceVillas::printVillasSignals() const {
std::dynamic_pointer_cast<InterfaceWorkerVillas>(mInterfaceWorker)
->printSignals();
}

} // namespace DPsim
Loading

0 comments on commit da76744

Please sign in to comment.