1
0
Fork 0
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:
Daniel Krebs 2018-01-31 10:38:42 +01:00
commit 959a6130d3
160 changed files with 28629 additions and 201 deletions

View file

@ -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
##############################################################################

View file

@ -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)

View file

@ -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" \

View file

@ -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"
}
}
}
}

View 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

View 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

View 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

View file

@ -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
/** @} */

View 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
/** @} */

View file

@ -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
/** @} */

View 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

View 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

View 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
/** @} */

View 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
/** @} */

View 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
/** @} */

View 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
/** @} */

View file

@ -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_ @} */

View 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_ @} */

View file

@ -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
/** @} */

View file

@ -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
/** @} */

View file

@ -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
/** @} */

View file

@ -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

View file

@ -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);

View 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;
}

View 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

View 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

View file

@ -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

View 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

View file

@ -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

View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -39,6 +39,7 @@
#endif
struct log *global_log;
struct log default_log;
/* We register a default log instance */
__attribute__((constructor))

View 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
View 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
View 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
View 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

View file

@ -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
View 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}

View file

@ -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})

View file

@ -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
View 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
View 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);
}
}

View file

@ -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
View 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;
}

@ -1 +1 @@
Subproject commit 89eb3ead0c210318144238f2b5b6a96ce3feec73
Subproject commit f08dc8e878719ab8937c47fedd20ebf3aa68e123

67
fpga/thirdparty/spdlog/.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,221 @@
# spdlog
Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=master)](https://travis-ci.org/gabime/spdlog)&nbsp; [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true)](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
View 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
View 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
View 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

View 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

View 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;
}

View 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
View 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

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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

View 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

View 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;
}

View 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;
}

View 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;
}

View 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();
}
}

View file

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View 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;
}

View 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;
}

View 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;
}

View 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
View 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();
}
}

View 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")

View file

@ -0,0 +1,6 @@
prefix=@CMAKE_INSTALL_PREFIX@
includedir=${prefix}/include
Name: @PROJECT_NAME@
Description: Super fast C++ logging library.
Version: @PROJECT_VERSION@

View 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
View 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

View 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

View 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
View 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;
}

View 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);
}

View 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

View 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>

View 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)

View file

@ -0,0 +1,2 @@
# Exceptions are used in spdlog. Link to an exception-ready C++ runtime.
APP_STL = gnustl_static

View file

@ -0,0 +1 @@
../example.cpp

View file

@ -0,0 +1 @@
*.txt

View 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
View 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