mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
Merge branch 'feature/cpp' into develop
Current work-in-progress of porting to C++
This commit is contained in:
commit
959a6130d3
160 changed files with 28629 additions and 201 deletions
|
@ -2,7 +2,7 @@ variables:
|
|||
GIT_STRATEGY: fetch
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
PREFIX: /usr/
|
||||
DOCKER_TAG_DEV: ${CI_COMMIT_REF_NAME}
|
||||
DOCKER_TAG_DEV: ${CI_BUILD_REF_SLUG}
|
||||
DOCKER_IMAGE_DEV: villas/fpga-dev
|
||||
|
||||
# For some reason, GitLab CI prunes the contents of the submodules so we need to restore them.
|
||||
|
@ -12,7 +12,7 @@ before_script:
|
|||
stages:
|
||||
- prepare
|
||||
- build
|
||||
# - test
|
||||
- test
|
||||
# - deploy
|
||||
|
||||
# Stage: prepare
|
||||
|
@ -33,7 +33,7 @@ docker-dev:
|
|||
build:source:
|
||||
stage: build
|
||||
script:
|
||||
- mkdir build && cd build && cmake .. && make
|
||||
- mkdir build && cd build && cmake .. && make -j8
|
||||
artifacts:
|
||||
expire_in: 1 week
|
||||
name: ${CI_PROJECT_NAME}-${CI_BUILD_REF}
|
||||
|
@ -59,16 +59,30 @@ build:source:
|
|||
# Stage: test
|
||||
##############################################################################
|
||||
|
||||
#test:unit:
|
||||
# stage: test
|
||||
test:unit-software:
|
||||
stage: test
|
||||
dependencies:
|
||||
- build:source
|
||||
script:
|
||||
- build/tests/unit-tests --filter 'graph/*'
|
||||
image: ${DOCKER_IMAGE_DEV}:${DOCKER_TAG_DEV}
|
||||
tags:
|
||||
- docker
|
||||
|
||||
test:unit-hardware:
|
||||
stage: test
|
||||
# dependencies:
|
||||
# - build:source
|
||||
# script:
|
||||
# - make test
|
||||
# image: ${DOCKER_IMAGE_DEV}:${DOCKER_TAG_DEV}
|
||||
# tags:
|
||||
# - docker
|
||||
# - fpga
|
||||
tags:
|
||||
- villas-fpga
|
||||
script: |
|
||||
rm -r build && mkdir build && cd build && cmake .. && make unit-tests -j8
|
||||
if [ "$(who | wc -l)" -eq "0" ]; then
|
||||
tests/unit-tests --jobs 1 --filter 'fpga/*'
|
||||
else
|
||||
echo "System is currently used by: $(who)"
|
||||
echo "We are skipping the test. Please restart manually."
|
||||
fi
|
||||
|
||||
# Stage: deploy
|
||||
##############################################################################
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(VILLASfpga C)
|
||||
project(VILLASfpga C CXX)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake)
|
||||
|
||||
set (CMAKE_CXX_STANDARD 17)
|
||||
|
||||
include_directories(thirdparty/spdlog/include)
|
||||
|
||||
add_subdirectory(lib)
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(src)
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
###################################################################################
|
||||
|
||||
FROM registry.fedoraproject.org/fedora:27
|
||||
FROM fedora:27
|
||||
|
||||
LABEL \
|
||||
org.label-schema.schema-version="1.0" \
|
||||
|
|
|
@ -26,78 +26,226 @@
|
|||
"slot": "03:00.0",
|
||||
"do_reset": true,
|
||||
"ips": {
|
||||
"axi_pcie_intc_0": {
|
||||
"vlnv": "acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:1.0",
|
||||
"baseaddr": 45056
|
||||
"bram_0_axi_bram_ctrl_0": {
|
||||
"vlnv": "xilinx.com:ip:axi_bram_ctrl:4.0",
|
||||
"s_axi_baseaddr": 0,
|
||||
"s_axi_highaddr": 8191,
|
||||
"size": 8192
|
||||
},
|
||||
"switch_0": {
|
||||
"vlnv": "xilinx.com:ip:axis_interconnect:2.1",
|
||||
"baseaddr": 20480,
|
||||
"num_ports": 10,
|
||||
"paths": [
|
||||
"hier_0_axi_dma_axi_dma_0": {
|
||||
"vlnv": "xilinx.com:ip:axi_dma:7.1",
|
||||
"memory-view": {
|
||||
"SG": {
|
||||
"bram_0_axi_bram_ctrl_0": {
|
||||
"baseaddr": 0,
|
||||
"highaddr": 8191
|
||||
},
|
||||
"hier_0_axi_dma_axi_dma_1": {
|
||||
"baseaddr": 8192,
|
||||
"highaddr": 12287
|
||||
},
|
||||
"hier_0_axi_dma_axi_dma_0": {
|
||||
"baseaddr": 12288,
|
||||
"highaddr": 16383
|
||||
},
|
||||
"timer_0_axi_timer_0": {
|
||||
"baseaddr": 16384,
|
||||
"highaddr": 20479
|
||||
},
|
||||
"hier_0_axis_interconnect_0_axis_interconnect_0_xbar": {
|
||||
"baseaddr": 20480,
|
||||
"highaddr": 24575
|
||||
},
|
||||
"hier_0_axi_fifo_mm_s_0": {
|
||||
"baseaddr": 49152,
|
||||
"highaddr": 57343
|
||||
},
|
||||
"pcie_0_axi_reset_0": {
|
||||
"baseaddr": 28672,
|
||||
"highaddr": 32767
|
||||
},
|
||||
"hier_0_rtds_axis_0": {
|
||||
"baseaddr": 32768,
|
||||
"highaddr": 36863
|
||||
},
|
||||
"hier_0_hls_dft_0": {
|
||||
"baseaddr": 36864,
|
||||
"highaddr": 40959
|
||||
},
|
||||
"pcie_0_axi_pcie_intc_0": {
|
||||
"baseaddr": 45056,
|
||||
"highaddr": 49151
|
||||
},
|
||||
"pcie_0_axi_pcie_0": {
|
||||
"baseaddr": 268435456,
|
||||
"highaddr": 536870911
|
||||
}
|
||||
},
|
||||
"MM2S": {
|
||||
"pcie_0_axi_pcie_0": {
|
||||
"baseaddr": 2147483648,
|
||||
"highaddr": 4294967295
|
||||
}
|
||||
},
|
||||
"S2MM": {
|
||||
"pcie_0_axi_pcie_0": {
|
||||
"baseaddr": 2147483648,
|
||||
"highaddr": 4294967295
|
||||
}
|
||||
}
|
||||
},
|
||||
"baseaddr": 12288,
|
||||
"highaddr": 16383,
|
||||
"ports": [
|
||||
{
|
||||
"in": "rtds_axis_0",
|
||||
"out": "dma_1",
|
||||
"reverse": true
|
||||
"role": "initiator",
|
||||
"target": "hier_0_axis_interconnect_0_axis_interconnect_0_xbar:1",
|
||||
"name": "MM2S"
|
||||
},
|
||||
{
|
||||
"role": "target",
|
||||
"target": "hier_0_axis_interconnect_0_axis_interconnect_0_xbar:1",
|
||||
"name": "S2MM"
|
||||
}
|
||||
]
|
||||
},
|
||||
"axi_reset_0": {
|
||||
"vlnv": "xilinx.com:ip:axi_gpio:2.0",
|
||||
"baseaddr": 28672
|
||||
},
|
||||
"timer_0": {
|
||||
"vlnv": "xilinx.com:ip:axi_timer:2.0",
|
||||
"baseaddr": 16384,
|
||||
"irq": 0
|
||||
},
|
||||
"dma_0": {
|
||||
"vlnv": "xilinx.com:ip:axi_dma:7.1",
|
||||
"baseaddr": 12288,
|
||||
"port": 1,
|
||||
"irq": 3
|
||||
},
|
||||
"dma_1": {
|
||||
"hier_0_axi_dma_axi_dma_1": {
|
||||
"vlnv": "xilinx.com:ip:axi_dma:7.1",
|
||||
"memory-view": {
|
||||
"MM2S": {
|
||||
"pcie_0_axi_pcie_0": {
|
||||
"baseaddr": 2147483648,
|
||||
"highaddr": 4294967295
|
||||
}
|
||||
},
|
||||
"S2MM": {
|
||||
"pcie_0_axi_pcie_0": {
|
||||
"baseaddr": 2147483648,
|
||||
"highaddr": 4294967295
|
||||
}
|
||||
}
|
||||
},
|
||||
"baseaddr": 8192,
|
||||
"port": 6,
|
||||
"irq": 3
|
||||
"highaddr": 12287,
|
||||
"ports": [
|
||||
{
|
||||
"role": "initiator",
|
||||
"target": "hier_0_axis_interconnect_0_axis_interconnect_0_xbar:6",
|
||||
"name": "MM2S"
|
||||
},
|
||||
{
|
||||
"role": "target",
|
||||
"target": "hier_0_axis_interconnect_0_axis_interconnect_0_xbar:6",
|
||||
"name": "S2MM"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fifo_mm_s_0": {
|
||||
"hier_0_axi_fifo_mm_s_0": {
|
||||
"vlnv": "xilinx.com:ip:axi_fifo_mm_s:4.1",
|
||||
"baseaddr": 24576,
|
||||
"baseaddr_axi4": 49152,
|
||||
"port": 2,
|
||||
"irq": 2
|
||||
"highaddr": 28671,
|
||||
"axi4_baseaddr": 49152,
|
||||
"axi4_highaddr": 57343,
|
||||
"ports": [
|
||||
{
|
||||
"role": "master",
|
||||
"target": "hier_0_axis_interconnect_0_axis_interconnect_0_xbar:2",
|
||||
"name": "STR_TXD"
|
||||
},
|
||||
{
|
||||
"role": "slave",
|
||||
"target": "hier_0_axis_interconnect_0_axis_interconnect_0_xbar:2",
|
||||
"name": "STR_RXD"
|
||||
}
|
||||
],
|
||||
"irqs": {
|
||||
"interrupt": "pcie_0_axi_pcie_intc_0:2"
|
||||
}
|
||||
},
|
||||
"rtds_axis_0": {
|
||||
"hier_0_axis_interconnect_0_axis_interconnect_0_xbar": {
|
||||
"vlnv": "xilinx.com:ip:axis_switch:1.1",
|
||||
"baseaddr": 20480,
|
||||
"highaddr": 24575,
|
||||
"ports": [
|
||||
{
|
||||
"role": "initiator",
|
||||
"target": "hier_0_axis_interconnect_0_axis_interconnect_0_xbar:3",
|
||||
"name": "M03_AXIS"
|
||||
},
|
||||
{
|
||||
"role": "target",
|
||||
"target": "hier_0_axis_interconnect_0_axis_interconnect_0_xbar:3",
|
||||
"name": "S03_AXIS"
|
||||
},
|
||||
{
|
||||
"role": "initiator",
|
||||
"target": "hier_0_axis_interconnect_0_axis_interconnect_0_xbar:4",
|
||||
"name": "M04_AXIS"
|
||||
},
|
||||
{
|
||||
"role": "target",
|
||||
"target": "hier_0_axis_interconnect_0_axis_interconnect_0_xbar:4",
|
||||
"name": "S04_AXIS"
|
||||
}
|
||||
],
|
||||
"num_ports": 14
|
||||
},
|
||||
"hier_0_hls_dft_0": {
|
||||
"vlnv": "acs.eonerc.rwth-aachen.de:hls:hls_dft:1.0",
|
||||
"s_axi_ctrl_baseaddr": 36864,
|
||||
"s_axi_ctrl_highaddr": 40959,
|
||||
"ports": [
|
||||
{
|
||||
"role": "master",
|
||||
"target": "hier_0_axis_interconnect_0_axis_interconnect_0_xbar:5",
|
||||
"name": "output_r"
|
||||
},
|
||||
{
|
||||
"role": "slave",
|
||||
"target": "hier_0_axis_interconnect_0_axis_interconnect_0_xbar:5",
|
||||
"name": "input_r"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hier_0_rtds_axis_0": {
|
||||
"vlnv": "acs.eonerc.rwth-aachen.de:user:rtds_axis:1.0",
|
||||
"baseaddr": 32768,
|
||||
"port": 0,
|
||||
"irq": 5
|
||||
},
|
||||
"hls_dft_0": {
|
||||
"vlnv": "acs.eonerc.rwth-aachen.de:hls:hls_dft:1.0",
|
||||
"baseaddr": 36864,
|
||||
"port": 5,
|
||||
"irq": 1,
|
||||
"period": 400,
|
||||
"harmonics": [
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
5,
|
||||
7
|
||||
"highaddr": 36863,
|
||||
"ports": [
|
||||
{
|
||||
"role": "master",
|
||||
"target": "hier_0_axis_interconnect_0_axis_interconnect_0_xbar:0",
|
||||
"name": "m_axis"
|
||||
},
|
||||
{
|
||||
"role": "slave",
|
||||
"target": "hier_0_axis_interconnect_0_axis_interconnect_0_xbar:0",
|
||||
"name": "s_axis"
|
||||
}
|
||||
],
|
||||
"decimation": 0
|
||||
"irqs": {
|
||||
"irq_ts": "pcie_0_axi_pcie_intc_0:5",
|
||||
"irq_overflow": "pcie_0_axi_pcie_intc_0:6",
|
||||
"irq_case": "pcie_0_axi_pcie_intc_0:7"
|
||||
}
|
||||
},
|
||||
"axis_data_fifo_0": {
|
||||
"vlnv": "xilinx.com:ip:axis_data_fifo:1.1",
|
||||
"port": 3
|
||||
"pcie_0_axi_pcie_intc_0": {
|
||||
"vlnv": "acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:1.0",
|
||||
"baseaddr": 45056,
|
||||
"highaddr": 49151
|
||||
},
|
||||
"axis_data_fifo_1": {
|
||||
"vlnv": "xilinx.com:ip:axis_data_fifo:1.1",
|
||||
"port": 6
|
||||
"pcie_0_axi_reset_0": {
|
||||
"vlnv": "xilinx.com:ip:axi_gpio:2.0",
|
||||
"baseaddr": 28672,
|
||||
"highaddr": 32767
|
||||
},
|
||||
"timer_0_axi_timer_0": {
|
||||
"vlnv": "xilinx.com:ip:axi_timer:2.0",
|
||||
"baseaddr": 16384,
|
||||
"highaddr": 20479,
|
||||
"irqs": {
|
||||
"generateout0": "pcie_0_axi_pcie_intc_0:0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
55
fpga/include/villas/dependency_graph.hpp
Normal file
55
fpga/include/villas/dependency_graph.hpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
#ifndef VILLAS_DEPENDENCY_GRAPH_HPP
|
||||
#define VILLAS_DEPENDENCY_GRAPH_HPP
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
#include "log.hpp"
|
||||
|
||||
namespace villas {
|
||||
namespace utils {
|
||||
|
||||
|
||||
template<typename T>
|
||||
class DependencyGraph {
|
||||
public:
|
||||
using NodeList = std::list<T>;
|
||||
|
||||
/// Create a node without dependencies if it not yet exists, return if a new
|
||||
/// node has been created.
|
||||
bool addNode(const T& node);
|
||||
|
||||
/// Remove a node and all other nodes that depend on it
|
||||
void removeNode(const T& node);
|
||||
|
||||
/// Add a dependency to a node. Will create the node if it not yet exists
|
||||
void addDependency(const T& node, const T& dependency);
|
||||
|
||||
void dump();
|
||||
|
||||
/// Return a sequential evaluation order list. If a circular dependency has been
|
||||
/// detected, all nodes involved will not be part of that list.
|
||||
NodeList getEvaluationOrder() const;
|
||||
|
||||
private:
|
||||
/// Return whether a node already exists or not
|
||||
bool nodeExists(const T& node)
|
||||
{ return graph.find(node) != graph.end(); }
|
||||
|
||||
static bool
|
||||
nodeInList(const NodeList& list, const T& node)
|
||||
{ return list.end() != std::find(list.begin(), list.end(), node); }
|
||||
|
||||
private:
|
||||
using Graph = std::map<T, NodeList>;
|
||||
|
||||
Graph graph;
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
} // namespace villas
|
||||
|
||||
#include "dependency_graph_impl.hpp"
|
||||
|
||||
#endif // VILLAS_DEPENDENCY_GRAPH_HPP
|
110
fpga/include/villas/dependency_graph_impl.hpp
Normal file
110
fpga/include/villas/dependency_graph_impl.hpp
Normal file
|
@ -0,0 +1,110 @@
|
|||
#ifndef VILLAS_DEPENDENCY_GRAPH_HPP
|
||||
#error "Do not include this file directly, please include depedency_graph.hpp"
|
||||
#endif
|
||||
|
||||
#include <sstream>
|
||||
#include "dependency_graph.hpp"
|
||||
|
||||
#include "log.hpp"
|
||||
|
||||
static auto logger = loggerGetOrCreate("DependencyGraph");
|
||||
|
||||
namespace villas {
|
||||
namespace utils {
|
||||
|
||||
template<typename T>
|
||||
bool
|
||||
DependencyGraph<T>::addNode(const T &node)
|
||||
{
|
||||
bool existedBefore = nodeExists(node);
|
||||
|
||||
// accessing is enough to create if not exists
|
||||
graph[node];
|
||||
|
||||
return existedBefore;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
DependencyGraph<T>::removeNode(const T &node)
|
||||
{
|
||||
graph.erase(node);
|
||||
|
||||
// check if other nodes depend on this one
|
||||
for(auto& [key, dependencies] : graph) {
|
||||
if(nodeInList(dependencies, node)) {
|
||||
// remove other node that depends on the one to delete
|
||||
removeNode(key);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
DependencyGraph<T>::addDependency(const T &node, const T &dependency)
|
||||
{
|
||||
NodeList& dependencies = graph[node];
|
||||
if(not nodeInList(dependencies, dependency))
|
||||
dependencies.push_back(dependency);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
DependencyGraph<T>::dump() {
|
||||
for(auto& node : graph) {
|
||||
std::stringstream ss;
|
||||
for(auto& dep : node.second) {
|
||||
ss << dep << " ";
|
||||
}
|
||||
logger->info("{}: {}", node.first, ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename DependencyGraph<T>::NodeList
|
||||
DependencyGraph<T>::getEvaluationOrder() const
|
||||
{
|
||||
// copy graph to preserve information (we have to delete entries later)
|
||||
Graph graph = this->graph;
|
||||
|
||||
// output list
|
||||
NodeList out;
|
||||
|
||||
while(graph.size() > 0) {
|
||||
int added = 0;
|
||||
|
||||
// look for nodes with no dependencies
|
||||
for(auto& [key, dependencies] : graph) {
|
||||
|
||||
for(auto dep = dependencies.begin(); dep != dependencies.end(); ++dep) {
|
||||
if(nodeInList(out, *dep)) {
|
||||
// dependency has been pushed to list in last round
|
||||
dep = dependencies.erase(dep);
|
||||
}
|
||||
}
|
||||
|
||||
// nodes with no dependencies can be pushed to list
|
||||
if(dependencies.empty()) {
|
||||
out.push_back(key);
|
||||
graph.erase(key);
|
||||
added++;
|
||||
}
|
||||
}
|
||||
|
||||
// if a round doesn't add any elements and is not the last, then
|
||||
// there is a circular dependency
|
||||
if(added == 0 and graph.size() > 0) {
|
||||
logger->error("Circular dependency detected! IPs not available:");
|
||||
for(auto& [key, value] : graph) {
|
||||
logger->error(" {}", key);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace villas
|
271
fpga/include/villas/directed_graph.hpp
Normal file
271
fpga/include/villas/directed_graph.hpp
Normal file
|
@ -0,0 +1,271 @@
|
|||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#include "log.hpp"
|
||||
|
||||
|
||||
namespace villas {
|
||||
namespace graph {
|
||||
|
||||
// use vector indices as identifiers
|
||||
|
||||
// forward declarations
|
||||
class Edge;
|
||||
class Vertex;
|
||||
|
||||
|
||||
class Vertex {
|
||||
template<typename VertexType, typename EdgeType>
|
||||
friend class DirectedGraph;
|
||||
|
||||
public:
|
||||
using Identifier = std::size_t;
|
||||
|
||||
friend std::ostream&
|
||||
operator<< (std::ostream& stream, const Vertex& vertex)
|
||||
{ return stream << vertex.id; }
|
||||
|
||||
bool
|
||||
operator==(const Vertex& other)
|
||||
{ return this->id == other.id; }
|
||||
|
||||
private:
|
||||
Identifier id;
|
||||
// HACK: how to resolve this circular type dependency?
|
||||
std::list<std::size_t> edges;
|
||||
};
|
||||
|
||||
|
||||
class Edge {
|
||||
template<typename VertexType, typename EdgeType>
|
||||
friend class DirectedGraph;
|
||||
|
||||
public:
|
||||
using Identifier = std::size_t;
|
||||
|
||||
friend std::ostream&
|
||||
operator<< (std::ostream& stream, const Edge& edge)
|
||||
{ return stream << edge.id; }
|
||||
|
||||
bool
|
||||
operator==(const Edge& other)
|
||||
{ return this->id == other.id; }
|
||||
|
||||
private:
|
||||
Identifier id;
|
||||
Vertex::Identifier from;
|
||||
Vertex::Identifier to;
|
||||
};
|
||||
|
||||
|
||||
template<typename VertexType = Vertex, typename EdgeType = Edge>
|
||||
class DirectedGraph {
|
||||
public:
|
||||
|
||||
using VertexIdentifier = Vertex::Identifier;
|
||||
using EdgeIdentifier = Edge::Identifier;
|
||||
|
||||
DirectedGraph(const std::string& name = "DirectedGraph") :
|
||||
lastVertexId(0), lastEdgeId(0)
|
||||
{
|
||||
logger = loggerGetOrCreate(name);
|
||||
}
|
||||
|
||||
std::shared_ptr<VertexType> getVertex(VertexIdentifier vertexId) const
|
||||
{
|
||||
if(vertexId < 0 or vertexId >= lastVertexId)
|
||||
throw std::invalid_argument("vertex doesn't exist");
|
||||
|
||||
// cannot use [] operator, because creates non-existing elements
|
||||
// at() will throw std::out_of_range if element does not exist
|
||||
return vertices.at(vertexId);
|
||||
}
|
||||
|
||||
std::shared_ptr<EdgeType> getEdge(EdgeIdentifier edgeId) const
|
||||
{
|
||||
if(edgeId < 0 or edgeId >= lastEdgeId)
|
||||
throw std::invalid_argument("edge doesn't exist");
|
||||
|
||||
// cannot use [] operator, because creates non-existing elements
|
||||
// at() will throw std::out_of_range if element does not exist
|
||||
return edges.at(edgeId);
|
||||
}
|
||||
|
||||
std::size_t getEdgeCount() const
|
||||
{ return edges.size(); }
|
||||
|
||||
std::size_t getVertexCount() const
|
||||
{ return vertices.size(); }
|
||||
|
||||
VertexIdentifier addVertex(std::shared_ptr<VertexType> vertex)
|
||||
{
|
||||
vertex->id = lastVertexId++;
|
||||
|
||||
logger->debug("New vertex: {}", *vertex);
|
||||
vertices[vertex->id] = vertex;
|
||||
|
||||
return vertex->id;
|
||||
}
|
||||
|
||||
EdgeIdentifier addEdge(std::shared_ptr<EdgeType> edge,
|
||||
VertexIdentifier fromVertexId,
|
||||
VertexIdentifier toVertexId)
|
||||
{
|
||||
// allocate edge id
|
||||
edge->id = lastEdgeId++;
|
||||
|
||||
// connect it
|
||||
edge->from = fromVertexId;
|
||||
edge->to = toVertexId;
|
||||
|
||||
logger->debug("New edge {}: {} -> {}", *edge, edge->from, edge->to);
|
||||
|
||||
// this is a directed graph, so only push edge to starting vertex
|
||||
getVertex(edge->from)->edges.push_back(edge->id);
|
||||
|
||||
// add new edge to graph
|
||||
edges[edge->id] = edge;
|
||||
|
||||
return edge->id;
|
||||
}
|
||||
|
||||
|
||||
EdgeIdentifier addDefaultEdge(VertexIdentifier fromVertexId,
|
||||
VertexIdentifier toVertexId)
|
||||
{
|
||||
// create a new edge
|
||||
std::shared_ptr<EdgeType> edge(new EdgeType);
|
||||
|
||||
return addEdge(edge, fromVertexId, toVertexId);
|
||||
}
|
||||
|
||||
void removeEdge(EdgeIdentifier edgeId)
|
||||
{
|
||||
auto edge = getEdge(edgeId);
|
||||
auto startVertex = getVertex(edge->from);
|
||||
|
||||
// remove edge only from starting vertex (this is a directed graph)
|
||||
logger->debug("Remove edge {} from vertex {}", edgeId, edge->from);
|
||||
startVertex->edges.remove(edgeId);
|
||||
|
||||
logger->debug("Remove edge {}", edgeId);
|
||||
edges.erase(edgeId);
|
||||
}
|
||||
|
||||
void removeVertex(VertexIdentifier vertexId)
|
||||
{
|
||||
// delete every edge that start or ends at this vertex
|
||||
auto it = edges.begin();
|
||||
while(it != edges.end()) {
|
||||
auto& [edgeId, edge] = *it;
|
||||
bool removeEdge = false;
|
||||
|
||||
if(edge->to == vertexId) {
|
||||
logger->debug("Remove edge {} from vertex {}'s edge list",
|
||||
edgeId, edge->from);
|
||||
|
||||
removeEdge = true;
|
||||
|
||||
auto startVertex = getVertex(edge->from);
|
||||
startVertex->edges.remove(edge->id);
|
||||
}
|
||||
|
||||
if((edge->from == vertexId) or removeEdge) {
|
||||
logger->debug("Remove edge {}", edgeId);
|
||||
// remove edge from global edge list
|
||||
it = edges.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
logger->debug("Remove vertex {}", vertexId);
|
||||
vertices.erase(vertexId);
|
||||
}
|
||||
|
||||
const std::list<EdgeIdentifier>&
|
||||
vertexGetEdges(VertexIdentifier vertexId) const
|
||||
{ return getVertex(vertexId)->edges; }
|
||||
|
||||
bool getPath(VertexIdentifier fromVertexId, VertexIdentifier toVertexId,
|
||||
std::list<EdgeIdentifier>& path)
|
||||
{
|
||||
if(fromVertexId == toVertexId) {
|
||||
// arrived at the destination
|
||||
return true;
|
||||
} else {
|
||||
auto fromVertex = getVertex(fromVertexId);
|
||||
|
||||
for(auto& edgeId : fromVertex->edges) {
|
||||
auto edge = getEdge(edgeId);
|
||||
|
||||
// loop detection
|
||||
bool loop = false;
|
||||
for(auto& edgeIdInPath : path) {
|
||||
auto edgeInPath = getEdge(edgeIdInPath);
|
||||
if(edgeInPath->from == edgeId) {
|
||||
loop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(loop) {
|
||||
logger->debug("Loop detected via edge {}", edgeId);
|
||||
continue;
|
||||
}
|
||||
|
||||
// remember the path we're investigating to detect loops
|
||||
path.push_back(edgeId);
|
||||
|
||||
// recursive, depth-first search
|
||||
if(getPath(edge->to, toVertexId, path)) {
|
||||
// path found, we're done
|
||||
return true;
|
||||
} else {
|
||||
// tear down path that didn't lead to the destination
|
||||
path.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void dump()
|
||||
{
|
||||
logger->info("Vertices:");
|
||||
for(auto& [vertexId, vertex] : vertices) {
|
||||
// format connected vertices into a list
|
||||
std::stringstream ssEdges;
|
||||
for(auto& edge : vertex->edges) {
|
||||
ssEdges << getEdge(edge)->to << " ";
|
||||
}
|
||||
|
||||
logger->info(" {} connected to: {}", *vertex, ssEdges.str());
|
||||
}
|
||||
|
||||
logger->info("Edges:");
|
||||
for(auto& [edgeId, edge] : edges) {
|
||||
logger->info(" {}: {} -> {}", *edge, edge->from, edge->to);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
VertexIdentifier lastVertexId;
|
||||
EdgeIdentifier lastEdgeId;
|
||||
|
||||
std::map<VertexIdentifier, std::shared_ptr<VertexType>> vertices;
|
||||
std::map<EdgeIdentifier, std::shared_ptr<EdgeType>> edges;
|
||||
|
||||
SpdLogger logger;
|
||||
};
|
||||
|
||||
} // namespacae graph
|
||||
} // namespace villas
|
|
@ -35,6 +35,10 @@
|
|||
#include "kernel/pci.h"
|
||||
#include "kernel/vfio.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Forward declarations */
|
||||
struct fpga_ip;
|
||||
struct vfio_container;
|
||||
|
@ -92,4 +96,8 @@ void fpga_card_dump(struct fpga_card *c);
|
|||
/** Reset the FPGA to a known state */
|
||||
int fpga_card_reset(struct fpga_card *c);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
|
125
fpga/include/villas/fpga/card.hpp
Normal file
125
fpga/include/villas/fpga/card.hpp
Normal file
|
@ -0,0 +1,125 @@
|
|||
/** FPGA card
|
||||
*
|
||||
* This class represents a FPGA device.
|
||||
*
|
||||
* @file
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @author Daniel Krebs <github@daniel-krebs.net>
|
||||
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
|
||||
* @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 <jansson.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "kernel/pci.h"
|
||||
#include "kernel/vfio.h"
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
#include "plugin.hpp"
|
||||
#include "fpga/ip.hpp"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define PCI_FILTER_DEFAULT_FPGA { \
|
||||
.id = { \
|
||||
.vendor = FPGA_PCI_VID_XILINX, \
|
||||
.device = FPGA_PCI_PID_VFPGA \
|
||||
} \
|
||||
}
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
struct vfio_container;
|
||||
class PCIeCardFactory;
|
||||
|
||||
class PCIeCard {
|
||||
public:
|
||||
|
||||
friend PCIeCardFactory;
|
||||
|
||||
PCIeCard() : filter(PCI_FILTER_DEFAULT_FPGA) {}
|
||||
|
||||
bool init();
|
||||
bool stop() { return true; }
|
||||
bool check() { return true; }
|
||||
bool reset() { return true; }
|
||||
void dump() { }
|
||||
|
||||
ip::IpCore* lookupIp(std::string name) const;
|
||||
|
||||
ip::IpCoreList ips; ///< IPs located on this FPGA card
|
||||
|
||||
bool do_reset; /**< Reset VILLASfpga during startup? */
|
||||
int affinity; /**< Affinity for MSI interrupts */
|
||||
|
||||
|
||||
std::string name; /**< The name of the FPGA card */
|
||||
|
||||
struct pci *pci;
|
||||
struct pci_device filter; /**< Filter for PCI device. */
|
||||
|
||||
::vfio_container *vfio_container;
|
||||
struct vfio_device vfio_device; /**< VFIO device handle. */
|
||||
|
||||
char *map; /**< PCI BAR0 mapping for register access */
|
||||
|
||||
size_t maplen;
|
||||
size_t dmalen;
|
||||
|
||||
protected:
|
||||
SpdLogger
|
||||
getLogger() const
|
||||
{ return loggerGetOrCreate(name); }
|
||||
};
|
||||
|
||||
using CardList = std::list<std::unique_ptr<PCIeCard>>;
|
||||
|
||||
class PCIeCardFactory : public Plugin {
|
||||
public:
|
||||
|
||||
PCIeCardFactory() :
|
||||
Plugin(Plugin::Type::FpgaCard, "FPGA Card plugin") {}
|
||||
|
||||
static CardList
|
||||
make(json_t *json, struct pci* pci, ::vfio_container* vc);
|
||||
|
||||
static PCIeCard*
|
||||
create();
|
||||
|
||||
static SpdLogger
|
||||
getStaticLogger()
|
||||
{ return loggerGetOrCreate("PCIeCardFactory"); }
|
||||
};
|
||||
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
||||
|
||||
/** @} */
|
|
@ -44,6 +44,10 @@
|
|||
#include "fpga/ips/dft.h"
|
||||
#include "fpga/ips/intc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum fpga_ip_types {
|
||||
FPGA_IP_TYPE_DM_DMA, /**< A datamover IP exchanges streaming data between the FPGA and the CPU. */
|
||||
FPGA_IP_TYPE_DM_FIFO, /**< A datamover IP exchanges streaming data between the FPGA and the CPU. */
|
||||
|
@ -51,7 +55,7 @@ enum fpga_ip_types {
|
|||
FPGA_IP_TYPE_MATH, /**< A math IP performs some kind of mathematical operation on the streaming data */
|
||||
FPGA_IP_TYPE_MISC, /**< Other IP components like timer, counters, interrupt conctrollers or routing. */
|
||||
FPGA_IP_TYPE_INTERFACE /**< A interface IP connects the FPGA to another system or controller. */
|
||||
} type;
|
||||
};
|
||||
|
||||
struct fpga_ip_type {
|
||||
struct fpga_vlnv vlnv;
|
||||
|
@ -115,5 +119,8 @@ int fpga_ip_reset(struct fpga_ip *c);
|
|||
/** Find a registered FPGA IP core type with the given VLNV identifier. */
|
||||
struct fpga_ip_type * fpga_ip_type_lookup(const char *vstr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
|
188
fpga/include/villas/fpga/ip.hpp
Normal file
188
fpga/include/villas/fpga/ip.hpp
Normal file
|
@ -0,0 +1,188 @@
|
|||
/** Interlectual Property component.
|
||||
*
|
||||
* This class represents a module within the FPGA.
|
||||
*
|
||||
* @file
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @author Daniel Krebs <github@daniel-krebs.net>
|
||||
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
|
||||
* @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
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef VILLAS_IP_HPP
|
||||
#define VILLAS_IP_HPP
|
||||
|
||||
#include "fpga/vlnv.hpp"
|
||||
#include "plugin.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
|
||||
class PCIeCard;
|
||||
|
||||
namespace ip {
|
||||
|
||||
|
||||
class IpIdentifier {
|
||||
public:
|
||||
IpIdentifier(Vlnv vlnv = Vlnv::getWildcard(), std::string name = "") :
|
||||
vlnv(vlnv), name(name) {}
|
||||
|
||||
IpIdentifier(std::string vlnvString, std::string name = "") :
|
||||
vlnv(vlnvString), name(name) {}
|
||||
|
||||
friend std::ostream&
|
||||
operator<< (std::ostream& stream, const IpIdentifier& id)
|
||||
{ return stream << TXT_BOLD(id.name) << " vlnv=" << id.vlnv; }
|
||||
|
||||
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) {}
|
||||
virtual ~IpCore() {}
|
||||
|
||||
// IPs can implement this interface
|
||||
virtual bool check() { return true; }
|
||||
virtual bool init() { return true; }
|
||||
virtual bool stop() { return true; }
|
||||
virtual bool reset() { return true; }
|
||||
virtual void dump();
|
||||
|
||||
bool
|
||||
operator== (const IpIdentifier& otherId) {
|
||||
const bool vlnvMatch = id.vlnv == otherId.vlnv;
|
||||
const bool nameWildcard = id.name.empty() or otherId.name.empty();
|
||||
|
||||
return vlnvMatch and (nameWildcard or id.name == otherId.name);
|
||||
}
|
||||
|
||||
bool
|
||||
operator!= (const IpIdentifier& otherId) {
|
||||
return !(*this == otherId);
|
||||
}
|
||||
|
||||
bool
|
||||
operator== (const Vlnv& otherVlnv)
|
||||
{ return id.vlnv == otherVlnv; }
|
||||
|
||||
bool
|
||||
operator== (const std::string& otherName)
|
||||
{ return id.name == otherName; }
|
||||
|
||||
|
||||
friend std::ostream&
|
||||
operator<< (std::ostream& stream, const IpCore& ip)
|
||||
{ return stream << ip.id; }
|
||||
|
||||
protected:
|
||||
uintptr_t
|
||||
getBaseaddr() const
|
||||
{ return getAddrMapped(this->baseaddr); }
|
||||
|
||||
uintptr_t
|
||||
getAddrMapped(uintptr_t address) const;
|
||||
|
||||
SpdLogger
|
||||
getLogger() { return loggerGetOrCreate(id.name); }
|
||||
|
||||
struct IrqPort {
|
||||
int num;
|
||||
std::string controllerName;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
protected:
|
||||
// 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
|
||||
};
|
||||
|
||||
|
||||
using IpCoreList = std::list<std::unique_ptr<IpCore>>;
|
||||
|
||||
|
||||
class IpCoreFactory : public Plugin {
|
||||
public:
|
||||
IpCoreFactory(std::string concreteName) :
|
||||
Plugin(Plugin::Type::FpgaIp, std::string("IpCore - ") + concreteName)
|
||||
{}
|
||||
|
||||
/// Returns a running and checked FPGA IP
|
||||
static IpCoreList
|
||||
make(PCIeCard* card, json_t *json_ips);
|
||||
|
||||
protected:
|
||||
SpdLogger
|
||||
getLogger() { return loggerGetOrCreate(getName()); }
|
||||
|
||||
private:
|
||||
/// Create a concrete IP instance
|
||||
virtual IpCore* create() = 0;
|
||||
|
||||
/// Configure IP instance from JSON config
|
||||
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
|
||||
getStaticLogger() { return loggerGetOrCreate("IpCoreFactory"); }
|
||||
|
||||
private:
|
||||
static IpCoreFactory*
|
||||
lookup(const Vlnv& vlnv);
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
||||
} // namespace ip
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
||||
|
||||
#endif // VILLAS_IP_HPP
|
84
fpga/include/villas/fpga/ip_node.hpp
Normal file
84
fpga/include/villas/fpga/ip_node.hpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
/** Interlectual Property component.
|
||||
*
|
||||
* This class represents a module within the FPGA.
|
||||
*
|
||||
* @file
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @author Daniel Krebs <github@daniel-krebs.net>
|
||||
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
|
||||
* @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
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef VILLAS_IP_NODE_HPP
|
||||
#define VILLAS_IP_NODE_HPP
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <jansson.h>
|
||||
|
||||
#include "ip.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
namespace ip {
|
||||
|
||||
// TODO: reflect on interface that an IpNode exposes and how to design it to
|
||||
// blend in with VILLASnode software nodes
|
||||
class IpNode : public IpCore {
|
||||
public:
|
||||
|
||||
friend class IpNodeFactory;
|
||||
|
||||
struct StreamPort {
|
||||
int portNumber;
|
||||
std::string nodeName;
|
||||
};
|
||||
|
||||
bool connect(std::string portName, const StreamPort& to);
|
||||
bool disconnect(std::string portName);
|
||||
|
||||
bool loopbackPossible() const;
|
||||
bool connectLoopback();
|
||||
|
||||
private:
|
||||
std::pair<std::string, std::string> getLoopbackPorts() const;
|
||||
|
||||
protected:
|
||||
std::map<std::string, StreamPort> portsMaster;
|
||||
std::map<std::string, StreamPort> portsSlave;
|
||||
};
|
||||
|
||||
class IpNodeFactory : public IpCoreFactory {
|
||||
public:
|
||||
IpNodeFactory(std::string name) : IpCoreFactory("Ip Node - " + name) {}
|
||||
|
||||
virtual bool configureJson(IpCore& ip, json_t *json_ip);
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
||||
} // namespace ip
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
||||
|
||||
#endif // VILLAS_IP_NODE_HPP
|
89
fpga/include/villas/fpga/ips/fifo.hpp
Normal file
89
fpga/include/villas/fpga/ips/fifo.hpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
/** Timer related helper functions
|
||||
*
|
||||
* These functions present a simpler interface to Xilinx' Timer Counter driver (XTmrCtr_*)
|
||||
*
|
||||
* @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 "fpga/ip_node.hpp"
|
||||
#include <xilinx/xllfifo.h>
|
||||
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
namespace ip {
|
||||
|
||||
|
||||
class Fifo : public IpNode
|
||||
{
|
||||
public:
|
||||
friend class FifoFactory;
|
||||
|
||||
bool init();
|
||||
bool stop();
|
||||
|
||||
size_t write(const void* buf, size_t len);
|
||||
size_t read(void* buf, size_t len);
|
||||
|
||||
private:
|
||||
XLlFifo xFifo;
|
||||
uintptr_t baseaddr_axi4;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class FifoFactory : public IpNodeFactory {
|
||||
public:
|
||||
FifoFactory() :
|
||||
IpNodeFactory(getName())
|
||||
{}
|
||||
|
||||
bool configureJson(IpCore& ip, json_t *json_ip);
|
||||
|
||||
IpCore* create()
|
||||
{ return new Fifo; }
|
||||
|
||||
std::string
|
||||
getName() const
|
||||
{ return "Fifo"; }
|
||||
|
||||
std::string
|
||||
getDescription() const
|
||||
{ return "Xilinx's AXI4 FIFO data mover"; }
|
||||
|
||||
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
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
||||
|
||||
/** @} */
|
104
fpga/include/villas/fpga/ips/intc.hpp
Normal file
104
fpga/include/villas/fpga/ips/intc.hpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
/** AXI-PCIe Interrupt controller
|
||||
*
|
||||
* @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 "fpga/ip.hpp"
|
||||
#include <xilinx/xintc.h>
|
||||
|
||||
#include "fpga/ip_node.hpp"
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
namespace ip {
|
||||
|
||||
|
||||
class InterruptController : public IpCore
|
||||
{
|
||||
public:
|
||||
using IrqMaskType = uint32_t;
|
||||
static constexpr int maxIrqs = 32;
|
||||
|
||||
~InterruptController();
|
||||
|
||||
bool init();
|
||||
|
||||
bool enableInterrupt(IrqMaskType mask, bool polling);
|
||||
bool enableInterrupt(IrqPort irq, bool polling)
|
||||
{ return enableInterrupt(1 << irq.num, polling); }
|
||||
|
||||
bool disableInterrupt(IrqMaskType mask);
|
||||
bool disableInterrupt(IrqPort irq)
|
||||
{ return disableInterrupt(1 << irq.num); }
|
||||
|
||||
int waitForInterrupt(int irq);
|
||||
int waitForInterrupt(IrqPort irq)
|
||||
{ return waitForInterrupt(irq.num); }
|
||||
|
||||
private:
|
||||
struct Interrupt {
|
||||
int eventFd; /**< Event file descriptor */
|
||||
int number; /**< Interrupt number from /proc/interrupts */
|
||||
bool polling; /**< Polled or not */
|
||||
};
|
||||
|
||||
int num_irqs; /**< Number of available MSI vectors */
|
||||
int efds[maxIrqs];
|
||||
int nos[maxIrqs];
|
||||
bool polling[maxIrqs];
|
||||
};
|
||||
|
||||
|
||||
|
||||
class InterruptControllerFactory : public IpCoreFactory {
|
||||
public:
|
||||
|
||||
InterruptControllerFactory() :
|
||||
IpCoreFactory(getName())
|
||||
{}
|
||||
|
||||
IpCore* create()
|
||||
{ return new InterruptController; }
|
||||
|
||||
std::string
|
||||
getName() const
|
||||
{ return "InterruptController"; }
|
||||
|
||||
std::string
|
||||
getDescription() const
|
||||
{ return "Xilinx's programmable interrupt controller"; }
|
||||
|
||||
Vlnv getCompatibleVlnv() const
|
||||
{ return Vlnv("acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:"); }
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
||||
|
||||
/** @} */
|
94
fpga/include/villas/fpga/ips/switch.hpp
Normal file
94
fpga/include/villas/fpga/ips/switch.hpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
/** 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 AxiStreamSwitch : public IpNode {
|
||||
public:
|
||||
friend class AxiStreamSwitchFactory;
|
||||
|
||||
bool init();
|
||||
|
||||
bool connect(int portSlave, int portMaster);
|
||||
bool disconnectMaster(int port);
|
||||
bool disconnectSlave(int port);
|
||||
|
||||
private:
|
||||
static constexpr int PORT_DISABLED = -1;
|
||||
|
||||
struct Path {
|
||||
IpCore* masterOut;
|
||||
IpCore* slaveIn;
|
||||
};
|
||||
|
||||
int num_ports;
|
||||
XAxis_Switch xSwitch;
|
||||
std::map<int, int> portMapping;
|
||||
};
|
||||
|
||||
|
||||
class AxiStreamSwitchFactory : public IpNodeFactory {
|
||||
public:
|
||||
AxiStreamSwitchFactory() :
|
||||
IpNodeFactory(getName()) {}
|
||||
|
||||
bool configureJson(IpCore& ip, json_t *json_ip);
|
||||
|
||||
IpCore* create()
|
||||
{ return new AxiStreamSwitch; }
|
||||
|
||||
std::string getName() const
|
||||
{ return "AxiStreamSwitch"; }
|
||||
|
||||
std::string getDescription() const
|
||||
{ return "Xilinx's AXI4-Stream switch"; }
|
||||
|
||||
Vlnv getCompatibleVlnv() const
|
||||
{ return Vlnv("xilinx.com:ip:axis_switch:"); }
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
||||
|
||||
/** @} */
|
100
fpga/include/villas/fpga/ips/timer.hpp
Normal file
100
fpga/include/villas/fpga/ips/timer.hpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
/** Timer related helper functions
|
||||
*
|
||||
* These functions present a simpler interface to Xilinx' Timer Counter driver (XTmrCtr_*)
|
||||
*
|
||||
* @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 <stdint.h>
|
||||
#include <xilinx/xtmrctr.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "fpga/ip.hpp"
|
||||
#include "fpga/ips/intc.hpp"
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
namespace ip {
|
||||
|
||||
|
||||
class Timer : public IpCore
|
||||
{
|
||||
public:
|
||||
bool init();
|
||||
|
||||
|
||||
bool start(uint32_t ticks);
|
||||
bool wait();
|
||||
uint32_t remaining();
|
||||
|
||||
inline bool isRunning()
|
||||
{ return remaining() != 0; }
|
||||
|
||||
inline bool isFinished()
|
||||
{ return remaining() == 0; }
|
||||
|
||||
static constexpr uint32_t
|
||||
getFrequency()
|
||||
{ return FPGA_AXI_HZ; }
|
||||
|
||||
private:
|
||||
XTmrCtr xTmr;
|
||||
InterruptController* intc;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class TimerFactory : public IpCoreFactory {
|
||||
public:
|
||||
|
||||
TimerFactory() :
|
||||
IpCoreFactory(getName())
|
||||
{}
|
||||
|
||||
IpCore* create()
|
||||
{ return new Timer; }
|
||||
|
||||
std::string
|
||||
getName() const
|
||||
{ return "Timer"; }
|
||||
|
||||
std::string
|
||||
getDescription() const
|
||||
{ return "Xilinx's programmable timer / counter"; }
|
||||
|
||||
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
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
||||
|
||||
/** @} */
|
|
@ -28,6 +28,10 @@
|
|||
#ifndef _FPGA_VLNV_H_
|
||||
#define _FPGA_VLNV_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Forward declarations */
|
||||
struct list;
|
||||
|
||||
|
@ -50,4 +54,8 @@ int fpga_vlnv_parse(struct fpga_vlnv *c, const char *vlnv);
|
|||
/** Release memory allocated by fpga_vlnv_parse(). */
|
||||
int fpga_vlnv_destroy(struct fpga_vlnv *v);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /** _FPGA_VLNV_H_ @} */
|
||||
|
|
92
fpga/include/villas/fpga/vlnv.hpp
Normal file
92
fpga/include/villas/fpga/vlnv.hpp
Normal file
|
@ -0,0 +1,92 @@
|
|||
/** Vendor, Library, Name, Version (VLNV) tag.
|
||||
*
|
||||
* @file
|
||||
* @author Daniel Krebs <github@daniel-krebs.net>
|
||||
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
|
||||
* @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 <sstream>
|
||||
#include <iostream>
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
|
||||
|
||||
class Vlnv {
|
||||
public:
|
||||
|
||||
static constexpr char delimiter = ':';
|
||||
|
||||
Vlnv() :
|
||||
vendor(""), library(""), name(""), version("") {}
|
||||
|
||||
Vlnv(std::string s) {
|
||||
parseFromString(s);
|
||||
}
|
||||
|
||||
static Vlnv
|
||||
getWildcard()
|
||||
{ return Vlnv(); }
|
||||
|
||||
std::string
|
||||
toString() const
|
||||
{
|
||||
std::stringstream stream;
|
||||
std::string string;
|
||||
|
||||
stream << *this;
|
||||
stream >> string;
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(const Vlnv& other) const;
|
||||
|
||||
friend std::ostream&
|
||||
operator<< (std::ostream& stream, const Vlnv& vlnv)
|
||||
{
|
||||
return stream
|
||||
<< (vlnv.vendor.empty() ? "*" : vlnv.vendor) << ":"
|
||||
<< (vlnv.library.empty() ? "*" : vlnv.library) << ":"
|
||||
<< (vlnv.name.empty() ? "*" : vlnv.name) << ":"
|
||||
<< (vlnv.version.empty() ? "*" : vlnv.version);
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
parseFromString(std::string vlnv);
|
||||
|
||||
std::string vendor;
|
||||
std::string library;
|
||||
std::string name;
|
||||
std::string version;
|
||||
};
|
||||
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
||||
|
||||
/** _FPGA_VLNV_HPP_ @} */
|
|
@ -28,6 +28,10 @@
|
|||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Forward declarations */
|
||||
struct version;
|
||||
|
||||
|
@ -85,6 +89,10 @@ int kernel_get_page_size();
|
|||
int kernel_get_hugepage_size();
|
||||
|
||||
/** Set SMP affinity of IRQ */
|
||||
int kernel_irq_setaffinity(unsigned irq, uintmax_t new, uintmax_t *old);
|
||||
int kernel_irq_setaffinity(unsigned irq, uintmax_t affinity, uintmax_t *old);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
|
|
@ -14,11 +14,15 @@
|
|||
#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
|
||||
#define PCI_FUNC(devfn) ((devfn) & 0x07)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct pci_device {
|
||||
struct {
|
||||
int vendor;
|
||||
int device;
|
||||
int class;
|
||||
int class_code;
|
||||
} id;
|
||||
|
||||
struct {
|
||||
|
@ -53,10 +57,17 @@ int pci_device_compare(const struct pci_device *d, const struct pci_device *f);
|
|||
|
||||
struct pci_device * pci_lookup_device(struct pci *p, struct pci_device *filter);
|
||||
|
||||
/** Get currently loaded driver for device */
|
||||
int pci_get_driver(struct pci_device *d, char *buf, size_t buflen);
|
||||
|
||||
/** Bind a new LKM to the PCI device */
|
||||
int pci_attach_driver(struct pci_device *d, const char *driver);
|
||||
|
||||
/** Return the IOMMU group of this PCI device or -1 if the device is not in a group. */
|
||||
int pci_get_iommu_group(struct pci_device *d);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
|
||||
#define VFIO_DEV(x) "/dev/vfio/" x
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Forward declarations */
|
||||
struct pci_device;
|
||||
|
||||
|
@ -109,4 +113,8 @@ int vfio_unmap_dma(struct vfio_container *c, uint64_t virt, uint64_t phys, size_
|
|||
/** munmap() a region which has been mapped by vfio_map_region() */
|
||||
int vfio_unmap_region(struct vfio_device *d, int idx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
|
|
@ -45,6 +45,10 @@ __attribute__((destructor(105))) static void UNIQUE(__dtor)() { \
|
|||
#define list_first(list) list_at(list, 0)
|
||||
#define list_last(list) list_at(list, (list)->length-1)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Callback to destroy list elements.
|
||||
*
|
||||
* @param data A pointer to the data which should be freed.
|
||||
|
@ -110,3 +114,7 @@ void list_sort(struct list *l, cmp_cb_t cmp);
|
|||
|
||||
/** Set single element in list */
|
||||
int list_set(struct list *l, int index, void *value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -106,8 +106,8 @@ struct log {
|
|||
};
|
||||
|
||||
/** The global log instance. */
|
||||
struct log *global_log;
|
||||
struct log default_log;
|
||||
extern struct log *global_log;
|
||||
extern struct log default_log;
|
||||
|
||||
/** Initialize log object */
|
||||
int log_init(struct log *l, int level, long faciltities);
|
||||
|
|
32
fpga/include/villas/log.hpp
Normal file
32
fpga/include/villas/log.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#define SPDLOG_LEVEL_NAMES { "trace", "debug", "info ", "warn ", "error", "crit ", "off " }
|
||||
#define SPDLOG_NAME_WIDTH 17
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/fmt/ostr.h>
|
||||
|
||||
#define _ESCAPE "\x1b"
|
||||
#define TXT_RESET_ALL _ESCAPE "[0m"
|
||||
|
||||
#define TXT_RESET_BOLD _ESCAPE "[21m"
|
||||
#define TXT_BOLD(s) _ESCAPE "[1m" + std::string(s) + TXT_RESET_BOLD
|
||||
|
||||
#define TXT_RESET_COLOR _ESCAPE "[39m"
|
||||
#define TXT_RED(s) _ESCAPE "[31m" + std::string(s) + TXT_RESET_COLOR
|
||||
#define TXT_GREEN(s) _ESCAPE "[32m" + std::string(s) + TXT_RESET_COLOR
|
||||
#define TXT_YELLOW(s) _ESCAPE "[33m" + std::string(s) + TXT_RESET_COLOR
|
||||
#define TXT_BLUE(s) _ESCAPE "[34m" + std::string(s) + TXT_RESET_COLOR
|
||||
|
||||
using SpdLogger = std::shared_ptr<spdlog::logger>;
|
||||
|
||||
inline SpdLogger loggerGetOrCreate(const std::string& logger_name)
|
||||
{
|
||||
auto logger = spdlog::get(logger_name);
|
||||
if(not logger) {
|
||||
logger = spdlog::stdout_color_mt(logger_name);
|
||||
}
|
||||
return logger;
|
||||
}
|
98
fpga/include/villas/memory_manager.hpp
Normal file
98
fpga/include/villas/memory_manager.hpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "log.hpp"
|
||||
#include "directed_graph.hpp"
|
||||
|
||||
namespace villas {
|
||||
|
||||
|
||||
class Mapping : public graph::Edge {
|
||||
friend class MemoryManager;
|
||||
|
||||
public:
|
||||
// create mapping here (if needed)
|
||||
Mapping() {}
|
||||
|
||||
// destroy mapping here (if needed)
|
||||
virtual ~Mapping();
|
||||
|
||||
friend std::ostream&
|
||||
operator<< (std::ostream& stream, const Mapping& mapping)
|
||||
{
|
||||
return stream << static_cast<const Edge&>(mapping) << " = "
|
||||
<< std::hex
|
||||
<< "(src=0x" << mapping.src
|
||||
<< ", dest=0x" << mapping.dest
|
||||
<< ", size=0x" << mapping.size
|
||||
<< ")";
|
||||
}
|
||||
|
||||
private:
|
||||
uintptr_t src;
|
||||
uintptr_t dest;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
class AddressSpace : public graph::Vertex {
|
||||
friend class MemoryManager;
|
||||
|
||||
public:
|
||||
friend std::ostream&
|
||||
operator<< (std::ostream& stream, const AddressSpace& addrSpace)
|
||||
{
|
||||
return stream << static_cast<const Vertex&>(addrSpace) << " = "
|
||||
<< addrSpace.name;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
};
|
||||
|
||||
|
||||
// is or has a graph
|
||||
class MemoryManager {
|
||||
private:
|
||||
// This is a singleton, so private constructor
|
||||
MemoryManager() :
|
||||
memoryGraph("MemoryGraph") {}
|
||||
|
||||
// no copying or assigning
|
||||
MemoryManager(const MemoryManager&) = delete;
|
||||
MemoryManager& operator=(const MemoryManager&) = delete ;
|
||||
|
||||
using MemoryGraph = graph::DirectedGraph<AddressSpace, Mapping>;
|
||||
|
||||
public:
|
||||
using AddressSpaceId = MemoryGraph::VertexIdentifier;
|
||||
using MappingId = MemoryGraph::EdgeIdentifier;
|
||||
|
||||
static MemoryManager& get();
|
||||
|
||||
|
||||
AddressSpaceId createAddressSpace(std::string name);
|
||||
|
||||
/// Create a default mapping
|
||||
MappingId createMapping(uintptr_t src, uintptr_t dest, size_t size,
|
||||
AddressSpaceId fromAddrSpace,
|
||||
AddressSpaceId toAddrSpace);
|
||||
|
||||
/// Add a mapping
|
||||
///
|
||||
/// Can be used to derive from Mapping in order to implement custom
|
||||
/// constructor/destructor.
|
||||
MappingId addMapping(std::shared_ptr<Mapping> mapping,
|
||||
AddressSpaceId fromAddrSpace,
|
||||
AddressSpaceId toAddrSpace);
|
||||
|
||||
void dump()
|
||||
{ memoryGraph.dump(); }
|
||||
|
||||
private:
|
||||
MemoryGraph memoryGraph;
|
||||
static MemoryManager* instance;
|
||||
};
|
||||
|
||||
} // namespace villas
|
112
fpga/include/villas/plugin.hpp
Normal file
112
fpga/include/villas/plugin.hpp
Normal file
|
@ -0,0 +1,112 @@
|
|||
/** Loadable / plugin support.
|
||||
*
|
||||
* @file
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @author Daniel Krebs <github@daniel-krebs.net>
|
||||
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <jansson.h>
|
||||
|
||||
#include "log.hpp"
|
||||
#include "utils.h"
|
||||
|
||||
namespace villas {
|
||||
|
||||
class Plugin {
|
||||
public:
|
||||
|
||||
enum class Type {
|
||||
Unknown,
|
||||
FpgaIp,
|
||||
FpgaCard,
|
||||
};
|
||||
|
||||
Plugin(Type type, const std::string& name);
|
||||
virtual ~Plugin();
|
||||
|
||||
// copying a plugin doesn't make sense, so explicitly deny it
|
||||
Plugin(Plugin const&) = delete;
|
||||
void operator=(Plugin const&) = delete;
|
||||
|
||||
int load();
|
||||
int unload();
|
||||
|
||||
virtual int parse(json_t *cfg);
|
||||
virtual void dump();
|
||||
|
||||
static void
|
||||
dumpList();
|
||||
|
||||
/// Find plugin by type and (optional if empty) by name. If more match, it
|
||||
/// is not defined which one will be returned.
|
||||
static Plugin *
|
||||
lookup(Type type, std::string name);
|
||||
|
||||
/// Get all plugins of a given type.
|
||||
static std::list<Plugin*>
|
||||
lookup(Type type);
|
||||
|
||||
// TODO: check if this makes sense! (no intermediate plugins)
|
||||
bool
|
||||
operator==(const Plugin& other) const;
|
||||
|
||||
Type pluginType;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string path;
|
||||
void *handle;
|
||||
enum state state;
|
||||
|
||||
protected:
|
||||
static SpdLogger
|
||||
getStaticLogger()
|
||||
{ return loggerGetOrCreate("Plugin"); }
|
||||
|
||||
private:
|
||||
/* Just using a standard std::list<> to hold plugins is problematic, because
|
||||
we want to push Plugins to the list from within each Plugin's constructor
|
||||
that is executed during static initialization. Since the order of static
|
||||
initialization is undefined in C++, it may happen that a Plugin
|
||||
constructor is executed before the list could be initialized. Therefore,
|
||||
we use the Nifty Counter Idiom [1] to initialize the list ourself before
|
||||
the first usage.
|
||||
|
||||
In short:
|
||||
- allocate a buffer for the list
|
||||
- initialize list before first usage
|
||||
- (complicatedly) declaring a buffer is neccessary in order to avoid
|
||||
that the constructor of the static list is executed again
|
||||
|
||||
[1] https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter
|
||||
*/
|
||||
|
||||
using PluginList = std::list<Plugin *>;
|
||||
using PluginListBuffer = typename std::aligned_storage<sizeof (Plugin::PluginList), alignof (Plugin::PluginList)>::type;
|
||||
|
||||
static PluginListBuffer pluginListBuffer; ///< buffer to hold a PluginList
|
||||
static PluginList& pluginList; ///< reference to pluginListBuffer
|
||||
static int pluginListNiftyCounter; ///< track if pluginList has been initialized
|
||||
};
|
||||
|
||||
} // namespace villas
|
|
@ -32,6 +32,10 @@
|
|||
|
||||
#include "log.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define LIKELY(x) __builtin_expect((x),1)
|
||||
#define UNLIKELY(x) __builtin_expect((x),0)
|
||||
|
@ -274,3 +278,7 @@ pid_t spawn(const char *name, char *const argv[]);
|
|||
|
||||
/** Determines the string length as printed on the screen (ignores escable sequences). */
|
||||
size_t strlenp(const char *str);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
15
fpga/include/villas/utils.hpp
Normal file
15
fpga/include/villas/utils.hpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
namespace villas {
|
||||
namespace utils {
|
||||
|
||||
std::vector<std::string>
|
||||
tokenize(std::string s, std::string delimiter);
|
||||
|
||||
} // namespace utils
|
||||
} // namespace villas
|
||||
|
|
@ -1,14 +1,22 @@
|
|||
set(SOURCES
|
||||
ip.cpp
|
||||
ip.c
|
||||
ip_node.cpp
|
||||
vlnv.cpp
|
||||
vlnv.c
|
||||
card.c
|
||||
card.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
|
||||
|
@ -17,8 +25,11 @@ set(SOURCES
|
|||
kernel/pci.c
|
||||
kernel/vfio.c
|
||||
|
||||
memory_manager.cpp
|
||||
plugin.c
|
||||
plugin.cpp
|
||||
utils.c
|
||||
utils.cpp
|
||||
list.c
|
||||
log.c
|
||||
log_config.c
|
||||
|
|
|
@ -241,7 +241,7 @@ void fpga_card_dump(struct fpga_card *c)
|
|||
info("Slot: %04x:%02x:%02x.%d", c->vfio_device.pci_device->slot.domain, c->vfio_device.pci_device->slot.bus, c->vfio_device.pci_device->slot.device, c->vfio_device.pci_device->slot.function);
|
||||
info("Vendor ID: %04x", c->vfio_device.pci_device->id.vendor);
|
||||
info("Device ID: %04x", c->vfio_device.pci_device->id.device);
|
||||
info("Class ID: %04x", c->vfio_device.pci_device->id.class);
|
||||
info("Class ID: %04x", c->vfio_device.pci_device->id.class_code);
|
||||
|
||||
info("BAR0 mapped at %p", c->map);
|
||||
|
||||
|
|
199
fpga/lib/card.cpp
Normal file
199
fpga/lib/card.cpp
Normal file
|
@ -0,0 +1,199 @@
|
|||
/** FPGA card.
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
|
||||
* @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 <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "log_config.h"
|
||||
#include "list.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "kernel/pci.h"
|
||||
#include "kernel/vfio.h"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "fpga/ip.hpp"
|
||||
#include "fpga/card.hpp"
|
||||
|
||||
#include "log.hpp"
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
|
||||
// instantiate factory to register
|
||||
static PCIeCardFactory PCIeCardFactory;
|
||||
|
||||
|
||||
CardList
|
||||
fpga::PCIeCardFactory::make(json_t *json, struct pci* pci, ::vfio_container* vc)
|
||||
{
|
||||
CardList cards;
|
||||
auto logger = getStaticLogger();
|
||||
|
||||
const char *card_name;
|
||||
json_t *json_card;
|
||||
json_object_foreach(json, card_name, json_card) {
|
||||
logger->info("Found config for FPGA card {}", card_name);
|
||||
|
||||
json_t* json_ips = nullptr;
|
||||
const char* pci_slot = nullptr;
|
||||
const char* pci_id = nullptr;
|
||||
int do_reset = 0;
|
||||
int affinity = 0;
|
||||
|
||||
int ret = json_unpack(json_card, "{ s: o, s?: i, s?: b, s?: s, s?: s }",
|
||||
"ips", &json_ips,
|
||||
"affinity", &affinity,
|
||||
"do_reset", &do_reset,
|
||||
"slot", &pci_slot,
|
||||
"id", &pci_id);
|
||||
|
||||
if(ret != 0) {
|
||||
logger->warn("Cannot parse JSON config");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto card = std::unique_ptr<PCIeCard>(create());
|
||||
|
||||
// populate generic properties
|
||||
card->name = std::string(card_name);
|
||||
card->pci = pci;
|
||||
card->vfio_container = vc;
|
||||
card->affinity = affinity;
|
||||
card->do_reset = do_reset != 0;
|
||||
|
||||
const char* error;
|
||||
|
||||
if (pci_slot != nullptr and pci_device_parse_slot(&card->filter, pci_slot, &error) != 0) {
|
||||
logger->warn("Failed to parse PCI slot: {}", error);
|
||||
}
|
||||
|
||||
if (pci_id != nullptr and pci_device_parse_id(&card->filter, pci_id, &error) != 0) {
|
||||
logger->warn("Failed to parse PCI ID: {}", error);
|
||||
}
|
||||
|
||||
|
||||
// TODO: currently fails, fix and remove comment
|
||||
if(not card->init()) {
|
||||
logger->warn("Cannot start FPGA card {}", card_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
card->ips = ip::IpCoreFactory::make(card.get(), json_ips);
|
||||
if(card->ips.empty()) {
|
||||
logger->error("Cannot initialize IPs of FPGA card {}", card_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(not card->check()) {
|
||||
logger->warn("Checking of FPGA card {} failed", card_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
cards.push_back(std::move(card));
|
||||
}
|
||||
|
||||
return cards;
|
||||
}
|
||||
|
||||
fpga::PCIeCard*
|
||||
fpga::PCIeCardFactory::create()
|
||||
{
|
||||
return new fpga::PCIeCard;
|
||||
}
|
||||
|
||||
|
||||
ip::IpCore*
|
||||
PCIeCard::lookupIp(std::string name) const {
|
||||
for(auto& ip : ips) {
|
||||
if(*ip == name) {
|
||||
return ip.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool fpga::PCIeCard::init()
|
||||
{
|
||||
int ret;
|
||||
struct pci_device *pdev;
|
||||
|
||||
auto logger = getLogger();
|
||||
|
||||
/* Search for FPGA card */
|
||||
pdev = pci_lookup_device(pci, &filter);
|
||||
if (!pdev) {
|
||||
logger->error("Failed to find PCI device");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Attach PCIe card to VFIO container */
|
||||
ret = ::vfio_pci_attach(&vfio_device, vfio_container, pdev);
|
||||
if (ret) {
|
||||
logger->error("Failed to attach VFIO device");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Map PCIe BAR */
|
||||
map = (char*) vfio_map_region(&vfio_device, VFIO_PCI_BAR0_REGION_INDEX);
|
||||
if (map == MAP_FAILED) {
|
||||
logger->error("Failed to mmap() BAR0");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Enable memory access and PCI bus mastering for DMA */
|
||||
ret = vfio_pci_enable(&vfio_device);
|
||||
if (ret) {
|
||||
logger->error("Failed to enable PCI device");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Reset system? */
|
||||
if (do_reset) {
|
||||
/* Reset / detect PCI device */
|
||||
ret = vfio_pci_reset(&vfio_device);
|
||||
if (ret) {
|
||||
logger->error("Failed to reset PCI device");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(not reset()) {
|
||||
logger->error("Failed to reset FGPA card");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
314
fpga/lib/ip.cpp
Normal file
314
fpga/lib/ip.cpp
Normal file
|
@ -0,0 +1,314 @@
|
|||
/** FPGA IP component.
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
|
||||
* @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 "log_config.h"
|
||||
#include "log.hpp"
|
||||
#include "plugin.h"
|
||||
#include "dependency_graph.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
#include "fpga/ip.hpp"
|
||||
#include "fpga/card.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "log.hpp"
|
||||
|
||||
using DependencyGraph = villas::utils::DependencyGraph<std::string>;
|
||||
|
||||
static
|
||||
std::list<std::string>
|
||||
dependencyTokens = {"irqs"};
|
||||
|
||||
static
|
||||
bool
|
||||
buildDependencyGraph(DependencyGraph& dependencyGraph, json_t* json_ips, std::string name)
|
||||
{
|
||||
const bool nodeExists = dependencyGraph.addNode(name);
|
||||
|
||||
// HACK: just get the right logger
|
||||
auto logger = loggerGetOrCreate("IpCoreFactory");
|
||||
|
||||
// do not add IP multiple times
|
||||
// this happens if more than 1 IP depends on a certain other IP
|
||||
if(nodeExists) {
|
||||
return true;
|
||||
}
|
||||
|
||||
json_t* json_ip = json_object_get(json_ips, name.c_str());
|
||||
if(json_ip == nullptr) {
|
||||
logger->error("IP {} not found in config", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
for(auto& dependencyToken : dependencyTokens) {
|
||||
json_t* json_dependency = json_object_get(json_ip, dependencyToken.c_str());
|
||||
if(json_dependency == nullptr) {
|
||||
logger->debug("Property {} of {} is not present",
|
||||
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, ":");
|
||||
|
||||
|
||||
if(mapping.size() != 2) {
|
||||
logger->error("Invalid {} mapping of {}",
|
||||
dependencyToken, TXT_BOLD(name));
|
||||
|
||||
dependencyGraph.removeNode(name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(name == mapping[0]) {
|
||||
logger->error("IP {} cannot depend on itself", TXT_BOLD(name));
|
||||
|
||||
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]);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
namespace ip {
|
||||
|
||||
void IpCore::dump() {
|
||||
auto logger = getLogger();
|
||||
|
||||
logger->info("Base address = {:08x}", baseaddr);
|
||||
for(auto& [num, irq] : irqs) {
|
||||
logger->info("IRQ {}: {}:{}", num, irq.controllerName, irq.num);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
IpCoreFactory* IpCoreFactory::lookup(const Vlnv &vlnv)
|
||||
{
|
||||
for(auto& ip : Plugin::lookup(Plugin::Type::FpgaIp)) {
|
||||
IpCoreFactory* ipCoreFactory = dynamic_cast<IpCoreFactory*>(ip);
|
||||
|
||||
if(ipCoreFactory->getCompatibleVlnv() == vlnv)
|
||||
return ipCoreFactory;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
IpCore::getAddrMapped(uintptr_t address) const
|
||||
{
|
||||
assert(card != nullptr);
|
||||
return reinterpret_cast<uintptr_t>(card->map) + address;
|
||||
}
|
||||
|
||||
|
||||
IpCoreList
|
||||
IpCoreFactory::make(PCIeCard* card, json_t *json_ips)
|
||||
{
|
||||
DependencyGraph dependencyGraph;
|
||||
IpCoreList initializedIps;
|
||||
auto loggerStatic = getStaticLogger();
|
||||
|
||||
|
||||
loggerStatic->debug("Parsing IP dependency graph:");
|
||||
void* iter = json_object_iter(json_ips);
|
||||
while(iter != nullptr) {
|
||||
buildDependencyGraph(dependencyGraph, json_ips, json_object_iter_key(iter));
|
||||
iter = json_object_iter_next(json_ips, iter);
|
||||
}
|
||||
|
||||
|
||||
loggerStatic->debug("IP initialization order:");
|
||||
for(auto& ipName : dependencyGraph.getEvaluationOrder()) {
|
||||
loggerStatic->debug(" {}", TXT_BOLD(ipName));
|
||||
}
|
||||
|
||||
|
||||
for(auto& ipName : dependencyGraph.getEvaluationOrder()) {
|
||||
loggerStatic->info("Initializing {}", TXT_BOLD(ipName));
|
||||
|
||||
json_t* json_ip = json_object_get(json_ips, ipName.c_str());
|
||||
|
||||
// extract VLNV from JSON
|
||||
const char* vlnv;
|
||||
if(json_unpack(json_ip, "{ s: s }", "vlnv", &vlnv) != 0) {
|
||||
loggerStatic->warn("IP {} has no entry 'vlnv'", ipName);
|
||||
continue;
|
||||
}
|
||||
|
||||
IpIdentifier id(Vlnv(vlnv), ipName);
|
||||
|
||||
// find the appropriate factory that can create the specified VLNV
|
||||
// Note:
|
||||
// This is the magic part! Factories automatically register as a
|
||||
// plugin as soon as they are instantiated. If there are multiple
|
||||
// candidates, the first suitable factory will be used.
|
||||
IpCoreFactory* ipCoreFactory = lookup(id.vlnv);
|
||||
|
||||
if(ipCoreFactory == nullptr) {
|
||||
loggerStatic->warn("No plugin found to handle {}", vlnv);
|
||||
continue;
|
||||
} else {
|
||||
loggerStatic->debug("Using {} for IP {}",
|
||||
ipCoreFactory->getName(), vlnv);
|
||||
}
|
||||
|
||||
auto logger = ipCoreFactory->getLogger();
|
||||
|
||||
// Create new IP instance. Since this function is virtual, it will
|
||||
// construct the right, specialized type without knowing it here
|
||||
// because we have already picked the right factory.
|
||||
// If something goes wrong with initialization, the shared_ptr will
|
||||
// take care to desctruct the IpCore again as it is not pushed to
|
||||
// the list and will run out of scope.
|
||||
auto ip = std::unique_ptr<IpCore>(ipCoreFactory->create());
|
||||
|
||||
if(ip == nullptr) {
|
||||
logger->warn("Cannot create an instance of {}",
|
||||
ipCoreFactory->getName());
|
||||
continue;
|
||||
}
|
||||
|
||||
// setup generic IP type properties
|
||||
ip->card = card;
|
||||
ip->id = id;
|
||||
|
||||
// extract base address if it has one
|
||||
if(json_unpack(json_ip, "{ s?: i }", "baseaddr", &ip->baseaddr) != 0) {
|
||||
logger->warn("Problem while parsing base address of IP {}",
|
||||
TXT_BOLD(ipName));
|
||||
continue;
|
||||
}
|
||||
|
||||
json_t* json_irqs = json_object_get(json_ip, "irqs");
|
||||
if(json_is_object(json_irqs)) {
|
||||
const char* irq_name;
|
||||
json_t* 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 {}",
|
||||
irq, TXT_BOLD(ipName));
|
||||
continue;
|
||||
}
|
||||
|
||||
int num;
|
||||
try {
|
||||
num = std::stoi(tokens[1]);
|
||||
} catch(const std::invalid_argument&) {
|
||||
logger->warn("IRQ number is not an integer: '{}'", irq);
|
||||
continue;
|
||||
}
|
||||
logger->debug("IRQ: {} -> {}:{}", irq_name, tokens[0], num);
|
||||
ip->irqs[irq_name] = {num, tokens[0], ""};
|
||||
}
|
||||
} else {
|
||||
logger->debug("IP has no interrupts");
|
||||
}
|
||||
|
||||
|
||||
bool dependenciesOk = true;
|
||||
for(auto& [depName, depVlnv] : ipCoreFactory->getDependencies()) {
|
||||
// lookup dependency IP core in list of already initialized IPs
|
||||
auto iter = std::find_if(initializedIps.begin(),
|
||||
initializedIps.end(),
|
||||
[&](const std::unique_ptr<IpCore>& ip) {
|
||||
return *ip == depVlnv;
|
||||
});
|
||||
|
||||
if(iter == initializedIps.end()) {
|
||||
logger->error("Cannot find '{}' dependency {} of {}",
|
||||
depName, depVlnv, TXT_BOLD(ipName));
|
||||
dependenciesOk = false;
|
||||
break;
|
||||
}
|
||||
|
||||
logger->debug("Found dependency IP {}", (*iter)->id);
|
||||
ip->dependencies[depName] = (*iter).get();
|
||||
}
|
||||
|
||||
if(not dependenciesOk) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// IP-specific setup via JSON config
|
||||
if(not ipCoreFactory->configureJson(*ip, json_ip)) {
|
||||
logger->warn("Cannot configure IP from JSON");
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: currently fails, fix and remove comment
|
||||
if(not ip->init()) {
|
||||
logger->error("Cannot start IP {}", ip->id.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(not ip->check()) {
|
||||
logger->error("Checking of IP {} failed", ip->id.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
initializedIps.push_back(std::move(ip));
|
||||
}
|
||||
|
||||
|
||||
return initializedIps;
|
||||
}
|
||||
|
||||
} // namespace ip
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
126
fpga/lib/ip_node.cpp
Normal file
126
fpga/lib/ip_node.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <jansson.h>
|
||||
|
||||
#include "utils.hpp"
|
||||
#include "fpga/ip_node.hpp"
|
||||
#include "fpga/ips/switch.hpp"
|
||||
#include "fpga/card.hpp"
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
namespace ip {
|
||||
|
||||
|
||||
bool
|
||||
IpNodeFactory::configureJson(IpCore& ip, json_t* json_ip)
|
||||
{
|
||||
auto& ipNode = reinterpret_cast<IpNode&>(ip);
|
||||
auto logger = getLogger();
|
||||
|
||||
json_t* json_ports = json_object_get(json_ip, "ports");
|
||||
if(not json_is_array(json_ports)) {
|
||||
logger->debug("IP has no ports");
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t index;
|
||||
json_t* json_port;
|
||||
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;
|
||||
}
|
||||
|
||||
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("Cannot parse port {}", index);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto tokens = utils::tokenize(target_raw, ":");
|
||||
if(tokens.size() != 2) {
|
||||
logger->error("Cannot parse 'target' of port {}", index);
|
||||
return false;
|
||||
}
|
||||
|
||||
int port_num;
|
||||
try {
|
||||
port_num = std::stoi(tokens[1]);
|
||||
} catch(const std::invalid_argument&) {
|
||||
logger->error("Target port number is not an integer: '{}'", target_raw);
|
||||
return false;
|
||||
}
|
||||
|
||||
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::string, std::string>
|
||||
IpNode::getLoopbackPorts() const
|
||||
{
|
||||
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 { masterName, slaveName };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { "", "" };
|
||||
}
|
||||
|
||||
bool
|
||||
IpNode::loopbackPossible() const
|
||||
{
|
||||
auto ports = getLoopbackPorts();
|
||||
return (not ports.first.empty()) and (not ports.second.empty());
|
||||
}
|
||||
|
||||
bool
|
||||
IpNode::connectLoopback()
|
||||
{
|
||||
auto logger = getLogger();
|
||||
|
||||
auto ports = getLoopbackPorts();
|
||||
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 switch");
|
||||
return false;
|
||||
}
|
||||
|
||||
// switch's slave port is our master port and vice versa
|
||||
return axiStreamSwitch->connect(portMaster.portNumber, portSlave.portNumber);
|
||||
}
|
||||
|
||||
} // namespace ip
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
195
fpga/lib/ips/fifo.cpp
Normal file
195
fpga/lib/ips/fifo.cpp
Normal file
|
@ -0,0 +1,195 @@
|
|||
/** FIFO related helper functions
|
||||
*
|
||||
* These functions present a simpler interface to Xilinx' FIFO driver (XLlFifo_*)
|
||||
*
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <xilinx/xstatus.h>
|
||||
#include <xilinx/xllfifo.h>
|
||||
|
||||
#include "log.hpp"
|
||||
#include "fpga/ips/fifo.hpp"
|
||||
#include "fpga/ips/intc.hpp"
|
||||
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
namespace ip {
|
||||
|
||||
// instantiate factory to make available to plugin infrastructure
|
||||
static FifoFactory factory;
|
||||
|
||||
bool
|
||||
FifoFactory::configureJson(IpCore &ip, json_t *json_ip)
|
||||
{
|
||||
auto logger = getLogger();
|
||||
|
||||
if(not IpNodeFactory::configureJson(ip, json_ip)) {
|
||||
logger->error("Configuring IpNode failed");
|
||||
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()
|
||||
{
|
||||
XLlFifo_Config fifo_cfg;
|
||||
|
||||
fifo_cfg.Axi4BaseAddress = getAddrMapped(this->baseaddr_axi4);
|
||||
|
||||
// use AXI4 for Data, AXI4-Lite for control
|
||||
fifo_cfg.Datainterface = (this->baseaddr_axi4 != -1) ? 1 : 0;
|
||||
|
||||
if (XLlFifo_CfgInitialize(&xFifo, &fifo_cfg, getBaseaddr()) != XST_SUCCESS)
|
||||
return false;
|
||||
|
||||
// Receive complete IRQ
|
||||
XLlFifo_IntEnable(&xFifo, XLLF_INT_RC_MASK);
|
||||
|
||||
auto intc = reinterpret_cast<InterruptController*>(dependencies["intc"]);
|
||||
intc->enableInterrupt(irqs["interrupt"], false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Fifo::stop()
|
||||
{
|
||||
// Receive complete IRQ
|
||||
XLlFifo_IntDisable(&xFifo, XLLF_INT_RC_MASK);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Fifo::write(const void *buf, size_t len)
|
||||
{
|
||||
|
||||
uint32_t tdfv;
|
||||
|
||||
tdfv = XLlFifo_TxVacancy(&xFifo);
|
||||
if (tdfv < len)
|
||||
return -1;
|
||||
|
||||
// buf has to be re-casted because Xilinx driver doesn't use const
|
||||
XLlFifo_Write(&xFifo, (void*) buf, len);
|
||||
XLlFifo_TxSetLen(&xFifo, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
XLlFifo_IntClear(&xFifo, XLLF_INT_RC_MASK);
|
||||
|
||||
/* Get length of next frame */
|
||||
rxlen = XLlFifo_RxGetLen(&xFifo);
|
||||
nextlen = std::min(rxlen, len);
|
||||
|
||||
/* Read from FIFO */
|
||||
XLlFifo_Read(&xFifo, buf, nextlen);
|
||||
|
||||
return nextlen;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
|
||||
ssize_t fifo_write(struct fpga_ip *c, char *buf, size_t len)
|
||||
{
|
||||
struct fifo *fifo = (struct fifo *) c->_vd;
|
||||
|
||||
XLlFifo *xllfifo = &fifo->inst;
|
||||
|
||||
}
|
||||
|
||||
ssize_t fifo_read(struct fpga_ip *c, char *buf, size_t len)
|
||||
{
|
||||
struct fifo *fifo = (struct fifo *) c->_vd;
|
||||
|
||||
XLlFifo *xllfifo = &fifo->inst;
|
||||
|
||||
size_t nextlen = 0;
|
||||
uint32_t rxlen;
|
||||
|
||||
while (!XLlFifo_IsRxDone(xllfifo))
|
||||
intc_wait(c->card->intc, c->irq);
|
||||
XLlFifo_IntClear(xllfifo, XLLF_INT_RC_MASK);
|
||||
|
||||
/* Get length of next frame */
|
||||
rxlen = XLlFifo_RxGetLen(xllfifo);
|
||||
nextlen = MIN(rxlen, len);
|
||||
|
||||
/* Read from FIFO */
|
||||
XLlFifo_Read(xllfifo, buf, nextlen);
|
||||
|
||||
return nextlen;
|
||||
}
|
||||
|
||||
int fifo_parse(struct fpga_ip *c, json_t *cfg)
|
||||
{
|
||||
struct fifo *fifo = (struct fifo *) c->_vd;
|
||||
|
||||
int baseaddr_axi4 = -1, ret;
|
||||
|
||||
json_error_t err;
|
||||
|
||||
fifo->baseaddr_axi4 = -1;
|
||||
|
||||
ret = json_unpack_ex(cfg, &err, 0, "{ s?: i }", "baseaddr_axi4", &baseaddr_axi4);
|
||||
if (ret)
|
||||
jerror(&err, "Failed to parse configuration of FPGA IP '%s'", c->name);
|
||||
|
||||
fifo->baseaddr_axi4 = baseaddr_axi4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fifo_reset(struct fpga_ip *c)
|
||||
{
|
||||
struct fifo *fifo = (struct fifo *) c->_vd;
|
||||
|
||||
XLlFifo_Reset(&fifo->inst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace ip
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
167
fpga/lib/ips/intc.cpp
Normal file
167
fpga/lib/ips/intc.cpp
Normal file
|
@ -0,0 +1,167 @@
|
|||
/** AXI-PCIe Interrupt controller
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "plugin.hpp"
|
||||
|
||||
#include "kernel/vfio.h"
|
||||
#include "kernel/kernel.h"
|
||||
|
||||
#include "fpga/card.hpp"
|
||||
#include "fpga/ips/intc.hpp"
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
namespace ip {
|
||||
|
||||
|
||||
// instantiate factory to make available to plugin infrastructure
|
||||
static InterruptControllerFactory factory;
|
||||
|
||||
InterruptController::~InterruptController()
|
||||
{
|
||||
vfio_pci_msi_deinit(&card->vfio_device , this->efds);
|
||||
}
|
||||
|
||||
bool
|
||||
InterruptController::init()
|
||||
{
|
||||
const uintptr_t base = getBaseaddr();
|
||||
auto logger = getLogger();
|
||||
|
||||
num_irqs = vfio_pci_msi_init(&card->vfio_device, efds);
|
||||
if (num_irqs < 0)
|
||||
return false;
|
||||
|
||||
if(vfio_pci_msi_find(&card->vfio_device, nos) != 0)
|
||||
return false;
|
||||
|
||||
/* For each IRQ */
|
||||
for (int i = 0; i < num_irqs; i++) {
|
||||
/* Pin to core */
|
||||
if(kernel_irq_setaffinity(nos[i], card->affinity, nullptr) != 0) {
|
||||
logger->error("Failed to change affinity of VFIO-MSI interrupt");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Setup vector */
|
||||
XIntc_Out32(base + XIN_IVAR_OFFSET + i * 4, i);
|
||||
}
|
||||
|
||||
XIntc_Out32(base + XIN_IMR_OFFSET, 0); /* Use manual acknowlegement for all IRQs */
|
||||
XIntc_Out32(base + XIN_IAR_OFFSET, 0xFFFFFFFF); /* Acknowlege all pending IRQs manually */
|
||||
XIntc_Out32(base + XIN_IMR_OFFSET, 0xFFFFFFFF); /* Use fast acknowlegement for all IRQs */
|
||||
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");
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
InterruptController::enableInterrupt(InterruptController::IrqMaskType mask, bool polling)
|
||||
{
|
||||
auto logger = getLogger();
|
||||
const uintptr_t base = getBaseaddr();
|
||||
|
||||
/* Current state of INTC */
|
||||
const uint32_t ier = XIntc_In32(base + XIN_IER_OFFSET);
|
||||
const uint32_t imr = XIntc_In32(base + XIN_IMR_OFFSET);
|
||||
|
||||
/* Clear pending IRQs */
|
||||
XIntc_Out32(base + XIN_IAR_OFFSET, mask);
|
||||
|
||||
for (int i = 0; i < num_irqs; i++) {
|
||||
if (mask & (1 << i))
|
||||
this->polling[i] = polling;
|
||||
}
|
||||
|
||||
if (polling) {
|
||||
XIntc_Out32(base + XIN_IMR_OFFSET, imr & ~mask);
|
||||
XIntc_Out32(base + XIN_IER_OFFSET, ier & ~mask);
|
||||
}
|
||||
else {
|
||||
XIntc_Out32(base + XIN_IER_OFFSET, ier | mask);
|
||||
XIntc_Out32(base + XIN_IMR_OFFSET, imr | mask);
|
||||
}
|
||||
|
||||
logger->debug("New ier = {:x}", XIntc_In32(base + XIN_IER_OFFSET));
|
||||
logger->debug("New imr = {:x}", XIntc_In32(base + XIN_IMR_OFFSET));
|
||||
logger->debug("New isr = {:x}", XIntc_In32(base + XIN_ISR_OFFSET));
|
||||
logger->debug("Interupts enabled: mask={:x} polling={:d}", mask, polling);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
InterruptController::disableInterrupt(InterruptController::IrqMaskType mask)
|
||||
{
|
||||
const uintptr_t base = getBaseaddr();
|
||||
uint32_t ier = XIntc_In32(base + XIN_IER_OFFSET);
|
||||
|
||||
XIntc_Out32(base + XIN_IER_OFFSET, ier & ~mask);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
InterruptController::waitForInterrupt(int irq)
|
||||
{
|
||||
assert(irq < maxIrqs);
|
||||
|
||||
const uintptr_t base = getBaseaddr();
|
||||
|
||||
if (this->polling[irq]) {
|
||||
uint32_t isr, mask = 1 << irq;
|
||||
|
||||
do {
|
||||
// poll status register
|
||||
isr = XIntc_In32(base + XIN_ISR_OFFSET);
|
||||
pthread_testcancel();
|
||||
} while ((isr & mask) != mask);
|
||||
|
||||
// acknowledge interrupt
|
||||
XIntc_Out32(base + XIN_IAR_OFFSET, mask);
|
||||
|
||||
// we can only tell that there has been (at least) one interrupt
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
uint64_t count;
|
||||
|
||||
// block until there has been an interrupt, read number of interrupts
|
||||
ssize_t ret = read(efds[irq], &count, sizeof(count));
|
||||
if (ret != sizeof(count))
|
||||
return -1;
|
||||
|
||||
return static_cast<int>(count);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ip
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
147
fpga/lib/ips/switch.cpp
Normal file
147
fpga/lib/ips/switch.cpp
Normal file
|
@ -0,0 +1,147 @@
|
|||
/** AXI Stream interconnect related helper functions
|
||||
*
|
||||
* These functions present a simpler interface to Xilinx' AXI Stream switch driver (XAxis_Switch_*)
|
||||
*
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#include <jansson.h>
|
||||
#include <xilinx/xaxis_switch.h>
|
||||
|
||||
#include "log.hpp"
|
||||
#include "fpga/ips/switch.hpp"
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
namespace ip {
|
||||
|
||||
static AxiStreamSwitchFactory factory;
|
||||
|
||||
bool
|
||||
AxiStreamSwitch::init()
|
||||
{
|
||||
/* 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) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Disable all masters */
|
||||
XAxisScr_RegUpdateDisable(&xSwitch);
|
||||
XAxisScr_MiPortDisableAll(&xSwitch);
|
||||
XAxisScr_RegUpdateEnable(&xSwitch);
|
||||
|
||||
// initialize internal mapping
|
||||
for(int portMaster = 0; portMaster < portsMaster.size(); portMaster++) {
|
||||
portMapping[portMaster] = PORT_DISABLED;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
AxiStreamSwitch::connect(int portSlave, int portMaster)
|
||||
{
|
||||
auto logger = getLogger();
|
||||
|
||||
if(portMapping[portMaster] == portSlave) {
|
||||
logger->debug("Ports already connected");
|
||||
return true;
|
||||
}
|
||||
|
||||
for(auto [master, slave] : portMapping) {
|
||||
if(slave == portSlave) {
|
||||
logger->warn("Slave {} has already been connected to master {}. "
|
||||
"Disabling master {}.",
|
||||
slave, master, master);
|
||||
|
||||
XAxisScr_RegUpdateDisable(&xSwitch);
|
||||
XAxisScr_MiPortDisable(&xSwitch, master);
|
||||
XAxisScr_RegUpdateEnable(&xSwitch);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reconfigure switch */
|
||||
XAxisScr_RegUpdateDisable(&xSwitch);
|
||||
XAxisScr_MiPortEnable(&xSwitch, portMaster, portSlave);
|
||||
XAxisScr_RegUpdateEnable(&xSwitch);
|
||||
|
||||
logger->debug("Connect slave {} to master {}", portSlave, portMaster);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
AxiStreamSwitch::disconnectMaster(int port)
|
||||
{
|
||||
auto logger = getLogger();
|
||||
|
||||
logger->debug("Disconnect slave {} from master {}",
|
||||
portMapping[port], port);
|
||||
|
||||
XAxisScr_MiPortDisable(&xSwitch, port);
|
||||
portMapping[port] = PORT_DISABLED;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
AxiStreamSwitch::disconnectSlave(int port)
|
||||
{
|
||||
auto logger = getLogger();
|
||||
|
||||
for(auto [master, slave] : portMapping) {
|
||||
if(slave == port) {
|
||||
logger->debug("Disconnect slave {} from master {}", slave, master);
|
||||
XAxisScr_MiPortDisable(&xSwitch, master);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
logger->debug("Slave {} hasn't been connected to any master", 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
|
93
fpga/lib/ips/timer.cpp
Normal file
93
fpga/lib/ips/timer.cpp
Normal file
|
@ -0,0 +1,93 @@
|
|||
/** Timer related helper functions
|
||||
*
|
||||
* These functions present a simpler interface to Xilinx' Timer Counter driver (XTmrCtr_*)
|
||||
*
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#include <xilinx/xtmrctr.h>
|
||||
|
||||
#include "log.hpp"
|
||||
#include "fpga/ips/timer.hpp"
|
||||
#include "fpga/ips/intc.hpp"
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
namespace ip {
|
||||
|
||||
|
||||
// instantiate factory to make available to plugin infrastructure
|
||||
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["generateout0"]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Timer::start(uint32_t ticks)
|
||||
{
|
||||
intc->enableInterrupt(irqs["generateout0"], false);
|
||||
|
||||
XTmrCtr_SetOptions(&xTmr, 0, XTC_EXT_COMPARE_OPTION | XTC_DOWN_COUNT_OPTION);
|
||||
XTmrCtr_SetResetValue(&xTmr, 0, ticks);
|
||||
XTmrCtr_Start(&xTmr, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Timer::wait()
|
||||
{
|
||||
int count = intc->waitForInterrupt(irqs["generateout0"]);
|
||||
intc->disableInterrupt(irqs["generateout0"]);
|
||||
|
||||
return (count == 1);
|
||||
}
|
||||
|
||||
uint32_t Timer::remaining()
|
||||
{
|
||||
return XTmrCtr_GetValue(&xTmr, 0);
|
||||
}
|
||||
|
||||
|
||||
} // namespace ip
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
|
@ -258,7 +258,7 @@ int kernel_has_cap(cap_value_t cap)
|
|||
}
|
||||
#endif
|
||||
|
||||
int kernel_irq_setaffinity(unsigned irq, uintmax_t new, uintmax_t *old)
|
||||
int kernel_irq_setaffinity(unsigned irq, uintmax_t affinity, uintmax_t *old)
|
||||
{
|
||||
char fn[64];
|
||||
FILE *f;
|
||||
|
@ -273,7 +273,7 @@ int kernel_irq_setaffinity(unsigned irq, uintmax_t new, uintmax_t *old)
|
|||
if (old)
|
||||
ret = fscanf(f, "%jx", old);
|
||||
|
||||
fprintf(f, "%jx", new);
|
||||
fprintf(f, "%jx", affinity);
|
||||
fclose(f);
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -221,7 +221,7 @@ int pci_device_parse_id(struct pci_device *f, const char *str, const char **erro
|
|||
goto fail;
|
||||
}
|
||||
|
||||
f->id.class = x;
|
||||
f->id.class_code = x;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -242,7 +242,7 @@ int pci_device_compare(const struct pci_device *d, const struct pci_device *f)
|
|||
(f->id.vendor != 0 && f->id.vendor != d->id.vendor))
|
||||
return 1;
|
||||
|
||||
if ((f->id.class != 0) || (f->id.class != d->id.class))
|
||||
if ((f->id.class_code != 0) || (f->id.class_code != d->id.class_code))
|
||||
return 1;
|
||||
|
||||
// found
|
||||
|
@ -254,10 +254,29 @@ struct pci_device * pci_lookup_device(struct pci *p, struct pci_device *f)
|
|||
return list_search(&p->devices, (cmp_cb_t) pci_device_compare, (void *) f);
|
||||
}
|
||||
|
||||
int pci_get_driver(struct pci_device *d, char *buf, size_t buflen)
|
||||
{
|
||||
int ret;
|
||||
char sysfs[1024], syml[1024];
|
||||
|
||||
snprintf(sysfs, sizeof(sysfs), "%s/bus/pci/devices/%04x:%02x:%02x.%x/driver", SYSFS_PATH,
|
||||
d->slot.domain, d->slot.bus, d->slot.device, d->slot.function);
|
||||
|
||||
ret = readlink(sysfs, syml, sizeof(syml));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
char *driver = basename(syml);
|
||||
|
||||
strncpy(buf, driver, buflen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pci_attach_driver(struct pci_device *d, const char *driver)
|
||||
{
|
||||
FILE *f;
|
||||
char fn[256];
|
||||
char fn[1024];
|
||||
|
||||
/* Add new ID to driver */
|
||||
snprintf(fn, sizeof(fn), "%s/bus/pci/drivers/%s/new_id", SYSFS_PATH, driver);
|
||||
|
|
|
@ -229,10 +229,13 @@ int vfio_pci_attach(struct vfio_device *d, struct vfio_container *c, struct pci_
|
|||
if (kernel_module_load("vfio_pci"))
|
||||
error("Failed to load kernel driver: %s", "vfio_pci");
|
||||
|
||||
/* Bind PCI card to vfio-pci driver*/
|
||||
ret = pci_attach_driver(pdev, "vfio-pci");
|
||||
if (ret)
|
||||
error("Failed to attach device to driver");
|
||||
/* Bind PCI card to vfio-pci driver if not already bound */
|
||||
ret = pci_get_driver(pdev, name, sizeof(name));
|
||||
if (ret || strcmp(name, "vfio-pci")) {
|
||||
ret = pci_attach_driver(pdev, "vfio-pci");
|
||||
if (ret)
|
||||
error("Failed to attach device to driver");
|
||||
}
|
||||
|
||||
/* Get IOMMU group of device */
|
||||
int index = pci_get_iommu_group(pdev);
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#endif
|
||||
|
||||
struct log *global_log;
|
||||
struct log default_log;
|
||||
|
||||
/* We register a default log instance */
|
||||
__attribute__((constructor))
|
||||
|
|
57
fpga/lib/memory_manager.cpp
Normal file
57
fpga/lib/memory_manager.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
#include <memory>
|
||||
|
||||
#include "memory_manager.hpp"
|
||||
|
||||
namespace villas {
|
||||
|
||||
MemoryManager*
|
||||
MemoryManager::instance = nullptr;
|
||||
|
||||
MemoryManager&
|
||||
MemoryManager::get()
|
||||
{
|
||||
if(instance == nullptr) {
|
||||
instance = new MemoryManager;
|
||||
}
|
||||
|
||||
return *instance;
|
||||
}
|
||||
|
||||
MemoryManager::AddressSpaceId
|
||||
MemoryManager::createAddressSpace(std::string name)
|
||||
{
|
||||
std::shared_ptr<AddressSpace> addrSpace(new AddressSpace);
|
||||
addrSpace->name = name;
|
||||
|
||||
return memoryGraph.addVertex(addrSpace);
|
||||
}
|
||||
|
||||
MemoryManager::MappingId
|
||||
MemoryManager::createMapping(uintptr_t src, uintptr_t dest, size_t size,
|
||||
MemoryManager::AddressSpaceId fromAddrSpace,
|
||||
MemoryManager::AddressSpaceId toAddrSpace)
|
||||
{
|
||||
std::shared_ptr<Mapping> mapping(new Mapping);
|
||||
mapping->src = src;
|
||||
mapping->dest = dest;
|
||||
mapping->size = size;
|
||||
|
||||
return addMapping(mapping, fromAddrSpace, toAddrSpace);
|
||||
}
|
||||
|
||||
MemoryManager::MappingId
|
||||
MemoryManager::addMapping(std::shared_ptr<Mapping> mapping,
|
||||
MemoryManager::AddressSpaceId fromAddrSpace,
|
||||
MemoryManager::AddressSpaceId toAddrSpace)
|
||||
{
|
||||
return memoryGraph.addEdge(mapping, fromAddrSpace, toAddrSpace);
|
||||
}
|
||||
|
||||
|
||||
Mapping::~Mapping()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace villas
|
164
fpga/lib/plugin.cpp
Normal file
164
fpga/lib/plugin.cpp
Normal file
|
@ -0,0 +1,164 @@
|
|||
/** Loadable / plugin support.
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
|
||||
* @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 <dlfcn.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
|
||||
#include "plugin.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
namespace villas {
|
||||
|
||||
// list of all registered plugins
|
||||
Plugin::PluginList&
|
||||
Plugin::pluginList = reinterpret_cast<Plugin::PluginList&>(Plugin::pluginListBuffer);
|
||||
|
||||
Plugin::PluginListBuffer
|
||||
Plugin::pluginListBuffer;
|
||||
|
||||
// relies on zero initialization
|
||||
int Plugin::pluginListNiftyCounter;
|
||||
|
||||
|
||||
Plugin::Plugin(Type type, const std::string& name) :
|
||||
pluginType(type),
|
||||
name(name),
|
||||
description(""),
|
||||
path(""),
|
||||
state(STATE_INITIALIZED)
|
||||
{
|
||||
// see comment in plugin.hpp on why we need to do this (Nifty Counter Idiom)
|
||||
if(pluginListNiftyCounter++ == 0)
|
||||
new (&pluginList) PluginList;
|
||||
|
||||
// push to global plugin list
|
||||
pluginList.push_back(this);
|
||||
}
|
||||
|
||||
Plugin::~Plugin()
|
||||
{
|
||||
// clean from global plugin list
|
||||
pluginList.remove(this);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
Plugin::parse(json_t *cfg)
|
||||
{
|
||||
const char *path;
|
||||
|
||||
path = json_string_value(cfg);
|
||||
if (!path)
|
||||
return -1;
|
||||
|
||||
this->path = std::string(path);
|
||||
this->state = STATE_PARSED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Plugin::load()
|
||||
{
|
||||
assert(this->state == STATE_PARSED);
|
||||
assert(not this->path.empty());
|
||||
|
||||
this->handle = dlopen(this->path.c_str(), RTLD_NOW);
|
||||
|
||||
if (this->handle == nullptr)
|
||||
return -1;
|
||||
|
||||
this->state = STATE_LOADED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Plugin::unload()
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(this->state == STATE_LOADED);
|
||||
|
||||
ret = dlclose(this->handle);
|
||||
if (ret != 0)
|
||||
return -1;
|
||||
|
||||
this->state = STATE_UNLOADED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
Plugin::dump()
|
||||
{
|
||||
auto logger = getStaticLogger();
|
||||
logger->info("Name: '{}' Description: '{}'", name, description);
|
||||
}
|
||||
|
||||
void
|
||||
Plugin::dumpList()
|
||||
{
|
||||
auto logger = getStaticLogger();
|
||||
|
||||
logger->info("Registered plugins:");
|
||||
for(auto& p : pluginList) {
|
||||
logger->info(" - {}", p->name);
|
||||
}
|
||||
}
|
||||
|
||||
Plugin*
|
||||
Plugin::lookup(Plugin::Type type, std::string name)
|
||||
{
|
||||
for(auto& p : pluginList) {
|
||||
if(p->pluginType == type and (name.empty() or p->name == name))
|
||||
return p;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::list<Plugin*>
|
||||
Plugin::lookup(Plugin::Type type)
|
||||
{
|
||||
std::list<Plugin*> list;
|
||||
for(auto& p : pluginList) {
|
||||
if(p->pluginType == type)
|
||||
list.push_back(p);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
bool
|
||||
Plugin::operator==(const Plugin &other) const
|
||||
{
|
||||
return (this->pluginType == other.pluginType) and (this->name == other.name);
|
||||
}
|
||||
|
||||
} // namespace villas
|
35
fpga/lib/utils.cpp
Normal file
35
fpga/lib/utils.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace villas {
|
||||
namespace utils {
|
||||
|
||||
std::vector<std::string>
|
||||
tokenize(std::string s, std::string delimiter)
|
||||
{
|
||||
std::vector<std::string> tokens;
|
||||
|
||||
size_t lastPos = 0;
|
||||
size_t curentPos;
|
||||
|
||||
while((curentPos = s.find(delimiter, lastPos)) != std::string::npos) {
|
||||
const size_t tokenLength = curentPos - lastPos;
|
||||
tokens.push_back(s.substr(lastPos, tokenLength));
|
||||
|
||||
// advance in string
|
||||
lastPos = curentPos + delimiter.length();
|
||||
}
|
||||
|
||||
// check if there's a last token behind the last delimiter
|
||||
if(lastPos != s.length()) {
|
||||
const size_t lastTokenLength = s.length() - lastPos;
|
||||
tokens.push_back(s.substr(lastPos, lastTokenLength));
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace villas
|
70
fpga/lib/vlnv.cpp
Normal file
70
fpga/lib/vlnv.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
/** Vendor, Library, Name, Version (VLNV) tag
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
|
||||
* @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 <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fpga/vlnv.hpp"
|
||||
#include "fpga/ip.hpp"
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
|
||||
bool
|
||||
Vlnv::operator==(const Vlnv &other) const
|
||||
{
|
||||
// if a field is empty, it means wildcard matching everything
|
||||
const bool vendorWildcard = vendor.empty() or other.vendor.empty();
|
||||
const bool libraryWildcard = library.empty() or other.library.empty();
|
||||
const bool nameWildcard = name.empty() or other.name.empty();
|
||||
const bool versionWildcard = version.empty() or other.version.empty();
|
||||
|
||||
const bool vendorMatch = vendorWildcard or vendor == other.vendor;
|
||||
const bool libraryMatch = libraryWildcard or library == other.library;
|
||||
const bool nameMatch = nameWildcard or name == other.name;
|
||||
const bool versionMatch = versionWildcard or version == other.version;
|
||||
|
||||
return vendorMatch and libraryMatch and nameMatch and versionMatch;
|
||||
}
|
||||
|
||||
void
|
||||
Vlnv::parseFromString(std::string vlnv)
|
||||
{
|
||||
// tokenize by delimiter
|
||||
std::stringstream sstream(vlnv);
|
||||
std::getline(sstream, vendor, delimiter);
|
||||
std::getline(sstream, library, delimiter);
|
||||
std::getline(sstream, name, delimiter);
|
||||
std::getline(sstream, version, delimiter);
|
||||
|
||||
// represent wildcard internally as empty string
|
||||
if(vendor == "*") vendor = "";
|
||||
if(library == "*") library = "";
|
||||
if(name == "*") name = "";
|
||||
if(version == "*") version = "";
|
||||
}
|
||||
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
|
@ -68,7 +68,7 @@ def bus_trace(root, busname, type, whitelist):
|
|||
instance = module[0].get('INSTANCE')
|
||||
|
||||
if vlnv_match(vlnv, whitelist):
|
||||
return instance
|
||||
return instance, busname
|
||||
elif vlnv_match(vlnv, axi_converter_whitelist):
|
||||
next_bus = module[0].xpath('.//BUSINTERFACE[@TYPE="{}" or @TYPE="{}"]'.format(opponent[type[0]][0], opponent[type[0]][1]))
|
||||
next_busname = next_bus[0].get('BUSNAME')
|
||||
|
@ -86,6 +86,17 @@ def vlnv_match(vlnv, whitelist):
|
|||
|
||||
return False
|
||||
|
||||
def remove_prefix(text, prefix):
|
||||
return text[text.startswith(prefix) and len(prefix):]
|
||||
|
||||
def sanitize_name(name):
|
||||
name = remove_prefix(name, 'S_')
|
||||
name = remove_prefix(name, 'M_')
|
||||
name = remove_prefix(name, 'AXI_')
|
||||
name = remove_prefix(name, 'AXIS_')
|
||||
|
||||
return name
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print('Usage: {} path/to/*.hwdef'.format(sys.argv[0]))
|
||||
print(' {} path/to/*.xml'.format(sys.argv[0]))
|
||||
|
@ -119,11 +130,26 @@ for module in modules:
|
|||
continue
|
||||
|
||||
ips[instance] = {
|
||||
'vlnv' : vlnv,
|
||||
'irqs' : { },
|
||||
'ports' : { }
|
||||
'vlnv' : vlnv
|
||||
}
|
||||
|
||||
# populate memory view
|
||||
mmap = module.find('.//MEMORYMAP')
|
||||
if not mmap:
|
||||
continue
|
||||
|
||||
mem = ips[instance].setdefault('memory-view', {})
|
||||
for mrange in mmap:
|
||||
mem_interface = remove_prefix(mrange.get('MASTERBUSINTERFACE'), 'M_AXI_')
|
||||
mem_instance = mrange.get('INSTANCE')
|
||||
|
||||
entry = mem.setdefault(mem_interface, {}).setdefault(mem_instance, {})
|
||||
|
||||
entry['baseaddr'] = int(mrange.get('BASEVALUE'), 16);
|
||||
entry['highaddr'] = int(mrange.get('HIGHVALUE'), 16);
|
||||
|
||||
|
||||
|
||||
# find PCI-e module to extract memory map
|
||||
pcie = root.find('.//MODULE[@MODTYPE="axi_pcie"]')
|
||||
mmap = pcie.find('.//MEMORYMAP')
|
||||
|
@ -131,16 +157,22 @@ for mrange in mmap:
|
|||
instance = mrange.get('INSTANCE')
|
||||
|
||||
if instance in ips:
|
||||
ips[instance]['baseaddr'] = int(mrange.get('BASEVALUE'), 16);
|
||||
ips[instance]['highaddr'] = int(mrange.get('HIGHVALUE'), 16);
|
||||
base_name = remove_prefix(mrange.get('BASENAME'), 'C_').lower()
|
||||
high_name = remove_prefix(mrange.get('HIGHNAME'), 'C_').lower()
|
||||
|
||||
ips[instance][base_name] = int(mrange.get('BASEVALUE'), 16);
|
||||
ips[instance][high_name] = int(mrange.get('HIGHVALUE'), 16);
|
||||
|
||||
# find AXI-Stream switch port mapping
|
||||
switch = root.find('.//MODULE[@MODTYPE="axis_switch"]')
|
||||
busifs = switch.find('.//BUSINTERFACES')
|
||||
switch_ports = 0
|
||||
for busif in busifs:
|
||||
if busif.get('VLNV') != 'xilinx.com:interface:axis:1.0':
|
||||
continue
|
||||
|
||||
switch_ports += 1
|
||||
|
||||
busname = busif.get('BUSNAME')
|
||||
name = busif.get('NAME')
|
||||
type = busif.get('TYPE')
|
||||
|
@ -150,10 +182,23 @@ for busif in busifs:
|
|||
|
||||
port = int(m.group(2))
|
||||
|
||||
ep = bus_trace(root, busname, opponent[type], whitelist)
|
||||
|
||||
ep, busname_ep = bus_trace(root, busname, opponent[type], whitelist)
|
||||
if ep in ips:
|
||||
ips[ep]['ports'][type.lower()] = port
|
||||
|
||||
ports = ips[ep].setdefault('ports', [])
|
||||
ports.append({
|
||||
'role': opponent[type][0].lower(),
|
||||
'target': '{}:{}'.format(switch.get('INSTANCE'), port)
|
||||
})
|
||||
|
||||
module_ep = root.find('.//MODULE[@INSTANCE="{}"]'.format(ep))
|
||||
busif_ep = module_ep.find('.//BUSINTERFACE[@BUSNAME="{}"]'.format(busname_ep))
|
||||
if busif_ep:
|
||||
ports[-1]['name'] = sanitize_name(busif_ep.get('NAME'))
|
||||
|
||||
# set number of master/slave port pairs for switch
|
||||
ips[switch.get('INSTANCE')]['num_ports'] = switch_ports / 2
|
||||
|
||||
|
||||
# find Interrupt assignments
|
||||
intc = root.find('.//MODULE[@MODTYPE="axi_pcie_intc"]')
|
||||
|
@ -182,7 +227,8 @@ for port in ports:
|
|||
irqname = port.get('NAME')
|
||||
|
||||
if instance in ips:
|
||||
ips[instance]['irqs'][irqname] = irq
|
||||
irqs = ips[instance].setdefault('irqs', {})
|
||||
irqs[irqname] = '{}:{}'.format(intc.get('INSTANCE'), irq)
|
||||
|
||||
# Find BRAM storage depths (size)
|
||||
brams = root.xpath('.//MODULE[@MODTYPE="axi_bram_ctrl"]')
|
||||
|
|
18
fpga/scripts/non_root.sh
Normal file
18
fpga/scripts/non_root.sh
Normal file
|
@ -0,0 +1,18 @@
|
|||
#!/bin/bash
|
||||
|
||||
IOMMU_GROUP=24
|
||||
PCI_BDF="0000:03:00.0"
|
||||
|
||||
modprobe vfio
|
||||
modprobe vfio_pci
|
||||
|
||||
echo "10ee 7022" > /sys/bus/pci/drivers/vfio-pci/new_id
|
||||
echo ${PCI_BDF} > /sys/bus/pci/drivers/vfio-pci/bind
|
||||
|
||||
groupadd -f fpga
|
||||
usermod -G fpga -a svg
|
||||
|
||||
chgrp fpga /dev/vfio/${IOMMU_GROUP}
|
||||
chmod g+rw /dev/vfio/${IOMMU_GROUP}
|
||||
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
set(SOURCES
|
||||
main.c
|
||||
dma.c
|
||||
fifo.c
|
||||
hls.c
|
||||
intc.c
|
||||
rtds_rtt.c
|
||||
tmrctr.c
|
||||
xsg.c
|
||||
main.cpp
|
||||
# dma.c
|
||||
fifo.cpp
|
||||
# hls.c
|
||||
# intc.c
|
||||
# rtds_rtt.c
|
||||
timer.cpp
|
||||
# xsg.c
|
||||
graph.cpp
|
||||
)
|
||||
|
||||
add_executable(unit-tests ${SOURCES})
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
/** FIFO unit test.
|
||||
*
|
||||
* @file
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#include <criterion/criterion.h>
|
||||
|
||||
#include <villas/utils.h>
|
||||
|
||||
#include <villas/fpga/card.h>
|
||||
#include <villas/fpga/vlnv.h>
|
||||
#include <villas/fpga/ip.h>
|
||||
|
||||
#include <villas/fpga/ips/fifo.h>
|
||||
|
||||
extern struct fpga_card *card;
|
||||
|
||||
Test(fpga, fifo, .description = "FIFO")
|
||||
{
|
||||
int ret;
|
||||
ssize_t len;
|
||||
char src[255], dst[255];
|
||||
struct fpga_ip *fifo;
|
||||
|
||||
fifo = fpga_vlnv_lookup(&card->ips, &(struct fpga_vlnv) { "xilinx.com", "ip", "axi_fifo_mm_s", NULL });
|
||||
cr_assert(fifo);
|
||||
|
||||
ret = intc_enable(card->intc, (1 << fifo->irq), 0);
|
||||
cr_assert_eq(ret, 0, "Failed to enable interrupt");
|
||||
|
||||
ret = switch_connect(card->sw, fifo, fifo);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
/* Get some random data to compare */
|
||||
memset(dst, 0, sizeof(dst));
|
||||
len = read_random((char *) src, sizeof(src));
|
||||
if (len != sizeof(src))
|
||||
error("Failed to get random data");
|
||||
|
||||
len = fifo_write(fifo, (char *) src, sizeof(src));
|
||||
if (len != sizeof(src))
|
||||
error("Failed to send to FIFO");
|
||||
|
||||
len = fifo_read(fifo, (char *) dst, sizeof(dst));
|
||||
if (len != sizeof(dst))
|
||||
error("Failed to read from FIFO");
|
||||
|
||||
ret = intc_disable(card->intc, (1 << fifo->irq));
|
||||
cr_assert_eq(ret, 0, "Failed to disable interrupt");
|
||||
|
||||
ret = switch_disconnect(card->sw, fifo, fifo);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
/* Compare data */
|
||||
cr_assert_eq(memcmp(src, dst, sizeof(src)), 0);
|
||||
}
|
83
fpga/tests/fifo.cpp
Normal file
83
fpga/tests/fifo.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
/** FIFO unit test.
|
||||
*
|
||||
* @file
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#include <criterion/criterion.h>
|
||||
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/fpga/card.hpp>
|
||||
#include <villas/fpga/ips/fifo.hpp>
|
||||
|
||||
|
||||
extern villas::fpga::PCIeCard* fpga;
|
||||
|
||||
Test(fpga, fifo, .description = "FIFO")
|
||||
{
|
||||
ssize_t len;
|
||||
char src[255], dst[255];
|
||||
|
||||
auto logger = loggerGetOrCreate("unittest:fifo");
|
||||
|
||||
for(auto& ip : fpga->ips) {
|
||||
// skip non-fifo IPs
|
||||
if(*ip != villas::fpga::Vlnv("xilinx.com:ip:axi_fifo_mm_s:"))
|
||||
continue;
|
||||
|
||||
logger->info("Testing {}", *ip);
|
||||
|
||||
auto fifo = reinterpret_cast<villas::fpga::ip::Fifo&>(*ip);
|
||||
|
||||
if(not fifo.loopbackPossible()) {
|
||||
logger->info("Loopback test not possible for {}", *ip);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(not fifo.connectLoopback()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get some random data to compare */
|
||||
memset(dst, 0, sizeof(dst));
|
||||
len = read_random((char *) src, sizeof(src));
|
||||
if (len != sizeof(src)) {
|
||||
logger->error("Failed to get random data");
|
||||
continue;
|
||||
}
|
||||
|
||||
len = fifo.write(src, sizeof(src));
|
||||
if (len != sizeof(src)) {
|
||||
logger->error("Failed to send to FIFO");
|
||||
continue;
|
||||
}
|
||||
|
||||
len = fifo.read(dst, sizeof(dst));
|
||||
if (len != sizeof(dst)) {
|
||||
logger->error("Failed to read from FIFO");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Compare data */
|
||||
cr_assert_eq(memcmp(src, dst, sizeof(src)), 0, "Data not equal");
|
||||
|
||||
logger->info(TXT_GREEN("Passed"));
|
||||
}
|
||||
}
|
100
fpga/tests/graph.cpp
Normal file
100
fpga/tests/graph.cpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
#include <memory>
|
||||
|
||||
#include <criterion/criterion.h>
|
||||
#include <villas/directed_graph.hpp>
|
||||
#include <villas/log.hpp>
|
||||
|
||||
Test(graph, basic, .description = "DirectedGraph")
|
||||
{
|
||||
auto logger = loggerGetOrCreate("unittest:basic");
|
||||
logger->info("Testing basic graph construction and modification");
|
||||
|
||||
villas::graph::DirectedGraph<> g("unittest:basic");
|
||||
|
||||
std::shared_ptr<villas::graph::Vertex> v1(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v2(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v3(new villas::graph::Vertex);
|
||||
|
||||
auto v1id = g.addVertex(v1);
|
||||
auto v2id = g.addVertex(v2);
|
||||
auto v3id = g.addVertex(v3);
|
||||
cr_assert(g.getVertexCount() == 3);
|
||||
|
||||
g.addDefaultEdge(v1id, v2id);
|
||||
g.addDefaultEdge(v3id, v2id);
|
||||
g.addDefaultEdge(v1id, v3id);
|
||||
g.addDefaultEdge(v2id, v1id);
|
||||
cr_assert(g.getEdgeCount() == 4);
|
||||
cr_assert(g.vertexGetEdges(v1id).size() == 2);
|
||||
cr_assert(g.vertexGetEdges(v2id).size() == 1);
|
||||
cr_assert(g.vertexGetEdges(v3id).size() == 1);
|
||||
|
||||
g.removeVertex(v1id);
|
||||
g.dump();
|
||||
cr_assert(g.getVertexCount() == 2);
|
||||
cr_assert(g.vertexGetEdges(v2id).size() == 0);
|
||||
}
|
||||
|
||||
Test(graph, path, .description = "Find path")
|
||||
{
|
||||
auto logger = loggerGetOrCreate("unittest:path");
|
||||
logger->info("Testing path finding algorithm");
|
||||
|
||||
using Graph = villas::graph::DirectedGraph<>;
|
||||
Graph g("unittest:path");
|
||||
|
||||
std::shared_ptr<villas::graph::Vertex> v1(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v2(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v3(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v4(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v5(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v6(new villas::graph::Vertex);
|
||||
|
||||
auto v1id = g.addVertex(v1);
|
||||
auto v2id = g.addVertex(v2);
|
||||
auto v3id = g.addVertex(v3);
|
||||
|
||||
auto v4id = g.addVertex(v4);
|
||||
auto v5id = g.addVertex(v5);
|
||||
auto v6id = g.addVertex(v6);
|
||||
|
||||
g.addDefaultEdge(v1id, v2id);
|
||||
g.addDefaultEdge(v2id, v3id);
|
||||
|
||||
// create circular subgraph
|
||||
g.addDefaultEdge(v4id, v5id);
|
||||
g.addDefaultEdge(v5id, v4id);
|
||||
g.addDefaultEdge(v5id, v6id);
|
||||
|
||||
g.dump();
|
||||
|
||||
logger->info("Find simple path via two edges");
|
||||
std::list<Graph::EdgeIdentifier> path1;
|
||||
cr_assert(g.getPath(v1id, v3id, path1));
|
||||
|
||||
logger->info(" Path from {} to {} via:", v1id, v3id);
|
||||
for(auto& edge : path1) {
|
||||
logger->info(" -> edge {}", edge);
|
||||
}
|
||||
|
||||
logger->info("Find path between two unconnected sub-graphs");
|
||||
std::list<Graph::EdgeIdentifier> path2;
|
||||
cr_assert(not g.getPath(v1id, v4id, path2));
|
||||
logger->info(" no path found -> ok");
|
||||
|
||||
|
||||
logger->info("Find non-existing path in circular sub-graph");
|
||||
std::list<Graph::EdgeIdentifier> path3;
|
||||
cr_assert(not g.getPath(v4id, v2id, path3));
|
||||
logger->info(" no path found -> ok");
|
||||
|
||||
|
||||
logger->info("Find path in circular graph");
|
||||
std::list<Graph::EdgeIdentifier> path4;
|
||||
cr_assert(g.getPath(v4id, v6id, path4));
|
||||
|
||||
logger->info(" Path from {} to {} via:", v4id, v6id);
|
||||
for(auto& edge : path4) {
|
||||
logger->info(" -> edge {}", edge);
|
||||
}
|
||||
}
|
|
@ -29,17 +29,26 @@
|
|||
#include <villas/fpga/card.h>
|
||||
#include <villas/fpga/vlnv.h>
|
||||
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/plugin.hpp>
|
||||
#include <villas/fpga/card.hpp>
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#define FPGA_CARD "vc707"
|
||||
#define TEST_CONFIG "/villas/etc/fpga.json"
|
||||
#define TEST_CONFIG "../etc/fpga.json"
|
||||
#define TEST_LEN 0x1000
|
||||
|
||||
#define CPU_HZ 3392389000
|
||||
#define FPGA_AXI_HZ 125000000
|
||||
|
||||
struct list cards;
|
||||
struct fpga_card *card;
|
||||
struct pci pci;
|
||||
struct vfio_container vc;
|
||||
villas::fpga::CardList fpgaCards;
|
||||
villas::fpga::PCIeCard* fpga;
|
||||
|
||||
// keep to make it compile with old C tests
|
||||
struct fpga_card* card;
|
||||
|
||||
static void init()
|
||||
{
|
||||
|
@ -48,6 +57,12 @@ static void init()
|
|||
FILE *f;
|
||||
json_error_t err;
|
||||
|
||||
villas::Plugin::dumpList();
|
||||
|
||||
auto logger = loggerGetOrCreate("unittest");
|
||||
spdlog::set_pattern("[%T] [%l] [%n] %v");
|
||||
spdlog::set_level(spdlog::level::debug);
|
||||
|
||||
ret = pci_init(&pci);
|
||||
cr_assert_eq(ret, 0, "Failed to initialize PCI sub-system");
|
||||
|
||||
|
@ -67,36 +82,28 @@ static void init()
|
|||
cr_assert_not_null(fpgas, "No section 'fpgas' found in config");
|
||||
cr_assert(json_object_size(json) > 0, "No FPGAs defined in config");
|
||||
|
||||
json_t *json_card = json_object_get(fpgas, FPGA_CARD);
|
||||
cr_assert_not_null(json_card, "FPGA card " FPGA_CARD " not found");
|
||||
// get the FPGA card plugin
|
||||
villas::Plugin* plugin = villas::Plugin::lookup(villas::Plugin::Type::FpgaCard, "");
|
||||
cr_assert_not_null(plugin, "No plugin for FPGA card found");
|
||||
villas::fpga::PCIeCardFactory* fpgaCardPlugin = dynamic_cast<villas::fpga::PCIeCardFactory*>(plugin);
|
||||
|
||||
card = (struct fpga_card *) alloc(sizeof(struct fpga_card));
|
||||
cr_assert_not_null(card, "Cannot allocate memory for FPGA card");
|
||||
// create all FPGA card instances using the corresponding plugin
|
||||
fpgaCards = fpgaCardPlugin->make(fpgas, &pci, &vc);
|
||||
|
||||
ret = fpga_card_init(card, &pci, &vc);
|
||||
cr_assert_eq(ret, 0, "FPGA card initialization failed");
|
||||
if(fpgaCards.size() == 0) {
|
||||
logger->error("No FPGA cards found!");
|
||||
} else {
|
||||
fpga = fpgaCards.front().get();
|
||||
}
|
||||
|
||||
ret = fpga_card_start(card);
|
||||
cr_assert_eq(ret, 0, "FPGA card cannot be started");
|
||||
|
||||
ret = fpga_card_parse(card, json_card, FPGA_CARD);
|
||||
cr_assert_eq(ret, 0, "Failed to parse FPGA config");
|
||||
|
||||
ret = fpga_card_check(card);
|
||||
cr_assert_eq(ret, 0, "FPGA card check failed");
|
||||
cr_assert_not_null(fpga, "No FPGA card available");
|
||||
|
||||
json_decref(json);
|
||||
|
||||
if (criterion_options.logging_threshold < CRITERION_IMPORTANT)
|
||||
fpga_card_dump(card);
|
||||
}
|
||||
|
||||
static void fini()
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = fpga_card_destroy(card);
|
||||
cr_assert_eq(ret, 0, "Failed to de-initilize FPGA");
|
||||
fpgaCards.clear();
|
||||
}
|
||||
|
||||
TestSuite(fpga,
|
||||
|
@ -104,3 +111,14 @@ TestSuite(fpga,
|
|||
.fini = fini,
|
||||
.description = "VILLASfpga"
|
||||
);
|
||||
|
||||
static void init_graph()
|
||||
{
|
||||
spdlog::set_pattern("[%T] [%l] [%n] %v");
|
||||
spdlog::set_level(spdlog::level::debug);
|
||||
}
|
||||
|
||||
TestSuite(graph,
|
||||
.init = init_graph,
|
||||
.description = "Graph library"
|
||||
);
|
73
fpga/tests/timer.cpp
Normal file
73
fpga/tests/timer.cpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
/** Timer/Counter unit test.
|
||||
*
|
||||
* @file
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#include <chrono>
|
||||
#include <criterion/criterion.h>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/fpga/card.hpp>
|
||||
#include <villas/fpga/ips/timer.hpp>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
extern villas::fpga::PCIeCard* fpga;
|
||||
|
||||
Test(fpga, timer, .description = "Timer Counter")
|
||||
{
|
||||
auto logger = loggerGetOrCreate("unittest:timer");
|
||||
|
||||
for(auto& ip : fpga->ips) {
|
||||
// skip non-timer IPs
|
||||
if(*ip != villas::fpga::Vlnv("xilinx.com:ip:axi_timer:")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
logger->info("Testing {}", *ip);
|
||||
|
||||
auto timer = reinterpret_cast<villas::fpga::ip::Timer&>(*ip);
|
||||
|
||||
logger->info("Test simple waiting");
|
||||
timer.start(timer.getFrequency() / 10);
|
||||
cr_assert(timer.wait(), "Waiting failed");
|
||||
logger->info("-> passed");
|
||||
|
||||
logger->info("Measure waiting time (1s)");
|
||||
timer.start(timer.getFrequency());
|
||||
|
||||
const auto start = std::chrono::high_resolution_clock::now();
|
||||
timer.wait();
|
||||
const auto stop = std::chrono::high_resolution_clock::now();
|
||||
|
||||
const int oneSecondInUs = 1000000;
|
||||
const auto duration = stop - start;
|
||||
const auto durationUs =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
|
||||
logger->info("-> time passed: {} us", durationUs);
|
||||
|
||||
cr_assert(std::abs(durationUs - oneSecondInUs) < 0.01 * oneSecondInUs,
|
||||
"Timer deviation > 1%%");
|
||||
|
||||
logger->info(TXT_GREEN("Passed"));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
2
fpga/thirdparty/libxil
vendored
2
fpga/thirdparty/libxil
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 89eb3ead0c210318144238f2b5b6a96ce3feec73
|
||||
Subproject commit f08dc8e878719ab8937c47fedd20ebf3aa68e123
|
67
fpga/thirdparty/spdlog/.gitignore
vendored
Normal file
67
fpga/thirdparty/spdlog/.gitignore
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
# Auto generated files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
*.suo
|
||||
*.tlog
|
||||
*.ilk
|
||||
*.log
|
||||
*.pdb
|
||||
*.idb
|
||||
*.iobj
|
||||
*.ipdb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# Codelite
|
||||
.codelite
|
||||
|
||||
# .orig files
|
||||
*.orig
|
||||
|
||||
# example files
|
||||
example/*
|
||||
!example/example.cpp
|
||||
!example/bench.cpp
|
||||
!example/utils.h
|
||||
!example/Makefile*
|
||||
!example/example.sln
|
||||
!example/example.vcxproj
|
||||
!example/CMakeLists.txt
|
||||
!example/multisink.cpp
|
||||
!example/jni
|
||||
|
||||
# generated files
|
||||
generated
|
||||
|
||||
# Cmake
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
/tests/tests.VC.VC.opendb
|
||||
/tests/tests.VC.db
|
||||
/tests/tests
|
||||
/tests/logs/*
|
||||
|
||||
# idea
|
||||
.idea/
|
91
fpga/thirdparty/spdlog/.travis.yml
vendored
Normal file
91
fpga/thirdparty/spdlog/.travis.yml
vendored
Normal file
|
@ -0,0 +1,91 @@
|
|||
# Adapted from various sources, including:
|
||||
# - Louis Dionne's Hana: https://github.com/ldionne/hana
|
||||
# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit
|
||||
# - Eric Niebler's range-v3: https://github.com/ericniebler/range-v3
|
||||
language: cpp
|
||||
|
||||
# Test matrix:
|
||||
# - Build matrix per compiler: C++11/C++14 + Debug/Release
|
||||
# - Optionally: AddressSanitizer (ASAN)
|
||||
# - Valgrind: all release builds are also tested with valgrind
|
||||
# - clang 3.4, 3.5, 3.6, trunk
|
||||
# - Note: 3.4 and trunk are tested with/without ASAN,
|
||||
# the rest is only tested with ASAN=On.
|
||||
# - gcc 4.9, 5.0
|
||||
#
|
||||
matrix:
|
||||
include:
|
||||
|
||||
# Test gcc-4.8: C++11, Build=Debug/Release, ASAN=Off
|
||||
- env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11 ASAN=Off LIBCXX=Off
|
||||
os: linux
|
||||
addons: &gcc48
|
||||
apt:
|
||||
packages:
|
||||
- g++-4.8
|
||||
- valgrind
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
- env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=Off
|
||||
os: linux
|
||||
addons: *gcc48
|
||||
|
||||
# Test gcc-4.9: C++11, Build=Debug/Release, ASAN=Off
|
||||
- env: GCC_VERSION=4.9 BUILD_TYPE=Debug CPP=11 ASAN=Off LIBCXX=Off
|
||||
os: linux
|
||||
addons: &gcc49
|
||||
apt:
|
||||
packages:
|
||||
- g++-4.9
|
||||
- valgrind
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
- env: GCC_VERSION=4.9 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=Off
|
||||
os: linux
|
||||
addons: *gcc49
|
||||
|
||||
# Install dependencies
|
||||
before_install:
|
||||
- export CHECKOUT_PATH=`pwd`;
|
||||
- if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi
|
||||
- if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi
|
||||
- if [ "$CLANG_VERSION" == "3.4" ]; then export CXX="/usr/local/clang-3.4/bin/clang++" CC="/usr/local/clang-3.4/bin/clang"; fi
|
||||
- which $CXX
|
||||
- which $CC
|
||||
- which valgrind
|
||||
- if [ -n "$CLANG_VERSION" ]; then sudo CXX=$CXX CC=$CC ./tests/install_libcxx.sh; fi
|
||||
|
||||
install:
|
||||
- cd $CHECKOUT_PATH
|
||||
|
||||
# Workaround for valgrind bug: https://bugs.kde.org/show_bug.cgi?id=326469.
|
||||
# It is fixed in valgrind 3.10 so this won't be necessary if someone
|
||||
# replaces the current valgrind (3.7) with valgrind-3.10
|
||||
- sed -i 's/march=native/msse4.2/' example/Makefile
|
||||
|
||||
- if [ ! -d build ]; then mkdir build; fi
|
||||
- export CXX_FLAGS="-I${CHECKOUT_PATH}/include"
|
||||
- export CXX_LINKER_FLAGS=""
|
||||
- if [ -z "$BUILD_TYPE" ]; then export BUILD_TYPE=Release; fi
|
||||
- if [ "$ASAN" == "On"]; then export CXX_FLAGS="${CXX_FLAGS} -fsanitize=address,undefined,integer -fno-omit-frame-pointer -fno-sanitize=unsigned-integer-overflow"; fi
|
||||
- if [ -n "$CLANG_VERSION" ]; then CXX_FLAGS="${CXX_FLAGS} -D__extern_always_inline=inline"; fi
|
||||
- if [ "$LIBCXX" == "On" ]; then CXX_FLAGS="${CXX_FLAGS} -stdlib=libc++ -I/usr/include/c++/v1/"; fi
|
||||
- if [ "$LIBCXX" == "On" ]; then CXX_LINKER_FLAGS="${CXX_FLAGS} -L/usr/lib/ -lc++"; fi
|
||||
- CXX_FLAGS="${CXX_FLAGS} -std=c++${CPP}"
|
||||
|
||||
# Build examples
|
||||
- cd example
|
||||
- if [ "$BUILD_TYPE" == "Release" ]; then make rebuild CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example; fi
|
||||
- if [ "$BUILD_TYPE" == "Debug" ]; then make rebuild debug CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example-debug; fi
|
||||
|
||||
|
||||
script:
|
||||
- ./"${BIN}"
|
||||
- valgrind --trace-children=yes --leak-check=full ./"${BIN}"
|
||||
- cd $CHECKOUT_PATH/tests; make rebuild; ./tests
|
||||
- cd $CHECKOUT_PATH/tests; STYLE=printf make rebuild; ./tests
|
||||
|
||||
notifications:
|
||||
email: false
|
116
fpga/thirdparty/spdlog/CMakeLists.txt
vendored
Normal file
116
fpga/thirdparty/spdlog/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,116 @@
|
|||
#
|
||||
# Copyright(c) 2015 Ruslan Baratov.
|
||||
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(spdlog VERSION 0.16.2)
|
||||
include(CTest)
|
||||
include(CMakeDependentOption)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
# compiler config
|
||||
#---------------------------------------------------------------------------------------
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
set(CMAKE_CXX_FLAGS "-Wall ${CMAKE_CXX_FLAGS}")
|
||||
endif()
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
# spdlog target
|
||||
#---------------------------------------------------------------------------------------
|
||||
add_library(spdlog INTERFACE)
|
||||
|
||||
option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF)
|
||||
cmake_dependent_option(SPDLOG_BUILD_TESTING
|
||||
"Build spdlog tests" ON
|
||||
"BUILD_TESTING" OFF
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
spdlog
|
||||
INTERFACE
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
)
|
||||
|
||||
set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
|
||||
if(SPDLOG_BUILD_EXAMPLES)
|
||||
add_subdirectory(example)
|
||||
endif()
|
||||
|
||||
if(SPDLOG_BUILD_TESTING)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
# Install/export targets and files
|
||||
#---------------------------------------------------------------------------------------
|
||||
# set files and directories
|
||||
set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
||||
set(include_install_dir "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
set(pkgconfig_install_dir "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||
set(version_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
|
||||
set(project_config "${PROJECT_NAME}Config.cmake")
|
||||
set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc")
|
||||
set(targets_export_name "${PROJECT_NAME}Targets")
|
||||
set(namespace "${PROJECT_NAME}::")
|
||||
|
||||
# generate package version file
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file(
|
||||
"${version_config}" COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
# configure pkg config file
|
||||
configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY)
|
||||
|
||||
# install targets
|
||||
install(
|
||||
TARGETS spdlog
|
||||
EXPORT "${targets_export_name}"
|
||||
INCLUDES DESTINATION "${include_install_dir}"
|
||||
)
|
||||
|
||||
# install headers
|
||||
install(
|
||||
DIRECTORY "${HEADER_BASE}/${PROJECT_NAME}"
|
||||
DESTINATION "${include_install_dir}"
|
||||
)
|
||||
|
||||
# install project version file
|
||||
install(
|
||||
FILES "${version_config}"
|
||||
DESTINATION "${config_install_dir}"
|
||||
)
|
||||
|
||||
# install pkg config file
|
||||
install(
|
||||
FILES "${pkg_config}"
|
||||
DESTINATION "${pkgconfig_install_dir}"
|
||||
)
|
||||
|
||||
# install project config file
|
||||
install(
|
||||
EXPORT "${targets_export_name}"
|
||||
NAMESPACE "${namespace}"
|
||||
DESTINATION "${config_install_dir}"
|
||||
FILE ${project_config}
|
||||
)
|
||||
|
||||
# export build directory config file
|
||||
export(
|
||||
EXPORT ${targets_export_name}
|
||||
NAMESPACE "${namespace}"
|
||||
FILE ${project_config}
|
||||
)
|
||||
|
||||
# register project in CMake user registry
|
||||
export(PACKAGE ${PROJECT_NAME})
|
||||
|
||||
file(GLOB_RECURSE spdlog_include_SRCS "${HEADER_BASE}/*.h")
|
||||
add_custom_target(spdlog_headers_for_ide SOURCES ${spdlog_include_SRCS})
|
13
fpga/thirdparty/spdlog/INSTALL
vendored
Normal file
13
fpga/thirdparty/spdlog/INSTALL
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
spdlog is header only library.
|
||||
Just copy the files to your build tree and use a C++11 compiler
|
||||
|
||||
Tested on:
|
||||
gcc 4.8.1 and above
|
||||
clang 3.5
|
||||
Visual Studio 2013
|
||||
|
||||
gcc 4.8 flags: --std==c++11 -pthread -O3 -flto -Wl,--no-as-needed
|
||||
gcc 4.9 flags: --std=c++11 -pthread -O3 -flto
|
||||
|
||||
|
||||
see the makefile in the example folder
|
22
fpga/thirdparty/spdlog/LICENSE
vendored
Normal file
22
fpga/thirdparty/spdlog/LICENSE
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Gabi Melman.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
221
fpga/thirdparty/spdlog/README.md
vendored
Normal file
221
fpga/thirdparty/spdlog/README.md
vendored
Normal file
|
@ -0,0 +1,221 @@
|
|||
# spdlog
|
||||
|
||||
Very fast, header only, C++ logging library. [](https://travis-ci.org/gabime/spdlog) [](https://ci.appveyor.com/project/gabime/spdlog)
|
||||
|
||||
|
||||
## Install
|
||||
#### Just copy the headers:
|
||||
|
||||
* Copy the source [folder](https://github.com/gabime/spdlog/tree/master/include/spdlog) to your build tree and use a C++11 compiler.
|
||||
|
||||
#### Or use your favorite package manager:
|
||||
|
||||
* Ubuntu: `apt-get install libspdlog-dev`
|
||||
* Homebrew: `brew install spdlog`
|
||||
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
|
||||
* Fedora: `yum install spdlog`
|
||||
* Gentoo: `emerge dev-libs/spdlog`
|
||||
* Arch Linux: `yaourt -S spdlog-git`
|
||||
* vcpkg: `vcpkg install spdlog`
|
||||
|
||||
|
||||
## Platforms
|
||||
* Linux, FreeBSD, Solaris
|
||||
* Windows (vc 2013+, cygwin)
|
||||
* Mac OSX (clang 3.5+)
|
||||
* Android
|
||||
|
||||
## Features
|
||||
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
|
||||
* Headers only, just copy and use.
|
||||
* Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library.
|
||||
* Optional printf syntax support.
|
||||
* Extremely fast asynchronous mode (optional) - using lockfree queues and other tricks to reach millions of calls/sec.
|
||||
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
|
||||
* Conditional Logging
|
||||
* Multi/Single threaded loggers.
|
||||
* Various log targets:
|
||||
* Rotating log files.
|
||||
* Daily log files.
|
||||
* Console logging (colors supported).
|
||||
* syslog.
|
||||
* Windows debugger (```OutputDebugString(..)```)
|
||||
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
|
||||
* Severity based filtering - threshold levels can be modified in runtime as well as in compile time.
|
||||
|
||||
|
||||
|
||||
## Benchmarks
|
||||
|
||||
Below are some [benchmarks](bench) comparing popular log libraries under Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
|
||||
|
||||
#### Synchronous mode
|
||||
Time needed to log 1,000,000 lines in synchronous mode (in seconds, the best of 3 runs):
|
||||
|
||||
|threads|boost log 1.54|glog |easylogging |spdlog|
|
||||
|-------|:-------:|:-----:|----------:|------:|
|
||||
|1| 4.169s |1.066s |0.975s |0.302s|
|
||||
|10| 6.180s |3.032s |2.857s |0.968s|
|
||||
|100| 5.981s |1.139s |4.512s |0.497s|
|
||||
|
||||
|
||||
#### Asynchronous mode
|
||||
Time needed to log 1,000,000 lines in asynchronous mode, i.e. the time it takes to put them in the async queue (in seconds, the best of 3 runs):
|
||||
|
||||
|threads|g2log <sup>async logger</sup> |spdlog <sup>async mode</sup>|
|
||||
|:-------|:-----:|-------------------------:|
|
||||
|1| 1.850s |0.216s |
|
||||
|10| 0.943s |0.173s|
|
||||
|100| 0.959s |0.202s|
|
||||
|
||||
|
||||
|
||||
|
||||
## Usage Example
|
||||
```c++
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
void async_example();
|
||||
void syslog_example();
|
||||
void user_defined_example();
|
||||
void err_handler_example();
|
||||
|
||||
namespace spd = spdlog;
|
||||
int main(int, char*[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Console logger with color
|
||||
auto console = spd::stdout_color_mt("console");
|
||||
console->info("Welcome to spdlog!");
|
||||
console->error("Some error message with arg{}..", 1);
|
||||
|
||||
// Formatting examples
|
||||
console->warn("Easy padding in numbers like {:08d}", 12);
|
||||
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||
console->info("Support for floats {:03.2f}", 1.23456);
|
||||
console->info("Positional args are {1} {0}..", "too", "supported");
|
||||
console->info("{:<30}", "left aligned");
|
||||
|
||||
// Use global registry to retrieve loggers
|
||||
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
|
||||
|
||||
// Create basic file logger (not rotated)
|
||||
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt");
|
||||
my_logger->info("Some log message");
|
||||
|
||||
// Create a file rotating logger with 5mb size max and 3 rotated files
|
||||
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile.txt", 1048576 * 5, 3);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
|
||||
|
||||
// Create a daily logger - a new file is created every day on 2:30am
|
||||
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
||||
// trigger flush if the log severity is error or higher
|
||||
daily_logger->flush_on(spd::level::err);
|
||||
daily_logger->info(123.44);
|
||||
|
||||
// Customize msg format for all messages
|
||||
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
|
||||
rotating_logger->info("This is another message with custom format");
|
||||
|
||||
|
||||
// Runtime log levels
|
||||
spd::set_level(spd::level::info); //Set global log level to info
|
||||
console->debug("This message should not be displayed!");
|
||||
console->set_level(spd::level::debug); // Set specific logger's log level
|
||||
console->debug("This message should be displayed..");
|
||||
|
||||
// Compile time log levels
|
||||
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
|
||||
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
|
||||
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
|
||||
|
||||
// Asynchronous logging is very fast..
|
||||
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
|
||||
async_example();
|
||||
|
||||
// syslog example. linux/osx only
|
||||
syslog_example();
|
||||
|
||||
// android example. compile with NDK
|
||||
android_example();
|
||||
|
||||
// Log user-defined types example
|
||||
user_defined_example();
|
||||
|
||||
// Change default log error handler
|
||||
err_handler_example();
|
||||
|
||||
// Apply a function on all registered loggers
|
||||
spd::apply_all([&](std::shared_ptr<spd::logger> l)
|
||||
{
|
||||
l->info("End of example.");
|
||||
});
|
||||
|
||||
// Release and close all loggers
|
||||
spd::drop_all();
|
||||
}
|
||||
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
|
||||
catch (const spd::spdlog_ex& ex)
|
||||
{
|
||||
std::cout << "Log init failed: " << ex.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void async_example()
|
||||
{
|
||||
size_t q_size = 4096; //queue size must be power of 2
|
||||
spd::set_async_mode(q_size);
|
||||
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
|
||||
for (int i = 0; i < 100; ++i)
|
||||
async_file->info("Async message #{}", i);
|
||||
}
|
||||
|
||||
//syslog example
|
||||
void syslog_example()
|
||||
{
|
||||
#ifdef SPDLOG_ENABLE_SYSLOG
|
||||
std::string ident = "spdlog-example";
|
||||
auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID);
|
||||
syslog_logger->warn("This is warning that will end up in syslog..");
|
||||
#endif
|
||||
}
|
||||
|
||||
// user defined types logging by implementing operator<<
|
||||
struct my_type
|
||||
{
|
||||
int i;
|
||||
template<typename OStream>
|
||||
friend OStream& operator<<(OStream& os, const my_type &c)
|
||||
{
|
||||
return os << "[my_type i="<<c.i << "]";
|
||||
}
|
||||
};
|
||||
|
||||
#include <spdlog/fmt/ostr.h> // must be included
|
||||
void user_defined_example()
|
||||
{
|
||||
spd::get("console")->info("user defined type: {}", my_type { 14 });
|
||||
}
|
||||
|
||||
//
|
||||
//custom error handler
|
||||
//
|
||||
void err_handler_example()
|
||||
{
|
||||
spd::set_error_handler([](const std::string& msg) {
|
||||
std::cerr << "my err handler: " << msg << std::endl;
|
||||
});
|
||||
// (or logger->set_error_handler(..) to set for specific logger)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Documentation
|
||||
Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages.
|
32
fpga/thirdparty/spdlog/appveyor.yml
vendored
Normal file
32
fpga/thirdparty/spdlog/appveyor.yml
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
version: 1.0.{build}
|
||||
image: Visual Studio 2015
|
||||
environment:
|
||||
matrix:
|
||||
- GENERATOR: '"MinGW Makefiles"'
|
||||
BUILD_TYPE: Debug
|
||||
- GENERATOR: '"MinGW Makefiles"'
|
||||
BUILD_TYPE: Release
|
||||
- GENERATOR: '"Visual Studio 14 2015"'
|
||||
BUILD_TYPE: Debug
|
||||
- GENERATOR: '"Visual Studio 14 2015"'
|
||||
BUILD_TYPE: Release
|
||||
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
||||
BUILD_TYPE: Debug
|
||||
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
||||
BUILD_TYPE: Release
|
||||
build_script:
|
||||
- cmd: >-
|
||||
set
|
||||
|
||||
mkdir build
|
||||
|
||||
cd build
|
||||
|
||||
set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
|
||||
|
||||
set PATH=C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin;%PATH%
|
||||
|
||||
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE%
|
||||
|
||||
cmake --build . --config %BUILD_TYPE%
|
||||
test: off
|
5
fpga/thirdparty/spdlog/astyle.sh
vendored
Executable file
5
fpga/thirdparty/spdlog/astyle.sh
vendored
Executable file
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
find . -name "*\.h" -o -name "*\.cpp"|xargs dos2unix
|
||||
find . -name "*\.h" -o -name "*\.cpp"|xargs astyle -n -c -A1
|
||||
|
||||
|
62
fpga/thirdparty/spdlog/bench/Makefile
vendored
Normal file
62
fpga/thirdparty/spdlog/bench/Makefile
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
CXX ?= g++
|
||||
CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -I../include
|
||||
CXX_RELEASE_FLAGS = -O3 -flto -DNDEBUG
|
||||
|
||||
|
||||
binaries=spdlog-bench spdlog-bench-mt spdlog-async spdlog-null-async boost-bench boost-bench-mt glog-bench glog-bench-mt g2log-async easylogging-bench easylogging-bench-mt
|
||||
|
||||
all: $(binaries)
|
||||
|
||||
spdlog-bench: spdlog-bench.cpp
|
||||
$(CXX) spdlog-bench.cpp -o spdlog-bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
spdlog-bench-mt: spdlog-bench-mt.cpp
|
||||
$(CXX) spdlog-bench-mt.cpp -o spdlog-bench-mt $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
spdlog-async: spdlog-async.cpp
|
||||
$(CXX) spdlog-async.cpp -o spdlog-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
|
||||
spdlog-null-async: spdlog-null-async.cpp
|
||||
$(CXX) spdlog-null-async.cpp -o spdlog-null-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
|
||||
|
||||
BOOST_FLAGS = -DBOOST_LOG_DYN_LINK -I/usr/include -lboost_log -lboost_log_setup -lboost_filesystem -lboost_system -lboost_thread -lboost_regex -lboost_date_time -lboost_chrono
|
||||
|
||||
boost-bench: boost-bench.cpp
|
||||
$(CXX) boost-bench.cpp -o boost-bench $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
boost-bench-mt: boost-bench-mt.cpp
|
||||
$(CXX) boost-bench-mt.cpp -o boost-bench-mt $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
|
||||
GLOG_FLAGS = -lglog
|
||||
glog-bench: glog-bench.cpp
|
||||
$(CXX) glog-bench.cpp -o glog-bench $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
glog-bench-mt: glog-bench-mt.cpp
|
||||
$(CXX) glog-bench-mt.cpp -o glog-bench-mt $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
|
||||
G2LOG_FLAGS = -I/home/gabi/devel/g2log/g2log/src -L/home/gabi/devel/g2log/g2log -llib_g2logger
|
||||
g2log-async: g2log-async.cpp
|
||||
$(CXX) g2log-async.cpp -o g2log-async $(CXXFLAGS) $(G2LOG_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
|
||||
EASYL_FLAGS = -I../../easylogging/src/
|
||||
easylogging-bench: easylogging-bench.cpp
|
||||
$(CXX) easylogging-bench.cpp -o easylogging-bench $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||
easylogging-bench-mt: easylogging-bench-mt.cpp
|
||||
$(CXX) easylogging-bench-mt.cpp -o easylogging-bench-mt $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
rm -f *.o logs/* $(binaries)
|
||||
|
||||
|
||||
rebuild: clean all
|
||||
|
||||
|
||||
|
57
fpga/thirdparty/spdlog/bench/Makefile.mingw
vendored
Normal file
57
fpga/thirdparty/spdlog/bench/Makefile.mingw
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
CXX ?= g++
|
||||
CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
|
||||
CXX_RELEASE_FLAGS = -O3 -flto
|
||||
|
||||
|
||||
binaries=spdlog-bench spdlog-bench-mt spdlog-async boost-bench boost-bench-mt glog-bench glog-bench-mt g2log-async easylogging-bench easylogging-bench-mt
|
||||
|
||||
all: $(binaries)
|
||||
|
||||
spdlog-bench: spdlog-bench.cpp
|
||||
$(CXX) spdlog-bench.cpp -o spdlog-bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
spdlog-bench-mt: spdlog-bench-mt.cpp
|
||||
$(CXX) spdlog-bench-mt.cpp -o spdlog-bench-mt $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
spdlog-async: spdlog-async.cpp
|
||||
$(CXX) spdlog-async.cpp -o spdlog-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
|
||||
BOOST_FLAGS = -DBOOST_LOG_DYN_LINK -I/home/gabi/devel/boost_1_56_0/ -L/home/gabi/devel/boost_1_56_0/stage/lib -lboost_log -lboost_log_setup -lboost_filesystem -lboost_system -lboost_thread -lboost_regex -lboost_date_time -lboost_chrono
|
||||
|
||||
boost-bench: boost-bench.cpp
|
||||
$(CXX) boost-bench.cpp -o boost-bench $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
boost-bench-mt: boost-bench-mt.cpp
|
||||
$(CXX) boost-bench-mt.cpp -o boost-bench-mt $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
|
||||
GLOG_FLAGS = -lglog
|
||||
glog-bench: glog-bench.cpp
|
||||
$(CXX) glog-bench.cpp -o glog-bench $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
glog-bench-mt: glog-bench-mt.cpp
|
||||
$(CXX) glog-bench-mt.cpp -o glog-bench-mt $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
|
||||
G2LOG_FLAGS = -I/home/gabi/devel/g2log/g2log/src -L/home/gabi/devel/g2log/g2log -llib_g2logger
|
||||
g2log-async: g2log-async.cpp
|
||||
$(CXX) g2log-async.cpp -o g2log-async $(CXXFLAGS) $(G2LOG_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
|
||||
EASYL_FLAGS = -I../../easylogging/src/
|
||||
easylogging-bench: easylogging-bench.cpp
|
||||
$(CXX) easylogging-bench.cpp -o easylogging-bench $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||
easylogging-bench-mt: easylogging-bench-mt.cpp
|
||||
$(CXX) easylogging-bench-mt.cpp -o easylogging-bench-mt $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
rm -f *.o logs/* $(binaries)
|
||||
|
||||
|
||||
rebuild: clean all
|
||||
|
||||
|
||||
|
84
fpga/thirdparty/spdlog/bench/boost-bench-mt.cpp
vendored
Normal file
84
fpga/thirdparty/spdlog/bench/boost-bench-mt.cpp
vendored
Normal file
|
@ -0,0 +1,84 @@
|
|||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks/text_file_backend.hpp>
|
||||
#include <boost/log/utility/setup/file.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
void init()
|
||||
{
|
||||
logging::add_file_log
|
||||
(
|
||||
keywords::file_name = "logs/boost-sample_%N.log", /*< file name pattern >*/
|
||||
keywords::auto_flush = false,
|
||||
keywords::format = "[%TimeStamp%]: %Message%"
|
||||
);
|
||||
|
||||
logging::core::get()->set_filter
|
||||
(
|
||||
logging::trivial::severity >= logging::trivial::info
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int thread_count = 10;
|
||||
if(argc > 1)
|
||||
thread_count = atoi(argv[1]);
|
||||
|
||||
int howmany = 1000000;
|
||||
|
||||
|
||||
init();
|
||||
logging::add_common_attributes();
|
||||
|
||||
|
||||
using namespace logging::trivial;
|
||||
|
||||
src::severity_logger_mt< severity_level > lg;
|
||||
|
||||
std::atomic<int > msg_counter {0};
|
||||
vector<thread> threads;
|
||||
|
||||
for (int t = 0; t < thread_count; ++t)
|
||||
{
|
||||
threads.push_back(std::thread([&]()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int counter = ++msg_counter;
|
||||
if (counter > howmany) break;
|
||||
BOOST_LOG_SEV(lg, info) << "boost message #" << counter << ": This is some text for your pleasure";
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
for(auto &t:threads)
|
||||
{
|
||||
t.join();
|
||||
};
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
47
fpga/thirdparty/spdlog/bench/boost-bench.cpp
vendored
Normal file
47
fpga/thirdparty/spdlog/bench/boost-bench.cpp
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks/text_file_backend.hpp>
|
||||
#include <boost/log/utility/setup/file.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
void init()
|
||||
{
|
||||
logging::add_file_log
|
||||
(
|
||||
keywords::file_name = "logs/boost-sample_%N.log", /*< file name pattern >*/
|
||||
keywords::auto_flush = false,
|
||||
keywords::format = "[%TimeStamp%]: %Message%"
|
||||
);
|
||||
|
||||
logging::core::get()->set_filter
|
||||
(
|
||||
logging::trivial::severity >= logging::trivial::info
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char* [])
|
||||
{
|
||||
int howmany = 1000000;
|
||||
init();
|
||||
logging::add_common_attributes();
|
||||
|
||||
using namespace logging::trivial;
|
||||
src::severity_logger_mt< severity_level > lg;
|
||||
for(int i = 0 ; i < howmany; ++i)
|
||||
BOOST_LOG_SEV(lg, info) << "boost message #" << i << ": This is some text for your pleasure";
|
||||
|
||||
return 0;
|
||||
}
|
10
fpga/thirdparty/spdlog/bench/easyl.conf
vendored
Normal file
10
fpga/thirdparty/spdlog/bench/easyl.conf
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
* GLOBAL:
|
||||
FORMAT = "[%datetime]: %msg"
|
||||
FILENAME = ./logs/easylogging.log
|
||||
ENABLED = true
|
||||
TO_FILE = true
|
||||
TO_STANDARD_OUTPUT = false
|
||||
MILLISECONDS_WIDTH = 3
|
||||
PERFORMANCE_TRACKING = false
|
||||
MAX_LOG_FILE_SIZE = 10485760
|
||||
Log_Flush_Threshold = 10485760
|
52
fpga/thirdparty/spdlog/bench/easylogging-bench-mt.cpp
vendored
Normal file
52
fpga/thirdparty/spdlog/bench/easylogging-bench-mt.cpp
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
|
||||
#define _ELPP_THREAD_SAFE
|
||||
#include "easylogging++.h"
|
||||
_INITIALIZE_EASYLOGGINGPP
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
||||
int thread_count = 10;
|
||||
if(argc > 1)
|
||||
thread_count = atoi(argv[1]);
|
||||
|
||||
int howmany = 1000000;
|
||||
|
||||
// Load configuration from file
|
||||
el::Configurations conf("easyl.conf");
|
||||
el::Loggers::reconfigureLogger("default", conf);
|
||||
|
||||
std::atomic<int > msg_counter {0};
|
||||
vector<thread> threads;
|
||||
|
||||
for (int t = 0; t < thread_count; ++t)
|
||||
{
|
||||
threads.push_back(std::thread([&]()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int counter = ++msg_counter;
|
||||
if (counter > howmany) break;
|
||||
LOG(INFO) << "easylog message #" << counter << ": This is some text for your pleasure";
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
for(auto &t:threads)
|
||||
{
|
||||
t.join();
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
22
fpga/thirdparty/spdlog/bench/easylogging-bench.cpp
vendored
Normal file
22
fpga/thirdparty/spdlog/bench/easylogging-bench.cpp
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
|
||||
#include "easylogging++.h"
|
||||
|
||||
_INITIALIZE_EASYLOGGINGPP
|
||||
|
||||
int main(int, char* [])
|
||||
{
|
||||
int howmany = 1000000;
|
||||
|
||||
// Load configuration from file
|
||||
el::Configurations conf("easyl.conf");
|
||||
el::Loggers::reconfigureLogger("default", conf);
|
||||
|
||||
for(int i = 0 ; i < howmany; ++i)
|
||||
LOG(INFO) << "easylog message #" << i << ": This is some text for your pleasure";
|
||||
return 0;
|
||||
}
|
62
fpga/thirdparty/spdlog/bench/g2log-async.cpp
vendored
Normal file
62
fpga/thirdparty/spdlog/bench/g2log-async.cpp
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
#include "g2logworker.h"
|
||||
#include "g2log.h"
|
||||
|
||||
using namespace std;
|
||||
template<typename T> std::string format(const T& value);
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
using namespace std::chrono;
|
||||
using clock=steady_clock;
|
||||
int thread_count = 10;
|
||||
|
||||
if(argc > 1)
|
||||
thread_count = atoi(argv[1]);
|
||||
int howmany = 1000000;
|
||||
|
||||
g2LogWorker g2log(argv[0], "logs");
|
||||
g2::initializeLogging(&g2log);
|
||||
|
||||
|
||||
std::atomic<int > msg_counter {0};
|
||||
vector<thread> threads;
|
||||
auto start = clock::now();
|
||||
for (int t = 0; t < thread_count; ++t)
|
||||
{
|
||||
threads.push_back(std::thread([&]()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int counter = ++msg_counter;
|
||||
if (counter > howmany) break;
|
||||
LOG(INFO) << "g2log message #" << counter << ": This is some text for your pleasure";
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
for(auto &t:threads)
|
||||
{
|
||||
t.join();
|
||||
};
|
||||
|
||||
duration<float> delta = clock::now() - start;
|
||||
float deltaf = delta.count();
|
||||
auto rate = howmany/deltaf;
|
||||
|
||||
cout << "Total: " << howmany << std::endl;
|
||||
cout << "Threads: " << thread_count << std::endl;
|
||||
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||
}
|
50
fpga/thirdparty/spdlog/bench/glog-bench-mt.cpp
vendored
Normal file
50
fpga/thirdparty/spdlog/bench/glog-bench-mt.cpp
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
||||
int thread_count = 10;
|
||||
if(argc > 1)
|
||||
thread_count = atoi(argv[1]);
|
||||
|
||||
int howmany = 1000000;
|
||||
|
||||
FLAGS_logtostderr = 0;
|
||||
FLAGS_log_dir = "logs";
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
|
||||
std::atomic<int > msg_counter {0};
|
||||
vector<thread> threads;
|
||||
|
||||
for (int t = 0; t < thread_count; ++t)
|
||||
{
|
||||
threads.push_back(std::thread([&]()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int counter = ++msg_counter;
|
||||
if (counter > howmany) break;
|
||||
LOG(INFO) << "glog message #" << counter << ": This is some text for your pleasure";
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
for(auto &t:threads)
|
||||
{
|
||||
t.join();
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
21
fpga/thirdparty/spdlog/bench/glog-bench.cpp
vendored
Normal file
21
fpga/thirdparty/spdlog/bench/glog-bench.cpp
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
|
||||
int main(int, char* argv[])
|
||||
{
|
||||
int howmany = 1000000;
|
||||
|
||||
|
||||
FLAGS_logtostderr = 0;
|
||||
FLAGS_log_dir = "logs";
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
for(int i = 0 ; i < howmany; ++i)
|
||||
LOG(INFO) << "glog message # " << i << ": This is some text for your pleasure";
|
||||
|
||||
return 0;
|
||||
}
|
32
fpga/thirdparty/spdlog/bench/latency/Makefile
vendored
Normal file
32
fpga/thirdparty/spdlog/bench/latency/Makefile
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
CXX ?= g++
|
||||
CXXFLAGS = -march=native -Wall -std=c++11 -pthread
|
||||
CXX_RELEASE_FLAGS = -O2 -DNDEBUG
|
||||
|
||||
|
||||
binaries=spdlog-latency g3log-latency g3log-crush
|
||||
|
||||
all: $(binaries)
|
||||
|
||||
spdlog-latency: spdlog-latency.cpp
|
||||
$(CXX) spdlog-latency.cpp -o spdlog-latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -I../../include
|
||||
|
||||
|
||||
|
||||
g3log-latency: g3log-latency.cpp
|
||||
$(CXX) g3log-latency.cpp -o g3log-latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -I../../../g3log/src -L. -lg3logger
|
||||
|
||||
|
||||
g3log-crush: g3log-crush.cpp
|
||||
$(CXX) g3log-crush.cpp -o g3log-crush $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -I../../../g3log/src -L. -lg3logger
|
||||
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
rm -f *.o *.log $(binaries)
|
||||
|
||||
|
||||
rebuild: clean all
|
||||
|
||||
|
||||
|
13
fpga/thirdparty/spdlog/bench/latency/compare.sh
vendored
Executable file
13
fpga/thirdparty/spdlog/bench/latency/compare.sh
vendored
Executable file
|
@ -0,0 +1,13 @@
|
|||
#!/bin/bash
|
||||
echo "running spdlog and g3log tests 10 time with ${1:-10} threads each (total 1,000,000 entries).."
|
||||
rm -f *.log
|
||||
for i in {1..10}
|
||||
|
||||
do
|
||||
echo
|
||||
sleep 0.5
|
||||
./spdlog-latency ${1:-10} 2>/dev/null || exit
|
||||
sleep 0.5
|
||||
./g3log-latency ${1:-10} 2>/dev/null || exit
|
||||
|
||||
done
|
37
fpga/thirdparty/spdlog/bench/latency/g3log-crush.cpp
vendored
Normal file
37
fpga/thirdparty/spdlog/bench/latency/g3log-crush.cpp
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
#include <iostream>
|
||||
|
||||
#include <g3log/g3log.hpp>
|
||||
#include <g3log/logworker.hpp>
|
||||
|
||||
void CrusherLoop()
|
||||
{
|
||||
size_t counter = 0;
|
||||
while (true)
|
||||
{
|
||||
LOGF(INFO, "Some text to crush you machine. thread:");
|
||||
if(++counter % 1000000 == 0)
|
||||
{
|
||||
std::cout << "Wrote " << counter << " entries" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
std::cout << "WARNING: This test will exaust all your machine memory and will crush it!" << std::endl;
|
||||
std::cout << "Are you sure you want to continue ? " << std::endl;
|
||||
char c;
|
||||
std::cin >> c;
|
||||
if (toupper( c ) != 'Y')
|
||||
return 0;
|
||||
|
||||
auto worker = g3::LogWorker::createLogWorker();
|
||||
auto handle= worker->addDefaultLogger(argv[0], "g3log.txt");
|
||||
g3::initializeLogging(worker.get());
|
||||
CrusherLoop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
129
fpga/thirdparty/spdlog/bench/latency/g3log-latency.cpp
vendored
Normal file
129
fpga/thirdparty/spdlog/bench/latency/g3log-latency.cpp
vendored
Normal file
|
@ -0,0 +1,129 @@
|
|||
#include <thread>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <numeric>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include "utils.h"
|
||||
#include <g3log/g3log.hpp>
|
||||
#include <g3log/logworker.hpp>
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
const uint64_t g_iterations = 1000000;
|
||||
|
||||
|
||||
std::atomic<size_t> g_counter = {0};
|
||||
|
||||
|
||||
void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t>& result)
|
||||
{
|
||||
|
||||
while (true)
|
||||
{
|
||||
const size_t value_now = ++g_counter;
|
||||
if (value_now > g_iterations)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto start_time = std::chrono::high_resolution_clock::now();
|
||||
LOGF(INFO, "Some text to log for thread: %ld", id);
|
||||
auto stop_time = std::chrono::high_resolution_clock::now();
|
||||
uint64_t time_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time - start_time).count();
|
||||
result.push_back(time_us);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PrintResults(const std::map<size_t, std::vector<uint64_t>>& threads_result, size_t total_us)
|
||||
{
|
||||
|
||||
std::vector<uint64_t> all_measurements;
|
||||
all_measurements.reserve(g_iterations);
|
||||
for (auto& t_result : threads_result)
|
||||
{
|
||||
all_measurements.insert(all_measurements.end(), t_result.second.begin(), t_result.second.end());
|
||||
}
|
||||
|
||||
// calc worst latenct
|
||||
auto worst = *std::max_element(all_measurements.begin(), all_measurements.end());
|
||||
|
||||
// calc avg
|
||||
auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus<uint64_t>());
|
||||
auto avg = double(total)/all_measurements.size();
|
||||
|
||||
std::cout << "[g3log] worst: " << std::setw(10) << std::right << worst << "\tAvg: " << avg << "\tTotal: " << utils::format(total_us) << " us" << std::endl;
|
||||
|
||||
}
|
||||
}// anonymous
|
||||
|
||||
|
||||
// The purpose of this test is NOT to see how fast
|
||||
// each thread can possibly write. It is to see what
|
||||
// the worst latency is for writing a log entry
|
||||
//
|
||||
// In the test 1 million log entries will be written
|
||||
// an atomic counter is used to give each thread what
|
||||
// it is to write next. The overhead of atomic
|
||||
// synchronization between the threads are not counted in the worst case latency
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
size_t number_of_threads {0};
|
||||
if (argc == 2)
|
||||
{
|
||||
number_of_threads = atoi(argv[1]);
|
||||
}
|
||||
if (argc != 2 || number_of_threads == 0)
|
||||
{
|
||||
std::cerr << "USAGE is: " << argv[0] << " number_threads" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::thread> threads(number_of_threads);
|
||||
std::map<size_t, std::vector<uint64_t>> threads_result;
|
||||
|
||||
for (size_t idx = 0; idx < number_of_threads; ++idx)
|
||||
{
|
||||
// reserve to 1 million for all the result
|
||||
// it's a test so let's not care about the wasted space
|
||||
threads_result[idx].reserve(g_iterations);
|
||||
}
|
||||
|
||||
const std::string g_path = "./" ;
|
||||
const std::string g_prefix_log_name = "g3log-performance-";
|
||||
const std::string g_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt";
|
||||
|
||||
auto worker = g3::LogWorker::createLogWorker();
|
||||
auto handle= worker->addDefaultLogger(argv[0], "g3log.txt");
|
||||
g3::initializeLogging(worker.get());
|
||||
|
||||
auto start_time_application_total = std::chrono::high_resolution_clock::now();
|
||||
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
|
||||
{
|
||||
threads[idx] = std::thread(MeasurePeakDuringLogWrites, idx, std::ref(threads_result[idx]));
|
||||
}
|
||||
for (size_t idx = 0; idx < number_of_threads; ++idx)
|
||||
{
|
||||
threads[idx].join();
|
||||
}
|
||||
auto stop_time_application_total = std::chrono::high_resolution_clock::now();
|
||||
|
||||
uint64_t total_time_in_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time_application_total - start_time_application_total).count();
|
||||
PrintResults(threads_result, total_time_in_us);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
128
fpga/thirdparty/spdlog/bench/latency/spdlog-latency.cpp
vendored
Normal file
128
fpga/thirdparty/spdlog/bench/latency/spdlog-latency.cpp
vendored
Normal file
|
@ -0,0 +1,128 @@
|
|||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <numeric>
|
||||
#include <functional>
|
||||
#include "utils.h"
|
||||
#include <thread>
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
namespace spd = spdlog;
|
||||
|
||||
namespace
|
||||
{
|
||||
const uint64_t g_iterations = 1000000;
|
||||
|
||||
|
||||
std::atomic<size_t> g_counter = {0};
|
||||
|
||||
|
||||
void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t>& result)
|
||||
{
|
||||
auto logger = spd::get("file_logger");
|
||||
while (true)
|
||||
{
|
||||
const size_t value_now = ++g_counter;
|
||||
if (value_now > g_iterations)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto start_time = std::chrono::high_resolution_clock::now();
|
||||
logger->info("Some text to log for thread: [somemore text...............................] {}", id);
|
||||
auto stop_time = std::chrono::high_resolution_clock::now();
|
||||
uint64_t time_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time - start_time).count();
|
||||
result.push_back(time_us);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PrintResults(const std::map<size_t, std::vector<uint64_t>>& threads_result, size_t total_us)
|
||||
{
|
||||
|
||||
std::vector<uint64_t> all_measurements;
|
||||
all_measurements.reserve(g_iterations);
|
||||
for (auto& t_result : threads_result)
|
||||
{
|
||||
all_measurements.insert(all_measurements.end(), t_result.second.begin(), t_result.second.end());
|
||||
}
|
||||
|
||||
// calc worst latenct
|
||||
auto worst = *std::max_element(all_measurements.begin(), all_measurements.end());
|
||||
|
||||
// calc avg
|
||||
auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus<uint64_t>());
|
||||
auto avg = double(total)/all_measurements.size();
|
||||
|
||||
std::cout << "[spdlog] worst: " << std::setw(10) << std::right << worst << "\tAvg: " << avg << "\tTotal: " << utils::format(total_us) << " us" << std::endl;
|
||||
|
||||
}
|
||||
}// anonymous
|
||||
|
||||
|
||||
// The purpose of this test is NOT to see how fast
|
||||
// each thread can possibly write. It is to see what
|
||||
// the worst latency is for writing a log entry
|
||||
//
|
||||
// In the test 1 million log entries will be written
|
||||
// an atomic counter is used to give each thread what
|
||||
// it is to write next. The overhead of atomic
|
||||
// synchronization between the threads are not counted in the worst case latency
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
size_t number_of_threads {0};
|
||||
if (argc == 2)
|
||||
{
|
||||
number_of_threads = atoi(argv[1]);
|
||||
}
|
||||
if (argc != 2 || number_of_threads == 0)
|
||||
{
|
||||
std::cerr << "usage: " << argv[0] << " number_threads" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::thread> threads(number_of_threads);
|
||||
std::map<size_t, std::vector<uint64_t>> threads_result;
|
||||
|
||||
for (size_t idx = 0; idx < number_of_threads; ++idx)
|
||||
{
|
||||
// reserve to 1 million for all the result
|
||||
// it's a test so let's not care about the wasted space
|
||||
threads_result[idx].reserve(g_iterations);
|
||||
}
|
||||
|
||||
int queue_size = 1048576; // 2 ^ 20
|
||||
spdlog::set_async_mode(queue_size);
|
||||
auto logger = spdlog::create<spd::sinks::simple_file_sink_mt>("file_logger", "spdlog.log", true);
|
||||
|
||||
//force flush on every call to compare with g3log
|
||||
auto s = (spd::sinks::simple_file_sink_mt*)logger->sinks()[0].get();
|
||||
s->set_force_flush(true);
|
||||
|
||||
auto start_time_application_total = std::chrono::high_resolution_clock::now();
|
||||
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
|
||||
{
|
||||
threads[idx] = std::thread(MeasurePeakDuringLogWrites, idx, std::ref(threads_result[idx]));
|
||||
}
|
||||
for (size_t idx = 0; idx < number_of_threads; ++idx)
|
||||
{
|
||||
threads[idx].join();
|
||||
}
|
||||
auto stop_time_application_total = std::chrono::high_resolution_clock::now();
|
||||
|
||||
uint64_t total_time_in_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time_application_total - start_time_application_total).count();
|
||||
|
||||
PrintResults(threads_result, total_time_in_us);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
35
fpga/thirdparty/spdlog/bench/latency/utils.h
vendored
Normal file
35
fpga/thirdparty/spdlog/bench/latency/utils.h
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <locale>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
inline std::string format(const T& value)
|
||||
{
|
||||
static std::locale loc("");
|
||||
std::stringstream ss;
|
||||
ss.imbue(loc);
|
||||
ss << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline std::string format(const double & value)
|
||||
{
|
||||
static std::locale loc("");
|
||||
std::stringstream ss;
|
||||
ss.imbue(loc);
|
||||
ss << std::fixed << std::setprecision(1) << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
}
|
4
fpga/thirdparty/spdlog/bench/logs/.gitignore
vendored
Normal file
4
fpga/thirdparty/spdlog/bench/logs/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
62
fpga/thirdparty/spdlog/bench/spdlog-async.cpp
vendored
Normal file
62
fpga/thirdparty/spdlog/bench/spdlog-async.cpp
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
||||
using namespace std::chrono;
|
||||
using clock=steady_clock;
|
||||
namespace spd = spdlog;
|
||||
|
||||
int thread_count = 10;
|
||||
if(argc > 1)
|
||||
thread_count = ::atoi(argv[1]);
|
||||
int howmany = 1000000;
|
||||
|
||||
spd::set_async_mode(1048576);
|
||||
auto logger = spdlog::create<spd::sinks::simple_file_sink_mt>("file_logger", "logs/spd-bench-async.txt", false);
|
||||
logger->set_pattern("[%Y-%b-%d %T.%e]: %v");
|
||||
|
||||
|
||||
std::atomic<int > msg_counter {0};
|
||||
vector<thread> threads;
|
||||
auto start = clock::now();
|
||||
for (int t = 0; t < thread_count; ++t)
|
||||
{
|
||||
threads.push_back(std::thread([&]()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int counter = ++msg_counter;
|
||||
if (counter > howmany) break;
|
||||
logger->info("spdlog message #{}: This is some text for your pleasure", counter);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
for(auto &t:threads)
|
||||
{
|
||||
t.join();
|
||||
};
|
||||
|
||||
duration<float> delta = clock::now() - start;
|
||||
float deltaf = delta.count();
|
||||
auto rate = howmany/deltaf;
|
||||
|
||||
cout << "Total: " << howmany << std::endl;
|
||||
cout << "Threads: " << thread_count << std::endl;
|
||||
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||
}
|
55
fpga/thirdparty/spdlog/bench/spdlog-bench-mt.cpp
vendored
Normal file
55
fpga/thirdparty/spdlog/bench/spdlog-bench-mt.cpp
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
||||
int thread_count = 10;
|
||||
if(argc > 1)
|
||||
thread_count = std::atoi(argv[1]);
|
||||
|
||||
int howmany = 1000000;
|
||||
|
||||
namespace spd = spdlog;
|
||||
|
||||
auto logger = spdlog::create<spd::sinks::simple_file_sink_mt>("file_logger", "logs/spd-bench-mt.txt", false);
|
||||
|
||||
logger->set_pattern("[%Y-%b-%d %T.%e]: %v");
|
||||
|
||||
std::atomic<int > msg_counter {0};
|
||||
std::vector<thread> threads;
|
||||
|
||||
for (int t = 0; t < thread_count; ++t)
|
||||
{
|
||||
threads.push_back(std::thread([&]()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int counter = ++msg_counter;
|
||||
if (counter > howmany) break;
|
||||
logger->info("spdlog message #{}: This is some text for your pleasure", counter);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
for(auto &t:threads)
|
||||
{
|
||||
t.join();
|
||||
};
|
||||
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
20
fpga/thirdparty/spdlog/bench/spdlog-bench.cpp
vendored
Normal file
20
fpga/thirdparty/spdlog/bench/spdlog-bench.cpp
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
|
||||
int main(int, char* [])
|
||||
{
|
||||
int howmany = 1000000;
|
||||
namespace spd = spdlog;
|
||||
///Create a file rotating logger with 5mb size max and 3 rotated files
|
||||
auto logger = spdlog::create<spd::sinks::simple_file_sink_st>("file_logger", "logs/spd-bench-st.txt", false);
|
||||
|
||||
logger->set_pattern("[%Y-%b-%d %T.%e]: %v");
|
||||
for(int i = 0 ; i < howmany; ++i)
|
||||
logger->info("spdlog message #{} : This is some text for your pleasure", i);
|
||||
return 0;
|
||||
}
|
112
fpga/thirdparty/spdlog/bench/spdlog-null-async.cpp
vendored
Normal file
112
fpga/thirdparty/spdlog/bench/spdlog-null-async.cpp
vendored
Normal file
|
@ -0,0 +1,112 @@
|
|||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
//
|
||||
// bench.cpp : spdlog benchmarks
|
||||
//
|
||||
#include <atomic>
|
||||
#include <cstdlib> // EXIT_FAILURE
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/async_logger.h"
|
||||
#include "spdlog/sinks/null_sink.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
using namespace spdlog;
|
||||
using namespace spdlog::sinks;
|
||||
using namespace utils;
|
||||
|
||||
|
||||
|
||||
size_t bench_as(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
||||
int queue_size = 1048576;
|
||||
int howmany = 1000000;
|
||||
int threads = 10;
|
||||
int iters = 10;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
if(argc > 1)
|
||||
howmany = atoi(argv[1]);
|
||||
if (argc > 2)
|
||||
threads = atoi(argv[2]);
|
||||
if (argc > 3)
|
||||
queue_size = atoi(argv[3]);
|
||||
|
||||
|
||||
cout << "\n*******************************************************************************\n";
|
||||
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " messages " << endl;
|
||||
cout << "*******************************************************************************\n";
|
||||
|
||||
spdlog::set_async_mode(queue_size);
|
||||
|
||||
size_t total_rate = 0;
|
||||
|
||||
for(int i = 0; i < iters; ++i)
|
||||
{
|
||||
//auto as = spdlog::daily_logger_st("as", "logs/daily_async");
|
||||
auto as = spdlog::create<null_sink_st>("async(null-sink)");
|
||||
total_rate+= bench_as(howmany, as, threads);
|
||||
spdlog::drop("async(null-sink)");
|
||||
}
|
||||
std::cout << endl;
|
||||
std::cout << "Avg rate: " << format(total_rate/iters) << "/sec" <<std::endl;
|
||||
|
||||
}
|
||||
catch (std::exception &ex)
|
||||
{
|
||||
std::cerr << "Error: " << ex.what() << std::endl;
|
||||
perror("Last error");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//return rate/sec
|
||||
size_t bench_as(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
|
||||
{
|
||||
cout << log->name() << "...\t\t" << flush;
|
||||
std::atomic<int > msg_counter {0};
|
||||
vector<thread> threads;
|
||||
auto start = system_clock::now();
|
||||
for (int t = 0; t < thread_count; ++t)
|
||||
{
|
||||
threads.push_back(std::thread([&]()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
int counter = ++msg_counter;
|
||||
if (counter > howmany) break;
|
||||
log->info("Hello logger: msg number {}", counter);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
for(auto &t:threads)
|
||||
{
|
||||
t.join();
|
||||
};
|
||||
|
||||
|
||||
auto delta = system_clock::now() - start;
|
||||
auto delta_d = duration_cast<duration<double>> (delta).count();
|
||||
auto per_sec = size_t(howmany / delta_d);
|
||||
cout << format(per_sec) << "/sec" << endl;
|
||||
return per_sec;
|
||||
}
|
35
fpga/thirdparty/spdlog/bench/utils.h
vendored
Normal file
35
fpga/thirdparty/spdlog/bench/utils.h
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <locale>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
inline std::string format(const T& value)
|
||||
{
|
||||
static std::locale loc("");
|
||||
std::stringstream ss;
|
||||
ss.imbue(loc);
|
||||
ss << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline std::string format(const double & value)
|
||||
{
|
||||
static std::locale loc("");
|
||||
std::stringstream ss;
|
||||
ss.imbue(loc);
|
||||
ss << std::fixed << std::setprecision(1) << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
}
|
24
fpga/thirdparty/spdlog/cmake/Config.cmake.in
vendored
Normal file
24
fpga/thirdparty/spdlog/cmake/Config.cmake.in
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
# *************************************************************************/
|
||||
# * Copyright (c) 2015 Ruslan Baratov. */
|
||||
# * */
|
||||
# * Permission is hereby granted, free of charge, to any person obtaining */
|
||||
# * a copy of this software and associated documentation files (the */
|
||||
# * "Software"), to deal in the Software without restriction, including */
|
||||
# * without limitation the rights to use, copy, modify, merge, publish, */
|
||||
# * distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
# * permit persons to whom the Software is furnished to do so, subject to */
|
||||
# * the following conditions: */
|
||||
# * */
|
||||
# * The above copyright notice and this permission notice shall be */
|
||||
# * included in all copies or substantial portions of the Software. */
|
||||
# * */
|
||||
# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
# *************************************************************************/
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
|
6
fpga/thirdparty/spdlog/cmake/spdlog.pc.in
vendored
Normal file
6
fpga/thirdparty/spdlog/cmake/spdlog.pc.in
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
includedir=${prefix}/include
|
||||
|
||||
Name: @PROJECT_NAME@
|
||||
Description: Super fast C++ logging library.
|
||||
Version: @PROJECT_VERSION@
|
49
fpga/thirdparty/spdlog/example/CMakeLists.txt
vendored
Normal file
49
fpga/thirdparty/spdlog/example/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
# *************************************************************************/
|
||||
# * Copyright (c) 2015 Ruslan Baratov. */
|
||||
# * */
|
||||
# * Permission is hereby granted, free of charge, to any person obtaining */
|
||||
# * a copy of this software and associated documentation files (the */
|
||||
# * "Software"), to deal in the Software without restriction, including */
|
||||
# * without limitation the rights to use, copy, modify, merge, publish, */
|
||||
# * distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
# * permit persons to whom the Software is furnished to do so, subject to */
|
||||
# * the following conditions: */
|
||||
# * */
|
||||
# * The above copyright notice and this permission notice shall be */
|
||||
# * included in all copies or substantial portions of the Software. */
|
||||
# * */
|
||||
# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
# *************************************************************************/
|
||||
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(SpdlogExamples)
|
||||
|
||||
if(TARGET spdlog)
|
||||
# Part of the main project
|
||||
add_library(spdlog::spdlog ALIAS spdlog)
|
||||
else()
|
||||
# Stand-alone build
|
||||
find_package(spdlog CONFIG REQUIRED)
|
||||
endif()
|
||||
|
||||
find_package(Threads)
|
||||
|
||||
add_executable(example example.cpp)
|
||||
target_link_libraries(example spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
add_executable(benchmark bench.cpp)
|
||||
target_link_libraries(benchmark spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
add_executable(multisink multisink.cpp)
|
||||
target_link_libraries(multisink spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
enable_testing()
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
|
||||
add_test(NAME RunExample COMMAND example)
|
||||
add_test(NAME RunBenchmark COMMAND benchmark)
|
29
fpga/thirdparty/spdlog/example/Makefile
vendored
Normal file
29
fpga/thirdparty/spdlog/example/Makefile
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
CXX ?= g++
|
||||
CXXFLAGS =
|
||||
CXX_FLAGS = -Wall -Wshadow -Wextra -pedantic -std=c++11 -pthread -I../include
|
||||
CXX_RELEASE_FLAGS = -O3 -march=native
|
||||
CXX_DEBUG_FLAGS= -g
|
||||
|
||||
|
||||
all: example bench
|
||||
debug: example-debug bench-debug
|
||||
|
||||
example: example.cpp
|
||||
$(CXX) example.cpp -o example $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS)
|
||||
|
||||
bench: bench.cpp
|
||||
$(CXX) bench.cpp -o bench $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS)
|
||||
|
||||
|
||||
example-debug: example.cpp
|
||||
$(CXX) example.cpp -o example-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS)
|
||||
|
||||
bench-debug: bench.cpp
|
||||
$(CXX) bench.cpp -o bench-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f *.o logs/*.txt example example-debug bench bench-debug
|
||||
|
||||
|
||||
rebuild: clean all
|
||||
rebuild-debug: clean debug
|
32
fpga/thirdparty/spdlog/example/Makefile.clang
vendored
Normal file
32
fpga/thirdparty/spdlog/example/Makefile.clang
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
CXX ?= clang++
|
||||
CXXFLAGS = -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -I../include
|
||||
CXX_RELEASE_FLAGS = -O2
|
||||
CXX_DEBUG_FLAGS= -g
|
||||
|
||||
|
||||
all: example bench
|
||||
debug: example-debug bench-debug
|
||||
|
||||
example: example.cpp
|
||||
$(CXX) example.cpp -o example-clang $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
bench: bench.cpp
|
||||
$(CXX) bench.cpp -o bench-clang $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
|
||||
example-debug: example.cpp
|
||||
$(CXX) example.cpp -o example-clang-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
|
||||
|
||||
bench-debug: bench.cpp
|
||||
$(CXX) bench.cpp -o bench-clang-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
|
||||
|
||||
|
||||
|
||||
clean:
|
||||
rm -f *.o logs/*.txt example-clang example-clang-debug bench-clang bench-clang-debug
|
||||
|
||||
|
||||
rebuild: clean all
|
||||
rebuild-debug: clean debug
|
||||
|
||||
|
32
fpga/thirdparty/spdlog/example/Makefile.mingw
vendored
Normal file
32
fpga/thirdparty/spdlog/example/Makefile.mingw
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
CXX ?= g++
|
||||
CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -Wshadow -pedantic -std=gnu++0x -pthread -Wl,--no-as-needed -I../include
|
||||
CXX_RELEASE_FLAGS = -O3
|
||||
CXX_DEBUG_FLAGS= -g
|
||||
|
||||
|
||||
all: example bench
|
||||
debug: example-debug bench-debug
|
||||
|
||||
example: example.cpp
|
||||
$(CXX) example.cpp -o example $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
bench: bench.cpp
|
||||
$(CXX) bench.cpp -o bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
|
||||
example-debug: example.cpp
|
||||
$(CXX) example.cpp -o example-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
|
||||
|
||||
bench-debug: bench.cpp
|
||||
$(CXX) bench.cpp -o bench-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
|
||||
|
||||
|
||||
|
||||
clean:
|
||||
rm -f *.o logs/*.txt example example-debug bench bench-debug
|
||||
|
||||
|
||||
rebuild: clean all
|
||||
rebuild-debug: clean debug
|
||||
|
||||
|
144
fpga/thirdparty/spdlog/example/bench.cpp
vendored
Normal file
144
fpga/thirdparty/spdlog/example/bench.cpp
vendored
Normal file
|
@ -0,0 +1,144 @@
|
|||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
//
|
||||
// bench.cpp : spdlog benchmarks
|
||||
//
|
||||
#include <atomic>
|
||||
#include <cstdlib> // EXIT_FAILURE
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/async_logger.h"
|
||||
#include "spdlog/sinks/file_sinks.h"
|
||||
#include "spdlog/sinks/null_sink.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
using namespace spdlog;
|
||||
using namespace spdlog::sinks;
|
||||
using namespace utils;
|
||||
|
||||
|
||||
void bench(int howmany, std::shared_ptr<spdlog::logger> log);
|
||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
||||
int queue_size = 1048576;
|
||||
int howmany = 1000000;
|
||||
int threads = 10;
|
||||
int file_size = 30 * 1024 * 1024;
|
||||
int rotating_files = 5;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
if(argc > 1)
|
||||
howmany = atoi(argv[1]);
|
||||
if (argc > 2)
|
||||
threads = atoi(argv[2]);
|
||||
if (argc > 3)
|
||||
queue_size = atoi(argv[3]);
|
||||
|
||||
|
||||
cout << "*******************************************************************************\n";
|
||||
cout << "Single thread, " << format(howmany) << " iterations" << endl;
|
||||
cout << "*******************************************************************************\n";
|
||||
|
||||
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st", file_size, rotating_files);
|
||||
bench(howmany, rotating_st);
|
||||
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st");
|
||||
bench(howmany, daily_st);
|
||||
bench(howmany, spdlog::create<null_sink_st>("null_st"));
|
||||
|
||||
cout << "\n*******************************************************************************\n";
|
||||
cout << threads << " threads sharing same logger, " << format(howmany) << " iterations" << endl;
|
||||
cout << "*******************************************************************************\n";
|
||||
|
||||
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt", file_size, rotating_files);
|
||||
bench_mt(howmany, rotating_mt, threads);
|
||||
|
||||
|
||||
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt");
|
||||
bench_mt(howmany, daily_mt, threads);
|
||||
bench(howmany, spdlog::create<null_sink_st>("null_mt"));
|
||||
|
||||
cout << "\n*******************************************************************************\n";
|
||||
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations " << endl;
|
||||
cout << "*******************************************************************************\n";
|
||||
|
||||
|
||||
spdlog::set_async_mode(queue_size);
|
||||
|
||||
for(int i = 0; i < 3; ++i)
|
||||
{
|
||||
auto as = spdlog::daily_logger_st("as", "logs/daily_async");
|
||||
bench_mt(howmany, as, threads);
|
||||
spdlog::drop("as");
|
||||
}
|
||||
}
|
||||
catch (std::exception &ex)
|
||||
{
|
||||
std::cerr << "Error: " << ex.what() << std::endl;
|
||||
perror("Last error");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void bench(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||
{
|
||||
cout << log->name() << "...\t\t" << flush;
|
||||
auto start = system_clock::now();
|
||||
for (auto i = 0; i < howmany; ++i)
|
||||
{
|
||||
log->info("Hello logger: msg number {}", i);
|
||||
}
|
||||
|
||||
|
||||
auto delta = system_clock::now() - start;
|
||||
auto delta_d = duration_cast<duration<double>> (delta).count();
|
||||
cout << format(int(howmany / delta_d)) << "/sec" << endl;
|
||||
}
|
||||
|
||||
|
||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
|
||||
{
|
||||
|
||||
cout << log->name() << "...\t\t" << flush;
|
||||
std::atomic<int > msg_counter {0};
|
||||
vector<thread> threads;
|
||||
auto start = system_clock::now();
|
||||
for (int t = 0; t < thread_count; ++t)
|
||||
{
|
||||
threads.push_back(std::thread([&]()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
int counter = ++msg_counter;
|
||||
if (counter > howmany) break;
|
||||
log->info("Hello logger: msg number {}", counter);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
for(auto &t:threads)
|
||||
{
|
||||
t.join();
|
||||
};
|
||||
|
||||
|
||||
auto delta = system_clock::now() - start;
|
||||
auto delta_d = duration_cast<duration<double>> (delta).count();
|
||||
cout << format(int(howmany / delta_d)) << "/sec" << endl;
|
||||
}
|
167
fpga/thirdparty/spdlog/example/example.cpp
vendored
Normal file
167
fpga/thirdparty/spdlog/example/example.cpp
vendored
Normal file
|
@ -0,0 +1,167 @@
|
|||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
//
|
||||
// spdlog usage example
|
||||
//
|
||||
//
|
||||
|
||||
#define SPDLOG_TRACE_ON
|
||||
#define SPDLOG_DEBUG_ON
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
void async_example();
|
||||
void syslog_example();
|
||||
void android_example();
|
||||
void user_defined_example();
|
||||
void err_handler_example();
|
||||
|
||||
namespace spd = spdlog;
|
||||
int main(int, char*[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Console logger with color
|
||||
auto console = spd::stdout_color_mt("console");
|
||||
console->info("Welcome to spdlog!");
|
||||
console->error("Some error message with arg{}..", 1);
|
||||
|
||||
|
||||
// Formatting examples
|
||||
console->warn("Easy padding in numbers like {:08d}", 12);
|
||||
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||
console->info("Support for floats {:03.2f}", 1.23456);
|
||||
console->info("Positional args are {1} {0}..", "too", "supported");
|
||||
console->info("{:<30}", "left aligned");
|
||||
|
||||
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
|
||||
|
||||
|
||||
// Create basic file logger (not rotated)
|
||||
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt");
|
||||
my_logger->info("Some log message");
|
||||
|
||||
// Create a file rotating logger with 5mb size max and 3 rotated files
|
||||
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
|
||||
|
||||
// Create a daily logger - a new file is created every day on 2:30am
|
||||
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
||||
// trigger flush if the log severity is error or higher
|
||||
daily_logger->flush_on(spd::level::err);
|
||||
daily_logger->info(123.44);
|
||||
|
||||
// Customize msg format for all messages
|
||||
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
|
||||
rotating_logger->info("This is another message with custom format");
|
||||
|
||||
|
||||
// Runtime log levels
|
||||
spd::set_level(spd::level::info); //Set global log level to info
|
||||
console->debug("This message shold not be displayed!");
|
||||
console->set_level(spd::level::debug); // Set specific logger's log level
|
||||
console->debug("This message shold be displayed..");
|
||||
|
||||
// Compile time log levels
|
||||
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
|
||||
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
|
||||
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
|
||||
|
||||
|
||||
// Asynchronous logging is very fast..
|
||||
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
|
||||
async_example();
|
||||
|
||||
// syslog example. linux/osx only
|
||||
syslog_example();
|
||||
|
||||
// android example. compile with NDK
|
||||
android_example();
|
||||
|
||||
// Log user-defined types example
|
||||
user_defined_example();
|
||||
|
||||
// Change default log error handler
|
||||
err_handler_example();
|
||||
|
||||
// Apply a function on all registered loggers
|
||||
spd::apply_all([&](std::shared_ptr<spdlog::logger> l)
|
||||
{
|
||||
l->info("End of example.");
|
||||
});
|
||||
|
||||
// Release and close all loggers
|
||||
spdlog::drop_all();
|
||||
}
|
||||
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
|
||||
catch (const spd::spdlog_ex& ex)
|
||||
{
|
||||
std::cout << "Log init failed: " << ex.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void async_example()
|
||||
{
|
||||
size_t q_size = 4096; //queue size must be power of 2
|
||||
spdlog::set_async_mode(q_size);
|
||||
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
|
||||
for (int i = 0; i < 100; ++i)
|
||||
async_file->info("Async message #{}", i);
|
||||
}
|
||||
|
||||
//syslog example (linux/osx/freebsd)
|
||||
void syslog_example()
|
||||
{
|
||||
#ifdef SPDLOG_ENABLE_SYSLOG
|
||||
std::string ident = "spdlog-example";
|
||||
auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID);
|
||||
syslog_logger->warn("This is warning that will end up in syslog.");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Android example
|
||||
void android_example()
|
||||
{
|
||||
#if defined(__ANDROID__)
|
||||
std::string tag = "spdlog-android";
|
||||
auto android_logger = spd::android_logger("android", tag);
|
||||
android_logger->critical("Use \"adb shell logcat\" to view this message.");
|
||||
#endif
|
||||
}
|
||||
|
||||
// user defined types logging by implementing operator<<
|
||||
struct my_type
|
||||
{
|
||||
int i;
|
||||
template<typename OStream>
|
||||
friend OStream& operator<<(OStream& os, const my_type &c)
|
||||
{
|
||||
return os << "[my_type i="<<c.i << "]";
|
||||
}
|
||||
};
|
||||
|
||||
#include "spdlog/fmt/ostr.h" // must be included
|
||||
void user_defined_example()
|
||||
{
|
||||
spd::get("console")->info("user defined type: {}", my_type { 14 });
|
||||
}
|
||||
|
||||
//
|
||||
//custom error handler
|
||||
//
|
||||
void err_handler_example()
|
||||
{
|
||||
//can be set globaly or per logger(logger->set_error_handler(..))
|
||||
spdlog::set_error_handler([](const std::string& msg)
|
||||
{
|
||||
std::cerr << "my err handler: " << msg << std::endl;
|
||||
});
|
||||
spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
|
||||
}
|
26
fpga/thirdparty/spdlog/example/example.sln
vendored
Normal file
26
fpga/thirdparty/spdlog/example/example.sln
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example", "example.vcxproj", "{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Debug|x64 = Debug|x64
|
||||
Release|Win32 = Release|Win32
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|x64.ActiveCfg = Debug|Win32
|
||||
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.Build.0 = Release|Win32
|
||||
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|x64.ActiveCfg = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
126
fpga/thirdparty/spdlog/example/example.vcxproj
vendored
Normal file
126
fpga/thirdparty/spdlog/example/example.vcxproj
vendored
Normal file
|
@ -0,0 +1,126 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="example.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\include\spdlog\async_logger.h" />
|
||||
<ClInclude Include="..\include\spdlog\common.h" />
|
||||
<ClInclude Include="..\include\spdlog\details\async_logger_impl.h" />
|
||||
<ClInclude Include="..\include\spdlog\details\async_log_helper.h" />
|
||||
<ClInclude Include="..\include\spdlog\details\file_helper.h" />
|
||||
<ClInclude Include="..\include\spdlog\details\logger_impl.h" />
|
||||
<ClInclude Include="..\include\spdlog\details\log_msg.h" />
|
||||
<ClInclude Include="..\include\spdlog\details\mpmc_bounded_q.h" />
|
||||
<ClInclude Include="..\include\spdlog\details\null_mutex.h" />
|
||||
<ClInclude Include="..\include\spdlog\details\os.h" />
|
||||
<ClInclude Include="..\include\spdlog\details\pattern_formatter_impl.h" />
|
||||
<ClInclude Include="..\include\spdlog\details\registry.h" />
|
||||
<ClInclude Include="..\include\spdlog\details\spdlog_impl.h" />
|
||||
<ClInclude Include="..\include\spdlog\fmt\fmt.h" />
|
||||
<ClInclude Include="..\include\spdlog\fmt\ostr.h" />
|
||||
<ClInclude Include="..\include\spdlog\formatter.h" />
|
||||
<ClInclude Include="..\include\spdlog\logger.h" />
|
||||
<ClInclude Include="..\include\spdlog\sinks\android_sink.h" />
|
||||
<ClInclude Include="..\include\spdlog\sinks\ansicolor_sink.h" />
|
||||
<ClInclude Include="..\include\spdlog\sinks\base_sink.h" />
|
||||
<ClInclude Include="..\include\spdlog\sinks\dist_sink.h" />
|
||||
<ClInclude Include="..\include\spdlog\sinks\file_sinks.h" />
|
||||
<ClInclude Include="..\include\spdlog\sinks\msvc_sink.h" />
|
||||
<ClInclude Include="..\include\spdlog\sinks\null_sink.h" />
|
||||
<ClInclude Include="..\include\spdlog\sinks\ostream_sink.h" />
|
||||
<ClInclude Include="..\include\spdlog\sinks\sink.h" />
|
||||
<ClInclude Include="..\include\spdlog\sinks\stdout_sinks.h" />
|
||||
<ClInclude Include="..\include\spdlog\sinks\syslog_sink.h" />
|
||||
<ClInclude Include="..\include\spdlog\sinks\wincolor_sink.h" />
|
||||
<ClInclude Include="..\include\spdlog\spdlog.h" />
|
||||
<ClInclude Include="..\include\spdlog\tweakme.h" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>.</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeaderFile />
|
||||
<PrecompiledHeaderOutputFile />
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeaderFile />
|
||||
<PrecompiledHeaderOutputFile />
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
15
fpga/thirdparty/spdlog/example/jni/Android.mk
vendored
Normal file
15
fpga/thirdparty/spdlog/example/jni/Android.mk
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Setup a project
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := example
|
||||
LOCAL_SRC_FILES := example.cpp
|
||||
LOCAL_CPPFLAGS += -Wall -Wshadow -Wextra -pedantic -std=c++11 -fPIE -pie
|
||||
LOCAL_LDFLAGS += -fPIE -pie
|
||||
|
||||
# Add exception support and set path for spdlog's headers
|
||||
LOCAL_CPPFLAGS += -fexceptions -I../include
|
||||
# Use android's log library
|
||||
LOCAL_LDFLAGS += -llog
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
2
fpga/thirdparty/spdlog/example/jni/Application.mk
vendored
Normal file
2
fpga/thirdparty/spdlog/example/jni/Application.mk
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Exceptions are used in spdlog. Link to an exception-ready C++ runtime.
|
||||
APP_STL = gnustl_static
|
1
fpga/thirdparty/spdlog/example/jni/example.cpp
vendored
Symbolic link
1
fpga/thirdparty/spdlog/example/jni/example.cpp
vendored
Symbolic link
|
@ -0,0 +1 @@
|
|||
../example.cpp
|
1
fpga/thirdparty/spdlog/example/logs/.gitignore
vendored
Normal file
1
fpga/thirdparty/spdlog/example/logs/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.txt
|
47
fpga/thirdparty/spdlog/example/multisink.cpp
vendored
Normal file
47
fpga/thirdparty/spdlog/example/multisink.cpp
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
#include "spdlog/spdlog.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
namespace spd = spdlog;
|
||||
int main(int, char*[])
|
||||
{
|
||||
bool enable_debug = true;
|
||||
try
|
||||
{
|
||||
// This other example use a single logger with multiple sinks.
|
||||
// This means that the same log_msg is forwarded to multiple sinks;
|
||||
// Each sink can have it's own log level and a message will be logged.
|
||||
std::vector<spdlog::sink_ptr> sinks;
|
||||
sinks.push_back( std::make_shared<spdlog::sinks::stdout_sink_mt>() );
|
||||
sinks.push_back( std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_regular_file.txt") );
|
||||
sinks.push_back( std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_debug_file.txt") );
|
||||
|
||||
spdlog::logger console_multisink("multisink", sinks.begin(), sinks.end() );
|
||||
console_multisink.set_level( spdlog::level::warn);
|
||||
|
||||
sinks[0]->set_level( spdlog::level::trace); // console. Allow everything. Default value
|
||||
sinks[1]->set_level( spdlog::level::trace); // regular file. Allow everything. Default value
|
||||
sinks[2]->set_level( spdlog::level::off); // regular file. Ignore everything.
|
||||
|
||||
console_multisink.warn("warn: will print only on console and regular file");
|
||||
|
||||
if( enable_debug )
|
||||
{
|
||||
console_multisink.set_level( spdlog::level::debug); // level of the logger
|
||||
sinks[1]->set_level( spdlog::level::debug); // regular file
|
||||
sinks[2]->set_level( spdlog::level::debug); // debug file
|
||||
}
|
||||
console_multisink.debug("Debug: you should see this on console and both files");
|
||||
|
||||
// Release and close all loggers
|
||||
spdlog::drop_all();
|
||||
}
|
||||
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
|
||||
catch (const spd::spdlog_ex& ex)
|
||||
{
|
||||
std::cout << "Log init failed: " << ex.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
35
fpga/thirdparty/spdlog/example/utils.h
vendored
Normal file
35
fpga/thirdparty/spdlog/example/utils.h
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <locale>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
inline std::string format(const T& value)
|
||||
{
|
||||
static std::locale loc("");
|
||||
std::stringstream ss;
|
||||
ss.imbue(loc);
|
||||
ss << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline std::string format(const double & value)
|
||||
{
|
||||
static std::locale loc("");
|
||||
std::stringstream ss;
|
||||
ss.imbue(loc);
|
||||
ss << std::fixed << std::setprecision(1) << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue