mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
remove old C code
This commit is contained in:
parent
72cfade589
commit
7ccb23d8b4
15 changed files with 0 additions and 1799 deletions
|
@ -1,52 +0,0 @@
|
|||
/** Moving window / Recursive DFT implementation based on HLS
|
||||
*
|
||||
* @file
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017-2022, 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);
|
||||
|
||||
/** @} */
|
|
@ -1,153 +0,0 @@
|
|||
/** Interface to Xilinx System Generator Models via PCIe
|
||||
*
|
||||
* @file
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017-2022, 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 {
|
||||
// Name of the parameter
|
||||
char *name;
|
||||
|
||||
// Read / Write / Read-write?
|
||||
enum model_parameter_direction direction;
|
||||
// Data type. Integers are represented by MODEL_GW_TYPE_(U)FIX with model_gw::binpt == 0
|
||||
enum model_parameter_type type;
|
||||
|
||||
// Binary point for type == MODEL_GW_TYPE_(U)FIX
|
||||
int binpt;
|
||||
// Register offset to model::baseaddress
|
||||
uintptr_t offset;
|
||||
|
||||
union model_parameter_value default_value;
|
||||
|
||||
// A pointer to the model structure to which this parameters belongs to.
|
||||
struct fpga_ip *ip;
|
||||
};
|
||||
|
||||
// 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);
|
||||
|
||||
/** @} */
|
|
@ -1,140 +0,0 @@
|
|||
/** Moving window / Recursive DFT implementation based on HLS
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017-2022, 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 <villas/log.h>
|
||||
#include <villas/log_config.h>
|
||||
#include <villas/plugin.h>
|
||||
|
||||
#include <villas/fpga/ip.h>
|
||||
#include <villas/fpga/card.h>
|
||||
#include <villas/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)
|
|
@ -1,427 +0,0 @@
|
|||
/** Interface to Xilinx System Generator Models via PCIe
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017-2022, 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 <villas/utils.hpp>
|
||||
#include <villas/log.h>
|
||||
#include <villas/log_config.h>
|
||||
#include <villas/plugin.h>
|
||||
|
||||
#include <villas/fpga/ip.h>
|
||||
#include <villas/fpga/card.h>
|
||||
#include <villas/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)
|
|
@ -1,141 +0,0 @@
|
|||
/** Data mover benchmarks.
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017-2022, 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 <villas/utils.hpp>
|
||||
#include <villas/log.h>
|
||||
|
||||
#include <villas/fpga/card.h>
|
||||
#include <villas/fpga/ip.h>
|
||||
|
||||
#include <villas/fpga/ips/dma.h>
|
||||
#include <villas/fpga/ips/switch.h>
|
||||
#include <villas/fpga/ips/intc.h>
|
||||
|
||||
#include "bench.h"
|
||||
|
||||
int fpga_benchmark_datamover(struct fpga_card *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct fpga_ip *dm;
|
||||
struct dma_mem mem, src, dst;
|
||||
|
||||
#if BENCH_DM == 1
|
||||
char *dm_name = "fifo_mm_s_0";
|
||||
#elif BENCH_DM == 2
|
||||
char *dm_name = "dma_0";
|
||||
#elif BENCH_DM == 3
|
||||
char *dm_name = "dma_1";
|
||||
#else
|
||||
#error "Invalid DM selected"
|
||||
#endif
|
||||
|
||||
dm = list_lookup(&c->ips, dm_name);
|
||||
if (!dm)
|
||||
error("Unknown datamover");
|
||||
|
||||
ret = switch_connect(c->sw, dm, dm);
|
||||
if (ret)
|
||||
error("Failed to configure switch");
|
||||
|
||||
ret = intc_enable(c->intc, (1 << dm->irq) | (1 << (dm->irq + 1)), intc_flags);
|
||||
if (ret)
|
||||
error("Failed to enable interrupt");
|
||||
|
||||
// Allocate DMA memory
|
||||
ret = dma_alloc(dm, &mem, 2 * (1 << BENCH_DM_EXP_MAX), 0);
|
||||
if (ret)
|
||||
error("Failed to allocate DMA memory");
|
||||
|
||||
ret = dma_mem_split(&mem, &src, &dst);
|
||||
if (ret)
|
||||
return -1;
|
||||
|
||||
// Open file for results
|
||||
char fn[256];
|
||||
snprintf(fn, sizeof(fn), "results/datamover_%s_%s_%s.dat", dm_name, intc_flags & INTC_POLLING ? "polling" : "irq", uts.release);
|
||||
FILE *g = fopen(fn, "w");
|
||||
|
||||
for (int exp = BENCH_DM_EXP_MIN; exp <= BENCH_DM_EXP_MAX; exp++) {
|
||||
uint64_t start, stop, total = 0, len = 1 << exp;
|
||||
|
||||
#if BENCH_DM == 1
|
||||
if (exp > 11)
|
||||
break; // FIFO and Simple DMA are limited to 4kb
|
||||
#elif BENCH_DM == 3
|
||||
if (exp >= 12)
|
||||
break; // FIFO and Simple DMA are limited to 4kb
|
||||
#endif
|
||||
|
||||
read_random(src.base_virt, len);
|
||||
memset(dst.base_virt, 0, len);
|
||||
|
||||
info("Start DM bench: len=%#jx", len);
|
||||
|
||||
uint64_t runs = BENCH_RUNS >> exp;
|
||||
for (int i = 0; i < runs + BENCH_WARMUP; i++) {
|
||||
start = rdtsc();
|
||||
#if BENCH_DM == 1
|
||||
ssize_t ret;
|
||||
|
||||
ret = fifo_write(dm, src.base_virt, len);
|
||||
if (ret < 0)
|
||||
error("Failed write to FIFO with len = %zu", len);
|
||||
|
||||
ret = fifo_read(dm, dst.base_virt, dst.len);
|
||||
if (ret < 0)
|
||||
error("Failed read from FIFO with len = %zu", len);
|
||||
#else
|
||||
ret = dma_ping_pong(dm, src.base_phys, dst.base_phys, len);
|
||||
if (ret)
|
||||
error("DMA ping pong failed");
|
||||
#endif
|
||||
stop = rdtsc();
|
||||
|
||||
if (memcmp(src.base_virt, dst.base_virt, len))
|
||||
warn("Compare failed");
|
||||
|
||||
if (i > BENCH_WARMUP)
|
||||
total += stop - start;
|
||||
}
|
||||
|
||||
info("exp %u avg %lu", exp, total / runs);
|
||||
fprintf(g, "%lu %lu\n", len, total / runs);
|
||||
}
|
||||
|
||||
fclose(g);
|
||||
|
||||
ret = switch_disconnect(c->sw, dm, dm);
|
||||
if (ret)
|
||||
error("Failed to configure switch");
|
||||
|
||||
ret = dma_free(dm, &mem);
|
||||
if (ret)
|
||||
error("Failed to release DMA memory");
|
||||
|
||||
ret = intc_disable(c->intc, (1 << dm->irq) | (1 << (dm->irq + 1)));
|
||||
if (ret)
|
||||
error("Failed to enable interrupt");
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
/** Jitter benchmarks.
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017-2022, 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 <villas/utils.hpp>
|
||||
|
||||
#include <villas/fpga/card.h>
|
||||
#include <villas/fpga/ip.h>
|
||||
|
||||
#include <villas/fpga/ips/timer.h>
|
||||
|
||||
#include "bench.h"
|
||||
|
||||
int fpga_benchmark_jitter(struct fpga_card *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct fpga_ip *ip = list_lookup(&c->ips, "timer_0");
|
||||
if (!ip || !c->intc)
|
||||
return -1;
|
||||
|
||||
struct timer *tmr = (struct timer *) ip->_vd;
|
||||
|
||||
XTmrCtr *xtmr = &tmr->inst;
|
||||
|
||||
ret = intc_enable(c->intc, (1 << ip->irq), intc_flags);
|
||||
if (ret)
|
||||
error("Failed to enable interrupt");
|
||||
|
||||
float period = 50e-6;
|
||||
int runs = 300.0 / period;
|
||||
|
||||
int *hist = alloc(8 << 20);
|
||||
|
||||
XTmrCtr_SetOptions(xtmr, 0, XTC_INT_MODE_OPTION | XTC_EXT_COMPARE_OPTION | XTC_DOWN_COUNT_OPTION | XTC_AUTO_RELOAD_OPTION);
|
||||
XTmrCtr_SetResetValue(xtmr, 0, period * FPGA_AXI_HZ);
|
||||
XTmrCtr_Start(xtmr, 0);
|
||||
|
||||
uint64_t end, start = rdtsc();
|
||||
for (int i = 0; i < runs; i++) {
|
||||
uint64_t cnt = intc_wait(c->intc, ip->irq);
|
||||
if (cnt != 1)
|
||||
warn("fail");
|
||||
|
||||
// Ackowledge IRQ
|
||||
XTmrCtr_WriteReg((uintptr_t) c->map + ip->baseaddr, 0, XTC_TCSR_OFFSET, XTmrCtr_ReadReg((uintptr_t) c->map + ip->baseaddr, 0, XTC_TCSR_OFFSET));
|
||||
|
||||
end = rdtsc();
|
||||
hist[i] = end - start;
|
||||
start = end;
|
||||
}
|
||||
|
||||
XTmrCtr_Stop(xtmr, 0);
|
||||
|
||||
char fn[256];
|
||||
snprintf(fn, sizeof(fn), "results/jitter_%s_%s.dat", intc_flags & INTC_POLLING ? "polling" : "irq", uts.release);
|
||||
FILE *g = fopen(fn, "w");
|
||||
for (int i = 0; i < runs; i++)
|
||||
fprintf(g, "%u\n", hist[i]);
|
||||
fclose(g);
|
||||
|
||||
free(hist);
|
||||
|
||||
ret = intc_disable(c->intc, (1 << ip->irq));
|
||||
if (ret)
|
||||
error("Failed to disable interrupt");
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
/** Latency benchmarks.
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017-2022, 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 <stdio.h>
|
||||
|
||||
#include <villas/log.h>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
#include <villas/fpga/card.h>
|
||||
#include <villas/fpga/ip.h>
|
||||
|
||||
#include "bench.h"
|
||||
|
||||
int fpga_benchmark_latency(struct fpga_card *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
uint64_t start, end;
|
||||
|
||||
if (!c->intc)
|
||||
return -1;
|
||||
|
||||
int runs = 1000000;
|
||||
int hist[runs];
|
||||
|
||||
ret = intc_enable(c->intc, 0x100, intc_flags);
|
||||
if (ret)
|
||||
error("Failed to enable interrupts");
|
||||
|
||||
for (int i = 0; i < runs; i++) {
|
||||
start = rdtsc();
|
||||
XIntc_Out32((uintptr_t) c->map + c->intc->baseaddr + XIN_ISR_OFFSET, 0x100);
|
||||
|
||||
intc_wait(c->intc, 8);
|
||||
end = rdtsc();
|
||||
|
||||
hist[i] = end - start;
|
||||
}
|
||||
|
||||
char fn[256];
|
||||
snprintf(fn, sizeof(fn), "results/latency_%s_%s.dat", intc_flags & INTC_POLLING ? "polling" : "irq", uts.release);
|
||||
FILE *g = fopen(fn, "w");
|
||||
for (int i = 0; i < runs; i++)
|
||||
fprintf(g, "%u\n", hist[i]);
|
||||
fclose(g);
|
||||
|
||||
ret = intc_disable(c->intc, 0x100);
|
||||
if (ret)
|
||||
error("Failed to disable interrupt");
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
/** Memcpy benchmarks.
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017-2022, 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 <villas/utils.hpp>
|
||||
|
||||
#include <villas/fpga/card.h>
|
||||
|
||||
#include <villas/fpga/ips/intc.h>
|
||||
|
||||
#include "bench.h"
|
||||
|
||||
int fpga_benchmark_memcpy(struct fpga_card *c)
|
||||
{
|
||||
char *map = c->map + 0x200000;
|
||||
uint32_t *mapi = (uint32_t *) map;
|
||||
|
||||
char fn[256];
|
||||
snprintf(fn, sizeof(fn), "results/bar0_%s_%s.dat", intc_flags & INTC_POLLING ? "polling" : "irq", uts.release);
|
||||
FILE *g = fopen(fn, "w");
|
||||
fprintf(g, "# bytes cycles\n");
|
||||
|
||||
uint32_t dummy = 0;
|
||||
|
||||
for (int exp = BENCH_DM_EXP_MIN; exp <= BENCH_DM_EXP_MAX; exp++) {
|
||||
uint64_t len = 1 << exp;
|
||||
uint64_t start, end, total = 0;
|
||||
uint64_t runs = (BENCH_RUNS << 2) >> exp;
|
||||
|
||||
for (int i = 0; i < runs + BENCH_WARMUP; i++) {
|
||||
start = rdtsc();
|
||||
|
||||
for (int j = 0; j < len / 4; j++)
|
||||
// mapi[j] = j; // Write
|
||||
dummy += mapi[j]; // Read
|
||||
|
||||
end = rdtsc();
|
||||
|
||||
if (i > BENCH_WARMUP)
|
||||
total += end - start;
|
||||
}
|
||||
|
||||
info("exp = %u\truns = %ju\ttotal = %ju\tavg = %ju\tavgw = %ju", exp, runs, total, total / runs, total / (runs * len));
|
||||
fprintf(g, "%zu %lu %ju\n", len, total / runs, runs);
|
||||
}
|
||||
|
||||
fclose(g);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
/** Benchmarks for VILLASfpga: LAPACK & BLAS
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017-2022, 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 <stdio.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include <fpga/card.h>
|
||||
#include <fpga/ip.h>
|
||||
#include <fpga/ips/switch.h>
|
||||
#include <fpga/ips/intc.h>
|
||||
#include <utils.h>
|
||||
#include <villas/log.h>
|
||||
|
||||
#include "bench.h"
|
||||
|
||||
// Some hard-coded configuration for the FPGA benchmarks
|
||||
#define BENCH_WARMUP 100
|
||||
|
||||
// Declared in fpga-bench.c
|
||||
extern int intc_flags;
|
||||
extern struct utsname uts;
|
||||
|
||||
// LAPACK & BLAS Fortran prototypes
|
||||
extern int dgemm_(char *transa, char *transb, int *m, int *n, int *k, double *alpha, double *a, int *lda, double *b, int *ldb, double *beta, double *c, int *ldc);
|
||||
extern int dgetrf_(int *m, int *n, double *a, int *lda, int *ipiv, int *info);
|
||||
extern int dgetri_(int *n, double *a, int *lda, int *ipiv, double *work, int *lwork, int *info);
|
||||
|
||||
static int lapack_generate_workload(int N, double *C)
|
||||
{
|
||||
double *A = alloc(N * N * sizeof(double));
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
for (int i = 0; i < N * N; i++)
|
||||
A[i] = 100 * (double) rand() / RAND_MAX + 1;
|
||||
|
||||
char transA = 'T';
|
||||
char transB = 'N';
|
||||
double alpha = 1;
|
||||
double beta = 1;
|
||||
|
||||
// C = A' * A, to get an invertible matrix
|
||||
dgemm_(&transA, &transB, &N, &N, &N, &alpha, A, &N, A, &N, &beta, C, &N);
|
||||
|
||||
free(A);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lapack_workload(int N, double *A)
|
||||
{
|
||||
int info = 0;
|
||||
int lworkspace = N;
|
||||
int ipiv[N];
|
||||
double workspace[N];
|
||||
|
||||
dgetrf_(&N, &N, A, &N, ipiv, &info);
|
||||
if (info > 0)
|
||||
error("Failed to pivot matrix");
|
||||
|
||||
dgetri_(&N, A, &N, ipiv, workspace, &lworkspace, &info);
|
||||
if (info > 0)
|
||||
error("Failed to LU factorized matrix");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fpga_benchmark_overruns(struct fpga_card *c)
|
||||
{
|
||||
struct fpga_ip *rtds, *dm;
|
||||
|
||||
dm = list_lookup(&c->ips, "dma_1");
|
||||
rtds = list_lookup(&c->ips, "rtds_axis_0");
|
||||
if (!rtds || !c->intc)
|
||||
return -1;
|
||||
|
||||
int ret;
|
||||
float period = 50e-6;
|
||||
int runs = 1.0 / period;
|
||||
int overruns;
|
||||
|
||||
info("runs = %u", runs);
|
||||
|
||||
switch_connect(c->sw, dm, rtds);
|
||||
switch_connect(c->sw, rtds, dm);
|
||||
|
||||
intc_enable(c->intc, (1 << (dm->irq + 1 )), intc_flags);
|
||||
|
||||
// Dump results
|
||||
char fn[256];
|
||||
snprintf(fn, sizeof(fn), "results/overruns_lu_rtds_axis_%s_%s.dat", intc_flags & INTC_POLLING ? "polling" : "irq", uts.release);
|
||||
FILE *g = fopen(fn, "w");
|
||||
fprintf(g, "# period = %f\n", period);
|
||||
fprintf(g, "# runs = %u\n", runs);
|
||||
|
||||
struct dma_mem mem;
|
||||
ret = dma_alloc(dm, &mem, 0x1000, 0);
|
||||
if (ret)
|
||||
error("Failed to allocate DMA memory");
|
||||
|
||||
uint32_t *data_rx = (uint32_t *) mem.base_virt;
|
||||
uint32_t *data_tx = (uint32_t *) mem.base_virt + 0x200;
|
||||
uint64_t total, start, stop;
|
||||
for (int p = 3; p < 45; p++) {
|
||||
double *A = alloc(p*p*sizeof(double));
|
||||
|
||||
lapack_generate_workload(p, A);
|
||||
|
||||
overruns = 0;
|
||||
total = 0;
|
||||
|
||||
for (int i = 0; i < 2000; i++) {
|
||||
dma_read(dm, mem.base_phys, 0x200);
|
||||
dma_read_complete(dm, NULL, NULL);
|
||||
}
|
||||
|
||||
for (int i = 0; i < runs + BENCH_WARMUP; i++) {
|
||||
dma_read(dm, mem.base_phys, 0x200);
|
||||
|
||||
start = rdtsc();
|
||||
lapack_workload(p, A);
|
||||
stop = rdtsc();
|
||||
|
||||
dma_read_complete(dm, NULL, NULL);
|
||||
|
||||
// Send data to rtds
|
||||
data_tx[0] = i;
|
||||
dma_write(dm, mem.base_phys + 0x200, 64 * sizeof(data_tx[0]));
|
||||
|
||||
if (i < BENCH_WARMUP)
|
||||
continue;
|
||||
|
||||
if (i - data_rx[0] > 2)
|
||||
overruns++;
|
||||
total += stop - start;
|
||||
}
|
||||
|
||||
free(A);
|
||||
|
||||
info("iter = %u clks = %ju overruns = %u", p, total / runs, overruns);
|
||||
fprintf(g, "%u %ju %u\n", p, total / runs, overruns);
|
||||
|
||||
if (overruns >= runs)
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(g);
|
||||
return 0;
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
/** Benchmarks for VILLASfpga
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017-2022, 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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/log.h>
|
||||
|
||||
#include <villas/fpga/ip.h>
|
||||
#include <villas/fpga/card.h>
|
||||
#include <villas/fpga/ips/intc.h>
|
||||
#include <villas/fpga/ips/timer.h>
|
||||
|
||||
#include "bench.h"
|
||||
|
||||
#ifdef WITH_LAPACK
|
||||
int fpga_benchmark_overruns(struct fpga_card *c);
|
||||
#endif
|
||||
|
||||
int intc_flags = 0;
|
||||
struct utsname uts;
|
||||
|
||||
int fpga_benchmarks(int argc, char *argv[], struct fpga_card *c)
|
||||
{
|
||||
int ret;
|
||||
struct bench {
|
||||
const char *name;
|
||||
int (*func)(struct fpga_card *c);
|
||||
} benchmarks[] = {
|
||||
{ "datamover", fpga_benchmark_datamover },
|
||||
{ "jitter", fpga_benchmark_jitter },
|
||||
{ "memcpy", fpga_benchmark_memcpy },
|
||||
#ifdef WITH_LAPACK
|
||||
{ "overruns", fpga_benchmark_overruns },
|
||||
#endif
|
||||
{ "latency", fpga_benchmark_latency }
|
||||
};
|
||||
|
||||
if (argc < 2)
|
||||
error("Usage: fpga benchmark (bench)");
|
||||
|
||||
struct bench *bench = NULL;
|
||||
for (int i = 0; i < ARRAY_LEN(benchmarks); i++) {
|
||||
if (strcmp(benchmarks[i].name, argv[1]) == 0) {
|
||||
bench = &benchmarks[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bench == NULL)
|
||||
error("There is no benchmark named: %s", argv[1]);
|
||||
|
||||
ret = uname(&uts);
|
||||
if (ret)
|
||||
return -1;
|
||||
|
||||
again: ret = bench->func(c);
|
||||
if (ret)
|
||||
error("Benchmark %s failed", bench->name);
|
||||
|
||||
// Rerun test with polling
|
||||
if (intc_flags == 0) {
|
||||
intc_flags |= INTC_POLLING;
|
||||
getchar();
|
||||
goto again;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
/** Benchmarks for VILLASfpga
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017-2022, 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/utsname.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
// Some hard-coded configuration for the FPGA benchmarks
|
||||
#define BENCH_DM 3
|
||||
// 1 FIFO
|
||||
// 2 DMA SG
|
||||
// 3 DMA Simple
|
||||
|
||||
#define BENCH_RUNS 3000000
|
||||
#define BENCH_WARMUP 100
|
||||
#define BENCH_DM_EXP_MIN 0
|
||||
#define BENCH_DM_EXP_MAX 20
|
||||
|
||||
int fpga_benchmark_datamover(struct fpga_card *c);
|
||||
int fpga_benchmark_jitter(struct fpga_card *c);
|
||||
int fpga_benchmark_memcpy(struct fpga_card *c);
|
||||
int fpga_benchmark_latency(struct fpga_card *c);
|
||||
|
||||
extern int intc_flags;
|
||||
extern struct utsname uts;
|
124
fpga/src/fpga.c
124
fpga/src/fpga.c
|
@ -1,124 +0,0 @@
|
|||
/** VILLASfpga utility for tests and benchmarks
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017-2022, 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <villas/log.h>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
#include <villas/kernel/pci.hpp>
|
||||
#include <villas/kernel/kernel.hpp>
|
||||
|
||||
#include <villas/fpga/card.h>
|
||||
|
||||
// Declarations
|
||||
int fpga_benchmarks(int argc, char *argv[], struct fpga_card *c);
|
||||
|
||||
void usage()
|
||||
{
|
||||
printf("Usage: villas-fpga [OPTIONS] CONFIG CARD\n\n");
|
||||
printf(" CONFIG path to a configuration file\n");
|
||||
printf(" CARD name of the FPGA card\n");
|
||||
printf(" OPTIONS is one or more of the following options:\n");
|
||||
printf(" -h show this help\n");
|
||||
printf(" -V show the version of the tool\n");
|
||||
printf("\n");
|
||||
print_copyright();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct list cards;
|
||||
struct vfio_container vc;
|
||||
struct fpga_card *card;
|
||||
|
||||
// Parse arguments
|
||||
char c, *endptr;
|
||||
while ((c = getopt(argc, argv, "Vh")) != -1) {
|
||||
switch (c) {
|
||||
case 'V':
|
||||
print_version();
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
case 'h':
|
||||
case '?':
|
||||
default:
|
||||
usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
check: if (optarg == endptr)
|
||||
error("Failed to parse parse option argument '-%c %s'", c, optarg);
|
||||
}
|
||||
|
||||
if (argc != optind + 2) {
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char *configfile = argv[optind];
|
||||
char *cardname = argv[optind+1];
|
||||
|
||||
FILE *f;
|
||||
json_error_t err;
|
||||
json_t *json;
|
||||
|
||||
auto pciDevices = std::make_shared<kernel::pci::DeviceList>();
|
||||
|
||||
ret = vfio_init(&vc);
|
||||
if (ret)
|
||||
return -1;
|
||||
|
||||
// Parse FPGA configuration
|
||||
f = fopen(configfile, "r");
|
||||
if (!f)
|
||||
return -1;
|
||||
|
||||
json = json_loadf(f, 0, &err);
|
||||
if (!json)
|
||||
return -1;
|
||||
|
||||
fclose(f);
|
||||
|
||||
list_init(&cards);
|
||||
ret = fpga_card_parse_list(&cards, json);
|
||||
if (ret)
|
||||
return -1;
|
||||
|
||||
json_decref(json);
|
||||
|
||||
card = list_lookup(&cards, cardname);
|
||||
if (!card)
|
||||
return -1;
|
||||
|
||||
fpga_card_dump(card);
|
||||
|
||||
// Run benchmarks
|
||||
fpga_benchmarks(argc-optind-1, argv+optind+1, card);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/** HLS unit test.
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017-2022, 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.hpp>
|
||||
#include <villas/fpga/vlnv.hpp>
|
||||
#include <villas/fpga/core.hpp>
|
||||
|
||||
extern struct fpga_card *card;
|
||||
|
||||
// cppcheck-suppress unknownMacro
|
||||
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");
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/** Intc unit test.
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017-2022, 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.hpp>
|
||||
#include <villas/fpga/core.hpp>
|
||||
|
||||
#include <villas/fpga/ips/intc.hpp>
|
||||
|
||||
extern struct fpga_card *card;
|
||||
|
||||
// cppcheck-suppress unknownMacro
|
||||
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
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
/** System Generator unit test.
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2017-2022, 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;
|
||||
|
||||
// cppcheck-suppress unknownMacro
|
||||
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