mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
imported source code from VILLASfpga repo and made it compile
This commit is contained in:
parent
01130e6fa4
commit
c3164e93ef
64 changed files with 16455 additions and 0 deletions
fpga
.editorconfig.gitlab-ci.ymlCMakeLists.txt
cmake
doc/pictures
etc
include/villas
lib
scripts
tests
17
fpga/.editorconfig
Normal file
17
fpga/.editorconfig
Normal file
|
@ -0,0 +1,17 @@
|
|||
# EditorConfig is awesome: http://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
# Matches multiple files with brace expansion notation
|
||||
# Set default charset
|
||||
[{etc,include,lib,plugins,src,tests,tools}/**.{c,h}]
|
||||
charset = utf-8
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
trim_trailing_whitespace=true
|
94
fpga/.gitlab-ci.yml
Normal file
94
fpga/.gitlab-ci.yml
Normal file
|
@ -0,0 +1,94 @@
|
|||
variables:
|
||||
GIT_STRATEGY: fetch
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
PREFIX: /usr/
|
||||
DOCKER_TAG_DEV: ${CI_COMMIT_REF_NAME}
|
||||
DOCKER_IMAGE_DEV: villas/fpga-dev
|
||||
|
||||
stages:
|
||||
- prepare
|
||||
- build
|
||||
- test
|
||||
- deploy
|
||||
- docker
|
||||
|
||||
# For some reason, GitLab CI prunes the contents of the submodules so we need to restore them.
|
||||
before_script:
|
||||
- git submodule foreach git checkout .
|
||||
|
||||
# Stage: prepare
|
||||
##############################################################################
|
||||
|
||||
# Build docker image which is used to build & test VILLASnode
|
||||
docker-dev:
|
||||
stage: prepare
|
||||
script:
|
||||
- docker build -t ${DOCKER_IMAGE_DEV}:${DOCKER_TAG_DEV} .
|
||||
tags:
|
||||
- shell
|
||||
- linux
|
||||
|
||||
# Stage: build
|
||||
##############################################################################
|
||||
|
||||
build:source:
|
||||
stage: build
|
||||
script:
|
||||
- mkdir build && cd build && cmake .. && make
|
||||
artifacts:
|
||||
expire_in: 1 week
|
||||
name: ${CI_PROJECT_NAME}-${CI_BUILD_REF}
|
||||
paths:
|
||||
- build/
|
||||
image: ${DOCKER_IMAGE_DEV}:${DOCKER_TAG_DEV}
|
||||
tags:
|
||||
- docker
|
||||
|
||||
#build:packages:
|
||||
# stage: build
|
||||
# script:
|
||||
# - mkdir build && cd build && cmake .. && make package
|
||||
# artifacts:
|
||||
# expire_in: 1 week
|
||||
# name: ${CI_PROJECT_NAME}-${CI_BUILD_REF}
|
||||
# paths:
|
||||
# - build/
|
||||
# image: ${DOCKER_IMAGE_DEV}:${DOCKER_TAG_DEV}
|
||||
# tags:
|
||||
# - docker
|
||||
|
||||
# Stage: test
|
||||
##############################################################################
|
||||
|
||||
#test:unit:
|
||||
# stage: test
|
||||
# dependencies:
|
||||
# - build:source
|
||||
# script:
|
||||
# - make test
|
||||
# image: ${DOCKER_IMAGE_DEV}:${DOCKER_TAG_DEV}
|
||||
# tags:
|
||||
# - docker
|
||||
# - fpga
|
||||
|
||||
# Stage: deploy
|
||||
##############################################################################
|
||||
|
||||
#deploy:packages:
|
||||
# stage: deploy
|
||||
# script:
|
||||
# - ssh ${DEPLOY_USER}@${DEPLOY_HOST} mkdir -p ${DEPLOY_PATH}/{dist,../packages}
|
||||
# - rsync ${RSYNC_OPTS} build/*.rpm ${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}/../packages/
|
||||
# - rsync ${RSYNC_OPTS} build//*.tar.gz ${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}/dist/
|
||||
# - ssh ${DEPLOY_USER}@${DEPLOY_HOST} createrepo ${DEPLOY_PATH}/../packages
|
||||
# dependencies:
|
||||
# - build:packages
|
||||
# tags:
|
||||
# - villas-deploy
|
||||
# only:
|
||||
# - tags
|
||||
#
|
||||
#deploy:git-mirror:
|
||||
# stage: deploy
|
||||
# script:
|
||||
# - git push --force --mirror --prune https://${GITHUB_USER}:${GITHUB_TOKEN}@github.com:VILLASframework/VILLASnode.git
|
8
fpga/CMakeLists.txt
Normal file
8
fpga/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(VILLASfpga C)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake)
|
||||
|
||||
add_subdirectory(lib)
|
||||
add_subdirectory(tests)
|
27
fpga/cmake/FindCriterion.cmake
Normal file
27
fpga/cmake/FindCriterion.cmake
Normal file
|
@ -0,0 +1,27 @@
|
|||
# This file is licensed under the WTFPL version 2 -- you can see the full
|
||||
# license over at http://www.wtfpl.net/txt/copying/
|
||||
#
|
||||
# - Try to find Criterion
|
||||
#
|
||||
# Once done this will define
|
||||
# CRITERION_FOUND - System has Criterion
|
||||
# CRITERION_INCLUDE_DIRS - The Criterion include directories
|
||||
# CRITERION_LIBRARIES - The libraries needed to use Criterion
|
||||
|
||||
find_package(PkgConfig)
|
||||
|
||||
find_path(CRITERION_INCLUDE_DIR criterion/criterion.h
|
||||
PATH_SUFFIXES criterion)
|
||||
|
||||
find_library(CRITERION_LIBRARY NAMES criterion libcriterion)
|
||||
|
||||
set(CRITERION_LIBRARIES ${CRITERION_LIBRARY})
|
||||
set(CRITERION_INCLUDE_DIRS ${CRITERION_INCLUDE_DIR})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
# handle the QUIET and REQUIRED arguments and set CRITERION_FOUND to TRUE
|
||||
# if all listed variables are TRUE
|
||||
find_package_handle_standard_args(Criterion DEFAULT_MSG
|
||||
CRITERION_LIBRARY CRITERION_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(CRITERION_INCLUDE_DIR CRITERION_LIBRARY)
|
BIN
fpga/doc/pictures/eonerc_logo.png
Normal file
BIN
fpga/doc/pictures/eonerc_logo.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 9.1 KiB |
BIN
fpga/doc/pictures/villas_fpga.png
Normal file
BIN
fpga/doc/pictures/villas_fpga.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 14 KiB |
113
fpga/doc/pictures/villas_fpga.svg
Normal file
113
fpga/doc/pictures/villas_fpga.svg
Normal file
|
@ -0,0 +1,113 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="148.89053mm"
|
||||
height="148.89053mm"
|
||||
viewBox="0 0 148.89053 148.89053"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.91+devel r14647 custom"
|
||||
sodipodi:docname="villas_fpga.svg"
|
||||
inkscape:export-filename="/Users/stv0g/workspace/rwth/villas/node/doc/pictures/villas_fpga.png"
|
||||
inkscape:export-xdpi="70"
|
||||
inkscape:export-ydpi="70">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.1539865"
|
||||
inkscape:cx="273.11099"
|
||||
inkscape:cy="286.34845"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:snap-global="true"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="851"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="23"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:object-nodes="false" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-2.3870832,-17.382582)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:1;fill:#f65d5d;fill-opacity:1;stroke:none;stroke-width:34.06763077;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m 12.855646,54.255283 -0.0023,0.0015 -1.23e-4,0.0028 z"
|
||||
id="path4267" />
|
||||
<circle
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:0.09076402;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="path4286"
|
||||
cx="77.834518"
|
||||
cy="91.776962"
|
||||
r="29.89892" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:1;fill:#f65d5d;fill-opacity:1;stroke:none;stroke-width:34.06763077;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m 76.974417,16.797321 -0.0023,0.0015 0.0046,-1.62e-4 z"
|
||||
id="path4275" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:1;fill:#f98e8e;fill-opacity:1;stroke:none;stroke-width:34.06763077;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m 81.536624,19.580815 -0.218452,23.613422 35.481498,20.486912 20.6168,-11.836694 z"
|
||||
id="path4273" />
|
||||
<path
|
||||
id="path42454"
|
||||
d="M 71.221452,19.580815 71.439902,43.194237 35.95843,63.681149 15.341626,51.844455 Z"
|
||||
style="opacity:1;fill:#f65d5d;fill-opacity:1;stroke:none;stroke-width:34.06763077;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
id="path42458"
|
||||
d="m 81.536624,164.22286 -0.218452,-23.61343 35.481498,-20.48693 20.6168,11.8367 z"
|
||||
style="opacity:1;fill:#8f1515;fill-opacity:1;stroke:none;stroke-width:34.06763077;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:1;fill:#aa2424;fill-opacity:1;stroke:none;stroke-width:34.06763077;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 71.221452,164.22286 71.439902,140.60943 35.95843,120.1225 15.341626,131.9592 Z"
|
||||
id="path42460" />
|
||||
<path
|
||||
id="path42462"
|
||||
d="M 11.878931,124.09941 32.437982,112.48188 32.439482,71.510572 11.88019,59.574251 Z"
|
||||
style="opacity:1;fill:#de2727;fill-opacity:1;stroke:none;stroke-width:34.06763077;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
</g>
|
||||
</svg>
|
After (image error) Size: 5 KiB |
73
fpga/etc/fpga-simple.conf
Normal file
73
fpga/etc/fpga-simple.conf
Normal file
|
@ -0,0 +1,73 @@
|
|||
/** Example configuration file for VILLASfpga.
|
||||
*
|
||||
* The syntax of this file is similar to JSON.
|
||||
* A detailed description of the format can be found here:
|
||||
* http://www.hyperrealm.com/libconfig/libconfig_manual.html#Configuration-Files
|
||||
*
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
# Some global settings are used by multiple configuration files
|
||||
# and therefore defined in separate files
|
||||
@include "global.conf"
|
||||
@include "plugins.conf"
|
||||
|
||||
fpgas = {
|
||||
vc707 = {
|
||||
/* Card identification */
|
||||
id = "10ee:7022";
|
||||
slot = "01:00.0";
|
||||
|
||||
intc = 0x5000;
|
||||
reset = 0x2000;
|
||||
do_reset = true;
|
||||
|
||||
ips = {
|
||||
switch_0 = {
|
||||
vlnv = "xilinx.com:ip:axis_interconnect:2.1"
|
||||
baseaddr = 0x0000;
|
||||
numports = 3;
|
||||
|
||||
paths = (
|
||||
{ in = "dma_0", out = "rtds_0" },
|
||||
{ in = "rtds_0", out = "dma_0" }
|
||||
)
|
||||
},
|
||||
rtds_0 = {
|
||||
vlnv = "acs.eonerc.rwth-aachen.de:user:rtds_axis:1.0"
|
||||
baseaddr = 0x3000;
|
||||
port = 0;
|
||||
},
|
||||
dma_0 = {
|
||||
vlnv = "xilinx.com:ip:axi_dma:7.1";
|
||||
baseaddr = 0x1000;
|
||||
port = 2;
|
||||
irq = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nodes = {
|
||||
rtds = {
|
||||
datamover = "dma_0";
|
||||
use_irqs = false;
|
||||
}
|
||||
}
|
168
fpga/etc/fpga.conf
Normal file
168
fpga/etc/fpga.conf
Normal file
|
@ -0,0 +1,168 @@
|
|||
/** Example configuration file for VILLASfpga / VILLASfpga.
|
||||
*
|
||||
* The syntax of this file is similar to JSON.
|
||||
* A detailed description of the format can be found here:
|
||||
* http://www.hyperrealm.com/libconfig/libconfig_manual.html#Configuration-Files
|
||||
*
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
# Some global settings are used by multiple configuration files
|
||||
# and therefore defined in separate files
|
||||
@include "global.conf"
|
||||
@include "plugins.conf"
|
||||
|
||||
############ Dictionary of FPGAs ############
|
||||
|
||||
fpgas = {
|
||||
vc707 = {
|
||||
id = "10ee:7022"; # Card identification
|
||||
slot = "01:00.0"; # Usually only id or slot is required
|
||||
|
||||
do_reset = true; # Perform a full reset of the FPGA board
|
||||
# Requires a IP core named 'axi_reset_0'
|
||||
|
||||
############ List of IP cores on FPGA ############
|
||||
#
|
||||
# Every IP core can have the following settings:
|
||||
# baseaddr Baseaddress as accessible from BAR0 memory region
|
||||
# irq Interrupt index of MSI interrupt controller
|
||||
# port Port index of AXI4-Stream interconnect
|
||||
|
||||
ips = {
|
||||
### Utility IPs
|
||||
axi_pcie_intc_0 = {
|
||||
vlnv = "acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:1.0";
|
||||
baseaddr = 0xb000;
|
||||
},
|
||||
switch_0 = {
|
||||
vlnv = "xilinx.com:ip:axis_interconnect:2.1"
|
||||
baseaddr = 0x5000;
|
||||
num_ports = 10;
|
||||
|
||||
paths = (
|
||||
// { in = "fifo_mm_s_0", out = "fifo_mm_s_0" }, # Loopback fifo_mm_s_0
|
||||
// { in = "dma_0", out = "dma_0" }, # Loopback dma_0
|
||||
// { in = "dma_1", out = "dma_1" } # Loopback dma_1
|
||||
// { in = "rtds_axis_0", out = "fifo_mm_s_0", reverse = true } # Linux <-> RTDS
|
||||
// { in = "rtds_axis_0", out = "dma_0", reverse = true } # Linux (dma_0) <-> RTDS
|
||||
{ in = "rtds_axis_0", out = "dma_1", reverse = true } # Linux (dma_1) <-> RTDS
|
||||
// { in = "rtds_axis_0", out = "fifo_mm_s_0", reverse = true } # Linux (fifo_mm_s_0) <-> RTDS
|
||||
// { in = "dma_0", out = "hls_dft_0", reverse = true } # DFT <-> Linux
|
||||
// { in = "rtds_axis_0", out = "hls_dft_0", reverse = true }, # DFT <-> RTDS
|
||||
)
|
||||
},
|
||||
axi_reset_0 = {
|
||||
vlnv = "xilinx.com:ip:axi_gpio:2.0";
|
||||
baseaddr = 0x7000;
|
||||
},
|
||||
timer_0 = {
|
||||
vlnv = "xilinx.com:ip:axi_timer:2.0";
|
||||
baseaddr = 0x4000;
|
||||
irq = 0;
|
||||
},
|
||||
|
||||
### Data mover IPs
|
||||
dma_0 = {
|
||||
vlnv = "xilinx.com:ip:axi_dma:7.1";
|
||||
baseaddr = 0x3000;
|
||||
port = 1;
|
||||
irq = 3; /* 3 - 4 */
|
||||
},
|
||||
dma_1 = {
|
||||
vlnv = "xilinx.com:ip:axi_dma:7.1";
|
||||
baseaddr = 0x2000;
|
||||
port = 6;
|
||||
irq = 3; /* 3 - 4 */
|
||||
},
|
||||
fifo_mm_s_0 = {
|
||||
vlnv = "xilinx.com:ip:axi_fifo_mm_s:4.1";
|
||||
baseaddr = 0x6000;
|
||||
baseaddr_axi4 = 0xC000;
|
||||
port = 2;
|
||||
irq = 2;
|
||||
},
|
||||
|
||||
### Interface IPs
|
||||
rtds_axis_0 = {
|
||||
vlnv = "acs.eonerc.rwth-aachen.de:user:rtds_axis:1.0";
|
||||
baseaddr = 0x8000;
|
||||
port = 0;
|
||||
irq = 5; /* 5 -7 */
|
||||
},
|
||||
|
||||
### Model IPs
|
||||
hls_dft_0 = {
|
||||
vlnv = "acs.eonerc.rwth-aachen.de:hls:hls_dft:1.0";
|
||||
baseaddr = 0x9000;
|
||||
port = 5;
|
||||
irq = 1;
|
||||
|
||||
period = 400; /* in samples: 20ms / 50uS = 400*/
|
||||
harmonics = [ 0, 1, 3, 5, 7 ]
|
||||
decimation = 0; /* 0 = disabled */
|
||||
//harmonics = [ 0, 1, 2, 5, 22 ]
|
||||
},
|
||||
axis_data_fifo_0 = {
|
||||
vlnv = "xilinx.com:ip:axis_data_fifo:1.1";
|
||||
port = 3;
|
||||
},
|
||||
axis_data_fifo_1 = {
|
||||
vlnv = "xilinx.com:ip:axis_data_fifo:1.1";
|
||||
port = 6;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
############ Dictionary of nodes ############
|
||||
|
||||
nodes = {
|
||||
dma_0 = {
|
||||
type = "fpga"; # Datamovers to VILLASfpga
|
||||
datamover = "dma_0"; # Name of IP core in fpga.ips
|
||||
use_irqs = false; # Use polling or MSI interrupts?
|
||||
},
|
||||
dma_1 = {
|
||||
type = "fpga";
|
||||
datamover = "dma_1";
|
||||
use_irqs = false;
|
||||
},
|
||||
fifo_0 = {
|
||||
type = "fpga";
|
||||
datamover = "fifo_mm_s_0";
|
||||
use_irqs = false;
|
||||
},
|
||||
simple_circuit = {
|
||||
type = "cbuilder";
|
||||
model = "simple_circuit",
|
||||
timestep = 25e-6; # in seconds
|
||||
parameters = [
|
||||
1.0, # R2 = 1 Ohm
|
||||
0.001 # C2 = 1000 uF
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
############ List of paths ############
|
||||
|
||||
paths = (
|
||||
{ in = "dma_1", out = "simple_circuit", reverse = true }
|
||||
)
|
36
fpga/include/villas/common.h
Normal file
36
fpga/include/villas/common.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/** Some common defines, enums and datastructures.
|
||||
*
|
||||
* @file
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Common states for most objects in VILLASfpga (paths, nodes, hooks, plugins) */
|
||||
enum state {
|
||||
STATE_DESTROYED = 0,
|
||||
STATE_INITIALIZED = 1,
|
||||
STATE_PARSED = 2,
|
||||
STATE_CHECKED = 3,
|
||||
STATE_STARTED = 4,
|
||||
STATE_LOADED = 4, /* alias for STATE_STARTED used by plugins */
|
||||
STATE_STOPPED = 5,
|
||||
STATE_UNLOADED = 5 /* alias for STATE_STARTED used by plugins */
|
||||
};
|
71
fpga/include/villas/config.h
Normal file
71
fpga/include/villas/config.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/** Static server configuration
|
||||
*
|
||||
* This file contains some compiled-in settings.
|
||||
* This settings are not part of the configuration file.
|
||||
*
|
||||
* @file
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef V
|
||||
#define V 2
|
||||
#endif
|
||||
|
||||
/* Paths */
|
||||
#define PLUGIN_PATH PREFIX "/share/villas/node/plugins"
|
||||
#define WEB_PATH PREFIX "/share/villas/node/web"
|
||||
#define SYSFS_PATH "/sys"
|
||||
#define PROCFS_PATH "/proc"
|
||||
|
||||
/** Default number of values in a sample */
|
||||
#define DEFAULT_SAMPLELEN 64
|
||||
#define DEFAULT_QUEUELEN 1024
|
||||
|
||||
/** Number of hugepages which are requested from the the kernel.
|
||||
* @see https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt */
|
||||
#define DEFAULT_NR_HUGEPAGES 100
|
||||
|
||||
/** Width of log output in characters */
|
||||
#define LOG_WIDTH 80
|
||||
#define LOG_HEIGHT 25
|
||||
|
||||
/** Socket priority */
|
||||
#define SOCKET_PRIO 7
|
||||
|
||||
/* Protocol numbers */
|
||||
#define IPPROTO_VILLAS 137
|
||||
#define ETH_P_VILLAS 0xBABE
|
||||
|
||||
#define USER_AGENT "VILLASfpga (" BUILDID ")"
|
||||
|
||||
/* Required kernel version */
|
||||
#define KERNEL_VERSION_MAJ 3
|
||||
#define KERNEL_VERSION_MIN 6
|
||||
|
||||
/** PCIe BAR number of VILLASfpga registers */
|
||||
#define FPGA_PCI_BAR 0
|
||||
#define FPGA_PCI_VID_XILINX 0x10ee
|
||||
#define FPGA_PCI_PID_VFPGA 0x7022
|
||||
|
||||
/** AXI Bus frequency for all components
|
||||
* except RTDS AXI Stream bridge which runs at RTDS_HZ (100 Mhz) */
|
||||
#define FPGA_AXI_HZ 125000000 // 125 MHz
|
95
fpga/include/villas/fpga/card.h
Normal file
95
fpga/include/villas/fpga/card.h
Normal file
|
@ -0,0 +1,95 @@
|
|||
/** FPGA card
|
||||
*
|
||||
* This class represents a FPGA device.
|
||||
*
|
||||
* @file
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
/** @addtogroup fpga VILLASfpga
|
||||
* @{
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "kernel/pci.h"
|
||||
#include "kernel/vfio.h"
|
||||
|
||||
/* Forward declarations */
|
||||
struct fpga_ip;
|
||||
struct vfio_container;
|
||||
|
||||
struct fpga_card {
|
||||
char *name; /**< The name of the FPGA card */
|
||||
|
||||
enum state state; /**< The state of this FPGA card. */
|
||||
|
||||
struct pci *pci;
|
||||
struct pci_device filter; /**< Filter for PCI device. */
|
||||
|
||||
struct vfio_container *vfio_container;
|
||||
struct vfio_device vfio_device; /**< VFIO device handle. */
|
||||
|
||||
int do_reset; /**< Reset VILLASfpga during startup? */
|
||||
int affinity; /**< Affinity for MSI interrupts */
|
||||
|
||||
struct list ips; /**< List of IP components on FPGA. */
|
||||
|
||||
char *map; /**< PCI BAR0 mapping for register access */
|
||||
|
||||
size_t maplen;
|
||||
size_t dmalen;
|
||||
|
||||
/* Some IP cores are special and referenced here */
|
||||
struct fpga_ip *intc;
|
||||
struct fpga_ip *reset;
|
||||
struct fpga_ip *sw;
|
||||
};
|
||||
|
||||
/** Initialize FPGA card and its IP components. */
|
||||
int fpga_card_init(struct fpga_card *c, struct pci *pci, struct vfio_container *vc);
|
||||
|
||||
/** Parse configuration of FPGA card including IP cores from config. */
|
||||
int fpga_card_parse(struct fpga_card *c, json_t *cfg, const char *name);
|
||||
|
||||
int fpga_card_parse_list(struct list *l, json_t *cfg);
|
||||
|
||||
/** Check if the FPGA card configuration is plausible. */
|
||||
int fpga_card_check(struct fpga_card *c);
|
||||
|
||||
/** Start FPGA card. */
|
||||
int fpga_card_start(struct fpga_card *c);
|
||||
|
||||
/** Stop FPGA card. */
|
||||
int fpga_card_stop(struct fpga_card *c);
|
||||
|
||||
/** Destroy FPGA card. */
|
||||
int fpga_card_destroy(struct fpga_card *c);
|
||||
|
||||
/** Dump details of FPGA card to stdout. */
|
||||
void fpga_card_dump(struct fpga_card *c);
|
||||
|
||||
/** Reset the FPGA to a known state */
|
||||
int fpga_card_reset(struct fpga_card *c);
|
||||
|
||||
/** @} */
|
119
fpga/include/villas/fpga/ip.h
Normal file
119
fpga/include/villas/fpga/ip.h
Normal file
|
@ -0,0 +1,119 @@
|
|||
/** Interlectual Property component.
|
||||
*
|
||||
* This class represents a module within the FPGA.
|
||||
*
|
||||
* @file
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
/** @addtogroup fpga VILLASfpga
|
||||
* @{
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "fpga/vlnv.h"
|
||||
|
||||
#include "fpga/ips/dma.h"
|
||||
#include "fpga/ips/switch.h"
|
||||
#include "fpga/ips/fifo.h"
|
||||
#include "fpga/ips/rtds_axis.h"
|
||||
#include "fpga/ips/timer.h"
|
||||
#include "fpga/ips/model.h"
|
||||
#include "fpga/ips/dft.h"
|
||||
#include "fpga/ips/intc.h"
|
||||
|
||||
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. */
|
||||
FPGA_IP_TYPE_MODEL, /**< A model IP simulates a system on the FPGA. */
|
||||
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;
|
||||
|
||||
enum fpga_ip_types type;
|
||||
|
||||
int (*init)(struct fpga_ip *c);
|
||||
int (*parse)(struct fpga_ip *c, json_t *cfg);
|
||||
int (*check)(struct fpga_ip *c);
|
||||
int (*start)(struct fpga_ip *c);
|
||||
int (*stop)(struct fpga_ip *c);
|
||||
int (*destroy)(struct fpga_ip *c);
|
||||
int (*reset)(struct fpga_ip *c);
|
||||
void (*dump)(struct fpga_ip *c);
|
||||
|
||||
size_t size; /**< Amount of memory which should be reserved for struct fpga_ip::_vd */
|
||||
};
|
||||
|
||||
struct fpga_ip {
|
||||
char *name; /**< Name of the FPGA IP component. */
|
||||
struct fpga_vlnv vlnv; /**< The Vendor, Library, Name, Version tag of the FPGA IP component. */
|
||||
|
||||
enum state state; /**< The current state of the FPGA IP component. */
|
||||
|
||||
struct fpga_ip_type *_vt; /**< Vtable containing FPGA IP type function pointers. */
|
||||
void *_vd; /**< Virtual data (used by struct fpga_ip::_vt functions) */
|
||||
|
||||
uintptr_t baseaddr; /**< The baseadress of this FPGA IP component */
|
||||
uintptr_t baseaddr_axi4; /**< Used by AXI4 FIFO DM */
|
||||
|
||||
int port; /**< The port of the AXI4-Stream switch to which this FPGA IP component is connected. */
|
||||
int irq; /**< The interrupt number of the FPGA IP component. */
|
||||
|
||||
struct fpga_card *card; /**< The FPGA to which this IP instance belongs to. */
|
||||
};
|
||||
|
||||
/** Initialize IP core. */
|
||||
int fpga_ip_init(struct fpga_ip *c, struct fpga_ip_type *vt);
|
||||
|
||||
/** Parse IP core configuration from configuration file */
|
||||
int fpga_ip_parse(struct fpga_ip *c, json_t *cfg, const char *name);
|
||||
|
||||
/** Check configuration of IP core. */
|
||||
int fpga_ip_check(struct fpga_ip *c);
|
||||
|
||||
/** Start IP core. */
|
||||
int fpga_ip_start(struct fpga_ip *c);
|
||||
|
||||
/** Stop IP core. */
|
||||
int fpga_ip_stop(struct fpga_ip *c);
|
||||
|
||||
/** Release dynamic memory allocated by this IP core. */
|
||||
int fpga_ip_destroy(struct fpga_ip *c);
|
||||
|
||||
/** Dump details about this IP core to stdout. */
|
||||
void fpga_ip_dump(struct fpga_ip *c);
|
||||
|
||||
/** Reset IP component to its initial state. */
|
||||
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);
|
||||
|
||||
|
||||
/** @} */
|
52
fpga/include/villas/fpga/ips/dft.h
Normal file
52
fpga/include/villas/fpga/ips/dft.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/** Moving window / Recursive DFT implementation based on HLS
|
||||
*
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
/** @addtogroup fpga VILLASfpga
|
||||
* @{
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <xilinx/xhls_dft.h>
|
||||
|
||||
/* Forward declaration */
|
||||
struct ip;
|
||||
|
||||
struct dft {
|
||||
XHls_dft inst;
|
||||
|
||||
int period; /* in samples */
|
||||
int num_harmonics;
|
||||
float *fharmonics;
|
||||
int decimation;
|
||||
};
|
||||
|
||||
int dft_parse(struct fpga_ip *c, json_t *cfg);
|
||||
|
||||
int dft_start(struct fpga_ip *c);
|
||||
|
||||
int dft_stop(struct fpga_ip *c);
|
||||
|
||||
int dft_destroy(struct fpga_ip *c);
|
||||
|
||||
/** @} */
|
88
fpga/include/villas/fpga/ips/dma.h
Normal file
88
fpga/include/villas/fpga/ips/dma.h
Normal file
|
@ -0,0 +1,88 @@
|
|||
/** DMA related helper functions.
|
||||
*
|
||||
* These functions present a simpler interface to Xilinx' DMA driver (XAxiDma_*).
|
||||
*
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
/** @addtogroup fpga VILLASfpga
|
||||
* @{
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <xilinx/xaxidma.h>
|
||||
|
||||
/* Forward declarations */
|
||||
struct fpga_ip;
|
||||
|
||||
#define FPGA_DMA_BASEADDR 0x00000000
|
||||
#define FPGA_DMA_BOUNDARY 0x1000
|
||||
#define FPGA_DMA_BD_OFFSET 0xC0000000
|
||||
#define FPGA_DMA_BD_SIZE (32 << 20) // 32 MB
|
||||
|
||||
#define XAXIDMA_SR_SGINCL_MASK 0x00000008
|
||||
|
||||
struct dma_mem {
|
||||
char *base_virt;
|
||||
char *base_phys;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
struct dma {
|
||||
XAxiDma inst;
|
||||
|
||||
struct dma_mem bd;
|
||||
};
|
||||
|
||||
struct ip;
|
||||
|
||||
int dma_mem_split(struct dma_mem *o, struct dma_mem *a, struct dma_mem *b);
|
||||
|
||||
int dma_alloc(struct fpga_ip *c, struct dma_mem *mem, size_t len, int flags);
|
||||
int dma_free(struct fpga_ip *c, struct dma_mem *mem);
|
||||
|
||||
int dma_write(struct fpga_ip *c, char *buf, size_t len);
|
||||
int dma_read(struct fpga_ip *c, char *buf, size_t len);
|
||||
int dma_read_complete(struct fpga_ip *c, char **buf, size_t *len);
|
||||
int dma_write_complete(struct fpga_ip *c, char **buf, size_t *len);
|
||||
|
||||
int dma_sg_write(struct fpga_ip *c, char *buf, size_t len);
|
||||
int dma_sg_read(struct fpga_ip *c, char *buf, size_t len);
|
||||
|
||||
int dma_sg_write_complete(struct fpga_ip *c, char **buf, size_t *len);
|
||||
int dma_sg_read_complete(struct fpga_ip *c, char **buf, size_t *len);
|
||||
|
||||
int dma_simple_read(struct fpga_ip *c, char *buf, size_t len);
|
||||
int dma_simple_write(struct fpga_ip *c, char *buf, size_t len);
|
||||
|
||||
int dma_simple_read_complete(struct fpga_ip *c, char **buf, size_t *len);
|
||||
int dma_simple_write_complete(struct fpga_ip *c, char **buf, size_t *len);
|
||||
|
||||
int dma_ping_pong(struct fpga_ip *c, char *src, char *dst, size_t len);
|
||||
|
||||
int dma_start(struct fpga_ip *c);
|
||||
|
||||
/** @} */
|
52
fpga/include/villas/fpga/ips/fifo.h
Normal file
52
fpga/include/villas/fpga/ips/fifo.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/** FIFO related helper functions
|
||||
*
|
||||
* These functions present a simpler interface to Xilinx' FIFO driver (XLlFifo_*)
|
||||
*
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
/** @addtogroup fpga VILLASfpga
|
||||
* @{
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <xilinx/xstatus.h>
|
||||
#include <xilinx/xllfifo.h>
|
||||
|
||||
struct fifo {
|
||||
XLlFifo inst;
|
||||
|
||||
uint32_t baseaddr_axi4;
|
||||
};
|
||||
|
||||
/* Forward declarations */
|
||||
struct ip;
|
||||
|
||||
int fifo_start(struct fpga_ip *c);
|
||||
|
||||
ssize_t fifo_write(struct fpga_ip *c, char *buf, size_t len);
|
||||
|
||||
ssize_t fifo_read(struct fpga_ip *c, char *buf, size_t len);
|
||||
|
||||
/** @} */
|
56
fpga/include/villas/fpga/ips/intc.h
Normal file
56
fpga/include/villas/fpga/ips/intc.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/** AXI-PCIe Interrupt controller
|
||||
*
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
/** @addtogroup fpga VILLASfpga
|
||||
* @{
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <xilinx/xintc.h>
|
||||
|
||||
enum intc_flags {
|
||||
INTC_ENABLED = (1 << 0),
|
||||
INTC_POLLING = (1 << 1)
|
||||
};
|
||||
|
||||
struct intc {
|
||||
int num_irqs; /**< Number of available MSI vectors */
|
||||
|
||||
int efds[32]; /**< Event FDs */
|
||||
int nos[32]; /**< Interrupt numbers from /proc/interrupts */
|
||||
|
||||
int flags[32]; /**< Mask of intc_flags */
|
||||
};
|
||||
|
||||
int intc_init(struct fpga_ip *c);
|
||||
|
||||
int intc_destroy(struct fpga_ip *c);
|
||||
|
||||
int intc_enable(struct fpga_ip *c, uint32_t mask, int poll);
|
||||
|
||||
int intc_disable(struct fpga_ip *c, uint32_t mask);
|
||||
|
||||
uint64_t intc_wait(struct fpga_ip *c, int irq);
|
||||
|
||||
/** @} */
|
147
fpga/include/villas/fpga/ips/model.h
Normal file
147
fpga/include/villas/fpga/ips/model.h
Normal file
|
@ -0,0 +1,147 @@
|
|||
/** Interface to Xilinx System Generator Models via PCIe
|
||||
*
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
/** @addtogroup fpga VILLASfpga
|
||||
* @{
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "list.h"
|
||||
|
||||
#define XSG_MAPLEN 0x1000
|
||||
#define XSG_MAGIC 0xDEADBABE
|
||||
|
||||
/* Forward declaration */
|
||||
struct ip;
|
||||
|
||||
enum model_type {
|
||||
MODEL_TYPE_HLS,
|
||||
MODEL_TYPE_XSG
|
||||
};
|
||||
|
||||
enum model_xsg_block_type {
|
||||
XSG_BLOCK_GATEWAY_IN = 0x1000,
|
||||
XSG_BLOCK_GATEWAY_OUT = 0x1001,
|
||||
XSG_BLOCK_INFO = 0x2000
|
||||
};
|
||||
|
||||
enum model_parameter_type {
|
||||
MODEL_PARAMETER_TYPE_UFIX,
|
||||
MODEL_PARAMETER_TYPE_FIX,
|
||||
MODEL_PARAMETER_TYPE_FLOAT,
|
||||
MODEL_PARAMETER_TYPE_BOOLEAN
|
||||
};
|
||||
|
||||
enum model_parameter_direction {
|
||||
MODEL_PARAMETER_IN,
|
||||
MODEL_PARAMETER_OUT,
|
||||
MODEL_PARAMETER_INOUT
|
||||
};
|
||||
|
||||
union model_parameter_value {
|
||||
uint32_t ufix;
|
||||
int32_t fix;
|
||||
float flt;
|
||||
bool bol;
|
||||
};
|
||||
|
||||
struct xsg_model {
|
||||
uint32_t *map;
|
||||
ssize_t maplen;
|
||||
};
|
||||
|
||||
struct hls_model {
|
||||
|
||||
};
|
||||
|
||||
struct model {
|
||||
enum model_type type; /**< Either HLS or XSG model */
|
||||
|
||||
struct list parameters; /**< List of model parameters. */
|
||||
struct list infos; /**< A list of key / value pairs with model details */
|
||||
|
||||
union {
|
||||
struct xsg_model xsg; /**< XSG specific model data */
|
||||
struct hls_model hls; /**< HLS specific model data */
|
||||
};
|
||||
};
|
||||
|
||||
struct model_info {
|
||||
char *field;
|
||||
char *value;
|
||||
};
|
||||
|
||||
struct model_parameter {
|
||||
char *name; /**< Name of the parameter */
|
||||
|
||||
enum model_parameter_direction direction; /**< Read / Write / Read-write? */
|
||||
enum model_parameter_type type; /**< Data type. Integers are represented by MODEL_GW_TYPE_(U)FIX with model_gw::binpt == 0 */
|
||||
|
||||
int binpt; /**< Binary point for type == MODEL_GW_TYPE_(U)FIX */
|
||||
uintptr_t offset; /**< Register offset to model::baseaddress */
|
||||
|
||||
union model_parameter_value default_value;
|
||||
|
||||
struct fpga_ip *ip; /**< A pointer to the model structure to which this parameters belongs to. */
|
||||
};
|
||||
|
||||
/** Initialize a model */
|
||||
int model_init(struct fpga_ip *c);
|
||||
|
||||
/** Parse model */
|
||||
int model_parse(struct fpga_ip *c, json_t *cfg);
|
||||
|
||||
/** Destroy a model */
|
||||
int model_destroy(struct fpga_ip *c);
|
||||
|
||||
/** Print detailed information about the model to the screen. */
|
||||
void model_dump(struct fpga_ip *c);
|
||||
|
||||
/** Add a new parameter to the model */
|
||||
void model_parameter_add(struct fpga_ip *c, const char *name, enum model_parameter_direction dir, enum model_parameter_type type);
|
||||
|
||||
/** Remove an existing parameter by its name */
|
||||
int model_parameter_remove(struct fpga_ip *c, const char *name);
|
||||
|
||||
/** Read a model parameter.
|
||||
*
|
||||
* Note: the data type of the register is taken into account.
|
||||
* All datatypes are converted to double.
|
||||
*/
|
||||
int model_parameter_read(struct model_parameter *p, double *v);
|
||||
|
||||
/** Update a model parameter.
|
||||
*
|
||||
* Note: the data type of the register is taken into account.
|
||||
* The double argument will be converted to the respective data type of the
|
||||
* GatewayIn/Out block.
|
||||
*/
|
||||
int model_parameter_write(struct model_parameter *p, double v);
|
||||
|
||||
int model_parameter_update(struct model_parameter *p, struct model_parameter *u);
|
||||
|
||||
/** @} */
|
62
fpga/include/villas/fpga/ips/rtds_axis.h
Normal file
62
fpga/include/villas/fpga/ips/rtds_axis.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
/** Driver for AXI Stream wrapper around RTDS_InterfaceModule (rtds_axis )
|
||||
*
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
/** @addtogroup fpga VILLASfpga
|
||||
* @{
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Forward declarations */
|
||||
struct ip;
|
||||
|
||||
#define RTDS_HZ 100000000 // 100 MHz
|
||||
|
||||
#define RTDS_AXIS_MAX_TX 64 /**< The amount of values which is supported by the vfpga card */
|
||||
#define RTDS_AXIS_MAX_RX 64 /**< The amount of values which is supported by the vfpga card */
|
||||
|
||||
/* Register offsets */
|
||||
#define RTDS_AXIS_SR_OFFSET 0x00 /**< Status Register (read-only). See RTDS_AXIS_SR_* constant. */
|
||||
#define RTDS_AXIS_CR_OFFSET 0x04 /**< Control Register (read/write) */
|
||||
#define RTDS_AXIS_TSCNT_LOW_OFFSET 0x08 /**< Lower 32 bits of timestep counter (read-only). */
|
||||
#define RTDS_AXIS_TSCNT_HIGH_OFFSET 0x0C /**< Higher 32 bits of timestep counter (read-only). */
|
||||
#define RTDS_AXIS_TS_PERIOD_OFFSET 0x10 /**< Period in clock cycles of previous timestep (read-only). */
|
||||
#define RTDS_AXIS_COALESC_OFFSET 0x14 /**< IRQ Coalescing register (read/write). */
|
||||
#define RTDS_AXIS_VERSION_OFFSET 0x18 /**< 16 bit version field passed back to the rack for version reporting (visible from “status” command, read/write). */
|
||||
#define RTDS_AXIS_MRATE 0x1C /**< Multi-rate register */
|
||||
|
||||
/* Status register bits */
|
||||
#define RTDS_AXIS_SR_CARDDETECTED (1 << 0)/**< ‘1’ when RTDS software has detected and configured card. */
|
||||
#define RTDS_AXIS_SR_LINKUP (1 << 1)/**< ‘1’ when RTDS communication link has been negotiated. */
|
||||
#define RTDS_AXIS_SR_TX_FULL (1 << 2)/**< Tx buffer is full, writes that happen when UserTxFull=’1’ will be dropped (Throttling / buffering is performed by hardware). */
|
||||
#define RTDS_AXIS_SR_TX_INPROGRESS (1 << 3)/**< Indicates when data is being put on link. */
|
||||
#define RTDS_AXIS_SR_CASE_RUNNING (1 << 4)/**< There is currently a simulation running. */
|
||||
|
||||
/* Control register bits */
|
||||
#define RTDS_AXIS_CR_DISABLE_LINK 0 /**< Disable SFP TX when set */
|
||||
|
||||
void rtds_axis_dump(struct fpga_ip *c);
|
||||
|
||||
double rtds_axis_dt(struct fpga_ip *c);
|
||||
|
||||
/** @} */
|
67
fpga/include/villas/fpga/ips/switch.h
Normal file
67
fpga/include/villas/fpga/ips/switch.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/** 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>
|
||||
* @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 <jansson.h>
|
||||
#include <xilinx/xaxis_switch.h>
|
||||
|
||||
#include "list.h"
|
||||
|
||||
/* Forward declarations */
|
||||
struct ip;
|
||||
|
||||
struct sw_path {
|
||||
const char *in;
|
||||
const char *out;
|
||||
};
|
||||
|
||||
struct sw {
|
||||
XAxis_Switch inst;
|
||||
|
||||
int num_ports;
|
||||
struct list paths;
|
||||
};
|
||||
|
||||
struct ip;
|
||||
|
||||
int switch_start(struct fpga_ip *c);
|
||||
|
||||
/** Initialize paths which have been parsed by switch_parse() */
|
||||
int switch_init_paths(struct fpga_ip *c);
|
||||
|
||||
int switch_destroy(struct fpga_ip *c);
|
||||
|
||||
int switch_parse(struct fpga_ip *c, json_t *cfg);
|
||||
|
||||
int switch_connect(struct fpga_ip *c, struct fpga_ip *mi, struct fpga_ip *si);
|
||||
|
||||
int switch_disconnect(struct fpga_ip *c, struct fpga_ip *mi, struct fpga_ip *si);
|
||||
|
||||
/** @} */
|
43
fpga/include/villas/fpga/ips/timer.h
Normal file
43
fpga/include/villas/fpga/ips/timer.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/** Timer related helper functions
|
||||
*
|
||||
* These functions present a simpler interface to Xilinx' Timer Counter driver (XTmrCtr_*)
|
||||
*
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
/** @addtogroup fpga VILLASfpga
|
||||
* @{
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <xilinx/xtmrctr.h>
|
||||
|
||||
/* Forward declarations */
|
||||
struct fpga_ip;
|
||||
|
||||
struct timer {
|
||||
XTmrCtr inst;
|
||||
};
|
||||
|
||||
int timer_start(struct fpga_ip *c);
|
||||
|
||||
/** @} */
|
53
fpga/include/villas/fpga/vlnv.h
Normal file
53
fpga/include/villas/fpga/vlnv.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/** Vendor, Library, Name, Version (VLNV) tag.
|
||||
*
|
||||
* @file
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
/** @addtogroup fpga VILLASfpga
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _FPGA_VLNV_H_
|
||||
#define _FPGA_VLNV_H_
|
||||
|
||||
/* Forward declarations */
|
||||
struct list;
|
||||
|
||||
struct fpga_vlnv {
|
||||
char *vendor;
|
||||
char *library;
|
||||
char *name;
|
||||
char *version;
|
||||
};
|
||||
|
||||
/** Return the first IP block in list \p l which matches the VLNV */
|
||||
struct fpga_ip * fpga_vlnv_lookup(struct list *l, struct fpga_vlnv *v);
|
||||
|
||||
/** Check if IP block \p c matched VLNV. */
|
||||
int fpga_vlnv_cmp(struct fpga_vlnv *a, struct fpga_vlnv *b);
|
||||
|
||||
/** Tokenizes VLNV \p vlnv and stores it into \p c */
|
||||
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);
|
||||
|
||||
#endif /** _FPGA_VLNV_H_ @} */
|
90
fpga/include/villas/kernel/kernel.h
Normal file
90
fpga/include/villas/kernel/kernel.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
/** Linux kernel related functions.
|
||||
*
|
||||
* @file
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
/** @addtogroup fpga Kernel @{ */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Forward declarations */
|
||||
struct version;
|
||||
|
||||
//#include <sys/capability.h>
|
||||
|
||||
/** Check if current process has capability \p cap.
|
||||
*
|
||||
* @retval 0 If capabilty is present.
|
||||
* @retval <0 If capability is not present.
|
||||
*/
|
||||
//int kernel_check_cap(cap_value_t cap);
|
||||
|
||||
/** Get number of reserved hugepages. */
|
||||
int kernel_get_nr_hugepages();
|
||||
|
||||
/** Set number of reserved hugepages. */
|
||||
int kernel_set_nr_hugepages(int nr);
|
||||
|
||||
/** Get kernel cmdline parameter
|
||||
*
|
||||
* See https://www.kernel.org/doc/Documentation/kernel-parameters.txt
|
||||
*
|
||||
* @param param The cmdline parameter to look for.
|
||||
* @param buf The string buffer to which the parameter value will be copied to.
|
||||
* @param len The length of the buffer \p value
|
||||
* @retval 0 Parameter \p key was found and value was copied to \p value
|
||||
* @reval <>0 Kernel was not booted with parameter \p key
|
||||
*/
|
||||
int kernel_get_cmdline_param(const char *param, char *buf, size_t len);
|
||||
|
||||
/** Get the version of the kernel. */
|
||||
int kernel_get_version(struct version *v);
|
||||
|
||||
/** Checks if a kernel module is loaded
|
||||
*
|
||||
* @param module the name of the module
|
||||
* @retval 0 Module is loaded.
|
||||
* @reval <>0 Module is not loaded.
|
||||
*/
|
||||
int kernel_module_loaded(const char *module);
|
||||
|
||||
/** Load kernel module via modprobe */
|
||||
int kernel_module_load(const char *module);
|
||||
|
||||
/** Set parameter of loaded kernel module */
|
||||
int kernel_module_set_param(const char *module, const char *param, const char *value);
|
||||
|
||||
/** Get cacheline size in bytes */
|
||||
int kernel_get_cacheline_size();
|
||||
|
||||
/** Get the size of a standard page in bytes. */
|
||||
int kernel_get_page_size();
|
||||
|
||||
/** Get the size of a huge page in bytes. */
|
||||
int kernel_get_hugepage_size();
|
||||
|
||||
/** Set SMP affinity of IRQ */
|
||||
int kernel_irq_setaffinity(unsigned irq, uintmax_t new, uintmax_t *old);
|
||||
|
||||
/** @} */
|
62
fpga/include/villas/kernel/pci.h
Normal file
62
fpga/include/villas/kernel/pci.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
/** Linux PCI helpers
|
||||
*
|
||||
* @file
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017, Steffen Vogel
|
||||
**********************************************************************************/
|
||||
|
||||
/** @addtogroup fpga Kernel @{ */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "list.h"
|
||||
|
||||
#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
|
||||
#define PCI_FUNC(devfn) ((devfn) & 0x07)
|
||||
|
||||
struct pci_device {
|
||||
struct {
|
||||
int vendor;
|
||||
int device;
|
||||
int class;
|
||||
} id;
|
||||
|
||||
struct {
|
||||
int domain;
|
||||
int bus;
|
||||
int device;
|
||||
int function;
|
||||
} slot; /**< Bus, Device, Function (BDF) */
|
||||
};
|
||||
|
||||
struct pci {
|
||||
struct list devices; /**< List of available PCI devices in the system (struct pci_device) */
|
||||
};
|
||||
|
||||
/** Initialize Linux PCI handle.
|
||||
*
|
||||
* This search for all available PCI devices under /sys/bus/pci
|
||||
*
|
||||
* @retval 0 Success. Everything went well.
|
||||
* @retval <0 Error. Something went wrong.
|
||||
*/
|
||||
int pci_init(struct pci *p);
|
||||
|
||||
/** Destroy handle. */
|
||||
int pci_destroy(struct pci *p);
|
||||
|
||||
int pci_device_parse_slot(struct pci_device *f, const char *str, const char **error);
|
||||
|
||||
int pci_device_parse_id(struct pci_device *f, const char *str, const char **error);
|
||||
|
||||
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);
|
||||
|
||||
/** 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);
|
||||
|
||||
/** @} */
|
112
fpga/include/villas/kernel/vfio.h
Normal file
112
fpga/include/villas/kernel/vfio.h
Normal file
|
@ -0,0 +1,112 @@
|
|||
/** Virtual Function IO wrapper around kernel API
|
||||
*
|
||||
* @file
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017, Steffen Vogel
|
||||
*********************************************************************************/
|
||||
|
||||
/** @addtogroup fpga Kernel @{ */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <linux/vfio.h>
|
||||
#include <linux/pci_regs.h>
|
||||
|
||||
#include "list.h"
|
||||
|
||||
#define VFIO_DEV(x) "/dev/vfio/" x
|
||||
|
||||
/* Forward declarations */
|
||||
struct pci_device;
|
||||
|
||||
struct vfio_group {
|
||||
int fd; /**< VFIO group file descriptor */
|
||||
int index; /**< Index of the IOMMU group as listed under /sys/kernel/iommu_groups/ */
|
||||
|
||||
struct vfio_group_status status; /**< Status of group */
|
||||
|
||||
struct list devices;
|
||||
|
||||
struct vfio_container *container; /**< The VFIO container to which this group is belonging */
|
||||
};
|
||||
|
||||
struct vfio_device {
|
||||
char *name; /**< Name of the device as listed under /sys/kernel/iommu_groups/[vfio_group::index]/devices/ */
|
||||
int fd; /**< VFIO device file descriptor */
|
||||
|
||||
struct vfio_device_info info;
|
||||
struct vfio_irq_info *irqs;
|
||||
struct vfio_region_info *regions;
|
||||
|
||||
void **mappings;
|
||||
|
||||
struct pci_device *pci_device; /**< libpci handle of the device */
|
||||
struct vfio_group *group; /**< The VFIO group this device belongs to */
|
||||
};
|
||||
|
||||
struct vfio_container {
|
||||
int fd;
|
||||
int version;
|
||||
int extensions;
|
||||
|
||||
uint64_t iova_next; /**< Next free IOVA address */
|
||||
|
||||
struct list groups;
|
||||
};
|
||||
|
||||
/** Initialize a new VFIO container. */
|
||||
int vfio_init(struct vfio_container *c);
|
||||
|
||||
/** Initialize a VFIO group and attach it to an existing VFIO container. */
|
||||
int vfio_group_attach(struct vfio_group *g, struct vfio_container *c, int index);
|
||||
|
||||
/** Initialize a VFIO device, lookup the VFIO group it belongs to, create the group if not already existing. */
|
||||
int vfio_device_attach(struct vfio_device *d, struct vfio_container *c, const char *name, int index);
|
||||
|
||||
/** Initialie a VFIO-PCI device (uses vfio_device_attach() internally) */
|
||||
int vfio_pci_attach(struct vfio_device *d, struct vfio_container *c, struct pci_device *pdev);
|
||||
|
||||
/** Hot resets a VFIO-PCI device */
|
||||
int vfio_pci_reset(struct vfio_device *d);
|
||||
|
||||
int vfio_pci_msi_init(struct vfio_device *d, int efds[32]);
|
||||
|
||||
int vfio_pci_msi_deinit(struct vfio_device *d, int efds[32]);
|
||||
|
||||
int vfio_pci_msi_find(struct vfio_device *d, int nos[32]);
|
||||
|
||||
/** Enable memory accesses and bus mastering for PCI device */
|
||||
int vfio_pci_enable(struct vfio_device *d);
|
||||
|
||||
/** Reset a VFIO device */
|
||||
int vfio_device_reset(struct vfio_device *d);
|
||||
|
||||
/** Release memory and close container */
|
||||
int vfio_destroy(struct vfio_container *c);
|
||||
|
||||
/** Release memory of group */
|
||||
int vfio_group_destroy(struct vfio_group *g);
|
||||
|
||||
/** Release memory of device */
|
||||
int vfio_device_destroy(struct vfio_device *g);
|
||||
|
||||
/** Print a dump of all attached groups and their devices including regions and IRQs */
|
||||
void vfio_dump(struct vfio_container *c);
|
||||
|
||||
/** Map a device memory region to the application address space (e.g. PCI BARs) */
|
||||
void * vfio_map_region(struct vfio_device *d, int idx);
|
||||
|
||||
/** Map VM to an IOVA, which is accessible by devices in the container */
|
||||
int vfio_map_dma(struct vfio_container *c, uint64_t virt, uint64_t phys, size_t len);
|
||||
|
||||
/** Unmap DMA memory */
|
||||
int vfio_unmap_dma(struct vfio_container *c, uint64_t virt, uint64_t phys, size_t len);
|
||||
|
||||
/** munmap() a region which has been mapped by vfio_map_region() */
|
||||
int vfio_unmap_region(struct vfio_device *d, int idx);
|
||||
|
||||
/** @} */
|
112
fpga/include/villas/list.h
Normal file
112
fpga/include/villas/list.h
Normal file
|
@ -0,0 +1,112 @@
|
|||
/** A generic list implementation.
|
||||
*
|
||||
* This is a generic implementation of a list which can store void pointers to
|
||||
* arbitrary data. The data itself is not stored or managed by the list.
|
||||
*
|
||||
* Internally, an array of pointers is used to store the pointers.
|
||||
* If needed, this array will grow by realloc().
|
||||
*
|
||||
* @file
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 20177, Institute for Automation of Complex Power Systems, EONERC
|
||||
*********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define LIST_CHUNKSIZE 16
|
||||
|
||||
/** Static list initialization */
|
||||
#define LIST_INIT() { \
|
||||
.array = NULL, \
|
||||
.length = 0, \
|
||||
.capacity = 0, \
|
||||
.lock = PTHREAD_MUTEX_INITIALIZER, \
|
||||
.state = STATE_INITIALIZED \
|
||||
}
|
||||
|
||||
#define LIST_INIT_STATIC(l) \
|
||||
__attribute__((constructor(105))) static void UNIQUE(__ctor)() {\
|
||||
if ((l)->state == STATE_DESTROYED) \
|
||||
list_init(l); \
|
||||
} \
|
||||
__attribute__((destructor(105))) static void UNIQUE(__dtor)() { \
|
||||
list_destroy(l, NULL, false); \
|
||||
}
|
||||
|
||||
#define list_length(list) ((list)->length)
|
||||
#define list_at_safe(list, index) ((list)->length > index ? (list)->array[index] : NULL)
|
||||
#define list_at(list, index) ((list)->array[index])
|
||||
|
||||
#define list_first(list) list_at(list, 0)
|
||||
#define list_last(list) list_at(list, (list)->length-1)
|
||||
|
||||
/** Callback to destroy list elements.
|
||||
*
|
||||
* @param data A pointer to the data which should be freed.
|
||||
*/
|
||||
typedef int (*dtor_cb_t)(void *);
|
||||
|
||||
/** Callback to search or sort a list. */
|
||||
typedef int (*cmp_cb_t)(const void *, const void *);
|
||||
|
||||
/* The list data structure. */
|
||||
struct list {
|
||||
void **array; /**< Array of pointers to list elements */
|
||||
size_t capacity; /**< Size of list::array in elements */
|
||||
size_t length; /**< Number of elements of list::array which are in use */
|
||||
pthread_mutex_t lock; /**< A mutex to allow thread-safe accesses */
|
||||
enum state state; /**< The state of this list. */
|
||||
};
|
||||
|
||||
/** Initialize a list.
|
||||
*
|
||||
* @param l A pointer to the list data structure.
|
||||
*/
|
||||
int list_init(struct list *l);
|
||||
|
||||
/** Destroy a list and call destructors for all list elements
|
||||
*
|
||||
* @param free free() all list members during when calling list_destroy()
|
||||
* @param dtor A function pointer to a desctructor which will be called for every list item when the list is destroyed.
|
||||
* @param l A pointer to the list data structure.
|
||||
*/
|
||||
int list_destroy(struct list *l, dtor_cb_t dtor, bool free);
|
||||
|
||||
/** Append an element to the end of the list */
|
||||
void list_push(struct list *l, void *p);
|
||||
|
||||
/** Remove all occurences of a list item */
|
||||
void list_remove(struct list *l, void *p);
|
||||
|
||||
/** Return the first list element which is identified by a string in its first member variable.
|
||||
*
|
||||
* List elements are pointers to structures of the following form:
|
||||
*
|
||||
* struct obj {
|
||||
* char *name;
|
||||
* // more members
|
||||
* }
|
||||
*
|
||||
* @see Only possible because of §1424 of http://c0x.coding-guidelines.com/6.7.2.1.html
|
||||
*/
|
||||
void * list_lookup(struct list *l, const char *name);
|
||||
|
||||
/** Return the first element of the list for which cmp returns zero */
|
||||
void * list_search(struct list *l, cmp_cb_t cmp, void *ctx);
|
||||
|
||||
/** Returns the number of occurences for which cmp returns zero when called on all list elements. */
|
||||
int list_count(struct list *l, cmp_cb_t cmp, void *ctx);
|
||||
|
||||
/** Return 0 if list contains pointer p */
|
||||
int list_contains(struct list *l, void *p);
|
||||
|
||||
/** Sort the list using the quicksort algorithm of libc */
|
||||
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);
|
194
fpga/include/villas/log.h
Normal file
194
fpga/include/villas/log.h
Normal file
|
@ -0,0 +1,194 @@
|
|||
/** Logging and debugging routines
|
||||
*
|
||||
* @file
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "log_config.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define INDENT int __attribute__ ((__cleanup__(log_outdent), unused)) _old_indent = log_indent(1);
|
||||
#define NOINDENT int __attribute__ ((__cleanup__(log_outdent), unused)) _old_indent = log_noindent();
|
||||
#else
|
||||
#define INDENT ;
|
||||
#define NOINDENT ;
|
||||
#endif
|
||||
|
||||
/* The log level which is passed as first argument to print() */
|
||||
#define LOG_LVL_DEBUG CLR_GRY("Debug")
|
||||
#define LOG_LVL_INFO CLR_WHT("Info ")
|
||||
#define LOG_LVL_WARN CLR_YEL("Warn ")
|
||||
#define LOG_LVL_ERROR CLR_RED("Error")
|
||||
#define LOG_LVL_STATS CLR_MAG("Stats")
|
||||
|
||||
/** Debug facilities.
|
||||
*
|
||||
* To be or-ed with the debug level
|
||||
*/
|
||||
enum log_facilities {
|
||||
LOG_POOL = (1L << 8),
|
||||
LOG_QUEUE = (1L << 9),
|
||||
LOG_CONFIG = (1L << 10),
|
||||
LOG_HOOK = (1L << 11),
|
||||
LOG_PATH = (1L << 12),
|
||||
LOG_NODE = (1L << 13),
|
||||
LOG_MEM = (1L << 14),
|
||||
LOG_WEB = (1L << 15),
|
||||
LOG_API = (1L << 16),
|
||||
LOG_LOG = (1L << 17),
|
||||
LOG_VFIO = (1L << 18),
|
||||
LOG_PCI = (1L << 19),
|
||||
LOG_XIL = (1L << 20),
|
||||
LOG_TC = (1L << 21),
|
||||
LOG_IF = (1L << 22),
|
||||
LOG_ADVIO = (1L << 23),
|
||||
|
||||
/* Node-types */
|
||||
LOG_SOCKET = (1L << 24),
|
||||
LOG_FILE = (1L << 25),
|
||||
LOG_FPGA = (1L << 26),
|
||||
LOG_NGSI = (1L << 27),
|
||||
LOG_WEBSOCKET = (1L << 28),
|
||||
LOG_OPAL = (1L << 30),
|
||||
|
||||
/* Classes */
|
||||
LOG_NODES = LOG_NODE | LOG_SOCKET | LOG_FILE | LOG_FPGA | LOG_NGSI | LOG_WEBSOCKET | LOG_OPAL,
|
||||
LOG_KERNEL = LOG_VFIO | LOG_PCI | LOG_TC | LOG_IF,
|
||||
LOG_ALL = ~0xFF
|
||||
};
|
||||
|
||||
struct log {
|
||||
enum state state;
|
||||
|
||||
struct timespec epoch; /**< A global clock used to prefix the log messages. */
|
||||
|
||||
struct winsize window; /**< Size of the terminal window. */
|
||||
int width; /**< The real usable log output width which fits into one line. */
|
||||
|
||||
/** Debug level used by the debug() macro.
|
||||
* It defaults to V (defined by the Makefile) and can be
|
||||
* overwritten by the 'debug' setting in the configuration file. */
|
||||
int level;
|
||||
long facilities; /**< Debug facilities used by the debug() macro. */
|
||||
const char *path; /**< Path of the log file. */
|
||||
char *prefix; /**< Prefix each line with this string. */
|
||||
int syslog; /**< Whether or not to log to syslogd. */
|
||||
|
||||
FILE *file; /**< Send all log output to this file / stdout / stderr. */
|
||||
};
|
||||
|
||||
/** The global log instance. */
|
||||
struct log *global_log;
|
||||
struct log default_log;
|
||||
|
||||
/** Initialize log object */
|
||||
int log_init(struct log *l, int level, long faciltities);
|
||||
|
||||
int log_start(struct log *l);
|
||||
|
||||
int log_stop(struct log *l);
|
||||
|
||||
/** Destroy log object */
|
||||
int log_destroy(struct log *l);
|
||||
|
||||
/** Change log indention for current thread.
|
||||
*
|
||||
* The argument level can be negative!
|
||||
*/
|
||||
int log_indent(int levels);
|
||||
|
||||
/** Disable log indention of current thread. */
|
||||
int log_noindent();
|
||||
|
||||
/** A helper function the restore the previous log indention level.
|
||||
*
|
||||
* This function is usually called by a __cleanup__ handler (GCC C Extension).
|
||||
* See INDENT macro.
|
||||
*/
|
||||
void log_outdent(int *);
|
||||
|
||||
/** Set logging facilities based on expression.
|
||||
*
|
||||
* Currently we support two types of expressions:
|
||||
* 1. A comma seperated list of logging facilities
|
||||
* 2. A comma seperated list of logging facilities which is prefixes with an exclamation mark '!'
|
||||
*
|
||||
* The first case enables only faciltities which are in the list.
|
||||
* The second case enables all faciltities with exception of those which are in the list.
|
||||
*
|
||||
* @param expression The expression
|
||||
* @return The new facilties mask (see enum log_faciltities)
|
||||
*/
|
||||
int log_set_facility_expression(struct log *l, const char *expression);
|
||||
|
||||
/** Logs variadic messages to stdout.
|
||||
*
|
||||
* @param lvl The log level
|
||||
* @param fmt The format string (printf alike)
|
||||
*/
|
||||
void log_print(struct log *l, const char *lvl, const char *fmt, ...)
|
||||
__attribute__ ((format(printf, 3, 4)));
|
||||
|
||||
/** Logs variadic messages to stdout.
|
||||
*
|
||||
* @param lvl The log level
|
||||
* @param fmt The format string (printf alike)
|
||||
* @param va The variadic argument list (see stdarg.h)
|
||||
*/
|
||||
void log_vprint(struct log *l, const char *lvl, const char *fmt, va_list va);
|
||||
|
||||
/** Printf alike debug message with level. */
|
||||
void debug(long lvl, const char *fmt, ...)
|
||||
__attribute__ ((format(printf, 2, 3)));
|
||||
|
||||
/** Printf alike info message. */
|
||||
void info(const char *fmt, ...)
|
||||
__attribute__ ((format(printf, 1, 2)));
|
||||
|
||||
/** Printf alike warning message. */
|
||||
void warn(const char *fmt, ...)
|
||||
__attribute__ ((format(printf, 1, 2)));
|
||||
|
||||
/** Printf alike statistics message. */
|
||||
void stats(const char *fmt, ...)
|
||||
__attribute__ ((format(printf, 1, 2)));
|
||||
|
||||
/** Print error and exit. */
|
||||
void error(const char *fmt, ...)
|
||||
__attribute__ ((format(printf, 1, 2)));
|
||||
|
||||
/** Print error and strerror(errno). */
|
||||
void serror(const char *fmt, ...)
|
||||
__attribute__ ((format(printf, 1, 2)));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
37
fpga/include/villas/log_config.h
Normal file
37
fpga/include/villas/log_config.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/** Logging routines that depend on jansson.
|
||||
*
|
||||
* @file
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct log;
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
/** Parse logging configuration. */
|
||||
int log_parse(struct log *l, json_t *cfg);
|
||||
|
||||
/** Print configuration error and exit. */
|
||||
void jerror(json_error_t *err, const char *fmt, ...)
|
||||
__attribute__ ((format(printf, 2, 3)));
|
82
fpga/include/villas/plugin.h
Normal file
82
fpga/include/villas/plugin.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
/** Loadable / plugin support.
|
||||
*
|
||||
* @file
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utils.h"
|
||||
#include "fpga/ip.h"
|
||||
|
||||
/** @todo This is ugly as hell and broken on OS X / Clang anyway. */
|
||||
#define REGISTER_PLUGIN(p) \
|
||||
__attribute__((constructor(110))) static void UNIQUE(__ctor)() {\
|
||||
if (plugins.state == STATE_DESTROYED) \
|
||||
list_init(&plugins); \
|
||||
list_push(&plugins, p); \
|
||||
} \
|
||||
__attribute__((destructor(110))) static void UNIQUE(__dtor)() { \
|
||||
if (plugins.state != STATE_DESTROYED) \
|
||||
list_remove(&plugins, p); \
|
||||
}
|
||||
|
||||
extern struct list plugins;
|
||||
|
||||
enum plugin_type {
|
||||
PLUGIN_TYPE_FPGA_IP,
|
||||
};
|
||||
|
||||
struct plugin {
|
||||
const char *name;
|
||||
const char *description;
|
||||
void *handle;
|
||||
char *path;
|
||||
|
||||
enum plugin_type type;
|
||||
|
||||
enum state state;
|
||||
|
||||
int (*load)(struct plugin *p);
|
||||
int (*unload)(struct plugin *p);
|
||||
|
||||
struct fpga_ip_type ip;
|
||||
};
|
||||
|
||||
/** Return a pointer to the plugin structure */
|
||||
#define plugin(vt) ((struct plugin *) ((char *) (vt) - offsetof(struct plugin, api)))
|
||||
|
||||
#define plugin_name(vt) plugin(vt)->name
|
||||
#define plugin_description(vt) plugin(vt)->description
|
||||
|
||||
int plugin_init(struct plugin *p);
|
||||
|
||||
int plugin_destroy(struct plugin *p);
|
||||
|
||||
int plugin_parse(struct plugin *p, json_t *cfg);
|
||||
|
||||
int plugin_load(struct plugin *p);
|
||||
|
||||
int plugin_unload(struct plugin *p);
|
||||
|
||||
void plugin_dump(enum plugin_type type);
|
||||
|
||||
/** Find registered and loaded plugin with given name and type. */
|
||||
struct plugin * plugin_lookup(enum plugin_type type, const char *name);
|
276
fpga/include/villas/utils.h
Normal file
276
fpga/include/villas/utils.h
Normal file
|
@ -0,0 +1,276 @@
|
|||
/** Various helper functions.
|
||||
*
|
||||
* @file
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <sched.h>
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define LIKELY(x) __builtin_expect((x),1)
|
||||
#define UNLIKELY(x) __builtin_expect((x),0)
|
||||
#else
|
||||
#define LIKELY(x) (x)
|
||||
#define UNLIKELY(x) (x)
|
||||
#endif
|
||||
|
||||
/* Some color escape codes for pretty log messages */
|
||||
#define CLR(clr, str) "\e[" XSTR(clr) "m" str "\e[0m"
|
||||
#define CLR_GRY(str) CLR(30, str) /**< Print str in gray */
|
||||
#define CLR_RED(str) CLR(31, str) /**< Print str in red */
|
||||
#define CLR_GRN(str) CLR(32, str) /**< Print str in green */
|
||||
#define CLR_YEL(str) CLR(33, str) /**< Print str in yellow */
|
||||
#define CLR_BLU(str) CLR(34, str) /**< Print str in blue */
|
||||
#define CLR_MAG(str) CLR(35, str) /**< Print str in magenta */
|
||||
#define CLR_CYN(str) CLR(36, str) /**< Print str in cyan */
|
||||
#define CLR_WHT(str) CLR(37, str) /**< Print str in white */
|
||||
#define CLR_BLD(str) CLR( 1, str) /**< Print str in bold */
|
||||
|
||||
/* Alternate character set
|
||||
*
|
||||
* The suffixed of the BOX_ macro a constructed by
|
||||
* combining the following letters in the written order:
|
||||
* - U for a line facing upwards
|
||||
* - D for a line facing downwards
|
||||
* - L for a line facing leftwards
|
||||
* - R for a line facing rightwards
|
||||
*
|
||||
* E.g. a cross can be constructed by combining all line fragments:
|
||||
* BOX_UDLR
|
||||
*/
|
||||
#define BOX(chr) "\e(0" chr "\e(B"
|
||||
#define BOX_LR BOX("\x71") /**< Boxdrawing: ─ */
|
||||
#define BOX_UD BOX("\x78") /**< Boxdrawing: │ */
|
||||
#define BOX_UDR BOX("\x74") /**< Boxdrawing: ├ */
|
||||
#define BOX_UDLR BOX("\x6E") /**< Boxdrawing: ┼ */
|
||||
#define BOX_UDL BOX("\x75") /**< Boxdrawing: ┤ */
|
||||
#define BOX_ULR BOX("\x76") /**< Boxdrawing: ┴ */
|
||||
#define BOX_UL BOX("\x6A") /**< Boxdrawing: ┘ */
|
||||
#define BOX_DLR BOX("\x77") /**< Boxdrawing: ┘ */
|
||||
#define BOX_DL BOX("\x6B") /**< Boxdrawing: ┘ */
|
||||
|
||||
/* CPP stringification */
|
||||
#define XSTR(x) STR(x)
|
||||
#define STR(x) #x
|
||||
|
||||
#define CONCAT_DETAIL(x, y) x##y
|
||||
#define CONCAT(x, y) CONCAT_DETAIL(x, y)
|
||||
#define UNIQUE(x) CONCAT(x, __COUNTER__)
|
||||
|
||||
#define ALIGN(x, a) ALIGN_MASK(x, (uintptr_t) (a) - 1)
|
||||
#define ALIGN_MASK(x, m) (((uintptr_t) (x) + (m)) & ~(m))
|
||||
#define IS_ALIGNED(x, a) (ALIGN(x, a) == (uintptr_t) x)
|
||||
|
||||
#define SWAP(x,y) do { \
|
||||
__auto_type _x = x; \
|
||||
__auto_type _y = y; \
|
||||
x = _y; \
|
||||
y = _x; \
|
||||
} while(0)
|
||||
|
||||
/** Round-up integer division */
|
||||
#define CEIL(x, y) (((x) + (y) - 1) / (y))
|
||||
|
||||
/** Get nearest up-rounded power of 2 */
|
||||
#define LOG2_CEIL(x) (1 << (log2i((x) - 1) + 1))
|
||||
|
||||
/** Check if the number is a power of 2 */
|
||||
#define IS_POW2(x) (((x) != 0) && !((x) & ((x) - 1)))
|
||||
|
||||
/** Calculate the number of elements in an array. */
|
||||
#define ARRAY_LEN(a) ( sizeof (a) / sizeof (a)[0] )
|
||||
|
||||
/* Return the bigger value */
|
||||
#define MAX(a, b) ({ __typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a > _b ? _a : _b; })
|
||||
|
||||
/* Return the smaller value */
|
||||
#define MIN(a, b) ({ __typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a < _b ? _a : _b; })
|
||||
|
||||
#ifndef offsetof
|
||||
#define offsetof(type, member) __builtin_offsetof(type, member)
|
||||
#endif
|
||||
|
||||
#ifndef container_of
|
||||
#define container_of(ptr, type, member) ({ const typeof( ((type *) 0)->member ) *__mptr = (ptr); \
|
||||
(type *) ( (char *) __mptr - offsetof(type, member) ); \
|
||||
})
|
||||
#endif
|
||||
|
||||
#define BITS_PER_LONGLONG (sizeof(long long) * 8)
|
||||
|
||||
/* Some helper macros */
|
||||
#define BITMASK(h, l) (((~0ULL) << (l)) & (~0ULL >> (BITS_PER_LONGLONG - 1 - (h))))
|
||||
#define BIT(nr) (1UL << (nr))
|
||||
|
||||
/* Forward declarations */
|
||||
struct timespec;
|
||||
|
||||
/** Print copyright message to stdout. */
|
||||
void print_copyright();
|
||||
|
||||
/** Print version to stdout. */
|
||||
void print_version();
|
||||
|
||||
/** Normal random variate generator using the Box-Muller method
|
||||
*
|
||||
* @param m Mean
|
||||
* @param s Standard deviation
|
||||
* @return Normal variate random variable (Gaussian)
|
||||
*/
|
||||
double box_muller(float m, float s);
|
||||
|
||||
/** Double precission uniform random variable */
|
||||
double randf();
|
||||
|
||||
/** Concat formatted string to an existing string.
|
||||
*
|
||||
* This function uses realloc() to resize the destination.
|
||||
* Please make sure to only on dynamic allocated destionations!!!
|
||||
*
|
||||
* @param dest A pointer to a malloc() allocated memory region
|
||||
* @param fmt A format string like for printf()
|
||||
* @param ... Optional parameters like for printf()
|
||||
* @retval The the new value of the dest buffer.
|
||||
*/
|
||||
char * strcatf(char **dest, const char *fmt, ...)
|
||||
__attribute__ ((format(printf, 2, 3)));
|
||||
|
||||
/** Variadic version of strcatf() */
|
||||
char * vstrcatf(char **dest, const char *fmt, va_list va)
|
||||
__attribute__ ((format(printf, 2, 0)));
|
||||
|
||||
/** Format string like strcatf() just starting with empty string */
|
||||
#define strf(fmt, ...) strcatf(&(char *) { NULL }, fmt, ##__VA_ARGS__)
|
||||
#define vstrf(fmt, va) vstrcatf(&(char *) { NULL }, fmt, va)
|
||||
|
||||
#ifdef __linux__
|
||||
/** Convert integer to cpu_set_t.
|
||||
*
|
||||
* @param set An integer number which is used as the mask
|
||||
* @param cset A pointer to the cpu_set_t datastructure
|
||||
*/
|
||||
void cpuset_from_integer(uintmax_t set, cpu_set_t *cset);
|
||||
|
||||
/** Convert cpu_set_t to an integer. */
|
||||
void cpuset_to_integer(cpu_set_t *cset, uintmax_t *set);
|
||||
|
||||
/** Parses string with list of CPU ranges.
|
||||
*
|
||||
* From: https://github.com/mmalecki/util-linux/blob/master/lib/cpuset.c
|
||||
*
|
||||
* @retval 0 On success.
|
||||
* @retval 1 On error.
|
||||
* @retval 2 If fail is set and a cpu number passed in the list doesn't fit
|
||||
* into the cpu_set. If fail is not set cpu numbers that do not fit are
|
||||
* ignored and 0 is returned instead.
|
||||
*/
|
||||
int cpulist_parse(const char *str, cpu_set_t *set, int fail);
|
||||
|
||||
/** Returns human readable representation of the cpuset.
|
||||
*
|
||||
* From: https://github.com/mmalecki/util-linux/blob/master/lib/cpuset.c
|
||||
*
|
||||
* The output format is a list of CPUs with ranges (for example, "0,1,3-9").
|
||||
*/
|
||||
char * cpulist_create(char *str, size_t len, cpu_set_t *set);
|
||||
#endif
|
||||
|
||||
/** Allocate and initialize memory. */
|
||||
void * alloc(size_t bytes);
|
||||
|
||||
/** Allocate and copy memory. */
|
||||
void * memdup(const void *src, size_t bytes);
|
||||
|
||||
/** Call quit() in the main thread. */
|
||||
void die();
|
||||
|
||||
/** Used by version_parse(), version_compare() */
|
||||
struct version {
|
||||
int major;
|
||||
int minor;
|
||||
};
|
||||
|
||||
/** Compare two versions. */
|
||||
int version_cmp(struct version *a, struct version *b);
|
||||
|
||||
/** Parse a dotted version string. */
|
||||
int version_parse(const char *s, struct version *v);
|
||||
|
||||
/** Check assertion and exit if failed. */
|
||||
#ifndef assert
|
||||
#define assert(exp) do { \
|
||||
if (!EXPECT(exp, 0)) \
|
||||
error("Assertion failed: '%s' in %s(), %s:%d", \
|
||||
XSTR(exp), __FUNCTION__, __BASE_FILE__, __LINE__); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/** Fill buffer with random data */
|
||||
ssize_t read_random(char *buf, size_t len);
|
||||
|
||||
/** Get CPU timestep counter */
|
||||
__attribute__((always_inline)) static inline uint64_t rdtsc()
|
||||
{
|
||||
uint64_t tsc;
|
||||
|
||||
__asm__ ("rdtsc;"
|
||||
"shl $32, %%rdx;"
|
||||
"or %%rdx,%%rax"
|
||||
: "=a" (tsc)
|
||||
:
|
||||
: "%rcx", "%rdx", "memory");
|
||||
|
||||
return tsc;
|
||||
}
|
||||
|
||||
/** Get log2 of long long integers */
|
||||
static inline int log2i(long long x) {
|
||||
if (x == 0)
|
||||
return 1;
|
||||
|
||||
return sizeof(x) * 8 - __builtin_clzll(x) - 1;
|
||||
}
|
||||
|
||||
/** Sleep with rdtsc */
|
||||
void rdtsc_sleep(uint64_t nanosecs, uint64_t start);
|
||||
|
||||
/** Register a exit callback for program termination: SIGINT, SIGKILL & SIGALRM. */
|
||||
int signals_init(void (*cb)(int signal, siginfo_t *sinfo, void *ctx));
|
||||
|
||||
/** Send signal \p sig to main thread. */
|
||||
void killme(int sig);
|
||||
|
||||
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);
|
53
fpga/lib/CMakeLists.txt
Normal file
53
fpga/lib/CMakeLists.txt
Normal file
|
@ -0,0 +1,53 @@
|
|||
set(SOURCES
|
||||
ip.c
|
||||
vlnv.c
|
||||
card.c
|
||||
|
||||
ips/timer.c
|
||||
ips/model.c
|
||||
ips/switch.c
|
||||
ips/dft.c
|
||||
ips/fifo.c
|
||||
ips/dma.c
|
||||
ips/intc.c
|
||||
ips/rtds_axis.c
|
||||
|
||||
kernel/kernel.c
|
||||
kernel/pci.c
|
||||
kernel/vfio.c
|
||||
|
||||
plugin.c
|
||||
utils.c
|
||||
list.c
|
||||
log.c
|
||||
log_config.c
|
||||
log_helper.c
|
||||
)
|
||||
|
||||
include(FindPkgConfig)
|
||||
|
||||
pkg_check_modules(JANSSON jansson)
|
||||
pkg_check_modules(XIL libxil)
|
||||
|
||||
find_package(Threads)
|
||||
|
||||
add_library(villas-fpga ${SOURCES})
|
||||
|
||||
target_compile_definitions(villas-fpga PRIVATE
|
||||
BUILDID=\"abc\"
|
||||
_GNU_SOURCE
|
||||
)
|
||||
|
||||
target_include_directories(villas-fpga PUBLIC
|
||||
../include/villas
|
||||
${XIL_INCLUDE_DIRS}
|
||||
${JANSSON_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(villas-fpga PUBLIC
|
||||
${XIL_LIBRARIES}
|
||||
${JANSSON_LIBRARIES}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${CMAKE_DL_LIBS}
|
||||
m
|
||||
)
|
313
fpga/lib/card.c
Normal file
313
fpga/lib/card.c
Normal file
|
@ -0,0 +1,313 @@
|
|||
/** 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 "config.h"
|
||||
#include "log.h"
|
||||
#include "log_config.h"
|
||||
#include "list.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "kernel/pci.h"
|
||||
#include "kernel/vfio.h"
|
||||
|
||||
#include "fpga/ip.h"
|
||||
#include "fpga/card.h"
|
||||
|
||||
int fpga_card_init(struct fpga_card *c, struct pci *pci, struct vfio_container *vc)
|
||||
{
|
||||
assert(c->state = STATE_DESTROYED);
|
||||
|
||||
c->vfio_container = vc;
|
||||
c->pci = pci;
|
||||
|
||||
list_init(&c->ips);
|
||||
|
||||
/* Default values */
|
||||
c->filter.id.vendor = FPGA_PCI_VID_XILINX;
|
||||
c->filter.id.device = FPGA_PCI_PID_VFPGA;
|
||||
|
||||
c->affinity = 0;
|
||||
c->do_reset = 0;
|
||||
|
||||
c->state = STATE_INITIALIZED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fpga_card_parse(struct fpga_card *c, json_t *cfg, const char *name)
|
||||
{
|
||||
int ret;
|
||||
|
||||
json_t *json_ips;
|
||||
json_t *json_slot = NULL;
|
||||
json_t *json_id = NULL;
|
||||
json_error_t err;
|
||||
|
||||
c->name = strdup(name);
|
||||
|
||||
ret = json_unpack_ex(cfg, &err, 0, "{ s?: i, s?: b, s?: o, s?: o, s: o }",
|
||||
"affinity", &c->affinity,
|
||||
"do_reset", &c->do_reset,
|
||||
"slot", &json_slot,
|
||||
"id", &json_id,
|
||||
"ips", &json_ips
|
||||
);
|
||||
if (ret)
|
||||
jerror(&err, "Failed to parse FPGA vard configuration");
|
||||
|
||||
if (json_slot) {
|
||||
const char *err, *slot;
|
||||
|
||||
slot = json_string_value(json_slot);
|
||||
if (slot) {
|
||||
ret = pci_device_parse_slot(&c->filter, slot, &err);
|
||||
if (ret)
|
||||
error("Failed to parse PCI slot: %s", err);
|
||||
}
|
||||
else
|
||||
error("PCI slot must be a string");
|
||||
}
|
||||
|
||||
if (json_id) {
|
||||
const char *err, *id;
|
||||
|
||||
id = json_string_value(json_id);
|
||||
if (id) {
|
||||
ret = pci_device_parse_id(&c->filter, (char*) id, &err);
|
||||
if (ret)
|
||||
error("Failed to parse PCI id: %s", err);
|
||||
}
|
||||
else
|
||||
error("PCI ID must be a string");
|
||||
}
|
||||
|
||||
if (!json_is_object(json_ips))
|
||||
error("FPGA card IPs section must be an object");
|
||||
|
||||
const char *name_ip;
|
||||
json_t *json_ip;
|
||||
json_object_foreach(json_ips, name_ip, json_ip) {
|
||||
const char *vlnv;
|
||||
|
||||
struct fpga_ip_type *vt;
|
||||
struct fpga_ip *ip = (struct fpga_ip *) alloc(sizeof(struct fpga_ip));
|
||||
|
||||
ip->card = c;
|
||||
|
||||
ret = json_unpack_ex(json_ip, &err, 0, "{ s: s }", "vlnv", &vlnv);
|
||||
if (ret)
|
||||
error("Failed to parse FPGA IP '%s' of card '%s'", name_ip, name);
|
||||
|
||||
vt = fpga_ip_type_lookup(vlnv);
|
||||
if (!vt)
|
||||
error("FPGA IP core VLNV identifier '%s' is invalid", vlnv);
|
||||
|
||||
ret = fpga_ip_init(ip, vt);
|
||||
if (ret)
|
||||
error("Failed to initalize FPGA IP core");
|
||||
|
||||
ret = fpga_ip_parse(ip, json_ip, name_ip);
|
||||
if (ret)
|
||||
error("Failed to parse FPGA IP core");
|
||||
|
||||
list_push(&c->ips, ip);
|
||||
}
|
||||
|
||||
c->state = STATE_PARSED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fpga_card_parse_list(struct list *cards, json_t *cfg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!json_is_object(cfg))
|
||||
error("FPGA card configuration section must be a JSON object");
|
||||
|
||||
const char *name;
|
||||
json_t *json_fpga;
|
||||
json_object_foreach(cfg, name, json_fpga) {
|
||||
struct fpga_card *c = (struct fpga_card *) alloc(sizeof(struct fpga_card));
|
||||
|
||||
ret = fpga_card_parse(c, json_fpga, name);
|
||||
if (ret)
|
||||
error("Failed to parse FPGA card configuration");
|
||||
|
||||
list_push(cards, c);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fpga_card_start(struct fpga_card *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct pci_device *pdev;
|
||||
|
||||
assert(c->state == STATE_CHECKED);
|
||||
|
||||
/* Search for FPGA card */
|
||||
pdev = pci_lookup_device(c->pci, &c->filter);
|
||||
if (!pdev)
|
||||
error("Failed to find PCI device");
|
||||
|
||||
/* Attach PCIe card to VFIO container */
|
||||
ret = vfio_pci_attach(&c->vfio_device, c->vfio_container, pdev);
|
||||
if (ret)
|
||||
error("Failed to attach VFIO device");
|
||||
|
||||
/* Map PCIe BAR */
|
||||
c->map = vfio_map_region(&c->vfio_device, VFIO_PCI_BAR0_REGION_INDEX);
|
||||
if (c->map == MAP_FAILED)
|
||||
serror("Failed to mmap() BAR0");
|
||||
|
||||
/* Enable memory access and PCI bus mastering for DMA */
|
||||
ret = vfio_pci_enable(&c->vfio_device);
|
||||
if (ret)
|
||||
serror("Failed to enable PCI device");
|
||||
|
||||
/* Reset system? */
|
||||
if (c->do_reset) {
|
||||
/* Reset / detect PCI device */
|
||||
ret = vfio_pci_reset(&c->vfio_device);
|
||||
if (ret)
|
||||
serror("Failed to reset PCI device");
|
||||
|
||||
ret = fpga_card_reset(c);
|
||||
if (ret)
|
||||
error("Failed to reset FGPA card");
|
||||
}
|
||||
|
||||
/* Initialize IP cores */
|
||||
for (size_t j = 0; j < list_length(&c->ips); j++) {
|
||||
struct fpga_ip *i = (struct fpga_ip *) list_at(&c->ips, j);
|
||||
|
||||
ret = fpga_ip_start(i);
|
||||
if (ret)
|
||||
error("Failed to initalize FPGA IP core: %s (%u)", i->name, ret);
|
||||
}
|
||||
|
||||
c->state = STATE_STARTED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fpga_card_stop(struct fpga_card *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(c->state == STATE_STOPPED);
|
||||
|
||||
for (size_t j = 0; j < list_length(&c->ips); j++) {
|
||||
struct fpga_ip *i = (struct fpga_ip *) list_at(&c->ips, j);
|
||||
|
||||
ret = fpga_ip_stop(i);
|
||||
if (ret)
|
||||
error("Failed to stop FPGA IP core: %s (%u)", i->name, ret);
|
||||
}
|
||||
|
||||
c->state = STATE_STOPPED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fpga_card_dump(struct fpga_card *c)
|
||||
{
|
||||
info("VILLASfpga card:");
|
||||
{ INDENT
|
||||
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("BAR0 mapped at %p", c->map);
|
||||
|
||||
info("IP blocks:");
|
||||
for (size_t j = 0; j < list_length(&c->ips); j++) { INDENT
|
||||
struct fpga_ip *i = (struct fpga_ip *) list_at(&c->ips, j);
|
||||
|
||||
fpga_ip_dump(i);
|
||||
}
|
||||
}
|
||||
|
||||
vfio_dump(c->vfio_device.group->container);
|
||||
}
|
||||
|
||||
int fpga_card_check(struct fpga_card *c)
|
||||
{
|
||||
/* Check FPGA configuration */
|
||||
c->reset = fpga_vlnv_lookup(&c->ips, &(struct fpga_vlnv) { "xilinx.com", "ip", "axi_gpio", NULL });
|
||||
if (!c->reset)
|
||||
error("FPGA is missing a reset controller");
|
||||
|
||||
c->intc = fpga_vlnv_lookup(&c->ips, &(struct fpga_vlnv) { "acs.eonerc.rwth-aachen.de", "user", "axi_pcie_intc", NULL });
|
||||
if (!c->intc)
|
||||
error("FPGA is missing a interrupt controller");
|
||||
|
||||
c->sw = fpga_vlnv_lookup(&c->ips, &(struct fpga_vlnv) { "xilinx.com", "ip", "axis_interconnect", NULL });
|
||||
if (!c->sw)
|
||||
warn("FPGA is missing an AXI4-Stream switch");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fpga_card_destroy(struct fpga_card *c)
|
||||
{
|
||||
list_destroy(&c->ips, (dtor_cb_t) fpga_ip_destroy, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fpga_card_reset(struct fpga_card *c)
|
||||
{
|
||||
int ret;
|
||||
char state[4096];
|
||||
|
||||
/* Save current state of PCI configuration space */
|
||||
ret = pread(c->vfio_device.fd, state, sizeof(state), (off_t) VFIO_PCI_CONFIG_REGION_INDEX << 40);
|
||||
if (ret != sizeof(state))
|
||||
return -1;
|
||||
|
||||
uint32_t *rst_reg = (uint32_t *) (c->map + c->reset->baseaddr);
|
||||
|
||||
debug(3, "FPGA: reset");
|
||||
rst_reg[0] = 1;
|
||||
|
||||
usleep(100000);
|
||||
|
||||
/* Restore previous state of PCI configuration space */
|
||||
ret = pwrite(c->vfio_device.fd, state, sizeof(state), (off_t) VFIO_PCI_CONFIG_REGION_INDEX << 40);
|
||||
if (ret != sizeof(state))
|
||||
return -1;
|
||||
|
||||
/* After reset the value should be zero again */
|
||||
if (rst_reg[0])
|
||||
return -2;
|
||||
|
||||
c->state = STATE_INITIALIZED;
|
||||
|
||||
return 0;
|
||||
}
|
167
fpga/lib/ip.c
Normal file
167
fpga/lib/ip.c
Normal file
|
@ -0,0 +1,167 @@
|
|||
/** 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.h"
|
||||
#include "plugin.h"
|
||||
|
||||
int fpga_ip_init(struct fpga_ip *c, struct fpga_ip_type *vt)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(c->state == STATE_DESTROYED);
|
||||
|
||||
c->_vt = vt;
|
||||
c->_vd = alloc(vt->size);
|
||||
|
||||
ret = c->_vt->init ? c->_vt->init(c) : 0;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
c->state = STATE_INITIALIZED;
|
||||
|
||||
debug(8, "IP Core %s initalized (%u)", c->name, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fpga_ip_parse(struct fpga_ip *c, json_t *cfg, const char *name)
|
||||
{
|
||||
int ret, baseaddr = -1;
|
||||
|
||||
assert(c->state != STATE_STARTED && c->state != STATE_DESTROYED);
|
||||
|
||||
c->name = strdup(name);
|
||||
c->baseaddr = -1;
|
||||
c->irq = -1;
|
||||
c->port = -1;
|
||||
|
||||
json_error_t err;
|
||||
|
||||
ret = json_unpack_ex(cfg, &err, 0, "{ s?: i, s?: i, s?: i }",
|
||||
"baseaddr", &baseaddr,
|
||||
"irq", &c->irq,
|
||||
"port", &c->port
|
||||
);
|
||||
if (ret)
|
||||
jerror(&err, "Failed to parse configuration for FPGA IP '%s'", name);
|
||||
|
||||
c->baseaddr = baseaddr;
|
||||
|
||||
/* Type sepecific settings */
|
||||
ret = c->_vt && c->_vt->parse ? c->_vt->parse(c, cfg) : 0;
|
||||
if (ret)
|
||||
error("Failed to parse settings for IP core '%s'", name);
|
||||
|
||||
c->state = STATE_PARSED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fpga_ip_start(struct fpga_ip *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(c->state == STATE_CHECKED);
|
||||
|
||||
ret = c->_vt->start ? c->_vt->start(c) : 0;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
c->state = STATE_STARTED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fpga_ip_stop(struct fpga_ip *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(c->state == STATE_STARTED);
|
||||
|
||||
ret = c->_vt->stop ? c->_vt->stop(c) : 0;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
c->state = STATE_STOPPED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fpga_ip_destroy(struct fpga_ip *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(c->state != STATE_DESTROYED);
|
||||
|
||||
fpga_vlnv_destroy(&c->vlnv);
|
||||
|
||||
ret = c->_vt->destroy ? c->_vt->destroy(c) : 0;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
c->state = STATE_DESTROYED;
|
||||
|
||||
free(c->_vd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fpga_ip_reset(struct fpga_ip *c)
|
||||
{
|
||||
debug(3, "Reset IP core: %s", c->name);
|
||||
|
||||
return c->_vt->reset ? c->_vt->reset(c) : 0;
|
||||
}
|
||||
|
||||
void fpga_ip_dump(struct fpga_ip *c)
|
||||
{
|
||||
assert(c->state != STATE_DESTROYED);
|
||||
|
||||
info("IP %s: vlnv=%s:%s:%s:%s baseaddr=%#jx, irq=%d, port=%d",
|
||||
c->name, c->vlnv.vendor, c->vlnv.library, c->vlnv.name, c->vlnv.version,
|
||||
c->baseaddr, c->irq, c->port);
|
||||
|
||||
if (c->_vt->dump)
|
||||
c->_vt->dump(c);
|
||||
}
|
||||
|
||||
struct fpga_ip_type * fpga_ip_type_lookup(const char *vstr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct fpga_vlnv vlnv;
|
||||
|
||||
ret = fpga_vlnv_parse(&vlnv, vstr);
|
||||
if (ret)
|
||||
return NULL;
|
||||
|
||||
/* Try to find matching IP type */
|
||||
for (size_t i = 0; i < list_length(&plugins); i++) {
|
||||
struct plugin *p = (struct plugin *) list_at(&plugins, i);
|
||||
|
||||
if (p->type == PLUGIN_TYPE_FPGA_IP && !fpga_vlnv_cmp(&vlnv, &p->ip.vlnv))
|
||||
return &p->ip;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
140
fpga/lib/ips/dft.c
Normal file
140
fpga/lib/ips/dft.c
Normal file
|
@ -0,0 +1,140 @@
|
|||
/** Moving window / Recursive DFT implementation based on HLS
|
||||
*
|
||||
* @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 "log.h"
|
||||
#include "log_config.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#include "fpga/ip.h"
|
||||
#include "fpga/card.h"
|
||||
#include "fpga/ips/dft.h"
|
||||
|
||||
int dft_parse(struct fpga_ip *c, json_t *cfg)
|
||||
{
|
||||
struct dft *dft = (struct dft *) c->_vd;
|
||||
|
||||
int ret;
|
||||
|
||||
json_t *json_harms;
|
||||
json_error_t err;
|
||||
|
||||
ret = json_unpack_ex(cfg, &err, 0, "{ s: i, s: i, s: o }",
|
||||
"period", &dft->period,
|
||||
"decimation", &dft->decimation,
|
||||
"harmonics", &json_harms
|
||||
);
|
||||
if (ret)
|
||||
jerror(&err, "Failed to parse configuration of FPGA IP '%s'", c->name);
|
||||
|
||||
if (!json_is_array(json_harms))
|
||||
error("DFT IP core requires 'harmonics' to be an array of integers!");
|
||||
|
||||
dft->num_harmonics = json_array_size(json_harms);
|
||||
if (dft->num_harmonics <= 0)
|
||||
error("DFT IP core requires 'harmonics' to contain at least 1 value!");
|
||||
|
||||
dft->fharmonics = alloc(sizeof(float) * dft->num_harmonics);
|
||||
|
||||
size_t index;
|
||||
json_t *json_harm;
|
||||
json_array_foreach(json_harms, index, json_harm) {
|
||||
if (!json_is_real(json_harm))
|
||||
error("DFT IP core requires all 'harmonics' values to be of floating point type");
|
||||
|
||||
dft->fharmonics[index] = (float) json_number_value(json_harm) / dft->period;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dft_start(struct fpga_ip *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct fpga_card *f = c->card;
|
||||
struct dft *dft = (struct dft *) c->_vd;
|
||||
|
||||
XHls_dft *xdft = &dft->inst;
|
||||
XHls_dft_Config xdft_cfg = {
|
||||
.Ctrl_BaseAddress = (uintptr_t) f->map + c->baseaddr
|
||||
};
|
||||
|
||||
ret = XHls_dft_CfgInitialize(xdft, &xdft_cfg);
|
||||
if (ret != XST_SUCCESS)
|
||||
return ret;
|
||||
|
||||
int max_harmonics = XHls_dft_Get_fharmonics_TotalBytes(xdft) / sizeof(dft->fharmonics[0]);
|
||||
|
||||
if (dft->num_harmonics > max_harmonics)
|
||||
error("DFT IP core supports a maximum of %u harmonics", max_harmonics);
|
||||
|
||||
XHls_dft_Set_num_harmonics_V(xdft, dft->num_harmonics);
|
||||
|
||||
XHls_dft_Set_decimation_V(xdft, dft->decimation);
|
||||
|
||||
memcpy((void *) (uintptr_t) XHls_dft_Get_fharmonics_BaseAddress(xdft), dft->fharmonics, dft->num_harmonics * sizeof(dft->fharmonics[0]));
|
||||
|
||||
XHls_dft_EnableAutoRestart(xdft);
|
||||
XHls_dft_Start(xdft);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dft_stop(struct fpga_ip *c)
|
||||
{
|
||||
struct dft *dft = (struct dft *) c->_vd;
|
||||
|
||||
XHls_dft *xdft = &dft->inst;
|
||||
|
||||
XHls_dft_DisableAutoRestart(xdft);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dft_destroy(struct fpga_ip *c)
|
||||
{
|
||||
struct dft *dft = (struct dft *) c->_vd;
|
||||
|
||||
if (dft->fharmonics) {
|
||||
free(dft->fharmonics);
|
||||
dft->fharmonics = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct plugin p = {
|
||||
.name = "Discrete Fourier Transform",
|
||||
.description = "Perfom Discrete Fourier Transforms with variable number of harmonics on the FPGA",
|
||||
.type = PLUGIN_TYPE_FPGA_IP,
|
||||
.ip = {
|
||||
.vlnv = { "acs.eonerc.rwth-aachen.de", "hls", "hls_dft", NULL },
|
||||
.type = FPGA_IP_TYPE_MATH,
|
||||
.start = dft_start,
|
||||
.stop = dft_stop,
|
||||
.destroy = dft_destroy,
|
||||
.parse = dft_parse,
|
||||
.size = sizeof(struct dft)
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_PLUGIN(&p)
|
657
fpga/lib/ips/dma.c
Normal file
657
fpga/lib/ips/dma.c
Normal file
|
@ -0,0 +1,657 @@
|
|||
/** DMA related helper functions
|
||||
*
|
||||
* These functions present a simpler interface to Xilinx' DMA driver (XAxiDma_*)
|
||||
*
|
||||
* @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 <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "plugin.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "fpga/ip.h"
|
||||
#include "fpga/card.h"
|
||||
#include "fpga/ips/dma.h"
|
||||
|
||||
int dma_mem_split(struct dma_mem *o, struct dma_mem *a, struct dma_mem *b)
|
||||
{
|
||||
int split = o->len / 2;
|
||||
|
||||
a->base_virt = o->base_virt;
|
||||
a->base_phys = o->base_phys;
|
||||
|
||||
b->base_virt = a->base_virt + split;
|
||||
b->base_phys = a->base_phys + split;
|
||||
|
||||
a->len = split;
|
||||
b->len = o->len - split;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dma_alloc(struct fpga_ip *c, struct dma_mem *mem, size_t len, int flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct fpga_card *f = c->card;
|
||||
|
||||
/* Align to next bigger page size chunk */
|
||||
if (len & 0xFFF) {
|
||||
len += 0x1000;
|
||||
len &= ~0xFFF;
|
||||
}
|
||||
|
||||
mem->len = len;
|
||||
mem->base_phys = (void *) -1; /* find free */
|
||||
mem->base_virt = mmap(0, mem->len, PROT_READ | PROT_WRITE, flags | MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, 0, 0);
|
||||
if (mem->base_virt == MAP_FAILED)
|
||||
return -1;
|
||||
|
||||
ret = vfio_map_dma(f->vfio_device.group->container, (uint64_t) mem->base_virt, (uint64_t) mem->base_phys, mem->len);
|
||||
if (ret)
|
||||
return -2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dma_free(struct fpga_ip *c, struct dma_mem *mem)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = vfio_unmap_dma(c->card->vfio_device.group->container, (uint64_t) mem->base_virt, (uint64_t) mem->base_phys, mem->len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = munmap(mem->base_virt, mem->len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dma_ping_pong(struct fpga_ip *c, char *src, char *dst, size_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = dma_read(c, dst, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dma_write(c, src, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dma_write_complete(c, NULL, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dma_read_complete(c, NULL, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dma_write(struct fpga_ip *c, char *buf, size_t len)
|
||||
{
|
||||
struct dma *dma = (struct dma *) c->_vd;
|
||||
|
||||
XAxiDma *xdma = &dma->inst;
|
||||
|
||||
debug(25, "DMA write: dmac=%s buf=%p len=%#zx", c->name, buf, len);
|
||||
|
||||
return xdma->HasSg
|
||||
? dma_sg_write(c, buf, len)
|
||||
: dma_simple_write(c, buf, len);
|
||||
}
|
||||
|
||||
int dma_read(struct fpga_ip *c, char *buf, size_t len)
|
||||
{
|
||||
struct dma *dma = (struct dma *) c->_vd;
|
||||
|
||||
XAxiDma *xdma = &dma->inst;
|
||||
|
||||
debug(25, "DMA read: dmac=%s buf=%p len=%#zx", c->name, buf, len);
|
||||
|
||||
return xdma->HasSg
|
||||
? dma_sg_read(c, buf, len)
|
||||
: dma_simple_read(c, buf, len);
|
||||
}
|
||||
|
||||
int dma_read_complete(struct fpga_ip *c, char **buf, size_t *len)
|
||||
{
|
||||
struct dma *dma = (struct dma *) c->_vd;
|
||||
|
||||
XAxiDma *xdma = &dma->inst;
|
||||
|
||||
debug(25, "DMA read complete: dmac=%s", c->name);
|
||||
|
||||
return xdma->HasSg
|
||||
? dma_sg_read_complete(c, buf, len)
|
||||
: dma_simple_read_complete(c, buf, len);
|
||||
}
|
||||
|
||||
int dma_write_complete(struct fpga_ip *c, char **buf, size_t *len)
|
||||
{
|
||||
struct dma *dma = (struct dma *) c->_vd;
|
||||
|
||||
XAxiDma *xdma = &dma->inst;
|
||||
|
||||
debug(25, "DMA write complete: dmac=%s", c->name);
|
||||
|
||||
return xdma->HasSg
|
||||
? dma_sg_write_complete(c, buf, len)
|
||||
: dma_simple_write_complete(c, buf, len);
|
||||
}
|
||||
|
||||
int dma_sg_write(struct fpga_ip *c, char *buf, size_t len)
|
||||
{
|
||||
int ret, bdcnt;
|
||||
|
||||
struct dma *dma = (struct dma *) c->_vd;
|
||||
|
||||
XAxiDma *xdma = &dma->inst;
|
||||
XAxiDma_BdRing *ring = XAxiDma_GetTxRing(xdma);
|
||||
XAxiDma_Bd *bds, *bd;
|
||||
|
||||
uint32_t remaining, bdlen, bdbuf, cr;
|
||||
|
||||
/* Checks */
|
||||
if (!xdma->HasSg)
|
||||
return -1;
|
||||
|
||||
if (len < 1)
|
||||
return -2;
|
||||
|
||||
if (!xdma->HasMm2S)
|
||||
return -3;
|
||||
|
||||
if (!ring->HasDRE) {
|
||||
uint32_t mask = xdma->MicroDmaMode ? XAXIDMA_MICROMODE_MIN_BUF_ALIGN : ring->DataWidth - 1;
|
||||
if ((uintptr_t) buf & mask)
|
||||
return -4;
|
||||
}
|
||||
|
||||
bdcnt = CEIL(len, FPGA_DMA_BOUNDARY);
|
||||
ret = XAxiDma_BdRingAlloc(ring, bdcnt, &bds);
|
||||
if (ret != XST_SUCCESS)
|
||||
return -5;
|
||||
|
||||
remaining = len;
|
||||
bdbuf = (uintptr_t) buf;
|
||||
bd = bds;
|
||||
for (int i = 0; i < bdcnt; i++) {
|
||||
bdlen = MIN(remaining, FPGA_DMA_BOUNDARY);
|
||||
|
||||
ret = XAxiDma_BdSetBufAddr(bd, bdbuf);
|
||||
if (ret != XST_SUCCESS)
|
||||
goto out;
|
||||
|
||||
ret = XAxiDma_BdSetLength(bd, bdlen, ring->MaxTransferLen);
|
||||
if (ret != XST_SUCCESS)
|
||||
goto out;
|
||||
|
||||
/* Set SOF / EOF / ID */
|
||||
cr = 0;
|
||||
if (i == 0)
|
||||
cr |= XAXIDMA_BD_CTRL_TXSOF_MASK;
|
||||
if (i == bdcnt - 1)
|
||||
cr |= XAXIDMA_BD_CTRL_TXEOF_MASK;
|
||||
|
||||
XAxiDma_BdSetCtrl(bd, cr);
|
||||
XAxiDma_BdSetId(bd, (uintptr_t) buf);
|
||||
|
||||
remaining -= bdlen;
|
||||
bdbuf += bdlen;
|
||||
bd = (XAxiDma_Bd *) XAxiDma_BdRingNext(ring, bd);
|
||||
}
|
||||
|
||||
/* Give the BD to DMA to kick off the transmission. */
|
||||
ret = XAxiDma_BdRingToHw(ring, bdcnt, bds);
|
||||
if (ret != XST_SUCCESS)
|
||||
return -8;
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
ret = XAxiDma_BdRingUnAlloc(ring, bdcnt, bds);
|
||||
if (ret != XST_SUCCESS)
|
||||
return -6;
|
||||
|
||||
return -5;
|
||||
}
|
||||
|
||||
int dma_sg_read(struct fpga_ip *c, char *buf, size_t len)
|
||||
{
|
||||
int ret, bdcnt;
|
||||
|
||||
struct dma *dma = (struct dma *) c->_vd;
|
||||
|
||||
XAxiDma *xdma = &dma->inst;
|
||||
XAxiDma_BdRing *ring = XAxiDma_GetRxRing(xdma);
|
||||
XAxiDma_Bd *bds, *bd;
|
||||
|
||||
uint32_t remaining, bdlen, bdbuf;
|
||||
|
||||
/* Checks */
|
||||
if (!xdma->HasSg)
|
||||
return -1;
|
||||
|
||||
if (len < 1)
|
||||
return -2;
|
||||
|
||||
if (!xdma->HasS2Mm)
|
||||
return -3;
|
||||
|
||||
if (!ring->HasDRE) {
|
||||
uint32_t mask = xdma->MicroDmaMode ? XAXIDMA_MICROMODE_MIN_BUF_ALIGN : ring->DataWidth - 1;
|
||||
if ((uintptr_t) buf & mask)
|
||||
return -4;
|
||||
}
|
||||
|
||||
bdcnt = CEIL(len, FPGA_DMA_BOUNDARY);
|
||||
ret = XAxiDma_BdRingAlloc(ring, bdcnt, &bds);
|
||||
if (ret != XST_SUCCESS)
|
||||
return -5;
|
||||
|
||||
bdbuf = (uintptr_t) buf;
|
||||
remaining = len;
|
||||
bd = bds;
|
||||
for (int i = 0; i < bdcnt; i++) {
|
||||
bdlen = MIN(remaining, FPGA_DMA_BOUNDARY);
|
||||
ret = XAxiDma_BdSetLength(bd, bdlen, ring->MaxTransferLen);
|
||||
if (ret != XST_SUCCESS)
|
||||
goto out;
|
||||
|
||||
ret = XAxiDma_BdSetBufAddr(bd, bdbuf);
|
||||
if (ret != XST_SUCCESS)
|
||||
goto out;
|
||||
|
||||
/* Receive BDs do not need to set anything for the control
|
||||
* The hardware will set the SOF/EOF bits per stream ret */
|
||||
XAxiDma_BdSetCtrl(bd, 0);
|
||||
XAxiDma_BdSetId(bd, (uintptr_t) buf);
|
||||
|
||||
remaining -= bdlen;
|
||||
bdbuf += bdlen;
|
||||
bd = (XAxiDma_Bd *) XAxiDma_BdRingNext(ring, bd);
|
||||
}
|
||||
|
||||
ret = XAxiDma_BdRingToHw(ring, bdcnt, bds);
|
||||
if (ret != XST_SUCCESS)
|
||||
return -8;
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
ret = XAxiDma_BdRingUnAlloc(ring, bdcnt, bds);
|
||||
if (ret != XST_SUCCESS)
|
||||
return -6;
|
||||
|
||||
return -5;
|
||||
}
|
||||
|
||||
int dma_sg_write_complete(struct fpga_ip *c, char **buf, size_t *len)
|
||||
{
|
||||
struct dma *dma = (struct dma *) c->_vd;
|
||||
|
||||
XAxiDma *xdma = &dma->inst;
|
||||
XAxiDma_BdRing *ring = XAxiDma_GetTxRing(xdma);
|
||||
XAxiDma_Bd *bds;
|
||||
|
||||
int processed, ret;
|
||||
|
||||
/* Wait until the one BD TX transaction is done */
|
||||
while (!(XAxiDma_IntrGetIrq(xdma, XAXIDMA_DMA_TO_DEVICE) & XAXIDMA_IRQ_IOC_MASK))
|
||||
intc_wait(c->card->intc, c->irq);
|
||||
XAxiDma_IntrAckIrq(xdma, XAXIDMA_IRQ_IOC_MASK, XAXIDMA_DMA_TO_DEVICE);
|
||||
|
||||
processed = XAxiDma_BdRingFromHw(ring, XAXIDMA_ALL_BDS, &bds);
|
||||
|
||||
if (len != NULL)
|
||||
*len = XAxiDma_BdGetActualLength(bds, XAXIDMA_MAX_TRANSFER_LEN);
|
||||
|
||||
if (buf != NULL)
|
||||
*buf = (char *) (uintptr_t) XAxiDma_BdGetId(bds);
|
||||
|
||||
/* Free all processed TX BDs for future transmission */
|
||||
ret = XAxiDma_BdRingFree(ring, processed, bds);
|
||||
if (ret != XST_SUCCESS)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dma_sg_read_complete(struct fpga_ip *c, char **buf, size_t *len)
|
||||
{
|
||||
struct dma *dma = (struct dma *) c->_vd;
|
||||
|
||||
XAxiDma *xdma = &dma->inst;
|
||||
XAxiDma_BdRing *ring = XAxiDma_GetRxRing(xdma);
|
||||
XAxiDma_Bd *bds, *bd;
|
||||
|
||||
int ret, bdcnt;
|
||||
uint32_t recvlen, sr;
|
||||
uintptr_t recvbuf = 0;
|
||||
|
||||
if (!xdma->HasSg)
|
||||
return -1;
|
||||
|
||||
while (!(XAxiDma_IntrGetIrq(xdma, XAXIDMA_DEVICE_TO_DMA) & XAXIDMA_IRQ_IOC_MASK))
|
||||
intc_wait(c->card->intc, c->irq + 1);
|
||||
XAxiDma_IntrAckIrq(xdma, XAXIDMA_IRQ_IOC_MASK, XAXIDMA_DEVICE_TO_DMA);
|
||||
|
||||
bdcnt = XAxiDma_BdRingFromHw(ring, XAXIDMA_ALL_BDS, &bds);
|
||||
|
||||
recvlen = 0;
|
||||
|
||||
bd = bds;
|
||||
for (int i = 0; i < bdcnt; i++) {
|
||||
recvlen += XAxiDma_BdGetActualLength(bd, ring->MaxTransferLen);
|
||||
|
||||
sr = XAxiDma_BdGetSts(bd);
|
||||
if (sr & XAXIDMA_BD_STS_RXSOF_MASK)
|
||||
if (i != 0)
|
||||
warn("sof not first");
|
||||
|
||||
if (sr & XAXIDMA_BD_STS_RXEOF_MASK)
|
||||
if (i != bdcnt - 1)
|
||||
warn("eof not last");
|
||||
|
||||
recvbuf = XAxiDma_BdGetId(bd);
|
||||
|
||||
bd = (XAxiDma_Bd *) XAxiDma_BdRingNext(ring, bd);
|
||||
}
|
||||
|
||||
if (len != NULL)
|
||||
*len = recvlen;
|
||||
if (buf != NULL)
|
||||
*buf = (char *) recvbuf;
|
||||
|
||||
/* Free all processed RX BDs for future transmission */
|
||||
ret = XAxiDma_BdRingFree(ring, bdcnt, bds);
|
||||
if (ret != XST_SUCCESS)
|
||||
return -3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dma_simple_read(struct fpga_ip *c, char *buf, size_t len)
|
||||
{
|
||||
struct dma *dma = (struct dma *) c->_vd;
|
||||
|
||||
XAxiDma *xdma = &dma->inst;
|
||||
XAxiDma_BdRing *ring = XAxiDma_GetRxRing(xdma);
|
||||
|
||||
/* Checks */
|
||||
if (xdma->HasSg)
|
||||
return -1;
|
||||
|
||||
if ((len < 1) || (len > FPGA_DMA_BOUNDARY))
|
||||
return -2;
|
||||
|
||||
if (!xdma->HasS2Mm)
|
||||
return -3;
|
||||
|
||||
if (!ring->HasDRE) {
|
||||
uint32_t mask = xdma->MicroDmaMode ? XAXIDMA_MICROMODE_MIN_BUF_ALIGN : ring->DataWidth - 1;
|
||||
if ((uintptr_t) buf & mask)
|
||||
return -4;
|
||||
}
|
||||
|
||||
if(!(XAxiDma_ReadReg(ring->ChanBase, XAXIDMA_SR_OFFSET) & XAXIDMA_HALTED_MASK)) {
|
||||
if (XAxiDma_Busy(xdma, XAXIDMA_DEVICE_TO_DMA))
|
||||
return -5;
|
||||
}
|
||||
|
||||
XAxiDma_WriteReg(ring->ChanBase, XAXIDMA_DESTADDR_OFFSET, LOWER_32_BITS((uintptr_t) buf));
|
||||
if (xdma->AddrWidth > 32)
|
||||
XAxiDma_WriteReg(ring->ChanBase, XAXIDMA_DESTADDR_MSB_OFFSET, UPPER_32_BITS((uintptr_t) buf));
|
||||
|
||||
XAxiDma_WriteReg(ring->ChanBase, XAXIDMA_CR_OFFSET, XAxiDma_ReadReg(ring->ChanBase, XAXIDMA_CR_OFFSET) | XAXIDMA_CR_RUNSTOP_MASK);
|
||||
XAxiDma_WriteReg(ring->ChanBase, XAXIDMA_BUFFLEN_OFFSET, len);
|
||||
|
||||
return XST_SUCCESS;
|
||||
}
|
||||
|
||||
int dma_simple_write(struct fpga_ip *c, char *buf, size_t len)
|
||||
{
|
||||
struct dma *dma = (struct dma *) c->_vd;
|
||||
|
||||
XAxiDma *xdma = &dma->inst;
|
||||
XAxiDma_BdRing *ring = XAxiDma_GetTxRing(xdma);
|
||||
|
||||
/* Checks */
|
||||
if (xdma->HasSg)
|
||||
return -1;
|
||||
|
||||
if ((len < 1) || (len > FPGA_DMA_BOUNDARY))
|
||||
return -2;
|
||||
|
||||
if (!xdma->HasMm2S)
|
||||
return -3;
|
||||
|
||||
if (!ring->HasDRE) {
|
||||
uint32_t mask = xdma->MicroDmaMode ? XAXIDMA_MICROMODE_MIN_BUF_ALIGN : ring->DataWidth - 1;
|
||||
if ((uintptr_t) buf & mask)
|
||||
return -4;
|
||||
}
|
||||
|
||||
/* If the engine is doing transfer, cannot submit */
|
||||
if(!(XAxiDma_ReadReg(ring->ChanBase, XAXIDMA_SR_OFFSET) & XAXIDMA_HALTED_MASK)) {
|
||||
if (XAxiDma_Busy(xdma, XAXIDMA_DMA_TO_DEVICE))
|
||||
return -5;
|
||||
}
|
||||
|
||||
XAxiDma_WriteReg(ring->ChanBase, XAXIDMA_SRCADDR_OFFSET, LOWER_32_BITS((uintptr_t) buf));
|
||||
if (xdma->AddrWidth > 32)
|
||||
XAxiDma_WriteReg(ring->ChanBase, XAXIDMA_SRCADDR_MSB_OFFSET, UPPER_32_BITS((uintptr_t) buf));
|
||||
|
||||
XAxiDma_WriteReg(ring->ChanBase, XAXIDMA_CR_OFFSET, XAxiDma_ReadReg(ring->ChanBase, XAXIDMA_CR_OFFSET) | XAXIDMA_CR_RUNSTOP_MASK);
|
||||
XAxiDma_WriteReg(ring->ChanBase, XAXIDMA_BUFFLEN_OFFSET, len);
|
||||
|
||||
return XST_SUCCESS;
|
||||
}
|
||||
|
||||
int dma_simple_read_complete(struct fpga_ip *c, char **buf, size_t *len)
|
||||
{
|
||||
struct dma *dma = (struct dma *) c->_vd;
|
||||
|
||||
XAxiDma *xdma = &dma->inst;
|
||||
XAxiDma_BdRing *ring = XAxiDma_GetRxRing(xdma);
|
||||
|
||||
while (!(XAxiDma_IntrGetIrq(xdma, XAXIDMA_DEVICE_TO_DMA) & XAXIDMA_IRQ_IOC_MASK))
|
||||
intc_wait(c->card->intc, c->irq + 1);
|
||||
XAxiDma_IntrAckIrq(xdma, XAXIDMA_IRQ_IOC_MASK, XAXIDMA_DEVICE_TO_DMA);
|
||||
|
||||
if (len)
|
||||
*len = XAxiDma_ReadReg(ring->ChanBase, XAXIDMA_BUFFLEN_OFFSET);
|
||||
|
||||
if (buf) {
|
||||
*buf = (char *) (uintptr_t) XAxiDma_ReadReg(ring->ChanBase, XAXIDMA_DESTADDR_OFFSET);
|
||||
if (xdma->AddrWidth > 32)
|
||||
*buf += XAxiDma_ReadReg(ring->ChanBase, XAXIDMA_DESTADDR_MSB_OFFSET);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dma_simple_write_complete(struct fpga_ip *c, char **buf, size_t *len)
|
||||
{
|
||||
struct dma *dma = (struct dma *) c->_vd;
|
||||
|
||||
XAxiDma *xdma = &dma->inst;
|
||||
XAxiDma_BdRing *ring = XAxiDma_GetTxRing(xdma);
|
||||
|
||||
while (!(XAxiDma_IntrGetIrq(xdma, XAXIDMA_DMA_TO_DEVICE) & XAXIDMA_IRQ_IOC_MASK))
|
||||
intc_wait(c->card->intc, c->irq);
|
||||
XAxiDma_IntrAckIrq(xdma, XAXIDMA_IRQ_IOC_MASK, XAXIDMA_DMA_TO_DEVICE);
|
||||
|
||||
if (len)
|
||||
*len = XAxiDma_ReadReg(ring->ChanBase, XAXIDMA_BUFFLEN_OFFSET);
|
||||
|
||||
if (buf) {
|
||||
*buf = (char *) (uintptr_t) XAxiDma_ReadReg(ring->ChanBase, XAXIDMA_SRCADDR_OFFSET);
|
||||
if (xdma->AddrWidth > 32)
|
||||
*buf += XAxiDma_ReadReg(ring->ChanBase, XAXIDMA_SRCADDR_MSB_OFFSET);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dma_setup_ring(XAxiDma_BdRing *ring, struct dma_mem *bdbuf)
|
||||
{
|
||||
int delay = 0;
|
||||
int coalesce = 1;
|
||||
int ret, cnt;
|
||||
|
||||
XAxiDma_Bd clearbd;
|
||||
|
||||
/* Disable all RX interrupts before RxBD space setup */
|
||||
XAxiDma_BdRingIntDisable(ring, XAXIDMA_IRQ_ALL_MASK);
|
||||
|
||||
/* Set delay and coalescing */
|
||||
XAxiDma_BdRingSetCoalesce(ring, coalesce, delay);
|
||||
|
||||
/* Setup Rx BD space */
|
||||
cnt = XAxiDma_BdRingCntCalc(XAXIDMA_BD_MINIMUM_ALIGNMENT, bdbuf->len);
|
||||
|
||||
ret = XAxiDma_BdRingCreate(ring, (uintptr_t) bdbuf->base_phys, (uintptr_t) bdbuf->base_virt, XAXIDMA_BD_MINIMUM_ALIGNMENT, cnt);
|
||||
if (ret != XST_SUCCESS)
|
||||
return -1;
|
||||
|
||||
XAxiDma_BdClear(&clearbd);
|
||||
ret = XAxiDma_BdRingClone(ring, &clearbd);
|
||||
if (ret != XST_SUCCESS)
|
||||
return -2;
|
||||
|
||||
/* Start the channel */
|
||||
ret = XAxiDma_BdRingStart(ring);
|
||||
if (ret != XST_SUCCESS)
|
||||
return -3;
|
||||
|
||||
return XST_SUCCESS;
|
||||
}
|
||||
|
||||
static int dma_init_rings(XAxiDma *xdma, struct dma_mem *bd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct dma_mem bd_rx, bd_tx;
|
||||
|
||||
ret = dma_mem_split(bd, &bd_rx, &bd_tx);
|
||||
if (ret)
|
||||
return -1;
|
||||
|
||||
ret = dma_setup_ring(XAxiDma_GetRxRing(xdma), &bd_rx);
|
||||
if (ret != XST_SUCCESS)
|
||||
return -2;
|
||||
|
||||
ret = dma_setup_ring(XAxiDma_GetTxRing(xdma), &bd_tx);
|
||||
if (ret != XST_SUCCESS)
|
||||
return -3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dma_start(struct fpga_ip *c)
|
||||
{
|
||||
int ret, sg;
|
||||
struct dma *dma = (struct dma *) c->_vd;
|
||||
|
||||
XAxiDma *xdma = &dma->inst;
|
||||
|
||||
/* Guess DMA type */
|
||||
sg = (XAxiDma_In32((uintptr_t) c->card->map + c->baseaddr + XAXIDMA_TX_OFFSET+ XAXIDMA_SR_OFFSET) &
|
||||
XAxiDma_In32((uintptr_t) c->card->map + c->baseaddr + XAXIDMA_RX_OFFSET+ XAXIDMA_SR_OFFSET) & XAXIDMA_SR_SGINCL_MASK) ? 1 : 0;
|
||||
|
||||
XAxiDma_Config xdma_cfg = {
|
||||
.BaseAddr = (uintptr_t) c->card->map + c->baseaddr,
|
||||
.HasStsCntrlStrm = 0,
|
||||
.HasMm2S = 1,
|
||||
.HasMm2SDRE = 1,
|
||||
.Mm2SDataWidth = 128,
|
||||
.HasS2Mm = 1,
|
||||
.HasS2MmDRE = 1, /* Data Realignment Engine */
|
||||
.HasSg = sg,
|
||||
.S2MmDataWidth = 128,
|
||||
.Mm2sNumChannels = 1,
|
||||
.S2MmNumChannels = 1,
|
||||
.Mm2SBurstSize = 64,
|
||||
.S2MmBurstSize = 64,
|
||||
.MicroDmaMode = 0,
|
||||
.AddrWidth = 32
|
||||
};
|
||||
|
||||
ret = XAxiDma_CfgInitialize(xdma, &xdma_cfg);
|
||||
if (ret != XST_SUCCESS)
|
||||
return -1;
|
||||
|
||||
/* Perform selftest */
|
||||
ret = XAxiDma_Selftest(xdma);
|
||||
if (ret != XST_SUCCESS)
|
||||
return -2;
|
||||
|
||||
/* Map buffer descriptors */
|
||||
if (xdma->HasSg) {
|
||||
ret = dma_alloc(c, &dma->bd, FPGA_DMA_BD_SIZE, 0);
|
||||
if (ret)
|
||||
return -3;
|
||||
|
||||
ret = dma_init_rings(xdma, &dma->bd);
|
||||
if (ret)
|
||||
return -4;
|
||||
}
|
||||
|
||||
/* Enable completion interrupts for both channels */
|
||||
XAxiDma_IntrEnable(xdma, XAXIDMA_IRQ_IOC_MASK, XAXIDMA_DMA_TO_DEVICE);
|
||||
XAxiDma_IntrEnable(xdma, XAXIDMA_IRQ_IOC_MASK, XAXIDMA_DEVICE_TO_DMA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dma_reset(struct fpga_ip *c)
|
||||
{
|
||||
struct dma *dma = (struct dma *) c->_vd;
|
||||
|
||||
XAxiDma_Reset(&dma->inst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct plugin p = {
|
||||
.name = "Xilinx's AXI4 Direct Memory Access Controller",
|
||||
.description = "Transfer data streams between VILLASnode and VILLASfpga",
|
||||
.type = PLUGIN_TYPE_FPGA_IP,
|
||||
.ip = {
|
||||
.vlnv = { "xilinx.com", "ip", "axi_dma", NULL },
|
||||
.type = FPGA_IP_TYPE_DM_DMA,
|
||||
.init = dma_start,
|
||||
.reset = dma_reset,
|
||||
.size = sizeof(struct dma)
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_PLUGIN(&p)
|
153
fpga/lib/ips/fifo.c
Normal file
153
fpga/lib/ips/fifo.c
Normal file
|
@ -0,0 +1,153 @@
|
|||
/** FIFO related helper functions
|
||||
*
|
||||
* These functions present a simpler interface to Xilinx' FIFO driver (XLlFifo_*)
|
||||
*
|
||||
* @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 "utils.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#include "fpga/ip.h"
|
||||
#include "fpga/card.h"
|
||||
#include "fpga/ips/fifo.h"
|
||||
#include "fpga/ips/intc.h"
|
||||
|
||||
int fifo_start(struct fpga_ip *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct fpga_card *f = c->card;
|
||||
struct fifo *fifo = (struct fifo *) c->_vd;
|
||||
|
||||
XLlFifo *xfifo = &fifo->inst;
|
||||
XLlFifo_Config fifo_cfg = {
|
||||
.BaseAddress = (uintptr_t) f->map + c->baseaddr,
|
||||
.Axi4BaseAddress = (uintptr_t) c->card->map + fifo->baseaddr_axi4,
|
||||
.Datainterface = (fifo->baseaddr_axi4 != -1) ? 1 : 0 /* use AXI4 for Data, AXI4-Lite for control */
|
||||
};
|
||||
|
||||
ret = XLlFifo_CfgInitialize(xfifo, &fifo_cfg, (uintptr_t) c->card->map + c->baseaddr);
|
||||
if (ret != XST_SUCCESS)
|
||||
return -1;
|
||||
|
||||
XLlFifo_IntEnable(xfifo, XLLF_INT_RC_MASK); /* Receive complete IRQ */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fifo_stop(struct fpga_ip *c)
|
||||
{
|
||||
struct fifo *fifo = (struct fifo *) c->_vd;
|
||||
|
||||
XLlFifo *xfifo = &fifo->inst;
|
||||
|
||||
XLlFifo_IntDisable(xfifo, XLLF_INT_RC_MASK); /* Receive complete IRQ */
|
||||
|
||||
return 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;
|
||||
|
||||
uint32_t tdfv;
|
||||
|
||||
tdfv = XLlFifo_TxVacancy(xllfifo);
|
||||
if (tdfv < len)
|
||||
return -1;
|
||||
|
||||
XLlFifo_Write(xllfifo, buf, len);
|
||||
XLlFifo_TxSetLen(xllfifo, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static struct plugin p = {
|
||||
.name = "Xilinx's AXI4 FIFO data mover",
|
||||
.description = "",
|
||||
.type = PLUGIN_TYPE_FPGA_IP,
|
||||
.ip = {
|
||||
.vlnv = { "xilinx.com", "ip", "axi_fifo_mm_s", NULL },
|
||||
.type = FPGA_IP_TYPE_DM_FIFO,
|
||||
.start = fifo_start,
|
||||
.stop = fifo_stop,
|
||||
.parse = fifo_parse,
|
||||
.reset = fifo_reset,
|
||||
.size = sizeof(struct fifo)
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_PLUGIN(&p)
|
180
fpga/lib/ips/intc.c
Normal file
180
fpga/lib/ips/intc.c
Normal file
|
@ -0,0 +1,180 @@
|
|||
/** 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.h"
|
||||
|
||||
#include "kernel/vfio.h"
|
||||
#include "kernel/kernel.h"
|
||||
|
||||
#include "fpga/ip.h"
|
||||
#include "fpga/card.h"
|
||||
#include "fpga/ips/intc.h"
|
||||
|
||||
int intc_start(struct fpga_ip *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct fpga_card *f = c->card;
|
||||
struct intc *intc = (struct intc *) c->_vd;
|
||||
|
||||
uintptr_t base = (uintptr_t) f->map + c->baseaddr;
|
||||
|
||||
if (c != f->intc)
|
||||
error("There can be only one interrupt controller per FPGA");
|
||||
|
||||
intc->num_irqs = vfio_pci_msi_init(&f->vfio_device, intc->efds);
|
||||
if (intc->num_irqs < 0)
|
||||
return -1;
|
||||
|
||||
ret = vfio_pci_msi_find(&f->vfio_device, intc->nos);
|
||||
if (ret)
|
||||
return -2;
|
||||
|
||||
/* For each IRQ */
|
||||
for (int i = 0; i < intc->num_irqs; i++) {
|
||||
/* Pin to core */
|
||||
ret = kernel_irq_setaffinity(intc->nos[i], f->affinity, NULL);
|
||||
if (ret)
|
||||
serror("Failed to change affinity of VFIO-MSI interrupt");
|
||||
|
||||
/* 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);
|
||||
|
||||
debug(4, "FPGA: enabled interrupts");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int intc_destroy(struct fpga_ip *c)
|
||||
{
|
||||
struct fpga_card *f = c->card;
|
||||
struct intc *intc = (struct intc *) c->_vd;
|
||||
|
||||
vfio_pci_msi_deinit(&f->vfio_device, intc->efds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int intc_enable(struct fpga_ip *c, uint32_t mask, int flags)
|
||||
{
|
||||
struct fpga_card *f = c->card;
|
||||
struct intc *intc = (struct intc *) c->_vd;
|
||||
|
||||
uint32_t ier, imr;
|
||||
uintptr_t base = (uintptr_t) f->map + c->baseaddr;
|
||||
|
||||
/* Current state of INTC */
|
||||
ier = XIntc_In32(base + XIN_IER_OFFSET);
|
||||
imr = XIntc_In32(base + XIN_IMR_OFFSET);
|
||||
|
||||
/* Clear pending IRQs */
|
||||
XIntc_Out32(base + XIN_IAR_OFFSET, mask);
|
||||
|
||||
for (int i = 0; i < intc->num_irqs; i++) {
|
||||
if (mask & (1 << i))
|
||||
intc->flags[i] = flags;
|
||||
}
|
||||
|
||||
if (flags & INTC_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);
|
||||
}
|
||||
|
||||
debug(3, "New ier = %#x", XIntc_In32(base + XIN_IER_OFFSET));
|
||||
debug(3, "New imr = %#x", XIntc_In32(base + XIN_IMR_OFFSET));
|
||||
debug(3, "New isr = %#x", XIntc_In32(base + XIN_ISR_OFFSET));
|
||||
|
||||
debug(8, "FPGA: Interupt enabled: mask=%#x flags=%#x", mask, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int intc_disable(struct fpga_ip *c, uint32_t mask)
|
||||
{
|
||||
struct fpga_card *f = c->card;
|
||||
|
||||
uintptr_t base = (uintptr_t) f->map + c->baseaddr;
|
||||
uint32_t ier = XIntc_In32(base + XIN_IER_OFFSET);
|
||||
|
||||
XIntc_Out32(base + XIN_IER_OFFSET, ier & ~mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t intc_wait(struct fpga_ip *c, int irq)
|
||||
{
|
||||
struct fpga_card *f = c->card;
|
||||
struct intc *intc = (struct intc *) c->_vd;
|
||||
|
||||
uintptr_t base = (uintptr_t) f->map + c->baseaddr;
|
||||
|
||||
if (intc->flags[irq] & INTC_POLLING) {
|
||||
uint32_t isr, mask = 1 << irq;
|
||||
|
||||
do {
|
||||
isr = XIntc_In32(base + XIN_ISR_OFFSET);
|
||||
pthread_testcancel();
|
||||
} while ((isr & mask) != mask);
|
||||
|
||||
XIntc_Out32(base + XIN_IAR_OFFSET, mask);
|
||||
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
uint64_t cnt;
|
||||
ssize_t ret = read(intc->efds[irq], &cnt, sizeof(cnt));
|
||||
if (ret != sizeof(cnt))
|
||||
return 0;
|
||||
|
||||
return cnt;
|
||||
}
|
||||
}
|
||||
|
||||
static struct plugin p = {
|
||||
.name = "Xilinx's programmable interrupt controller",
|
||||
.description = "",
|
||||
.type = PLUGIN_TYPE_FPGA_IP,
|
||||
.ip = {
|
||||
.vlnv = { "acs.eonerc.rwth-aachen.de", "user", "axi_pcie_intc", NULL },
|
||||
.type = FPGA_IP_TYPE_MISC,
|
||||
.start = intc_start,
|
||||
.destroy = intc_destroy,
|
||||
.size = sizeof(struct intc)
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_PLUGIN(&p)
|
427
fpga/lib/ips/model.c
Normal file
427
fpga/lib/ips/model.c
Normal file
|
@ -0,0 +1,427 @@
|
|||
/** Interface to Xilinx System Generator Models via PCIe
|
||||
*
|
||||
* @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 <stdint.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "log.h"
|
||||
#include "log_config.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#include "fpga/ip.h"
|
||||
#include "fpga/card.h"
|
||||
#include "fpga/ips/model.h"
|
||||
|
||||
static int model_parameter_destroy(struct model_parameter *p)
|
||||
{
|
||||
free(p->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int model_info_destroy(struct model_info *i)
|
||||
{
|
||||
free(i->field);
|
||||
free(i->value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t model_xsg_map_checksum(uint32_t *map, size_t len)
|
||||
{
|
||||
uint32_t chks = 0;
|
||||
|
||||
for (int i = 2; i < len-1; i++)
|
||||
chks += map[i];
|
||||
|
||||
return chks; /* moduluo 2^32 because of overflow */
|
||||
}
|
||||
|
||||
static int model_xsg_map_parse(uint32_t *map, size_t len, struct list *parameters, struct list *infos)
|
||||
{
|
||||
#define copy_string(off) strndup((char *) (data + (off)), (length - (off)) * 4);
|
||||
int j;
|
||||
struct model_info *i;
|
||||
|
||||
/* Check magic */
|
||||
if (map[0] != XSG_MAGIC)
|
||||
error("Invalid magic: %#x", map[0]);
|
||||
|
||||
for (j = 2; j < len-1;) {
|
||||
uint16_t type = map[j] & 0xFFFF;
|
||||
uint16_t length = map[j] >> 16;
|
||||
uint32_t *data = &map[j+1];
|
||||
|
||||
switch (type) {
|
||||
case XSG_BLOCK_GATEWAY_IN:
|
||||
case XSG_BLOCK_GATEWAY_OUT:
|
||||
if (length < 4)
|
||||
break; /* block is to small to describe a gateway */
|
||||
|
||||
struct model_parameter *e, *p = (struct model_parameter *) alloc(sizeof(struct model_parameter));
|
||||
|
||||
p->name = copy_string(3);
|
||||
p->default_value.flt = *((float *) &data[1]);
|
||||
p->offset = data[2];
|
||||
p->direction = type & 0x1;
|
||||
p->type = (data[0] >> 0) & 0xFF;
|
||||
p->binpt = (data[0] >> 8) & 0xFF;
|
||||
|
||||
e = list_lookup(parameters, p->name);
|
||||
if (e)
|
||||
model_parameter_update(e, p);
|
||||
else
|
||||
list_push(parameters, p);
|
||||
break;
|
||||
|
||||
case XSG_BLOCK_INFO:
|
||||
i = alloc(sizeof(struct model_info));
|
||||
|
||||
i->field = copy_string(0);
|
||||
i->value = copy_string((int) ceil((double) (strlen(i->field) + 1) / 4))
|
||||
|
||||
list_push(infos, i);
|
||||
break;
|
||||
|
||||
default:
|
||||
warn("Unknown block type: %#06x", type);
|
||||
}
|
||||
|
||||
j += length + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
#undef copy_string
|
||||
}
|
||||
|
||||
static uint32_t model_xsg_map_read_word(uint32_t offset, void *baseaddr)
|
||||
{
|
||||
volatile uint32_t *addr = baseaddr + 0x00;
|
||||
volatile uint32_t *data = baseaddr + 0x04;
|
||||
|
||||
*addr = offset; /* Update addr reg */
|
||||
|
||||
return *data; /* Read data reg */
|
||||
}
|
||||
|
||||
static int model_xsg_map_read(uint32_t *map, size_t len, void *baseaddr)
|
||||
{
|
||||
size_t maplen;
|
||||
uint32_t magic;
|
||||
|
||||
/* Check magic */
|
||||
magic = model_xsg_map_read_word(0, baseaddr);
|
||||
if (magic != XSG_MAGIC)
|
||||
return -1;
|
||||
|
||||
maplen = model_xsg_map_read_word(1, baseaddr);
|
||||
if (maplen < 3)
|
||||
return -2;
|
||||
|
||||
/* Read Data */
|
||||
int i;
|
||||
for (i = 0; i < MIN(maplen, len); i++)
|
||||
map[i] = model_xsg_map_read_word(i, baseaddr);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int model_parse(struct fpga_ip *c, json_t *cfg)
|
||||
{
|
||||
struct model *m = (struct model *) c->_vd;
|
||||
|
||||
int ret;
|
||||
|
||||
json_t *json_params;
|
||||
json_error_t err;
|
||||
|
||||
if (strcmp(c->vlnv.library, "hls") == 0)
|
||||
m->type = MODEL_TYPE_HLS;
|
||||
else if (strcmp(c->vlnv.library, "sysgen") == 0)
|
||||
m->type = MODEL_TYPE_XSG;
|
||||
else
|
||||
error("Unsupported model type: %s", c->vlnv.library);
|
||||
|
||||
ret = json_unpack_ex(cfg, &err, 0, "{ s?: o }", "parameters", &json_params);
|
||||
if (ret)
|
||||
jerror(&err, "Failed to parse configuration of FPGA IP '%s'", c->name);
|
||||
|
||||
if (json_params) {
|
||||
if (!json_is_object(json_params))
|
||||
error("Setting 'parameters' must be a JSON object");
|
||||
|
||||
const char *name;
|
||||
json_t *value;
|
||||
json_object_foreach(json_params, name, value) {
|
||||
if (!json_is_real(value))
|
||||
error("Parameters of FPGA IP '%s' must be of type floating point", c->name);
|
||||
|
||||
struct model_parameter *p = (struct model_parameter *) alloc(sizeof(struct model_parameter));
|
||||
|
||||
p->name = strdup(name);
|
||||
p->default_value.flt = json_real_value(value);
|
||||
|
||||
list_push(&m->parameters, p);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int model_init_from_xsg_map(struct model *m, void *baseaddr)
|
||||
{
|
||||
int ret, chks;
|
||||
|
||||
if (baseaddr == (void *) -1)
|
||||
return -1;
|
||||
|
||||
m->xsg.map = alloc(XSG_MAPLEN);
|
||||
m->xsg.maplen = model_xsg_map_read(m->xsg.map, XSG_MAPLEN, baseaddr);
|
||||
if (m->xsg.maplen < 0)
|
||||
return -1;
|
||||
|
||||
debug(5, "XSG: memory map length = %#zx", m->xsg.maplen);
|
||||
|
||||
chks = m->xsg.map[m->xsg.maplen - 1];
|
||||
if (chks != model_xsg_map_checksum(m->xsg.map, m->xsg.maplen))
|
||||
return -2;
|
||||
|
||||
ret = model_xsg_map_parse(m->xsg.map, m->xsg.maplen, &m->parameters, &m->infos);
|
||||
if (ret)
|
||||
return -3;
|
||||
|
||||
debug(5, "XSG: Parsed %zu parameters and %zu model infos", list_length(&m->parameters), list_length(&m->infos));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int model_init(struct fpga_ip *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct model *m = (struct model *) c->_vd;
|
||||
|
||||
list_init(&m->parameters);
|
||||
list_init(&m->infos);
|
||||
|
||||
if (!fpga_vlnv_cmp(&c->vlnv, &(struct fpga_vlnv) { NULL, "sysgen", NULL, NULL }))
|
||||
ret = model_init_from_xsg_map(m, c->card->map + c->baseaddr);
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
/* Set default values for parameters */
|
||||
for (size_t i = 0; i < list_length(&m->parameters); i++) {
|
||||
struct model_parameter *p = (struct model_parameter *) list_at(&m->parameters, i);
|
||||
|
||||
p->ip = c;
|
||||
|
||||
if (p->direction == MODEL_PARAMETER_IN) {
|
||||
model_parameter_write(p, p->default_value.flt);
|
||||
info("Set parameter '%s' updated to default value: %f", p->name, p->default_value.flt);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret)
|
||||
error("Failed to init XSG model: %d", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int model_destroy(struct fpga_ip *c)
|
||||
{
|
||||
struct model *m = (struct model *) c->_vd;
|
||||
|
||||
list_destroy(&m->parameters, (dtor_cb_t) model_parameter_destroy, true);
|
||||
list_destroy(&m->infos, (dtor_cb_t) model_info_destroy, true);
|
||||
|
||||
if (m->xsg.map != NULL)
|
||||
free(m->xsg.map);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void model_dump(struct fpga_ip *c)
|
||||
{
|
||||
struct model *m = (struct model *) c->_vd;
|
||||
|
||||
const char *param_type[] = { "UFix", "Fix", "Float", "Boolean" };
|
||||
const char *parameter_dirs[] = { "In", "Out", "In/Out" };
|
||||
|
||||
{ INDENT
|
||||
info("Parameters:");
|
||||
for (size_t i = 0; i < list_length(&m->parameters); i++) { INDENT
|
||||
struct model_parameter *p = (struct model_parameter *) list_at(&m->parameters, i);
|
||||
|
||||
if (p->direction == MODEL_PARAMETER_IN)
|
||||
info("%#jx: %s (%s) = %.3f %s %u",
|
||||
p->offset,
|
||||
p->name,
|
||||
parameter_dirs[p->direction],
|
||||
p->default_value.flt,
|
||||
param_type[p->type],
|
||||
p->binpt
|
||||
);
|
||||
else if (p->direction == MODEL_PARAMETER_OUT)
|
||||
info("%#jx: %s (%s)",
|
||||
p->offset,
|
||||
p->name,
|
||||
parameter_dirs[p->direction]
|
||||
);
|
||||
}
|
||||
|
||||
info("Infos:");
|
||||
for (size_t j = 0; j < list_length(&m->infos); j++) { INDENT
|
||||
struct model_info *i = (struct model_info *) list_at(&m->infos, j);
|
||||
|
||||
info("%s: %s", i->field, i->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int model_parameter_read(struct model_parameter *p, double *v)
|
||||
{
|
||||
struct fpga_ip *c = p->ip;
|
||||
|
||||
union model_parameter_value *ptr = (union model_parameter_value *) (c->card->map + c->baseaddr + p->offset);
|
||||
|
||||
switch (p->type) {
|
||||
case MODEL_PARAMETER_TYPE_UFIX:
|
||||
*v = (double) ptr->ufix / (1 << p->binpt);
|
||||
break;
|
||||
|
||||
case MODEL_PARAMETER_TYPE_FIX:
|
||||
*v = (double) ptr->fix / (1 << p->binpt);
|
||||
break;
|
||||
|
||||
case MODEL_PARAMETER_TYPE_FLOAT:
|
||||
*v = (double) ptr->flt;
|
||||
break;
|
||||
|
||||
case MODEL_PARAMETER_TYPE_BOOLEAN:
|
||||
*v = (double) ptr->ufix ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int model_parameter_write(struct model_parameter *p, double v)
|
||||
{
|
||||
struct fpga_ip *c = p->ip;
|
||||
|
||||
union model_parameter_value *ptr = (union model_parameter_value *) (c->card->map + c->baseaddr + p->offset);
|
||||
|
||||
switch (p->type) {
|
||||
case MODEL_PARAMETER_TYPE_UFIX:
|
||||
ptr->ufix = (uint32_t) (v * (1 << p->binpt));
|
||||
break;
|
||||
|
||||
case MODEL_PARAMETER_TYPE_FIX:
|
||||
ptr->fix = (int32_t) (v * (1 << p->binpt));
|
||||
break;
|
||||
|
||||
case MODEL_PARAMETER_TYPE_FLOAT:
|
||||
ptr->flt = (float) v;
|
||||
break;
|
||||
|
||||
case MODEL_PARAMETER_TYPE_BOOLEAN:
|
||||
ptr->bol = (bool) v;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void model_parameter_add(struct fpga_ip *c, const char *name, enum model_parameter_direction dir, enum model_parameter_type type)
|
||||
{
|
||||
struct model *m = (struct model *) c->_vd;
|
||||
struct model_parameter *p = (struct model_parameter *) alloc(sizeof(struct model_parameter));
|
||||
|
||||
p->name = strdup(name);
|
||||
p->type = type;
|
||||
p->direction = dir;
|
||||
|
||||
list_push(&m->parameters, p);
|
||||
}
|
||||
|
||||
int model_parameter_remove(struct fpga_ip *c, const char *name)
|
||||
{
|
||||
struct model *m = (struct model *) c->_vd;
|
||||
struct model_parameter *p;
|
||||
|
||||
p = list_lookup(&m->parameters, name);
|
||||
if (!p)
|
||||
return -1;
|
||||
|
||||
list_remove(&m->parameters, p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int model_parameter_update(struct model_parameter *p, struct model_parameter *u)
|
||||
{
|
||||
if (strcmp(p->name, u->name) != 0)
|
||||
return -1;
|
||||
|
||||
p->direction = u->direction;
|
||||
p->type = u->type;
|
||||
p->binpt = u->binpt;
|
||||
p->offset = u->offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct plugin p_hls = {
|
||||
.name = "Xilinx High Level Synthesis (HLS) model",
|
||||
.description = "",
|
||||
.type = PLUGIN_TYPE_FPGA_IP,
|
||||
.ip = {
|
||||
.vlnv = { NULL, "hls", NULL, NULL },
|
||||
.type = FPGA_IP_TYPE_MODEL,
|
||||
.init = model_init,
|
||||
.destroy = model_destroy,
|
||||
.dump = model_dump,
|
||||
.parse = model_parse
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_PLUGIN(&p_hls)
|
||||
|
||||
static struct plugin p_sysgen = {
|
||||
.name = "Xilinx System Generator for DSP (XSG) model",
|
||||
.description = "",
|
||||
.type = PLUGIN_TYPE_FPGA_IP,
|
||||
.ip = {
|
||||
.vlnv = { NULL, "sysgen", NULL, NULL },
|
||||
.type = FPGA_IP_TYPE_MODEL,
|
||||
.init = model_init,
|
||||
.destroy = model_destroy,
|
||||
.dump = model_dump,
|
||||
.parse = model_parse,
|
||||
.size = sizeof(struct model)
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_PLUGIN(&p_sysgen)
|
80
fpga/lib/ips/rtds_axis.c
Normal file
80
fpga/lib/ips/rtds_axis.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
/** Driver for AXI Stream wrapper around RTDS_InterfaceModule (rtds_axis )
|
||||
*
|
||||
* @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 <stdint.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "utils.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#include "fpga/ip.h"
|
||||
#include "fpga/card.h"
|
||||
#include "fpga/ips/rtds_axis.h"
|
||||
|
||||
void rtds_axis_dump(struct fpga_ip *c)
|
||||
{
|
||||
/* Check RTDS_Axis registers */
|
||||
uint32_t *regs = (uint32_t *) (c->card->map + c->baseaddr);
|
||||
|
||||
uint32_t sr = regs[RTDS_AXIS_SR_OFFSET/4];
|
||||
info("RTDS AXI Stream interface details");
|
||||
{ INDENT
|
||||
info("RTDS status: %#08x", sr);
|
||||
{ INDENT
|
||||
info("Card detected: %s", sr & RTDS_AXIS_SR_CARDDETECTED ? CLR_GRN("yes") : CLR_RED("no"));
|
||||
info("Link up: %s", sr & RTDS_AXIS_SR_LINKUP ? CLR_GRN("yes") : CLR_RED("no"));
|
||||
info("TX queue full: %s", sr & RTDS_AXIS_SR_TX_FULL ? CLR_RED("yes") : CLR_GRN("no"));
|
||||
info("TX in progress: %s", sr & RTDS_AXIS_SR_TX_INPROGRESS ? CLR_YEL("yes") : "no");
|
||||
info("Case running: %s", sr & RTDS_AXIS_SR_CASE_RUNNING ? CLR_GRN("yes") : CLR_RED("no"));
|
||||
}
|
||||
|
||||
info("RTDS control: %#08x", regs[RTDS_AXIS_CR_OFFSET/4]);
|
||||
info("RTDS IRQ coalesc: %u", regs[RTDS_AXIS_COALESC_OFFSET/4]);
|
||||
info("RTDS IRQ version: %#06x", regs[RTDS_AXIS_VERSION_OFFSET/4]);
|
||||
info("RTDS IRQ multi-rate: %u", regs[RTDS_AXIS_MRATE/4]);
|
||||
|
||||
info("RTDS timestep counter: %lu", (uint64_t) regs[RTDS_AXIS_TSCNT_LOW_OFFSET/4] | (uint64_t) regs[RTDS_AXIS_TSCNT_HIGH_OFFSET/4] << 32);
|
||||
info("RTDS timestep period: %.3f uS", rtds_axis_dt(c) * 1e6);
|
||||
}
|
||||
}
|
||||
|
||||
double rtds_axis_dt(struct fpga_ip *c)
|
||||
{
|
||||
uint32_t *regs = (uint32_t *) (c->card->map + c->baseaddr);
|
||||
uint16_t dt = regs[RTDS_AXIS_TS_PERIOD_OFFSET/4];
|
||||
|
||||
return (dt == 0xFFFF) ? -1.0 : (double) dt / RTDS_HZ;
|
||||
}
|
||||
|
||||
static struct plugin p = {
|
||||
.name = "RTDS's AXI4-Stream - GTFPGA interface",
|
||||
.description = "",
|
||||
.type = PLUGIN_TYPE_FPGA_IP,
|
||||
.ip = {
|
||||
.vlnv = { "acs.eonerc.rwth-aachen.de", "user", "rtds_axis", NULL },
|
||||
.type = FPGA_IP_TYPE_INTERFACE,
|
||||
.dump = rtds_axis_dump,
|
||||
.size = 0
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_PLUGIN(&p)
|
221
fpga/lib/ips/switch.c
Normal file
221
fpga/lib/ips/switch.c
Normal file
|
@ -0,0 +1,221 @@
|
|||
/** 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>
|
||||
* @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 "list.h"
|
||||
#include "log.h"
|
||||
#include "log_config.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#include "fpga/ip.h"
|
||||
#include "fpga/card.h"
|
||||
#include "fpga/ips/switch.h"
|
||||
|
||||
int switch_start(struct fpga_ip *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct fpga_card *f = c->card;
|
||||
struct sw *sw = (struct sw *) c->_vd;
|
||||
|
||||
XAxis_Switch *xsw = &sw->inst;
|
||||
|
||||
if (c != f->sw)
|
||||
error("There can be only one AXI4-Stream interconnect per FPGA");
|
||||
|
||||
|
||||
/* Setup AXI-stream switch */
|
||||
XAxis_Switch_Config sw_cfg = {
|
||||
.BaseAddress = (uintptr_t) f->map + c->baseaddr,
|
||||
.MaxNumMI = sw->num_ports,
|
||||
.MaxNumSI = sw->num_ports
|
||||
};
|
||||
|
||||
ret = XAxisScr_CfgInitialize(xsw, &sw_cfg, (uintptr_t) c->card->map + c->baseaddr);
|
||||
if (ret != XST_SUCCESS)
|
||||
return -1;
|
||||
|
||||
/* Disable all masters */
|
||||
XAxisScr_RegUpdateDisable(xsw);
|
||||
XAxisScr_MiPortDisableAll(xsw);
|
||||
XAxisScr_RegUpdateEnable(xsw);
|
||||
|
||||
switch_init_paths(c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int switch_init_paths(struct fpga_ip *c)
|
||||
{
|
||||
int ret;
|
||||
struct sw *sw = (struct sw *) c->_vd;
|
||||
|
||||
XAxis_Switch *xsw = &sw->inst;
|
||||
|
||||
XAxisScr_RegUpdateDisable(xsw);
|
||||
XAxisScr_MiPortDisableAll(xsw);
|
||||
|
||||
for (size_t i = 0; i < list_length(&sw->paths); i++) {
|
||||
struct sw_path *p = (struct sw_path *) list_at(&sw->paths, i);
|
||||
struct fpga_ip *mi, *si;
|
||||
|
||||
mi = list_lookup(&c->card->ips, p->out);
|
||||
si = list_lookup(&c->card->ips, p->in);
|
||||
|
||||
if (!mi || !si || mi->port == -1 || si->port == -1)
|
||||
error("Invalid path configuration for FPGA");
|
||||
|
||||
ret = switch_connect(c, mi, si);
|
||||
if (ret)
|
||||
error("Failed to configure switch");
|
||||
}
|
||||
|
||||
XAxisScr_RegUpdateEnable(xsw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int switch_destroy(struct fpga_ip *c)
|
||||
{
|
||||
struct sw *sw = (struct sw *) c->_vd;
|
||||
|
||||
list_destroy(&sw->paths, NULL, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int switch_parse(struct fpga_ip *c, json_t *cfg)
|
||||
{
|
||||
struct sw *sw = (struct sw *) c->_vd;
|
||||
|
||||
int ret;
|
||||
size_t index;
|
||||
json_error_t err;
|
||||
json_t *json_path, *json_paths = NULL;
|
||||
|
||||
list_init(&sw->paths);
|
||||
|
||||
ret = json_unpack_ex(cfg, &err, 0, "{ s: i, s?: o }",
|
||||
"num_ports", &sw->num_ports,
|
||||
"paths", &json_paths
|
||||
);
|
||||
if (ret)
|
||||
jerror(&err, "Failed to parse configuration of FPGA IP '%s'", c->name);
|
||||
|
||||
if (!json_paths)
|
||||
return 0; /* no switch config available */
|
||||
|
||||
if (!json_is_array(json_paths))
|
||||
error("Setting 'paths' of FPGA IP '%s' should be an array of JSON objects", c->name);
|
||||
|
||||
json_array_foreach(json_paths, index, json_path) {
|
||||
struct sw_path *p = (struct sw_path *) alloc(sizeof(struct sw_path));
|
||||
int reverse = 0;
|
||||
|
||||
ret = json_unpack_ex(json_path, &err, 0, "{ s?: b, s: s, s: s }",
|
||||
"reverse", &reverse,
|
||||
"in", &p->in,
|
||||
"out", &p->out
|
||||
);
|
||||
if (ret)
|
||||
jerror(&err, "Failed to parse path %zu of FPGA IP '%s'", index, c->name);
|
||||
|
||||
list_push(&sw->paths, p);
|
||||
|
||||
if (reverse) {
|
||||
struct sw_path *r = memdup(p, sizeof(struct sw_path));
|
||||
|
||||
r->in = p->out;
|
||||
r->out = p->in;
|
||||
|
||||
list_push(&sw->paths, r);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int switch_connect(struct fpga_ip *c, struct fpga_ip *mi, struct fpga_ip *si)
|
||||
{
|
||||
struct sw *sw = (struct sw *) c->_vd;
|
||||
XAxis_Switch *xsw = &sw->inst;
|
||||
|
||||
uint32_t mux, port;
|
||||
|
||||
/* Check if theres already something connected */
|
||||
for (int i = 0; i < sw->num_ports; i++) {
|
||||
mux = XAxisScr_ReadReg(xsw->Config.BaseAddress, XAXIS_SCR_MI_MUX_START_OFFSET + i * 4);
|
||||
if (!(mux & XAXIS_SCR_MI_X_DISABLE_MASK)) {
|
||||
port = mux & ~XAXIS_SCR_MI_X_DISABLE_MASK;
|
||||
|
||||
if (port == si->port) {
|
||||
warn("Switch: Slave port %s (%u) has been connected already to port %u. Disconnecting...", si->name, si->port, i);
|
||||
XAxisScr_RegUpdateDisable(xsw);
|
||||
XAxisScr_MiPortDisable(xsw, i);
|
||||
XAxisScr_RegUpdateEnable(xsw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Reconfigure switch */
|
||||
XAxisScr_RegUpdateDisable(xsw);
|
||||
XAxisScr_MiPortEnable(xsw, mi->port, si->port);
|
||||
XAxisScr_RegUpdateEnable(xsw);
|
||||
|
||||
/* Reset IPs */
|
||||
/*ip_reset(mi);
|
||||
ip_reset(si);*/
|
||||
|
||||
debug(8, "FPGA: Switch connected %s (%u) to %s (%u)", mi->name, mi->port, si->name, si->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int switch_disconnect(struct fpga_ip *c, struct fpga_ip *mi, struct fpga_ip *si)
|
||||
{
|
||||
struct sw *sw = (struct sw *) c->_vd;
|
||||
XAxis_Switch *xsw = &sw->inst;
|
||||
|
||||
if (!XAxisScr_IsMiPortEnabled(xsw, mi->port, si->port))
|
||||
return -1;
|
||||
|
||||
XAxisScr_MiPortDisable(xsw, mi->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct plugin p = {
|
||||
.name = "Xilinx's AXI4-Stream switch",
|
||||
.description = "",
|
||||
.type = PLUGIN_TYPE_FPGA_IP,
|
||||
.ip = {
|
||||
.vlnv = { "xilinx.com", "ip", "axis_interconnect", NULL },
|
||||
.type = FPGA_IP_TYPE_MISC,
|
||||
.start = switch_start,
|
||||
.destroy = switch_destroy,
|
||||
.parse = switch_parse,
|
||||
.size = sizeof(struct sw)
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_PLUGIN(&p)
|
61
fpga/lib/ips/timer.c
Normal file
61
fpga/lib/ips/timer.c
Normal file
|
@ -0,0 +1,61 @@
|
|||
/** Timer related helper functions
|
||||
*
|
||||
* These functions present a simpler interface to Xilinx' Timer Counter driver (XTmrCtr_*)
|
||||
*
|
||||
* @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 "config.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#include "fpga/ip.h"
|
||||
#include "fpga/card.h"
|
||||
#include "fpga/ips/timer.h"
|
||||
|
||||
int timer_start(struct fpga_ip *c)
|
||||
{
|
||||
struct fpga_card *f = c->card;
|
||||
struct timer *tmr = (struct timer *) c->_vd;
|
||||
|
||||
XTmrCtr *xtmr = &tmr->inst;
|
||||
XTmrCtr_Config xtmr_cfg = {
|
||||
.BaseAddress = (uintptr_t) f->map + c->baseaddr,
|
||||
.SysClockFreqHz = FPGA_AXI_HZ
|
||||
};
|
||||
|
||||
XTmrCtr_CfgInitialize(xtmr, &xtmr_cfg, (uintptr_t) f->map + c->baseaddr);
|
||||
XTmrCtr_InitHw(xtmr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct plugin p = {
|
||||
.name = "Xilinx's programmable timer / counter",
|
||||
.description = "",
|
||||
.type = PLUGIN_TYPE_FPGA_IP,
|
||||
.ip = {
|
||||
.vlnv = { "xilinx.com", "ip", "axi_timer", NULL },
|
||||
.type = FPGA_IP_TYPE_MISC,
|
||||
.start = timer_start,
|
||||
.size = sizeof(struct timer)
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_PLUGIN(&p)
|
282
fpga/lib/kernel/kernel.c
Normal file
282
fpga/lib/kernel/kernel.c
Normal file
|
@ -0,0 +1,282 @@
|
|||
/** Linux kernel related functions.
|
||||
*
|
||||
* @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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "config.h"
|
||||
#include "kernel/kernel.h"
|
||||
|
||||
int kernel_get_cacheline_size()
|
||||
{
|
||||
#ifdef __linux__
|
||||
return sysconf(_SC_LEVEL1_ICACHE_LINESIZE);
|
||||
#else
|
||||
return 64; /** @todo fixme */
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
int kernel_module_set_param(const char *module, const char *param, const char *value)
|
||||
{
|
||||
FILE *f;
|
||||
char fn[256];
|
||||
|
||||
snprintf(fn, sizeof(fn), "%s/module/%s/parameters/%s", SYSFS_PATH, module, param);
|
||||
f = fopen(fn, "w");
|
||||
if (!f)
|
||||
serror("Failed set parameter %s for kernel module %s to %s", module, param, value);
|
||||
|
||||
debug(LOG_KERNEL | 5, "Set parameter %s of kernel module %s to %s", module, param, value);
|
||||
fprintf(f, "%s", value);
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kernel_module_load(const char *module)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = kernel_module_loaded(module);
|
||||
if (!ret) {
|
||||
debug(LOG_KERNEL | 5, "Kernel module %s already loaded...", module);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
switch (pid) {
|
||||
case -1: // error
|
||||
return -1;
|
||||
|
||||
case 0: // child
|
||||
execlp("modprobe", "modprobe", module, (char *) 0);
|
||||
exit(EXIT_FAILURE); // exec never returns
|
||||
|
||||
default:
|
||||
wait(&ret);
|
||||
|
||||
return kernel_module_loaded(module);
|
||||
}
|
||||
}
|
||||
|
||||
int kernel_module_loaded(const char *module)
|
||||
{
|
||||
FILE *f;
|
||||
int ret = -1;
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
f = fopen(PROCFS_PATH "/modules", "r");
|
||||
if (!f)
|
||||
return -1;
|
||||
|
||||
while (getline(&line, &len, f) >= 0) {
|
||||
if (strstr(line, module) == line) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(line);
|
||||
fclose(f);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int kernel_get_version(struct version *v)
|
||||
{
|
||||
struct utsname uts;
|
||||
|
||||
if (uname(&uts) < 0)
|
||||
return -1;
|
||||
|
||||
if (version_parse(uts.release, v))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kernel_get_cmdline_param(const char *param, char *buf, size_t len)
|
||||
{
|
||||
int ret;
|
||||
char cmdline[512];
|
||||
|
||||
FILE *f = fopen(PROCFS_PATH "/cmdline", "r");
|
||||
if (!f)
|
||||
return -1;
|
||||
|
||||
if (!fgets(cmdline, sizeof(cmdline), f))
|
||||
goto out;
|
||||
|
||||
char *tok = strtok(cmdline, " \t");
|
||||
do {
|
||||
char key[128], value[128];
|
||||
|
||||
ret = sscanf(tok, "%127[^=]=%127s", key, value);
|
||||
if (ret >= 1) {
|
||||
if (ret >= 2)
|
||||
debug(30, "Found kernel param: %s=%s", key, value);
|
||||
else
|
||||
debug(30, "Found kernel param: %s", key);
|
||||
|
||||
if (strcmp(param, key) == 0) {
|
||||
if (ret >= 2 && buf)
|
||||
strncpy(buf, value, len);
|
||||
|
||||
return 0; /* found */
|
||||
}
|
||||
}
|
||||
} while((tok = strtok(NULL, " \t")));
|
||||
|
||||
out:
|
||||
fclose(f);
|
||||
|
||||
return -1; /* not found or error */
|
||||
}
|
||||
|
||||
int kernel_get_page_size()
|
||||
{
|
||||
return sysconf(_SC_PAGESIZE);
|
||||
}
|
||||
|
||||
/* There is no sysconf interface to get the hugepage size */
|
||||
int kernel_get_hugepage_size()
|
||||
{
|
||||
char *key, *value, *unit, *line = NULL;
|
||||
int sz = -1;
|
||||
size_t len = 0;
|
||||
FILE *f;
|
||||
|
||||
f = fopen(PROCFS_PATH "/meminfo", "r");
|
||||
if (!f)
|
||||
return -1;
|
||||
|
||||
while (getline(&line, &len, f) != -1) {
|
||||
key = strtok(line, ": ");
|
||||
value = strtok(NULL, " ");
|
||||
unit = strtok(NULL, "\n");
|
||||
|
||||
if (!strcmp(key, "Hugepagesize") && !strcmp(unit, "kB")) {
|
||||
sz = strtoul(value, NULL, 10) * 1024;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(line);
|
||||
fclose(f);
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
int kernel_get_nr_hugepages()
|
||||
{
|
||||
FILE *f;
|
||||
int nr, ret;
|
||||
|
||||
f = fopen(PROCFS_PATH "/sys/vm/nr_hugepages", "r");
|
||||
if (!f)
|
||||
serror("Failed to open %s", PROCFS_PATH "/sys/vm/nr_hugepages");
|
||||
|
||||
ret = fscanf(f, "%d", &nr);
|
||||
if (ret != 1)
|
||||
nr = -1;
|
||||
|
||||
fclose(f);
|
||||
|
||||
return nr;
|
||||
}
|
||||
|
||||
int kernel_set_nr_hugepages(int nr)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
f = fopen(PROCFS_PATH "/sys/vm/nr_hugepages", "w");
|
||||
if (!f)
|
||||
serror("Failed to open %s", PROCFS_PATH "/sys/vm/nr_hugepages");
|
||||
|
||||
fprintf(f, "%d\n", nr);
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
int kernel_has_cap(cap_value_t cap)
|
||||
{
|
||||
int ret;
|
||||
|
||||
cap_t caps;
|
||||
cap_flag_value_t value;
|
||||
|
||||
caps = cap_get_proc();
|
||||
if (caps == NULL)
|
||||
return -1;
|
||||
|
||||
ret = cap_get_proc(caps);
|
||||
if (ret == -1)
|
||||
return -1;
|
||||
|
||||
ret = cap_get_flag(caps, cap, CAP_EFFECTIVE, &value);
|
||||
if (ret == -1)
|
||||
return -1;
|
||||
|
||||
ret = cap_free(caps);
|
||||
if (ret)
|
||||
return -1;
|
||||
|
||||
return value == CAP_SET ? 0 : -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int kernel_irq_setaffinity(unsigned irq, uintmax_t new, uintmax_t *old)
|
||||
{
|
||||
char fn[64];
|
||||
FILE *f;
|
||||
int ret = 0;
|
||||
|
||||
snprintf(fn, sizeof(fn), "/proc/irq/%u/smp_affinity", irq);
|
||||
|
||||
f = fopen(fn, "w+");
|
||||
if (!f)
|
||||
return -1; /* IRQ does not exist */
|
||||
|
||||
if (old)
|
||||
ret = fscanf(f, "%jx", old);
|
||||
|
||||
fprintf(f, "%jx", new);
|
||||
fclose(f);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* __linux__ */
|
294
fpga/lib/kernel/pci.c
Normal file
294
fpga/lib/kernel/pci.c
Normal file
|
@ -0,0 +1,294 @@
|
|||
/** Linux PCI helpers
|
||||
*
|
||||
* @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 <dirent.h>
|
||||
#include <libgen.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "kernel/pci.h"
|
||||
#include "config.h"
|
||||
|
||||
int pci_init(struct pci *p)
|
||||
{
|
||||
struct dirent *e;
|
||||
DIR *dp;
|
||||
FILE *f;
|
||||
char path[PATH_MAX];
|
||||
int ret;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/bus/pci/devices", SYSFS_PATH);
|
||||
|
||||
dp = opendir(path);
|
||||
if (dp == NULL) {
|
||||
serror("Failed to detect PCI devices");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((e = readdir(dp))) {
|
||||
struct pci_device *d = (struct pci_device *) alloc(sizeof(struct pci_device));
|
||||
|
||||
struct { const char *s; int *p; } map[] = {
|
||||
{ "vendor", &d->id.vendor },
|
||||
{ "device", &d->id.device }
|
||||
};
|
||||
|
||||
/* Read vendor & device id */
|
||||
for (int i = 0; i < 2; i++) {
|
||||
snprintf(path, sizeof(path), "%s/bus/pci/devices/%s/%s", SYSFS_PATH, e->d_name, map[i].s);
|
||||
|
||||
f = fopen(path, "r");
|
||||
if (!f)
|
||||
serror("Failed to open '%s'", path);
|
||||
|
||||
ret = fscanf(f, "%x", map[i].p);
|
||||
if (ret != 1)
|
||||
error("Failed to parse %s ID from: %s", map[i].s, path);
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/* Get slot id */
|
||||
ret = sscanf(e->d_name, "%4x:%2x:%2x.%u", &d->slot.domain, &d->slot.bus, &d->slot.device, &d->slot.function);
|
||||
if (ret != 4)
|
||||
error("Failed to parse PCI slot number: %s", e->d_name);
|
||||
|
||||
list_push(&p->devices, d);
|
||||
}
|
||||
|
||||
closedir(dp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pci_destroy(struct pci *p)
|
||||
{
|
||||
list_destroy(&p->devices, NULL, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pci_device_parse_slot(struct pci_device *f, const char *s, const char **error)
|
||||
{
|
||||
char *str = strdup(s);
|
||||
char *colon = strrchr(str, ':');
|
||||
char *dot = strchr((colon ? colon + 1 : str), '.');
|
||||
char *mid = str;
|
||||
char *e, *bus, *colon2;
|
||||
|
||||
if (colon) {
|
||||
*colon++ = 0;
|
||||
mid = colon;
|
||||
|
||||
colon2 = strchr(str, ':');
|
||||
if (colon2) {
|
||||
*colon2++ = 0;
|
||||
bus = colon2;
|
||||
|
||||
if (str[0] && strcmp(str, "*")) {
|
||||
long int x = strtol(str, &e, 16);
|
||||
if ((e && *e) || (x < 0 || x > 0x7fffffff)) {
|
||||
*error = "Invalid domain number";
|
||||
goto fail;
|
||||
}
|
||||
|
||||
f->slot.domain = x;
|
||||
}
|
||||
}
|
||||
else
|
||||
bus = str;
|
||||
|
||||
if (bus[0] && strcmp(bus, "*")) {
|
||||
long int x = strtol(bus, &e, 16);
|
||||
if ((e && *e) || (x < 0 || x > 0xff)) {
|
||||
*error = "Invalid bus number";
|
||||
goto fail;
|
||||
}
|
||||
|
||||
f->slot.bus = x;
|
||||
}
|
||||
}
|
||||
|
||||
if (dot)
|
||||
*dot++ = 0;
|
||||
|
||||
if (mid[0] && strcmp(mid, "*")) {
|
||||
long int x = strtol(mid, &e, 16);
|
||||
|
||||
if ((e && *e) || (x < 0 || x > 0x1f)) {
|
||||
*error = "Invalid slot number";
|
||||
goto fail;
|
||||
}
|
||||
|
||||
f->slot.device = x;
|
||||
}
|
||||
|
||||
if (dot && dot[0] && strcmp(dot, "*")) {
|
||||
long int x = strtol(dot, &e, 16);
|
||||
|
||||
if ((e && *e) || (x < 0 || x > 7)) {
|
||||
*error = "Invalid function number";
|
||||
goto fail;
|
||||
}
|
||||
|
||||
f->slot.function = x;
|
||||
}
|
||||
|
||||
free(str);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
free(str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ID filter syntax: [vendor]:[device][:class] */
|
||||
int pci_device_parse_id(struct pci_device *f, const char *str, const char **error)
|
||||
{
|
||||
char *s, *c, *e;
|
||||
|
||||
if (!*str)
|
||||
return 0;
|
||||
|
||||
s = strchr(str, ':');
|
||||
if (!s) {
|
||||
*error = "':' expected";
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*s++ = 0;
|
||||
if (str[0] && strcmp(str, "*")) {
|
||||
long int x = strtol(str, &e, 16);
|
||||
|
||||
if ((e && *e) || (x < 0 || x > 0xffff)) {
|
||||
*error = "Invalid vendor ID";
|
||||
goto fail;
|
||||
}
|
||||
|
||||
f->id.vendor = x;
|
||||
}
|
||||
|
||||
c = strchr(s, ':');
|
||||
if (c)
|
||||
*c++ = 0;
|
||||
|
||||
if (s[0] && strcmp(s, "*")) {
|
||||
long int x = strtol(s, &e, 16);
|
||||
if ((e && *e) || (x < 0 || x > 0xffff)) {
|
||||
*error = "Invalid device ID";
|
||||
goto fail;
|
||||
}
|
||||
|
||||
f->id.device = x;
|
||||
}
|
||||
|
||||
if (c && c[0] && strcmp(s, "*")) {
|
||||
long int x = strtol(c, &e, 16);
|
||||
|
||||
if ((e && *e) || (x < 0 || x > 0xffff)) {
|
||||
*error = "Invalid class code";
|
||||
goto fail;
|
||||
}
|
||||
|
||||
f->id.class = x;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int pci_device_compare(const struct pci_device *d, const struct pci_device *f)
|
||||
{
|
||||
if ((f->slot.domain >= 0 && f->slot.domain != d->slot.domain) ||
|
||||
(f->slot.bus >= 0 && f->slot.bus != d->slot.bus) ||
|
||||
(f->slot.device >= 0 && f->slot.device != d->slot.device) ||
|
||||
(f->slot.function >= 0 && f->slot.function != d->slot.function))
|
||||
return 0;
|
||||
|
||||
if (f->id.device >= 0 || f->id.vendor >= 0) {
|
||||
if ((f->id.device >= 0 && f->id.device != d->id.device) || (f->id.vendor >= 0 && f->id.vendor != d->id.vendor))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (f->id.class >= 0) {
|
||||
if (f->id.class != d->id.class)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
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_attach_driver(struct pci_device *d, const char *driver)
|
||||
{
|
||||
FILE *f;
|
||||
char fn[256];
|
||||
|
||||
/* Add new ID to driver */
|
||||
snprintf(fn, sizeof(fn), "%s/bus/pci/drivers/%s/new_id", SYSFS_PATH, driver);
|
||||
f = fopen(fn, "w");
|
||||
if (!f)
|
||||
serror("Failed to add PCI id to %s driver (%s)", driver, fn);
|
||||
|
||||
debug(5, "Adding ID to %s module: %04x %04x", driver, d->id.vendor, d->id.device);
|
||||
fprintf(f, "%04x %04x", d->id.vendor, d->id.device);
|
||||
fclose(f);
|
||||
|
||||
/* Bind to driver */
|
||||
snprintf(fn, sizeof(fn), "%s/bus/pci/drivers/%s/bind", SYSFS_PATH, driver);
|
||||
f = fopen(fn, "w");
|
||||
if (!f)
|
||||
serror("Failed to bind PCI device to %s driver (%s)", driver, fn);
|
||||
|
||||
debug(5, "Bind device to %s driver", driver);
|
||||
fprintf(f, "%04x:%02x:%02x.%x\n", d->slot.domain, d->slot.bus, d->slot.device, d->slot.function);
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pci_get_iommu_group(struct pci_device *d)
|
||||
{
|
||||
int ret;
|
||||
char *group, link[1024], sysfs[1024];
|
||||
|
||||
snprintf(sysfs, sizeof(sysfs), "%s/bus/pci/devices/%04x:%02x:%02x.%x/iommu_group", SYSFS_PATH,
|
||||
d->slot.domain, d->slot.bus, d->slot.device, d->slot.function);
|
||||
|
||||
ret = readlink(sysfs, link, sizeof(link));
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
group = basename(link);
|
||||
|
||||
return atoi(group);
|
||||
}
|
641
fpga/lib/kernel/vfio.c
Normal file
641
fpga/lib/kernel/vfio.c
Normal file
|
@ -0,0 +1,641 @@
|
|||
/** Virtual Function IO wrapper around kernel API
|
||||
*
|
||||
* @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/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#define _DEFAULT_SOURCE
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "log.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "kernel/kernel.h"
|
||||
#include "kernel/vfio.h"
|
||||
#include "kernel/pci.h"
|
||||
|
||||
static const char *vfio_pci_region_names[] = {
|
||||
"PCI_BAR0", // VFIO_PCI_BAR0_REGION_INDEX,
|
||||
"PCI_BAR1", // VFIO_PCI_BAR1_REGION_INDEX,
|
||||
"PCI_BAR2", // VFIO_PCI_BAR2_REGION_INDEX,
|
||||
"PCI_BAR3", // VFIO_PCI_BAR3_REGION_INDEX,
|
||||
"PCI_BAR4", // VFIO_PCI_BAR4_REGION_INDEX,
|
||||
"PCI_BAR5", // VFIO_PCI_BAR5_REGION_INDEX,
|
||||
"PCI_ROM", // VFIO_PCI_ROM_REGION_INDEX,
|
||||
"PCI_CONFIG", // VFIO_PCI_CONFIG_REGION_INDEX,
|
||||
"PCI_VGA" // VFIO_PCI_INTX_IRQ_INDEX,
|
||||
};
|
||||
|
||||
static const char *vfio_pci_irq_names[] = {
|
||||
"PCI_INTX", // VFIO_PCI_INTX_IRQ_INDEX,
|
||||
"PCI_MSI", // VFIO_PCI_MSI_IRQ_INDEX,
|
||||
"PCI_MSIX", // VFIO_PCI_MSIX_IRQ_INDEX,
|
||||
"PCI_ERR", // VFIO_PCI_ERR_IRQ_INDEX,
|
||||
"PCI_REQ" // VFIO_PCI_REQ_IRQ_INDEX,
|
||||
};
|
||||
|
||||
/* Helpers */
|
||||
int vfio_get_iommu_name(int index, char *buf, size_t len)
|
||||
{
|
||||
FILE *f;
|
||||
char fn[256];
|
||||
|
||||
snprintf(fn, sizeof(fn), "/sys/kernel/iommu_groups/%d/name", index);
|
||||
|
||||
f = fopen(fn, "r");
|
||||
if (!f)
|
||||
return -1;
|
||||
|
||||
int ret = fgets(buf, len, f) == buf ? 0 : -1;
|
||||
|
||||
/* Remove trailing newline */
|
||||
char *c = strrchr(buf, '\n');
|
||||
if (c)
|
||||
*c = 0;
|
||||
|
||||
fclose(f);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Destructors */
|
||||
int vfio_destroy(struct vfio_container *v)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Release memory and close fds */
|
||||
list_destroy(&v->groups, (dtor_cb_t) vfio_group_destroy, true);
|
||||
|
||||
/* Close container */
|
||||
ret = close(v->fd);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
debug(5, "VFIO: closed container: fd=%d", v->fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vfio_group_destroy(struct vfio_group *g)
|
||||
{
|
||||
int ret;
|
||||
|
||||
list_destroy(&g->devices, (dtor_cb_t) vfio_device_destroy, false);
|
||||
|
||||
ret = ioctl(g->fd, VFIO_GROUP_UNSET_CONTAINER);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
debug(5, "VFIO: released group from container: group=%u", g->index);
|
||||
|
||||
ret = close(g->fd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
debug(5, "VFIO: closed group: group=%u, fd=%d", g->index, g->fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vfio_device_destroy(struct vfio_device *d)
|
||||
{
|
||||
int ret;
|
||||
|
||||
for (int i = 0; i < d->info.num_regions; i++)
|
||||
vfio_unmap_region(d, i);
|
||||
|
||||
ret = close(d->fd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
debug(5, "VFIO: closed device: name=%s, fd=%d", d->name, d->fd);
|
||||
|
||||
free(d->mappings);
|
||||
free(d->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Constructors */
|
||||
int vfio_init(struct vfio_container *v)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Initialize datastructures */
|
||||
memset(v, 0, sizeof(*v));
|
||||
|
||||
list_init(&v->groups);
|
||||
|
||||
/* Load VFIO kernel module */
|
||||
if (kernel_module_load("vfio"))
|
||||
error("Failed to load kernel module: %s", "vfio");
|
||||
|
||||
/* Open VFIO API */
|
||||
v->fd = open(VFIO_DEV("vfio"), O_RDWR);
|
||||
if (v->fd < 0)
|
||||
error("Failed to open VFIO container");
|
||||
|
||||
/* Check VFIO API version */
|
||||
v->version = ioctl(v->fd, VFIO_GET_API_VERSION);
|
||||
if (v->version < 0 || v->version != VFIO_API_VERSION)
|
||||
error("Failed to get VFIO version");
|
||||
|
||||
/* Check available VFIO extensions (IOMMU types) */
|
||||
v->extensions = 0;
|
||||
for (int i = 1; i < VFIO_DMA_CC_IOMMU; i++) {
|
||||
ret = ioctl(v->fd, VFIO_CHECK_EXTENSION, i);
|
||||
if (ret < 0)
|
||||
error("Failed to get VFIO extensions");
|
||||
else if (ret > 0)
|
||||
v->extensions |= (1 << i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vfio_group_attach(struct vfio_group *g, struct vfio_container *c, int index)
|
||||
{
|
||||
int ret;
|
||||
char buf[128];
|
||||
|
||||
g->index = index;
|
||||
g->container = c;
|
||||
|
||||
list_init(&g->devices);
|
||||
|
||||
/* Open group fd */
|
||||
snprintf(buf, sizeof(buf), VFIO_DEV("%u"), g->index);
|
||||
g->fd = open(buf, O_RDWR);
|
||||
if (g->fd < 0)
|
||||
serror("Failed to open VFIO group: %u", g->index);
|
||||
|
||||
/* Claim group ownership */
|
||||
ret = ioctl(g->fd, VFIO_GROUP_SET_CONTAINER, &c->fd);
|
||||
if (ret < 0)
|
||||
serror("Failed to attach VFIO group to container");
|
||||
|
||||
/* Set IOMMU type */
|
||||
ret = ioctl(c->fd, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU);
|
||||
if (ret < 0)
|
||||
serror("Failed to set IOMMU type of container");
|
||||
|
||||
/* Check group viability and features */
|
||||
g->status.argsz = sizeof(g->status);
|
||||
|
||||
ret = ioctl(g->fd, VFIO_GROUP_GET_STATUS, &g->status);
|
||||
if (ret < 0)
|
||||
serror("Failed to get VFIO group status");
|
||||
|
||||
if (!(g->status.flags & VFIO_GROUP_FLAGS_VIABLE))
|
||||
error("VFIO group is not available: bind all devices to the VFIO driver!");
|
||||
|
||||
list_push(&c->groups, g);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vfio_pci_attach(struct vfio_device *d, struct vfio_container *c, struct pci_device *pdev)
|
||||
{
|
||||
char name[32];
|
||||
int ret;
|
||||
|
||||
/* Load PCI bus driver for VFIO */
|
||||
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");
|
||||
|
||||
/* Get IOMMU group of device */
|
||||
int index = pci_get_iommu_group(pdev);
|
||||
if (index < 0)
|
||||
error("Failed to get IOMMU group of device");
|
||||
|
||||
/* VFIO device name consists of PCI BDF */
|
||||
snprintf(name, sizeof(name), "%04x:%02x:%02x.%x", pdev->slot.domain, pdev->slot.bus, pdev->slot.device, pdev->slot.function);
|
||||
|
||||
ret = vfio_device_attach(d, c, name, index);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Check if this is really a vfio-pci device */
|
||||
if (!(d->info.flags & VFIO_DEVICE_FLAGS_PCI)) {
|
||||
vfio_device_destroy(d);
|
||||
return -1;
|
||||
}
|
||||
|
||||
d->pci_device = pdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vfio_device_attach(struct vfio_device *d, struct vfio_container *c, const char *name, int index)
|
||||
{
|
||||
int ret;
|
||||
struct vfio_group *g = NULL;
|
||||
|
||||
/* Check if group already exists */
|
||||
for (size_t i = 0; i < list_length(&c->groups); i++) {
|
||||
struct vfio_group *h = (struct vfio_group *) list_at(&c->groups, i);
|
||||
|
||||
if (h->index == index)
|
||||
g = h;
|
||||
}
|
||||
|
||||
if (!g) {
|
||||
g = alloc(sizeof(struct vfio_group));
|
||||
|
||||
/* Aquire group ownership */
|
||||
ret = vfio_group_attach(g, c, index);
|
||||
if (ret)
|
||||
error("Failed to attach to IOMMU group: %u", index);
|
||||
|
||||
info("Attached new group %u to VFIO container", g->index);
|
||||
}
|
||||
|
||||
d->group = g;
|
||||
d->name = strdup(name);
|
||||
|
||||
/* Open device fd */
|
||||
d->fd = ioctl(g->fd, VFIO_GROUP_GET_DEVICE_FD, d->name);
|
||||
if (d->fd < 0)
|
||||
serror("Failed to open VFIO device: %s", d->name);
|
||||
|
||||
/* Get device info */
|
||||
d->info.argsz = sizeof(d->info);
|
||||
|
||||
ret = ioctl(d->fd, VFIO_DEVICE_GET_INFO, &d->info);
|
||||
if (ret < 0)
|
||||
serror("Failed to get VFIO device info for: %s", d->name);
|
||||
|
||||
d->irqs = alloc(d->info.num_irqs * sizeof(struct vfio_irq_info));
|
||||
d->regions = alloc(d->info.num_regions * sizeof(struct vfio_region_info));
|
||||
d->mappings = alloc(d->info.num_regions * sizeof(void *));
|
||||
|
||||
/* Get device regions */
|
||||
for (int i = 0; i < d->info.num_regions && i < 8; i++) {
|
||||
struct vfio_region_info *region = &d->regions[i];
|
||||
|
||||
region->argsz = sizeof(*region);
|
||||
region->index = i;
|
||||
|
||||
ret = ioctl(d->fd, VFIO_DEVICE_GET_REGION_INFO, region);
|
||||
if (ret < 0)
|
||||
serror("Failed to get regions of VFIO device: %s", d->name);
|
||||
}
|
||||
|
||||
/* Get device irqs */
|
||||
for (int i = 0; i < d->info.num_irqs; i++) {
|
||||
struct vfio_irq_info *irq = &d->irqs[i];
|
||||
|
||||
irq->argsz = sizeof(*irq);
|
||||
irq->index = i;
|
||||
|
||||
ret = ioctl(d->fd, VFIO_DEVICE_GET_IRQ_INFO, irq);
|
||||
if (ret < 0)
|
||||
serror("Failed to get IRQs of VFIO device: %s", d->name);
|
||||
}
|
||||
|
||||
list_push(&d->group->devices, d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vfio_pci_reset(struct vfio_device *d)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Check if this is really a vfio-pci device */
|
||||
if (!(d->info.flags & VFIO_DEVICE_FLAGS_PCI))
|
||||
return -1;
|
||||
|
||||
size_t reset_infolen = sizeof(struct vfio_pci_hot_reset_info) + sizeof(struct vfio_pci_dependent_device) * 64;
|
||||
size_t resetlen = sizeof(struct vfio_pci_hot_reset) + sizeof(int32_t) * 1;
|
||||
|
||||
struct vfio_pci_hot_reset_info *reset_info = (struct vfio_pci_hot_reset_info *) alloc(reset_infolen);
|
||||
struct vfio_pci_hot_reset *reset = (struct vfio_pci_hot_reset *) alloc(resetlen);
|
||||
|
||||
reset_info->argsz = reset_infolen;
|
||||
reset->argsz = resetlen;
|
||||
|
||||
ret = ioctl(d->fd, VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, reset_info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
debug(5, "VFIO: dependent devices for hot-reset:");
|
||||
for (int i = 0; i < reset_info->count; i++) { INDENT
|
||||
struct vfio_pci_dependent_device *dd = &reset_info->devices[i];
|
||||
debug(5, "%04x:%02x:%02x.%01x: iommu_group=%u", dd->segment, dd->bus, PCI_SLOT(dd->devfn), PCI_FUNC(dd->devfn), dd->group_id);
|
||||
|
||||
if (dd->group_id != d->group->index)
|
||||
return -3;
|
||||
}
|
||||
|
||||
reset->count = 1;
|
||||
reset->group_fds[0] = d->group->fd;
|
||||
|
||||
ret = ioctl(d->fd, VFIO_DEVICE_PCI_HOT_RESET, reset);
|
||||
|
||||
free(reset_info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vfio_pci_msi_find(struct vfio_device *d, int nos[32])
|
||||
{
|
||||
int ret, idx, irq;
|
||||
char *end, *col, *last, line[1024], name[13];
|
||||
FILE *f;
|
||||
|
||||
f = fopen("/proc/interrupts", "r");
|
||||
if (!f)
|
||||
return -1;
|
||||
|
||||
for (int i = 0; i < 32; i++)
|
||||
nos[i] = -1;
|
||||
|
||||
/* For each line in /proc/interruipts */
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
col = strtok(line, " ");
|
||||
|
||||
/* IRQ number is in first column */
|
||||
irq = strtol(col, &end, 10);
|
||||
if (col == end)
|
||||
continue;
|
||||
|
||||
/* Find last column of line */
|
||||
do {
|
||||
last = col;
|
||||
} while ((col = strtok(NULL, " ")));
|
||||
|
||||
|
||||
ret = sscanf(last, "vfio-msi[%u](%12[0-9:])", &idx, name);
|
||||
if (ret == 2) {
|
||||
if (strstr(d->name, name) == d->name)
|
||||
nos[idx] = irq;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vfio_pci_msi_deinit(struct vfio_device *d, int efds[32])
|
||||
{
|
||||
int ret, irq_setlen, irq_count = d->irqs[VFIO_PCI_MSI_IRQ_INDEX].count;
|
||||
struct vfio_irq_set *irq_set;
|
||||
|
||||
/* Check if this is really a vfio-pci device */
|
||||
if (!(d->info.flags & VFIO_DEVICE_FLAGS_PCI))
|
||||
return -1;
|
||||
|
||||
irq_setlen = sizeof(struct vfio_irq_set) + sizeof(int) * irq_count;
|
||||
irq_set = alloc(irq_setlen);
|
||||
|
||||
irq_set->argsz = irq_setlen;
|
||||
irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
|
||||
irq_set->index = VFIO_PCI_MSI_IRQ_INDEX;
|
||||
irq_set->count = irq_count;
|
||||
irq_set->start = 0;
|
||||
|
||||
for (int i = 0; i < irq_count; i++) {
|
||||
close(efds[i]);
|
||||
efds[i] = -1;
|
||||
}
|
||||
|
||||
memcpy(irq_set->data, efds, sizeof(int) * irq_count);
|
||||
|
||||
ret = ioctl(d->fd, VFIO_DEVICE_SET_IRQS, irq_set);
|
||||
if (ret)
|
||||
return -4;
|
||||
|
||||
free(irq_set);
|
||||
|
||||
return irq_count;
|
||||
}
|
||||
|
||||
int vfio_pci_msi_init(struct vfio_device *d, int efds[32])
|
||||
{
|
||||
int ret, irq_setlen, irq_count = d->irqs[VFIO_PCI_MSI_IRQ_INDEX].count;
|
||||
struct vfio_irq_set *irq_set;
|
||||
|
||||
/* Check if this is really a vfio-pci device */
|
||||
if (!(d->info.flags & VFIO_DEVICE_FLAGS_PCI))
|
||||
return -1;
|
||||
|
||||
irq_setlen = sizeof(struct vfio_irq_set) + sizeof(int) * irq_count;
|
||||
irq_set = alloc(irq_setlen);
|
||||
|
||||
irq_set->argsz = irq_setlen;
|
||||
irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
|
||||
irq_set->index = VFIO_PCI_MSI_IRQ_INDEX;
|
||||
irq_set->start = 0;
|
||||
irq_set->count = irq_count;
|
||||
|
||||
/* Now set the new eventfds */
|
||||
for (int i = 0; i < irq_count; i++) {
|
||||
efds[i] = eventfd(0, 0);
|
||||
if (efds[i] < 0)
|
||||
return -3;
|
||||
}
|
||||
memcpy(irq_set->data, efds, sizeof(int) * irq_count);
|
||||
|
||||
ret = ioctl(d->fd, VFIO_DEVICE_SET_IRQS, irq_set);
|
||||
if (ret)
|
||||
return -4;
|
||||
|
||||
free(irq_set);
|
||||
|
||||
return irq_count;
|
||||
}
|
||||
|
||||
int vfio_pci_enable(struct vfio_device *d)
|
||||
{
|
||||
int ret;
|
||||
uint32_t reg;
|
||||
off_t offset = ((off_t) VFIO_PCI_CONFIG_REGION_INDEX << 40) + PCI_COMMAND;
|
||||
|
||||
/* Check if this is really a vfio-pci device */
|
||||
if (!(d->info.flags & VFIO_DEVICE_FLAGS_PCI))
|
||||
return -1;
|
||||
|
||||
ret = pread(d->fd, ®, sizeof(reg), offset);
|
||||
if (ret != sizeof(reg))
|
||||
return -1;
|
||||
|
||||
/* Enable memory access and PCI bus mastering which is required for DMA */
|
||||
reg |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
|
||||
|
||||
ret = pwrite(d->fd, ®, sizeof(reg), offset);
|
||||
if (ret != sizeof(reg))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vfio_device_reset(struct vfio_device *d)
|
||||
{
|
||||
if (d->info.flags & VFIO_DEVICE_FLAGS_RESET)
|
||||
return ioctl(d->fd, VFIO_DEVICE_RESET);
|
||||
else
|
||||
return -1; /* not supported by this device */
|
||||
}
|
||||
|
||||
void vfio_dump(struct vfio_container *v)
|
||||
{
|
||||
info("VFIO Version: %u", v->version);
|
||||
info("VFIO Extensions: %#x", v->extensions);
|
||||
|
||||
for (size_t i = 0; i < list_length(&v->groups); i++) {
|
||||
struct vfio_group *g = (struct vfio_group *) list_at(&v->groups, i);
|
||||
|
||||
info("VFIO Group %u, viable=%u, container=%d", g->index,
|
||||
(g->status.flags & VFIO_GROUP_FLAGS_VIABLE) > 0,
|
||||
(g->status.flags & VFIO_GROUP_FLAGS_CONTAINER_SET) > 0
|
||||
);
|
||||
|
||||
|
||||
for (size_t i = 0; i < list_length(&g->devices); i++) { INDENT
|
||||
struct vfio_device *d = (struct vfio_device *) list_at(&g->devices, i);
|
||||
|
||||
info("Device %s: regions=%u, irqs=%u, flags=%#x", d->name,
|
||||
d->info.num_regions,
|
||||
d->info.num_irqs,
|
||||
d->info.flags
|
||||
);
|
||||
|
||||
for (int i = 0; i < d->info.num_regions && i < 8; i++) { INDENT
|
||||
struct vfio_region_info *region = &d->regions[i];
|
||||
|
||||
if (region->size > 0)
|
||||
info("Region %u %s: size=%#llx, offset=%#llx, flags=%u",
|
||||
region->index, (d->info.flags & VFIO_DEVICE_FLAGS_PCI) ? vfio_pci_region_names[i] : "",
|
||||
region->size,
|
||||
region->offset,
|
||||
region->flags
|
||||
);
|
||||
}
|
||||
|
||||
for (int i = 0; i < d->info.num_irqs; i++) { INDENT
|
||||
struct vfio_irq_info *irq = &d->irqs[i];
|
||||
|
||||
if (irq->count > 0)
|
||||
info("IRQ %u %s: count=%u, flags=%u",
|
||||
irq->index, (d->info.flags & VFIO_DEVICE_FLAGS_PCI ) ? vfio_pci_irq_names[i] : "",
|
||||
irq->count,
|
||||
irq->flags
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void * vfio_map_region(struct vfio_device *d, int idx)
|
||||
{
|
||||
struct vfio_region_info *r = &d->regions[idx];
|
||||
|
||||
if (!(r->flags & VFIO_REGION_INFO_FLAG_MMAP))
|
||||
return MAP_FAILED;
|
||||
|
||||
d->mappings[idx] = mmap(NULL, r->size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_32BIT, d->fd, r->offset);
|
||||
|
||||
return d->mappings[idx];
|
||||
}
|
||||
|
||||
int vfio_unmap_region(struct vfio_device *d, int idx)
|
||||
{
|
||||
int ret;
|
||||
struct vfio_region_info *r = &d->regions[idx];
|
||||
|
||||
if (!d->mappings[idx])
|
||||
return -1; /* was not mapped */
|
||||
|
||||
debug(3, "VFIO: unmap region %u from device", idx);
|
||||
|
||||
ret = munmap(d->mappings[idx], r->size);
|
||||
if (ret)
|
||||
return -2;
|
||||
|
||||
d->mappings[idx] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vfio_map_dma(struct vfio_container *c, uint64_t virt, uint64_t phys, size_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (len & 0xFFF) {
|
||||
len += 0x1000;
|
||||
len &= ~0xFFF;
|
||||
}
|
||||
|
||||
/* Super stupid allocator */
|
||||
if (phys == -1) {
|
||||
phys = c->iova_next;
|
||||
c->iova_next += len;
|
||||
}
|
||||
|
||||
struct vfio_iommu_type1_dma_map dma_map = {
|
||||
.argsz = sizeof(struct vfio_iommu_type1_dma_map),
|
||||
.vaddr = virt,
|
||||
.iova = phys,
|
||||
.size = len,
|
||||
.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE
|
||||
};
|
||||
|
||||
ret = ioctl(c->fd, VFIO_IOMMU_MAP_DMA, &dma_map);
|
||||
if (ret)
|
||||
serror("Failed to create DMA mapping");
|
||||
|
||||
info("DMA map size=%#llx, iova=%#llx, vaddr=%#llx", dma_map.size, dma_map.iova, dma_map.vaddr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vfio_unmap_dma(struct vfio_container *c, uint64_t virt, uint64_t phys, size_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct vfio_iommu_type1_dma_unmap dma_unmap = {
|
||||
.argsz = sizeof(struct vfio_iommu_type1_dma_unmap),
|
||||
.flags = 0,
|
||||
.iova = phys,
|
||||
.size = len,
|
||||
};
|
||||
|
||||
ret = ioctl(c->fd, VFIO_IOMMU_UNMAP_DMA, &dma_unmap);
|
||||
if (ret)
|
||||
serror("Failed to unmap DMA mapping");
|
||||
|
||||
return 0;
|
||||
}
|
203
fpga/lib/list.c
Normal file
203
fpga/lib/list.c
Normal file
|
@ -0,0 +1,203 @@
|
|||
/** A generic linked list
|
||||
*
|
||||
* Linked lists a used for several data structures in the code.
|
||||
*
|
||||
* @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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "utils.h"
|
||||
|
||||
/* Compare functions */
|
||||
static int cmp_lookup(const void *a, const void *b) {
|
||||
const struct {
|
||||
char *name;
|
||||
} *obj = a;
|
||||
|
||||
return strcmp(obj->name, b);
|
||||
}
|
||||
|
||||
static int cmp_contains(const void *a, const void *b) {
|
||||
return a == b ? 0 : 1;
|
||||
}
|
||||
|
||||
static int cmp_sort(const void *a, const void *b, void *thunk) {
|
||||
cmp_cb_t cmp = (cmp_cb_t) thunk;
|
||||
|
||||
return cmp(*(void **) a, *(void **) b);
|
||||
}
|
||||
|
||||
int list_init(struct list *l)
|
||||
{
|
||||
assert(l->state == STATE_DESTROYED);
|
||||
|
||||
pthread_mutex_init(&l->lock, NULL);
|
||||
|
||||
l->length = 0;
|
||||
l->capacity = 0;
|
||||
l->array = NULL;
|
||||
|
||||
l->state = STATE_INITIALIZED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int list_destroy(struct list *l, dtor_cb_t destructor, bool release)
|
||||
{
|
||||
pthread_mutex_lock(&l->lock);
|
||||
|
||||
assert(l->state != STATE_DESTROYED);
|
||||
|
||||
for (size_t i = 0; i < list_length(l); i++) {
|
||||
void *p = list_at(l, i);
|
||||
|
||||
if (destructor)
|
||||
destructor(p);
|
||||
if (release)
|
||||
free(p);
|
||||
}
|
||||
|
||||
free(l->array);
|
||||
|
||||
l->array = NULL;
|
||||
|
||||
l->length = -1;
|
||||
l->capacity = 0;
|
||||
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
pthread_mutex_destroy(&l->lock);
|
||||
|
||||
l->state = STATE_DESTROYED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void list_push(struct list *l, void *p)
|
||||
{
|
||||
pthread_mutex_lock(&l->lock);
|
||||
|
||||
assert(l->state == STATE_INITIALIZED);
|
||||
|
||||
/* Resize array if out of capacity */
|
||||
if (l->length >= l->capacity) {
|
||||
l->capacity += LIST_CHUNKSIZE;
|
||||
l->array = realloc(l->array, l->capacity * sizeof(void *));
|
||||
}
|
||||
|
||||
l->array[l->length] = p;
|
||||
l->length++;
|
||||
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
}
|
||||
|
||||
void list_remove(struct list *l, void *p)
|
||||
{
|
||||
int removed = 0;
|
||||
|
||||
pthread_mutex_lock(&l->lock);
|
||||
|
||||
assert(l->state == STATE_INITIALIZED);
|
||||
|
||||
for (size_t i = 0; i < list_length(l); i++) {
|
||||
if (l->array[i] == p)
|
||||
removed++;
|
||||
else
|
||||
l->array[i - removed] = l->array[i];
|
||||
}
|
||||
|
||||
l->length -= removed;
|
||||
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
}
|
||||
|
||||
void * list_lookup(struct list *l, const char *name)
|
||||
{
|
||||
return list_search(l, cmp_lookup, (void *) name);
|
||||
}
|
||||
|
||||
int list_contains(struct list *l, void *p)
|
||||
{
|
||||
return list_count(l, cmp_contains, p);
|
||||
}
|
||||
|
||||
int list_count(struct list *l, cmp_cb_t cmp, void *ctx)
|
||||
{
|
||||
int c = 0;
|
||||
|
||||
pthread_mutex_lock(&l->lock);
|
||||
|
||||
assert(l->state == STATE_INITIALIZED);
|
||||
|
||||
for (size_t i = 0; i < list_length(l); i++) {
|
||||
void *e = list_at(l, i);
|
||||
|
||||
if (cmp(e, ctx) == 0)
|
||||
c++;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void * list_search(struct list *l, cmp_cb_t cmp, void *ctx)
|
||||
{
|
||||
void *e;
|
||||
|
||||
pthread_mutex_lock(&l->lock);
|
||||
|
||||
assert(l->state == STATE_INITIALIZED);
|
||||
|
||||
for (size_t i = 0; i < list_length(l); i++) {
|
||||
e = list_at(l, i);
|
||||
if (cmp(e, ctx) == 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
e = NULL; /* not found */
|
||||
|
||||
out: pthread_mutex_unlock(&l->lock);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
void list_sort(struct list *l, cmp_cb_t cmp)
|
||||
{
|
||||
pthread_mutex_lock(&l->lock);
|
||||
|
||||
assert(l->state == STATE_INITIALIZED);
|
||||
|
||||
qsort_r(l->array, l->length, sizeof(void *), cmp_sort, (void *) cmp);
|
||||
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
}
|
||||
|
||||
int list_set(struct list *l, int index, void *value)
|
||||
{
|
||||
if (index >= l->length)
|
||||
return -1;
|
||||
|
||||
l->array[index] = value;
|
||||
|
||||
return 0;
|
||||
}
|
309
fpga/lib/log.c
Normal file
309
fpga/lib/log.c
Normal file
|
@ -0,0 +1,309 @@
|
|||
/** Logging and debugging routines
|
||||
*
|
||||
* @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 <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "utils.h"
|
||||
|
||||
#ifdef ENABLE_OPAL_ASYNC
|
||||
/* Define RTLAB before including OpalPrint.h for messages to be sent
|
||||
* to the OpalDisplay. Otherwise stdout will be used. */
|
||||
#define RTLAB
|
||||
#include "OpalPrint.h"
|
||||
#endif
|
||||
|
||||
struct log *global_log;
|
||||
|
||||
/* We register a default log instance */
|
||||
__attribute__((constructor))
|
||||
void register_default_log()
|
||||
{
|
||||
int ret;
|
||||
static struct log default_log;
|
||||
|
||||
ret = log_init(&default_log, V, LOG_ALL);
|
||||
if (ret)
|
||||
error("Failed to initalize log");
|
||||
|
||||
ret = log_start(&default_log);
|
||||
if (ret)
|
||||
error("Failed to start log");
|
||||
}
|
||||
|
||||
/** List of debug facilities as strings */
|
||||
static const char *facilities_strs[] = {
|
||||
"pool", /* LOG_POOL */
|
||||
"queue", /* LOG_QUEUE */
|
||||
"config", /* LOG_CONFIG */
|
||||
"hook", /* LOG_HOOK */
|
||||
"path", /* LOG_PATH */
|
||||
"node", /* LOG_NODE */
|
||||
"mem", /* LOG_MEM */
|
||||
"web", /* LOG_WEB */
|
||||
"api", /* LOG_API */
|
||||
"log", /* LOG_LOG */
|
||||
"vfio", /* LOG_VFIO */
|
||||
"pci", /* LOG_PCI */
|
||||
"xil", /* LOG_XIL */
|
||||
"tc", /* LOG_TC */
|
||||
"if", /* LOG_IF */
|
||||
"advio", /* LOG_ADVIO */
|
||||
|
||||
/* Node-types */
|
||||
"socket", /* LOG_SOCKET */
|
||||
"file", /* LOG_FILE */
|
||||
"fpga", /* LOG_FPGA */
|
||||
"ngsi", /* LOG_NGSI */
|
||||
"websocket", /* LOG_WEBSOCKET */
|
||||
"opal", /* LOG_OPAL */
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
/** The current log indention level (per thread!). */
|
||||
static __thread int indent = 0;
|
||||
|
||||
int log_indent(int levels)
|
||||
{
|
||||
int old = indent;
|
||||
indent += levels;
|
||||
return old;
|
||||
}
|
||||
|
||||
int log_noindent()
|
||||
{
|
||||
int old = indent;
|
||||
indent = 0;
|
||||
return old;
|
||||
}
|
||||
|
||||
void log_outdent(int *old)
|
||||
{
|
||||
indent = *old;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void log_resize(int signal, siginfo_t *sinfo, void *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ioctl(STDOUT_FILENO, TIOCGWINSZ, &global_log->window);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
global_log->width = global_log->window.ws_col - 25;
|
||||
if (global_log->prefix)
|
||||
global_log->width -= strlenp(global_log->prefix);
|
||||
|
||||
debug(LOG_LOG | 15, "New terminal size: %dx%x", global_log->window.ws_row, global_log->window.ws_col);
|
||||
}
|
||||
|
||||
int log_init(struct log *l, int level, long facilitites)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Register this log instance globally */
|
||||
global_log = l;
|
||||
|
||||
l->level = level;
|
||||
l->syslog = 0;
|
||||
l->facilities = facilitites;
|
||||
l->file = stderr;
|
||||
l->path = NULL;
|
||||
|
||||
l->prefix = getenv("VILLAS_LOG_PREFIX");
|
||||
|
||||
/* Register signal handler which is called whenever the
|
||||
* terminal size changes. */
|
||||
if (l->file == stderr) {
|
||||
struct sigaction sa_resize = {
|
||||
.sa_flags = SA_SIGINFO,
|
||||
.sa_sigaction = log_resize
|
||||
};
|
||||
|
||||
sigemptyset(&sa_resize.sa_mask);
|
||||
|
||||
ret = sigaction(SIGWINCH, &sa_resize, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Try to get initial window size */
|
||||
ioctl(STDERR_FILENO, TIOCGWINSZ, &global_log->window);
|
||||
}
|
||||
else {
|
||||
l->window.ws_col = LOG_WIDTH;
|
||||
l->window.ws_row = LOG_HEIGHT;
|
||||
}
|
||||
|
||||
l->width = l->window.ws_col - 25;
|
||||
if (l->prefix)
|
||||
l->width -= strlenp(l->prefix);
|
||||
|
||||
l->state = STATE_INITIALIZED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int log_start(struct log *l)
|
||||
{
|
||||
if (l->path) {
|
||||
l->file = fopen(l->path, "a+");;
|
||||
if (!l->file) {
|
||||
l->file = stderr;
|
||||
error("Failed to open log file '%s'", l->path);
|
||||
}
|
||||
}
|
||||
else
|
||||
l->file = stderr;
|
||||
|
||||
l->state = STATE_STARTED;
|
||||
|
||||
if (l->syslog) {
|
||||
openlog(NULL, LOG_PID, LOG_DAEMON);
|
||||
}
|
||||
|
||||
debug(LOG_LOG | 5, "Log sub-system started: level=%d, faciltities=%#lx, path=%s", l->level, l->facilities, l->path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int log_stop(struct log *l)
|
||||
{
|
||||
if (l->state != STATE_STARTED)
|
||||
return 0;
|
||||
|
||||
if (l->file != stderr && l->file != stdout) {
|
||||
fclose(l->file);
|
||||
}
|
||||
|
||||
if (l->syslog) {
|
||||
closelog();
|
||||
}
|
||||
|
||||
l->state = STATE_STOPPED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int log_destroy(struct log *l)
|
||||
{
|
||||
default_log.epoch = l->epoch;
|
||||
|
||||
global_log = &default_log;
|
||||
|
||||
l->state = STATE_DESTROYED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int log_set_facility_expression(struct log *l, const char *expression)
|
||||
{
|
||||
bool negate;
|
||||
char *copy, *token;
|
||||
long mask = 0, facilities = 0;
|
||||
|
||||
copy = strdup(expression);
|
||||
token = strtok(copy, ",");
|
||||
|
||||
while (token != NULL) {
|
||||
if (token[0] == '!') {
|
||||
token++;
|
||||
negate = true;
|
||||
}
|
||||
else
|
||||
negate = false;
|
||||
|
||||
/* Check for some classes */
|
||||
if (!strcmp(token, "all"))
|
||||
mask = LOG_ALL;
|
||||
else if (!strcmp(token, "nodes"))
|
||||
mask = LOG_NODES;
|
||||
else if (!strcmp(token, "kernel"))
|
||||
mask = LOG_KERNEL;
|
||||
else {
|
||||
for (int ind = 0; ind < ARRAY_LEN(facilities_strs); ind++) {
|
||||
if (!strcmp(token, facilities_strs[ind])) {
|
||||
mask = (1 << (ind+8));
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
error("Invalid log class '%s'", token);
|
||||
}
|
||||
|
||||
found: if (negate)
|
||||
facilities &= ~mask;
|
||||
else
|
||||
facilities |= mask;
|
||||
|
||||
token = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
l->facilities = facilities;
|
||||
|
||||
free(copy);
|
||||
|
||||
return l->facilities;
|
||||
}
|
||||
|
||||
void log_print(struct log *l, const char *lvl, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
log_vprint(l, lvl, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void log_vprint(struct log *l, const char *lvl, const char *fmt, va_list ap)
|
||||
{
|
||||
char *buf = alloc(512);
|
||||
|
||||
/* Optional prefix */
|
||||
if (l->prefix)
|
||||
strcatf(&buf, "%s", l->prefix);
|
||||
|
||||
/* Indention */
|
||||
#ifdef __GNUC__
|
||||
for (int i = 0; i < indent; i++)
|
||||
strcatf(&buf, "%s ", BOX_UD);
|
||||
|
||||
strcatf(&buf, "%s ", BOX_UDR);
|
||||
#endif
|
||||
|
||||
/* Format String */
|
||||
vstrcatf(&buf, fmt, ap);
|
||||
|
||||
/* Output */
|
||||
#ifdef ENABLE_OPAL_ASYNC
|
||||
OpalPrint("VILLASfpga: %s\n", buf);
|
||||
#endif
|
||||
fprintf(l->file ? l->file : stderr, "%s\n", buf);
|
||||
|
||||
free(buf);
|
||||
}
|
85
fpga/lib/log_config.c
Normal file
85
fpga/lib/log_config.c
Normal file
|
@ -0,0 +1,85 @@
|
|||
/** Logging routines that depend on jansson.
|
||||
*
|
||||
* @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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "log_config.h"
|
||||
#include "utils.h"
|
||||
#include "string.h"
|
||||
|
||||
int log_parse(struct log *l, json_t *cfg)
|
||||
{
|
||||
const char *facilities = NULL;
|
||||
const char *path = NULL;
|
||||
int ret;
|
||||
|
||||
json_error_t err;
|
||||
|
||||
ret = json_unpack_ex(cfg, &err, 0, "{ s?: i, s?: s, s?: s, s?: b }",
|
||||
"level", &l->level,
|
||||
"file", &path,
|
||||
"facilities", &facilities,
|
||||
"syslog", &l->syslog
|
||||
);
|
||||
if (ret)
|
||||
jerror(&err, "Failed to parse logging configuration");
|
||||
|
||||
if (path)
|
||||
l->path = strdup(path);
|
||||
|
||||
if (facilities)
|
||||
log_set_facility_expression(l, facilities);
|
||||
|
||||
l->state = STATE_PARSED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void jerror(json_error_t *err, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *buf = NULL;
|
||||
|
||||
struct log *l = global_log ? global_log : &default_log;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vstrcatf(&buf, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
log_print(l, LOG_LVL_ERROR, "%s:", buf);
|
||||
{ INDENT
|
||||
log_print(l, LOG_LVL_ERROR, "%s in %s:%d:%d", err->text, err->source, err->line, err->column);
|
||||
|
||||
if (l->syslog)
|
||||
syslog(LOG_ERR, "%s in %s:%d:%d", err->text, err->source, err->line, err->column);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
killme(SIGABRT);
|
||||
pause();
|
||||
}
|
138
fpga/lib/log_helper.c
Normal file
138
fpga/lib/log_helper.c
Normal file
|
@ -0,0 +1,138 @@
|
|||
/** Logging and debugging routines
|
||||
*
|
||||
* @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 <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "log.h"
|
||||
|
||||
void debug(long class, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
struct log *l = global_log;
|
||||
|
||||
int lvl = class & 0xFF;
|
||||
int fac = class & ~0xFF;
|
||||
|
||||
if (((fac == 0) || (fac & l->facilities)) && (lvl <= l->level)) {
|
||||
va_start(ap, fmt);
|
||||
|
||||
log_vprint(l, LOG_LVL_DEBUG, fmt, ap);
|
||||
|
||||
if (l->syslog)
|
||||
syslog(LOG_DEBUG, fmt, ap);
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
void info(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
struct log *l = global_log;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
log_vprint(l, LOG_LVL_INFO, fmt, ap);
|
||||
|
||||
if (l->syslog)
|
||||
syslog(LOG_INFO, fmt, ap);
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void warn(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
struct log *l = global_log;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
log_vprint(l, LOG_LVL_WARN, fmt, ap);
|
||||
|
||||
if (l->syslog)
|
||||
syslog(LOG_WARNING, fmt, ap);
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void stats(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
struct log *l = global_log;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
log_vprint(l, LOG_LVL_STATS, fmt, ap);
|
||||
|
||||
if (l->syslog)
|
||||
syslog(LOG_INFO, fmt, ap);
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void error(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
struct log *l = global_log;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
log_vprint(l, LOG_LVL_ERROR, fmt, ap);
|
||||
|
||||
if (l->syslog)
|
||||
syslog(LOG_ERR, fmt, ap);
|
||||
|
||||
va_end(ap);
|
||||
|
||||
killme(SIGABRT);
|
||||
pause();
|
||||
}
|
||||
|
||||
void serror(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *buf = NULL;
|
||||
|
||||
struct log *l = global_log;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vstrcatf(&buf, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
log_print(l, LOG_LVL_ERROR, "%s: %m (%u)", buf, errno);
|
||||
|
||||
if (l->syslog)
|
||||
syslog(LOG_ERR, "%s: %m (%u)", buf, errno);
|
||||
|
||||
free(buf);
|
||||
|
||||
killme(SIGABRT);
|
||||
pause();
|
||||
}
|
111
fpga/lib/plugin.c
Normal file
111
fpga/lib/plugin.c
Normal file
|
@ -0,0 +1,111 @@
|
|||
/** 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 "plugin.h"
|
||||
|
||||
/** Global list of all known plugins */
|
||||
struct list plugins = { .state = STATE_DESTROYED };
|
||||
|
||||
LIST_INIT_STATIC(&plugins)
|
||||
|
||||
int plugin_init(struct plugin *p)
|
||||
{
|
||||
assert(p->state == STATE_DESTROYED);
|
||||
|
||||
p->state = STATE_INITIALIZED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plugin_parse(struct plugin *p, json_t *cfg)
|
||||
{
|
||||
const char *path;
|
||||
|
||||
path = json_string_value(cfg);
|
||||
if (!path)
|
||||
return -1;
|
||||
|
||||
p->path = strdup(path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plugin_load(struct plugin *p)
|
||||
{
|
||||
p->handle = dlopen(p->path, RTLD_NOW);
|
||||
if (!p->path)
|
||||
return -1;
|
||||
|
||||
p->state = STATE_LOADED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plugin_unload(struct plugin *p)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(p->state == STATE_LOADED);
|
||||
|
||||
ret = dlclose(p->handle);
|
||||
if (ret)
|
||||
return -1;
|
||||
|
||||
p->state = STATE_UNLOADED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plugin_destroy(struct plugin *p)
|
||||
{
|
||||
assert(p->state != STATE_DESTROYED && p->state != STATE_LOADED);
|
||||
|
||||
if (p->path)
|
||||
free(p->path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct plugin * plugin_lookup(enum plugin_type type, const char *name)
|
||||
{
|
||||
for (size_t i = 0; i < list_length(&plugins); i++) {
|
||||
struct plugin *p = (struct plugin *) list_at(&plugins, i);
|
||||
|
||||
if (p->type == type && strcmp(p->name, name) == 0)
|
||||
return p;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void plugin_dump(enum plugin_type type)
|
||||
{
|
||||
for (size_t i = 0; i < list_length(&plugins); i++) {
|
||||
struct plugin *p = (struct plugin *) list_at(&plugins, i);
|
||||
|
||||
if (p->type == type)
|
||||
printf(" - %-13s: %s\n", p->name, p->description);
|
||||
}
|
||||
}
|
401
fpga/lib/utils.c
Normal file
401
fpga/lib/utils.c
Normal file
|
@ -0,0 +1,401 @@
|
|||
/** General purpose helper functions.
|
||||
*
|
||||
* @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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <pthread.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "utils.h"
|
||||
|
||||
pthread_t main_thread;
|
||||
|
||||
void print_copyright()
|
||||
{
|
||||
printf("VILLASfpga %s (built on %s %s)\n",
|
||||
CLR_BLU(BUILDID), CLR_MAG(__DATE__), CLR_MAG(__TIME__));
|
||||
printf(" Copyright 2014-2017, Institute for Automation of Complex Power Systems, EONERC\n");
|
||||
printf(" Steffen Vogel <StVogel@eonerc.rwth-aachen.de>\n");
|
||||
}
|
||||
|
||||
void print_version()
|
||||
{
|
||||
printf("%s\n", BUILDID);
|
||||
}
|
||||
|
||||
int version_parse(const char *s, struct version *v)
|
||||
{
|
||||
return sscanf(s, "%u.%u", &v->major, &v->minor) != 2;
|
||||
}
|
||||
|
||||
int version_cmp(struct version *a, struct version *b) {
|
||||
int major = a->major - b->major;
|
||||
int minor = a->minor - b->minor;
|
||||
|
||||
return major ? major : minor;
|
||||
}
|
||||
|
||||
double box_muller(float m, float s)
|
||||
{
|
||||
double x1, x2, y1;
|
||||
static double y2;
|
||||
static int use_last = 0;
|
||||
|
||||
if (use_last) { /* use value from previous call */
|
||||
y1 = y2;
|
||||
use_last = 0;
|
||||
}
|
||||
else {
|
||||
double w;
|
||||
do {
|
||||
x1 = 2.0 * randf() - 1.0;
|
||||
x2 = 2.0 * randf() - 1.0;
|
||||
w = x1*x1 + x2*x2;
|
||||
} while (w >= 1.0);
|
||||
|
||||
w = sqrt(-2.0 * log(w) / w);
|
||||
y1 = x1 * w;
|
||||
y2 = x2 * w;
|
||||
use_last = 1;
|
||||
}
|
||||
|
||||
return m + y1 * s;
|
||||
}
|
||||
|
||||
double randf()
|
||||
{
|
||||
return (double) random() / RAND_MAX;
|
||||
}
|
||||
|
||||
char * strcatf(char **dest, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vstrcatf(dest, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return *dest;
|
||||
}
|
||||
|
||||
char * vstrcatf(char **dest, const char *fmt, va_list ap)
|
||||
{
|
||||
char *tmp;
|
||||
int n = *dest ? strlen(*dest) : 0;
|
||||
int i = vasprintf(&tmp, fmt, ap);
|
||||
|
||||
*dest = (char *)(realloc(*dest, n + i + 1));
|
||||
if (*dest != NULL)
|
||||
strncpy(*dest+n, tmp, i + 1);
|
||||
|
||||
free(tmp);
|
||||
|
||||
return *dest;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
void cpuset_to_integer(cpu_set_t *cset, uintmax_t *set)
|
||||
{
|
||||
*set = 0;
|
||||
for (int i = 0; i < MIN(sizeof(*set) * 8, CPU_SETSIZE); i++) {
|
||||
if (CPU_ISSET(i, cset))
|
||||
*set |= 1ULL << i;
|
||||
}
|
||||
}
|
||||
|
||||
void cpuset_from_integer(uintmax_t set, cpu_set_t *cset)
|
||||
{
|
||||
CPU_ZERO(cset);
|
||||
for (int i = 0; i < MIN(sizeof(set) * 8, CPU_SETSIZE); i++) {
|
||||
if (set & (1L << i))
|
||||
CPU_SET(i, cset);
|
||||
}
|
||||
}
|
||||
|
||||
/* From: https://github.com/mmalecki/util-linux/blob/master/lib/cpuset.c */
|
||||
static const char *nexttoken(const char *q, int sep)
|
||||
{
|
||||
if (q)
|
||||
q = strchr(q, sep);
|
||||
if (q)
|
||||
q++;
|
||||
return q;
|
||||
}
|
||||
|
||||
int cpulist_parse(const char *str, cpu_set_t *set, int fail)
|
||||
{
|
||||
const char *p, *q;
|
||||
int r = 0;
|
||||
|
||||
q = str;
|
||||
CPU_ZERO(set);
|
||||
|
||||
while (p = q, q = nexttoken(q, ','), p) {
|
||||
unsigned int a; /* beginning of range */
|
||||
unsigned int b; /* end of range */
|
||||
unsigned int s; /* stride */
|
||||
const char *c1, *c2;
|
||||
char c;
|
||||
|
||||
if ((r = sscanf(p, "%u%c", &a, &c)) < 1)
|
||||
return 1;
|
||||
b = a;
|
||||
s = 1;
|
||||
|
||||
c1 = nexttoken(p, '-');
|
||||
c2 = nexttoken(p, ',');
|
||||
if (c1 != NULL && (c2 == NULL || c1 < c2)) {
|
||||
if ((r = sscanf(c1, "%u%c", &b, &c)) < 1)
|
||||
return 1;
|
||||
c1 = nexttoken(c1, ':');
|
||||
if (c1 != NULL && (c2 == NULL || c1 < c2)) {
|
||||
if ((r = sscanf(c1, "%u%c", &s, &c)) < 1)
|
||||
return 1;
|
||||
if (s == 0)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(a <= b))
|
||||
return 1;
|
||||
while (a <= b) {
|
||||
if (fail && (a >= CPU_SETSIZE))
|
||||
return 2;
|
||||
CPU_SET(a, set);
|
||||
a += s;
|
||||
}
|
||||
}
|
||||
|
||||
if (r == 2)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *cpulist_create(char *str, size_t len, cpu_set_t *set)
|
||||
{
|
||||
size_t i;
|
||||
char *ptr = str;
|
||||
int entry_made = 0;
|
||||
|
||||
for (i = 0; i < CPU_SETSIZE; i++) {
|
||||
if (CPU_ISSET(i, set)) {
|
||||
int rlen;
|
||||
size_t j, run = 0;
|
||||
entry_made = 1;
|
||||
for (j = i + 1; j < CPU_SETSIZE; j++) {
|
||||
if (CPU_ISSET(j, set))
|
||||
run++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (!run)
|
||||
rlen = snprintf(ptr, len, "%zd,", i);
|
||||
else if (run == 1) {
|
||||
rlen = snprintf(ptr, len, "%zd,%zd,", i, i + 1);
|
||||
i++;
|
||||
} else {
|
||||
rlen = snprintf(ptr, len, "%zd-%zd,", i, i + run);
|
||||
i += run;
|
||||
}
|
||||
if (rlen < 0 || (size_t) rlen + 1 > len)
|
||||
return NULL;
|
||||
ptr += rlen;
|
||||
if (rlen > 0 && len > (size_t) rlen)
|
||||
len -= rlen;
|
||||
else
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
ptr -= entry_made;
|
||||
*ptr = '\0';
|
||||
|
||||
return str;
|
||||
}
|
||||
#endif /* __linux__ */
|
||||
|
||||
void * alloc(size_t bytes)
|
||||
{
|
||||
void *p = malloc(bytes);
|
||||
if (!p)
|
||||
error("Failed to allocate memory");
|
||||
|
||||
memset(p, 0, bytes);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void * memdup(const void *src, size_t bytes)
|
||||
{
|
||||
void *dst = alloc(bytes);
|
||||
|
||||
memcpy(dst, src, bytes);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
ssize_t read_random(char *buf, size_t len)
|
||||
{
|
||||
int fd;
|
||||
ssize_t bytes, total;
|
||||
|
||||
fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
bytes = 0;
|
||||
total = 0;
|
||||
while (total < len) {
|
||||
bytes = read(fd, buf + total, len - total);
|
||||
if (bytes < 0)
|
||||
break;
|
||||
|
||||
total += bytes;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void rdtsc_sleep(uint64_t nanosecs, uint64_t start)
|
||||
{
|
||||
uint64_t cycles;
|
||||
|
||||
/** @todo Replace the hard coded CPU clock frequency */
|
||||
cycles = (double) nanosecs / (1e9 / 3392389000);
|
||||
|
||||
if (start == 0)
|
||||
start = rdtsc();
|
||||
|
||||
do {
|
||||
__asm__("nop");
|
||||
} while (rdtsc() - start < cycles);
|
||||
}
|
||||
|
||||
/* Setup exit handler */
|
||||
int signals_init(void (*cb)(int signal, siginfo_t *sinfo, void *ctx))
|
||||
{
|
||||
int ret;
|
||||
|
||||
info("Initialize signals");
|
||||
|
||||
struct sigaction sa_quit = {
|
||||
.sa_flags = SA_SIGINFO | SA_NODEFER,
|
||||
.sa_sigaction = cb
|
||||
};
|
||||
|
||||
struct sigaction sa_chld = {
|
||||
.sa_flags = 0,
|
||||
.sa_handler = SIG_IGN
|
||||
};
|
||||
|
||||
main_thread = pthread_self();
|
||||
|
||||
sigemptyset(&sa_quit.sa_mask);
|
||||
|
||||
ret = sigaction(SIGINT, &sa_quit, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sigaction(SIGTERM, &sa_quit, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sigaction(SIGALRM, &sa_quit, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sigaction(SIGCHLD, &sa_chld, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void killme(int sig)
|
||||
{
|
||||
/* Send only to main thread in case the ID was initilized by signals_init() */
|
||||
if (main_thread)
|
||||
pthread_kill(main_thread, sig);
|
||||
else
|
||||
kill(0, sig);
|
||||
}
|
||||
|
||||
pid_t spawn(const char* name, char *const argv[])
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
switch (pid) {
|
||||
case -1: return -1;
|
||||
case 0: return execvp(name, (char * const*) argv);
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
size_t strlenp(const char *str)
|
||||
{
|
||||
size_t sz = 0;
|
||||
|
||||
for (const char *d = str; *d; d++) {
|
||||
const unsigned char *c = (const unsigned char *) d;
|
||||
|
||||
if (isprint(*c))
|
||||
sz++;
|
||||
else if (c[0] == '\b')
|
||||
sz--;
|
||||
else if (c[0] == '\t')
|
||||
sz += 4; /* tab width == 4 */
|
||||
/* CSI sequence */
|
||||
else if (c[0] == '\e' && c[1] == '[') {
|
||||
c += 2;
|
||||
while (*c && *c != 'm')
|
||||
c++;
|
||||
}
|
||||
/* UTF-8 */
|
||||
else if (c[0] >= 0xc2 && c[0] <= 0xdf) {
|
||||
sz++;
|
||||
c += 1;
|
||||
}
|
||||
else if (c[0] >= 0xe0 && c[0] <= 0xef) {
|
||||
sz++;
|
||||
c += 2;
|
||||
}
|
||||
else if (c[0] >= 0xf0 && c[0] <= 0xf4) {
|
||||
sz++;
|
||||
c += 3;
|
||||
}
|
||||
|
||||
d = (const char *) c;
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
64
fpga/lib/vlnv.c
Normal file
64
fpga/lib/vlnv.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
/** 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fpga/vlnv.h"
|
||||
#include "fpga/ip.h"
|
||||
|
||||
struct fpga_ip * fpga_vlnv_lookup(struct list *l, struct fpga_vlnv *v)
|
||||
{
|
||||
return (struct fpga_ip *) list_search(l, (cmp_cb_t) fpga_vlnv_cmp, v);
|
||||
}
|
||||
|
||||
int fpga_vlnv_cmp(struct fpga_vlnv *a, struct fpga_vlnv *b)
|
||||
{
|
||||
return ((!a->vendor || !b->vendor || !strcmp(a->vendor, b->vendor )) &&
|
||||
(!a->library || !b->library || !strcmp(a->library, b->library)) &&
|
||||
(!a->name || !b->name || !strcmp(a->name, b->name )) &&
|
||||
(!a->version || !b->version || !strcmp(a->version, b->version))) ? 0 : 1;
|
||||
}
|
||||
|
||||
int fpga_vlnv_parse(struct fpga_vlnv *c, const char *vlnv)
|
||||
{
|
||||
char *tmp = strdup(vlnv);
|
||||
|
||||
c->vendor = strdup(strtok(tmp, ":"));
|
||||
c->library = strdup(strtok(NULL, ":"));
|
||||
c->name = strdup(strtok(NULL, ":"));
|
||||
c->version = strdup(strtok(NULL, ":"));
|
||||
|
||||
free(tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fpga_vlnv_destroy(struct fpga_vlnv *v)
|
||||
{
|
||||
free(v->vendor);
|
||||
free(v->library);
|
||||
free(v->name);
|
||||
free(v->version);
|
||||
|
||||
return 0;
|
||||
}
|
200
fpga/scripts/hwdef-parse.py
Executable file
200
fpga/scripts/hwdef-parse.py
Executable file
|
@ -0,0 +1,200 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Author: Daniel Krebs <github@daniel-krebs.net>
|
||||
Copyright: 2017, Institute for Automation of Complex Power Systems, EONERC
|
||||
License: GNU General Public License (version 3)
|
||||
|
||||
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/>.
|
||||
"""
|
||||
|
||||
from lxml import etree
|
||||
import zipfile
|
||||
import sys
|
||||
import re
|
||||
import json
|
||||
|
||||
whitelist = [
|
||||
[ 'xilinx.com', 'ip', 'axi_timer' ],
|
||||
[ 'xilinx.com', 'ip', 'axis_switch' ],
|
||||
[ 'xilinx.com', 'ip', 'axi_fifo_mm_s' ],
|
||||
[ 'xilinx.com', 'ip', 'axi_dma' ],
|
||||
[ 'acs.eonerc.rwth-aachen.de', 'user', 'axi_pcie_intc' ],
|
||||
[ 'acs.eonerc.rwth-aachen.de', 'user', 'rtds_axis' ],
|
||||
[ 'acs.eonerc.rwth-aachen.de', 'hls' ],
|
||||
[ 'acs.eonerc.rwth-aachen.de', 'sysgen' ],
|
||||
[ 'xilinx.com', 'ip', 'axi_gpio' ],
|
||||
[ 'xilinx.com', 'ip', 'axi_bram_ctrl' ]
|
||||
]
|
||||
|
||||
# List of VLNI ids of AXI4-Stream infrastructure IP cores which do not alter data
|
||||
# see PG085 (AXI4-Stream Infrastructure IP Suite v2.2)
|
||||
axi_converter_whitelist = [
|
||||
[ 'xilinx.com', 'ip', 'axis_subset_converter' ],
|
||||
[ 'xilinx.com', 'ip', 'axis_clock_converter' ],
|
||||
[ 'xilinx.com', 'ip', 'axis_register_slice' ],
|
||||
[ 'xilinx.com', 'ip', 'axis_data_fifo' ],
|
||||
[ 'xilinx.com', 'ip', 'axis_dwidth_converter' ],
|
||||
[ 'xilinx.com', 'ip', 'axis_register_slice' ]
|
||||
]
|
||||
|
||||
opponent = {
|
||||
'MASTER' : ('SLAVE', 'TARGET'),
|
||||
'SLAVE' : ('MASTER', 'INITIATOR'),
|
||||
'INITIATOR' : ('TARGET', 'SLAVE'),
|
||||
'TARGET' : ('INITIATOR', 'MASTER')
|
||||
}
|
||||
|
||||
def port_trace(root, signame, idx):
|
||||
pass
|
||||
|
||||
def port_find_driver(root, signame):
|
||||
pass
|
||||
|
||||
def bus_trace(root, busname, type, whitelist):
|
||||
module = root.xpath('.//MODULE[.//BUSINTERFACE[@BUSNAME="{}" and (@TYPE="{}" or @TYPE="{}")]]'.format(busname, type[0], type[1]))
|
||||
|
||||
vlnv = module[0].get('VLNV')
|
||||
instance = module[0].get('INSTANCE')
|
||||
|
||||
if vlnv_match(vlnv, whitelist):
|
||||
return instance
|
||||
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')
|
||||
|
||||
return bus_trace(root, next_busname, type, whitelist)
|
||||
else:
|
||||
raise TypeError('Unsupported AXI4-Stream IP core: %s (%s)' % (instance, vlnv))
|
||||
|
||||
def vlnv_match(vlnv, whitelist):
|
||||
c = vlnv.split(':')
|
||||
|
||||
for w in whitelist:
|
||||
if c[:len(w)] == w:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print('Usage: {} path/to/*.hwdef'.format(sys.argv[0]))
|
||||
print(' {} path/to/*.xml'.format(sys.argv[0]))
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
# read .hwdef which is actually a zip-file
|
||||
zip = zipfile.ZipFile(sys.argv[1], 'r')
|
||||
hwh = zip.read('top.hwh')
|
||||
except:
|
||||
f = open(sys.argv[1], 'r')
|
||||
hwh = f.read()
|
||||
|
||||
# parse .hwh file which is actually XML
|
||||
try:
|
||||
root = etree.XML(hwh)
|
||||
except:
|
||||
print('Bad format of "{}"! Did you choose the right file?'.format(sys.argv[1]))
|
||||
sys.exit(1)
|
||||
|
||||
ips = {}
|
||||
|
||||
# find all whitelisted modules
|
||||
modules = root.find('.//MODULES')
|
||||
for module in modules:
|
||||
instance = module.get('INSTANCE')
|
||||
vlnv = module.get('VLNV')
|
||||
|
||||
# Ignroing unkown
|
||||
if not vlnv_match(vlnv, whitelist):
|
||||
continue
|
||||
|
||||
ips[instance] = {
|
||||
'vlnv' : vlnv,
|
||||
'irqs' : { },
|
||||
'ports' : { }
|
||||
}
|
||||
|
||||
# find PCI-e module to extract memory map
|
||||
pcie = root.find('.//MODULE[@MODTYPE="axi_pcie"]')
|
||||
mmap = pcie.find('.//MEMORYMAP')
|
||||
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);
|
||||
|
||||
# find AXI-Stream switch port mapping
|
||||
switch = root.find('.//MODULE[@MODTYPE="axis_switch"]')
|
||||
busifs = switch.find('.//BUSINTERFACES')
|
||||
for busif in busifs:
|
||||
if busif.get('VLNV') != 'xilinx.com:interface:axis:1.0':
|
||||
continue
|
||||
|
||||
busname = busif.get('BUSNAME')
|
||||
name = busif.get('NAME')
|
||||
type = busif.get('TYPE')
|
||||
|
||||
r = re.compile('(M|S)([0-9]+)_AXIS')
|
||||
m = r.search(name)
|
||||
|
||||
port = int(m.group(2))
|
||||
|
||||
ep = bus_trace(root, busname, opponent[type], whitelist)
|
||||
|
||||
if ep in ips:
|
||||
ips[ep]['ports'][type.lower()] = port
|
||||
|
||||
# find Interrupt assignments
|
||||
intc = root.find('.//MODULE[@MODTYPE="axi_pcie_intc"]')
|
||||
intr = intc.xpath('.//PORT[@NAME="intr" and @DIR="I"]')[0]
|
||||
concat = root.xpath('.//MODULE[@MODTYPE="xlconcat" and .//PORT[@SIGNAME="{}" and @DIR="O"]]'.format(intr.get('SIGNAME')))[0]
|
||||
ports = concat.xpath('.//PORT[@DIR="I"]')
|
||||
|
||||
for port in ports:
|
||||
name = port.get('NAME')
|
||||
signame = port.get('SIGNAME')
|
||||
|
||||
# Skip unconnected IRQs
|
||||
if not signame:
|
||||
continue
|
||||
|
||||
r = re.compile('In([0-9+])')
|
||||
m = r.search(name)
|
||||
|
||||
irq = int(m.group(1))
|
||||
ip = root.xpath('.//MODULE[.//PORT[@SIGNAME="{}" and @DIR="O"]]'.format(signame))[0]
|
||||
|
||||
instance = ip.get('INSTANCE')
|
||||
vlnv = ip.get('VLNV')
|
||||
|
||||
port = ip.xpath('.//PORT[@SIGNAME="{}" and @DIR="O"]'.format(signame))[0]
|
||||
irqname = port.get('NAME')
|
||||
|
||||
if instance in ips:
|
||||
ips[instance]['irqs'][irqname] = irq
|
||||
|
||||
# Find BRAM storage depths (size)
|
||||
brams = root.xpath('.//MODULE[@MODTYPE="axi_bram_ctrl"]')
|
||||
for bram in brams:
|
||||
instance = bram.get('INSTANCE')
|
||||
|
||||
width = bram.find('.//PARAMETER[@NAME="DATA_WIDTH"]').get('VALUE')
|
||||
depth = bram.find('.//PARAMETER[@NAME="MEM_DEPTH"]').get('VALUE')
|
||||
|
||||
size = int(width) * int(depth) / 8
|
||||
|
||||
if instance in ips:
|
||||
ips[instance]['size'] = int(size)
|
||||
|
||||
print(json.dumps(ips, indent=2))
|
8119
fpga/scripts/hwdef-villas.xml
Normal file
8119
fpga/scripts/hwdef-villas.xml
Normal file
File diff suppressed because it is too large
Load diff
47
fpga/scripts/rebind_device.sh
Executable file
47
fpga/scripts/rebind_device.sh
Executable file
|
@ -0,0 +1,47 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Detach and rebind a PCI device to a PCI kernel driver
|
||||
#
|
||||
# @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)
|
||||
#
|
||||
# VILLASnode
|
||||
#
|
||||
# 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/>.
|
||||
##################################################################################
|
||||
|
||||
if [ "$#" -ne 2 ]; then
|
||||
echo "usage: $0 BUS:DEV:FNC DRIVER"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BDF=$1
|
||||
DRIVER=$2
|
||||
|
||||
VENDOR=$(cut -b3- /sys/bus/pci/devices/${BDF}/vendor)
|
||||
DEVICE=$(cut -b3- /sys/bus/pci/devices/${BDF}/device)
|
||||
|
||||
SYSFS_DEVICE=/sys/bus/pci/devices/${BDF}
|
||||
SYSFS_DRIVER=/sys/bus/pci/drivers/${DRIVER}
|
||||
|
||||
echo "Device: $VENDOR $DEVICE $BDF"
|
||||
|
||||
if [ -L "${SYSFS_DEVICE}/driver" ] && [ -d "${SYSFS_DEVICE}/driver" ]; then
|
||||
echo ${BDF} > ${SYSFS_DEVICE}/driver/unbind
|
||||
fi
|
||||
|
||||
echo "${VENDOR} ${DEVICE}" > ${SYSFS_DRIVER}/new_id
|
||||
echo "${BDF}" > ${SYSFS_DRIVER}/bind
|
||||
echo "${VENDOR} ${DEVICE}" > ${SYSFS_DRIVER}/remove_id
|
35
fpga/scripts/reset_pci_device.sh
Executable file
35
fpga/scripts/reset_pci_device.sh
Executable file
|
@ -0,0 +1,35 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Reset PCI devices like FPGAs after a reflash
|
||||
#
|
||||
# @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)
|
||||
#
|
||||
# VILLASnode
|
||||
#
|
||||
# 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/>.
|
||||
##################################################################################
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "usage: $0 BUS:DEV:FNC"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BDF=$1
|
||||
|
||||
echo "1" > /sys/bus/pci/devices/$BDF/remove
|
||||
echo "1" > /sys/bus/pci/rescan
|
||||
echo "1" > /sys/bus/pci/devices/$BDF/enable
|
||||
|
24
fpga/tests/CMakeLists.txt
Normal file
24
fpga/tests/CMakeLists.txt
Normal file
|
@ -0,0 +1,24 @@
|
|||
set(SOURCES
|
||||
main.c
|
||||
dma.c
|
||||
fifo.c
|
||||
hls.c
|
||||
intc.c
|
||||
rtds_rtt.c
|
||||
tmrctr.c
|
||||
xsg.c
|
||||
)
|
||||
|
||||
add_executable(unit-tests ${SOURCES})
|
||||
|
||||
find_package(Criterion REQUIRED)
|
||||
|
||||
target_include_directories(unit-tests PUBLIC
|
||||
../include
|
||||
${CRITERION_INCLUDE_DIRECTORIES}
|
||||
)
|
||||
|
||||
target_link_libraries(unit-tests PUBLIC
|
||||
villas-fpga
|
||||
${CRITERION_LIBRARIES}
|
||||
)
|
94
fpga/tests/dma.c
Normal file
94
fpga/tests/dma.c
Normal file
|
@ -0,0 +1,94 @@
|
|||
/** DMA 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/dma.h>
|
||||
|
||||
#include <villas/log.h>
|
||||
#include <villas/list.h>
|
||||
|
||||
extern struct fpga_card *card;
|
||||
|
||||
Test(fpga, dma, .description = "DMA")
|
||||
{
|
||||
int ret = -1;
|
||||
struct dma_mem mem, src, dst;
|
||||
|
||||
for (size_t i = 0; i < list_length(&card->ips); i++) { INDENT
|
||||
struct fpga_ip *dm = (struct fpga_ip *) list_at(&card->ips, i);
|
||||
|
||||
if (fpga_vlnv_cmp(&dm->vlnv, &(struct fpga_vlnv) { "xilinx.com", "ip", "axi_dma", NULL }))
|
||||
continue; /* skip non DMA IP cores */
|
||||
|
||||
struct dma *dma = (struct dma *) dm->_vd;
|
||||
|
||||
/* Simple DMA can only transfer up to 4 kb due to
|
||||
* PCIe page size burst limitation */
|
||||
ssize_t len2, len = dma->inst.HasSg ? 64 << 20 : 1 << 2;
|
||||
|
||||
ret = dma_alloc(dm, &mem, 2 * len, 0);
|
||||
cr_assert_eq(ret, 0);
|
||||
|
||||
ret = dma_mem_split(&mem, &src, &dst);
|
||||
cr_assert_eq(ret, 0);
|
||||
|
||||
/* Get new random data */
|
||||
len2 = read_random(src.base_virt, len);
|
||||
if (len2 != len)
|
||||
serror("Failed to get random data");
|
||||
|
||||
int irq_mm2s = dm->irq;
|
||||
int irq_s2mm = dm->irq + 1;
|
||||
|
||||
ret = intc_enable(card->intc, (1 << irq_mm2s) | (1 << irq_s2mm), 0);
|
||||
cr_assert_eq(ret, 0, "Failed to enable interrupt");
|
||||
|
||||
ret = switch_connect(card->sw, dm, dm);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
/* Start transfer */
|
||||
ret = dma_ping_pong(dm, src.base_phys, dst.base_phys, dst.len);
|
||||
cr_assert_eq(ret, 0, "DMA ping pong failed");
|
||||
|
||||
ret = memcmp(src.base_virt, dst.base_virt, src.len);
|
||||
|
||||
info("DMA %s (%s): %s", dm->name, dma->inst.HasSg ? "scatter-gather" : "simple", ret ? CLR_RED("failed") : CLR_GRN("passed"));
|
||||
|
||||
ret = switch_disconnect(card->sw, dm, dm);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
ret = intc_disable(card->intc, (1 << irq_mm2s) | (1 << irq_s2mm));
|
||||
cr_assert_eq(ret, 0, "Failed to disable interrupt");
|
||||
|
||||
ret = dma_free(dm, &mem);
|
||||
cr_assert_eq(ret, 0, "Failed to release DMA memory");
|
||||
}
|
||||
|
||||
cr_assert_eq(ret, 0);
|
||||
}
|
74
fpga/tests/fifo.c
Normal file
74
fpga/tests/fifo.c
Normal file
|
@ -0,0 +1,74 @@
|
|||
/** 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);
|
||||
}
|
80
fpga/tests/hls.c
Normal file
80
fpga/tests/hls.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
/** HLS 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 <unistd.h>
|
||||
|
||||
#include <criterion/criterion.h>
|
||||
|
||||
#include <villas/fpga/card.h>
|
||||
#include <villas/fpga/vlnv.h>
|
||||
#include <villas/fpga/ip.h>
|
||||
|
||||
extern struct fpga_card *card;
|
||||
|
||||
Test(fpga, hls_dft, .description = "HLS: hls_dft")
|
||||
{
|
||||
int ret;
|
||||
struct fpga_ip *hls, *rtds;
|
||||
|
||||
rtds = fpga_vlnv_lookup(&card->ips, &(struct fpga_vlnv) { "acs.eonerc.rwth-aachen.de", "user", "rtds_axis", NULL });
|
||||
hls = fpga_vlnv_lookup(&card->ips, &(struct fpga_vlnv) { NULL, "hls", "hls_dft", NULL });
|
||||
|
||||
/* Check if required IP is available on FPGA */
|
||||
cr_assert(hls && rtds);
|
||||
|
||||
ret = intc_enable(card->intc, (1 << rtds->irq), 0);
|
||||
cr_assert_eq(ret, 0, "Failed to enable interrupt");
|
||||
|
||||
ret = switch_connect(card->sw, rtds, hls);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
ret = switch_connect(card->sw, hls, rtds);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
while(1) {
|
||||
/* Dump RTDS AXI Stream state */
|
||||
rtds_axis_dump(rtds);
|
||||
sleep(1);
|
||||
}
|
||||
#if 0
|
||||
int len = 2000;
|
||||
int NSAMPLES = 400;
|
||||
float src[len], dst[len];
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
src[i] = 4 + 5.0 * sin(2.0 * M_PI * 1 * i / NSAMPLES) +
|
||||
2.0 * sin(2.0 * M_PI * 2 * i / NSAMPLES) +
|
||||
1.0 * sin(2.0 * M_PI * 5 * i / NSAMPLES) +
|
||||
0.5 * sin(2.0 * M_PI * 9 * i / NSAMPLES) +
|
||||
0.2 * sin(2.0 * M_PI * 15 * i / NSAMPLES);
|
||||
|
||||
fifo_write()
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = switch_disconnect(card->sw, rtds, hls);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
ret = switch_disconnect(card->sw, hls, rtds);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
}
|
57
fpga/tests/intc.c
Normal file
57
fpga/tests/intc.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
/** Intc 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/fpga/card.h>
|
||||
#include <villas/fpga/ip.h>
|
||||
|
||||
#include <villas/fpga/ips/intc.h>
|
||||
|
||||
extern struct fpga_card *card;
|
||||
|
||||
Test(fpga, intc, .description = "Interrupt Controller")
|
||||
{
|
||||
int ret;
|
||||
uint32_t isr;
|
||||
|
||||
cr_assert(card->intc);
|
||||
|
||||
ret = intc_enable(card->intc, 0xFF00, 0);
|
||||
cr_assert_eq(ret, 0, "Failed to enable interrupt");
|
||||
|
||||
/* Fake IRQs in software by writing to ISR */
|
||||
XIntc_Out32((uintptr_t) card->map + card->intc->baseaddr + XIN_ISR_OFFSET, 0xFF00);
|
||||
|
||||
/* Wait for 8 SW triggered IRQs */
|
||||
for (int i = 0; i < 8; i++)
|
||||
intc_wait(card->intc, i+8);
|
||||
|
||||
/* Check ISR if all SW IRQs have been deliverd */
|
||||
isr = XIntc_In32((uintptr_t) card->map + card->intc->baseaddr + XIN_ISR_OFFSET);
|
||||
|
||||
ret = intc_disable(card->intc, 0xFF00);
|
||||
cr_assert_eq(ret, 0, "Failed to disable interrupt");
|
||||
|
||||
cr_assert_eq(isr & 0xFF00, 0); /* ISR should get cleared by MSI_Grant_signal */
|
||||
}
|
91
fpga/tests/main.c
Normal file
91
fpga/tests/main.c
Normal file
|
@ -0,0 +1,91 @@
|
|||
/** Main Unit Test entry point.
|
||||
*
|
||||
* @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 <criterion/options.h>
|
||||
|
||||
#include <villas/utils.h>
|
||||
#include <villas/fpga/ip.h>
|
||||
#include <villas/fpga/card.h>
|
||||
#include <villas/fpga/vlnv.h>
|
||||
|
||||
#define TEST_CONFIG "/villas/etc/fpga.conf"
|
||||
#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;
|
||||
|
||||
static void init()
|
||||
{
|
||||
int ret;
|
||||
|
||||
FILE *f;
|
||||
json_error_t err;
|
||||
json_t *json;
|
||||
|
||||
ret = pci_init(&pci);
|
||||
cr_assert_eq(ret, 0, "Failed to initialize PCI sub-system");
|
||||
|
||||
ret = vfio_init(&vc);
|
||||
cr_assert_eq(ret, 0, "Failed to initiliaze VFIO sub-system");
|
||||
|
||||
/* Parse FPGA configuration */
|
||||
f = fopen(TEST_CONFIG, "r");
|
||||
cr_assert_not_null(f);
|
||||
|
||||
json = json_loadf(f, 0, &err);
|
||||
cr_assert_not_null(json);
|
||||
|
||||
fclose(f);
|
||||
|
||||
list_init(&cards);
|
||||
ret = fpga_card_parse_list(&cards, json);
|
||||
cr_assert_eq(ret, 0, "Failed to parse FPGA config");
|
||||
|
||||
json_decref(json);
|
||||
|
||||
card = list_lookup(&cards, "vc707");
|
||||
cr_assert(card, "FPGA card not found");
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
TestSuite(fpga,
|
||||
.init = init,
|
||||
.fini = fini,
|
||||
.description = "VILLASfpga"
|
||||
);
|
81
fpga/tests/rtds_rtt.c
Normal file
81
fpga/tests/rtds_rtt.c
Normal file
|
@ -0,0 +1,81 @@
|
|||
/** RTDS AXI-Stream RTT 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/fpga/card.h>
|
||||
#include <villas/fpga/vlnv.h>
|
||||
|
||||
#include <villas/fpga/ips/dma.h>
|
||||
#include <villas/fpga/ips/switch.h>
|
||||
#include <villas/fpga/ips/rtds_axis.h>
|
||||
|
||||
extern struct fpga_card *card;
|
||||
|
||||
Test(fpga, rtds_rtt, .description = "RTDS: tight rtt")
|
||||
{
|
||||
int ret;
|
||||
struct fpga_ip *ip, *rtds;
|
||||
struct dma_mem buf;
|
||||
size_t recvlen;
|
||||
|
||||
/* Get IP cores */
|
||||
rtds = fpga_vlnv_lookup(&card->ips, &(struct fpga_vlnv) { "acs.eonerc.rwth-aachen.de", "user", "rtds_axis", NULL });
|
||||
cr_assert(rtds);
|
||||
|
||||
ip = fpga_vlnv_lookup(&card->ips, &(struct fpga_vlnv) { "xilinx.com", "ip", "axi_dma", NULL });
|
||||
cr_assert(ip);
|
||||
|
||||
ret = switch_connect(card->sw, rtds, ip);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
ret = switch_connect(card->sw, ip, rtds);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
ret = dma_alloc(ip, &buf, 0x100, 0);
|
||||
cr_assert_eq(ret, 0, "Failed to allocate DMA memory");
|
||||
|
||||
while (1) {
|
||||
|
||||
ret = dma_read(ip, buf.base_phys, buf.len);
|
||||
cr_assert_eq(ret, 0, "Failed to start DMA read: %d", ret);
|
||||
|
||||
ret = dma_read_complete(ip, NULL, &recvlen);
|
||||
cr_assert_eq(ret, 0, "Failed to complete DMA read: %d", ret);
|
||||
|
||||
ret = dma_write(ip, buf.base_phys, recvlen);
|
||||
cr_assert_eq(ret, 0, "Failed to start DMA write: %d", ret);
|
||||
|
||||
ret = dma_write_complete(ip, NULL, NULL);
|
||||
cr_assert_eq(ret, 0, "Failed to complete DMA write: %d", ret);
|
||||
}
|
||||
|
||||
ret = switch_disconnect(card->sw, rtds, ip);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
ret = switch_disconnect(card->sw, ip, rtds);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
ret = dma_free(ip, &buf);
|
||||
cr_assert_eq(ret, 0, "Failed to release DMA memory");
|
||||
}
|
70
fpga/tests/tmrctr.c
Normal file
70
fpga/tests/tmrctr.c
Normal file
|
@ -0,0 +1,70 @@
|
|||
/** 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 <criterion/criterion.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <villas/log.h>
|
||||
|
||||
#include <villas/fpga/card.h>
|
||||
#include <villas/fpga/vlnv.h>
|
||||
#include <villas/fpga/ip.h>
|
||||
|
||||
#include <villas/fpga/ips/timer.h>
|
||||
|
||||
extern struct fpga_card *card;
|
||||
|
||||
Test(fpga, timer, .description = "Timer Counter")
|
||||
{
|
||||
int ret;
|
||||
struct fpga_ip *ip;
|
||||
struct timer *tmr;
|
||||
|
||||
ip = fpga_vlnv_lookup(&card->ips, &(struct fpga_vlnv) { "xilinx.com", "ip", "axi_timer", NULL });
|
||||
cr_assert(ip);
|
||||
|
||||
tmr = (struct timer *) ip->_vd;
|
||||
|
||||
XTmrCtr *xtmr = &tmr->inst;
|
||||
|
||||
ret = intc_enable(card->intc, (1 << ip->irq), 0);
|
||||
cr_assert_eq(ret, 0, "Failed to enable interrupt");
|
||||
|
||||
XTmrCtr_SetOptions(xtmr, 0, XTC_EXT_COMPARE_OPTION | XTC_DOWN_COUNT_OPTION);
|
||||
XTmrCtr_SetResetValue(xtmr, 0, FPGA_AXI_HZ / 125);
|
||||
XTmrCtr_Start(xtmr, 0);
|
||||
|
||||
uint64_t counter = intc_wait(card->intc, ip->irq);
|
||||
info("Got IRQ: counter = %ju", counter);
|
||||
|
||||
if (counter == 1)
|
||||
return;
|
||||
else
|
||||
warn("Counter was not 1");
|
||||
|
||||
intc_disable(card->intc, (1 << ip->irq));
|
||||
cr_assert_eq(ret, 0, "Failed to disable interrupt");
|
||||
|
||||
return;
|
||||
}
|
97
fpga/tests/xsg.c
Normal file
97
fpga/tests/xsg.c
Normal file
|
@ -0,0 +1,97 @@
|
|||
/** System Generator 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 <math.h>
|
||||
|
||||
#include <criterion/criterion.h>
|
||||
|
||||
#include <villas/log.h>
|
||||
|
||||
#include <villas/fpga/card.h>
|
||||
#include <villas/fpga/ip.h>
|
||||
#include <villas/fpga/vlnv.h>
|
||||
|
||||
#include <villas/fpga/ips/dma.h>
|
||||
|
||||
extern struct fpga_card *card;
|
||||
|
||||
Test(fpga, xsg, .description = "XSG: multiply_add")
|
||||
{
|
||||
int ret;
|
||||
double factor, err = 0;
|
||||
|
||||
struct fpga_ip *ip, *dma;
|
||||
struct model_parameter *p;
|
||||
struct dma_mem mem;
|
||||
|
||||
ip = fpga_vlnv_lookup(&card->ips, &(struct fpga_vlnv) { NULL, "sysgen", "xsg_multiply", NULL });
|
||||
cr_assert(ip);
|
||||
|
||||
dma = fpga_vlnv_lookup(&card->ips, &(struct fpga_vlnv) { "xilinx.com", "ip", "axi_dma", NULL });
|
||||
cr_assert(dma);
|
||||
|
||||
struct model *model = (struct model *) ip->_vd;
|
||||
|
||||
p = list_lookup(&model->parameters, "factor");
|
||||
if (!p)
|
||||
error("Missing parameter 'factor' for model '%s'", ip->name);
|
||||
|
||||
ret = model_parameter_read(p, &factor);
|
||||
cr_assert_eq(ret, 0, "Failed to read parameter 'factor' from model '%s'", ip->name);
|
||||
|
||||
info("Model param: factor = %f", factor);
|
||||
|
||||
ret = switch_connect(card->sw, dma, ip);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
ret = switch_connect(card->sw, ip, dma);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
ret = dma_alloc(dma, &mem, 0x1000, 0);
|
||||
cr_assert_eq(ret, 0, "Failed to allocate DMA memory");
|
||||
|
||||
float *src = (float *) mem.base_virt;
|
||||
float *dst = (float *) mem.base_virt + 0x800;
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
src[i] = 1.1 * (i+1);
|
||||
|
||||
ret = dma_ping_pong(dma, (char *) src, (char *) dst, 6 * sizeof(float));
|
||||
cr_assert_eq(ret, 0, "Failed to to ping pong DMA transfer: %d", ret);
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
err += fabs(factor * src[i] - dst[i]);
|
||||
|
||||
info("Error after FPGA operation: err = %f", err);
|
||||
|
||||
ret = switch_disconnect(card->sw, dma, ip);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
ret = switch_disconnect(card->sw, ip, dma);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
ret = dma_free(dma, &mem);
|
||||
cr_assert_eq(ret, 0, "Failed to release DMA memory");
|
||||
|
||||
cr_assert(err < 1e-3);
|
||||
}
|
Loading…
Add table
Reference in a new issue