Xilinx FPGA里面的AXI DMA IP核的简单用法

在fpga里面,axi dma这个ip核的主要作用,就是在verilog语言和c语言之间传输大批量的数据,使用的通信协议为axi4-stream。
xilinx很多ip核都是基于axi4-stream协议的,例如浮点数floating-point ip核,以及以太网tri mode ethernet mac ip核。要想将verilog层面的数据搬运到c语言里面处理,就要使用dma ip核。
本文以浮点数floating-point ip核将定点数转换为浮点数为例,详细讲解axi dma ip核的使用方法。
浮点数ip核的输入输出数据都是32位,协议均为axi4-stream。c语言程序首先将要转换的定点数数据通过dma发送给浮点数ip核,浮点数ip核转换完成后再通过dma将单精度浮点数结果发回c语言程序,再通过printf打印出来。
定点数的数据类型为int,小数点定在第四位上,即:xxxxxxx.x。整数部分占28位,小数部分占4位。
转换后浮点数的数据类型为float,可以用printf的%f直接打印出来。
工程下载地址:https://pan.baidu.com/s/1sxpphmdhroft8vgciysytq(提取码:u7wf)
microblaze c语言工程的建法不再赘述,请参阅:https://blog.csdn.net/zlk1214/article/details/111824576
首先添加floating-point ip核,作为dma的外设端:(主存端为bram)
这里要注意一下,一定要勾选上tlast,否则dma接收端会出现dma internal error的错误:
下面是xilinx dma手册里面对dma internal error错误的描述:
添加axi dma ip核:
ip核添加好了,但还没有连线:
点击run connection automation,自动连接dma的s_axi_lite接口:
自动连接浮点数ip核的时钟引脚:
添加bram控制器:
最终的连线结果:
修改新建的bram的容量为64kb:
最终的地址分配方式:
保存block design,然后生成bitstream:
bitstream生成后,导出xsa文件:
vitis platform工程重新导入xsa文件:
修改c程序(helloworld.c)的代码:
(这里面xpar_bram_2_baseaddr最好改成0xc0000000,因为生成的xparameters.h配置文件里面bram号可能有变化)
/******************************************************************************
*
* copyright (c) 2009 - 2014 xilinx, inc. all rights reserved.
*
* permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the software), to deal
* in the software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the software, and to permit persons to whom the software is
* furnished to do so, subject to the following conditions:
*
* the above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the software.
*
* use of the software is limited solely to applications:
* (a) running on a xilinx device, or
* (b) that interact with a xilinx device through a bus or interconnect.
*
* the software is provided as is, without warranty of any kind, express or
* implied, including but not limited to the warranties of merchantability,
* fitness for a particular purpose and noninfringement. in no event shall
* xilinx be liable for any claim, damages or other liability,
* whether in an action of contract, tort or otherwise, arising from, out of
* or in connection with the software or the use or other dealings in the
* software.
*
* except as contained in this notice, the name of the xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this software without prior written authorization from xilinx.
*
******************************************************************************/
/*
* helloworld.c: simple test application
*
* this application configures uart 16550 to baud rate 9600.
* ps7 uart (zynq) is not initialized by this application, since
* bootrom/bsp configures it to baud rate 115200
*
* ------------------------------------------------
* | uart type baud rate |
* ------------------------------------------------
* uartns550 9600
* uartlite configurable only in hw design
* ps7_uart 115200 (configured by bootrom/bsp)
*/
#include
#include
#include platform.h
// dma无法通过axi interconnect访问microblaze本身的bram内存
// 只能访问挂接在axi interconnect上的内存
#define _countof(arr) (sizeof(arr) / sizeof(*(arr)))
typedef struct
{
int numbers_in[40];
float numbers_out[40];
} bram2_data;
static bram2_data *bram2_data = (bram2_data *)xpar_bram_2_baseaddr;
static xaxidma xaxidma;
int main(void)
{
int i, ret = 0;
xaxidma_config *xaxidma_cfg;
init_platform();
printf(hello world\n);
printf(successfully ran hello world application\n);
// 初始化dma
xaxidma_cfg = xaxidma_lookupconfig(xpar_axidma_0_device_id);
xaxidma_cfginitialize(&xaxidma, xaxidma_cfg);
ret = xaxidma_selftest(&xaxidma);
if (ret != xst_success)
{
printf(xaxidma_selftest() failed! ret=%d\n, ret);
goto err;
}
// 初始化dma的输入数据
printf(numbers_in=%p, numbers_out=%p\n, bram2_data->numbers_in, bram2_data->numbers_out);
for (i = 0; i numbers_in); i++)
{
bram2_data->numbers_in[i] = 314 * (i + 1);
if (i & 1)
bram2_data->numbers_in[i] = -bram2_data->numbers_in[i];
}
// dma开始发送数据 (length参数的单位为字节)
ret = xaxidma_simpletransfer(&xaxidma, (uintptr_t)bram2_data->numbers_in, sizeof(bram2_data->numbers_in), xaxidma_dma_to_device);
if (ret != xst_success)
{
printf(xaxidma_simpletransfer(xaxidma_dma_to_device) failed! ret=%d\n, ret);
goto err;
}
// dma开始接收数据
ret = xaxidma_simpletransfer(&xaxidma, (uintptr_t)bram2_data->numbers_out, sizeof(bram2_data->numbers_out), xaxidma_device_to_dma);
if (ret != xst_success)
{
printf(xaxidma_simpletransfer(xaxidma_device_to_dma) failed! ret=%d\n, ret);
goto err;
}
// 等待dma发送完毕
i = 0;
while (xaxidma_busy(&xaxidma, xaxidma_dma_to_device))
{
i++;
if (i == 200000)
{
// 必须确保dma访问的内存是直接挂接在axi interconnect上的
// 否则这里会报dma decode error的错误 (the address request points to an invalid address)
printf(dma tx timeout! dmasr=0x%08lx\n, xaxidma_readreg(xaxidma.regbase + xaxidma_tx_offset, xaxidma_sr_offset));
goto err;
}
}
printf(dma tx complete!\n);
// 等待dma接收完毕
i = 0;
while (xaxidma_busy(&xaxidma, xaxidma_device_to_dma))
{
i++;
if (i == 200000)
{
// floating-point ip核的配置里面一定要把a通道的tlast复选框勾选上, 使输入端和输出端都有tlast信号
// 否则s_axis_s2mm_tlast一直为0, dma以为数据还没接收完, 就会报dma internal error的错误
// (the incoming packet is bigger than what is specified in the dma length register)
printf(dma rx timeout! dmasr=0x%08lx\n, xaxidma_readreg(xaxidma.regbase + xaxidma_rx_offset, xaxidma_sr_offset));
goto err;
}
}
printf(dma rx complete!\n);
err:
for (i = 0; i numbers_out); i++)
printf(numbers_out[%d]=%f\n, i, bram2_data->numbers_out[i]);
cleanup_platform();
return 0;
}
c程序的运行结果:
hello world
successfully ran hello world application
numbers_in=0xc0000000, numbers_out=0xc00000a0
dma tx complete!
dma rx complete!
numbers_out[0]=19.625000
numbers_out[1]=-39.250000
numbers_out[2]=58.875000
numbers_out[3]=-78.500000
numbers_out[4]=98.125000
numbers_out[5]=-117.750000
numbers_out[6]=137.375000
numbers_out[7]=-157.000000
numbers_out[8]=176.625000
numbers_out[9]=-196.250000
numbers_out[10]=215.875000
numbers_out[11]=-235.500000
numbers_out[12]=255.125000
numbers_out[13]=-274.750000
numbers_out[14]=294.375000
numbers_out[15]=-314.000000
numbers_out[16]=333.625000
numbers_out[17]=-353.250000
numbers_out[18]=372.875000
numbers_out[19]=-392.500000
numbers_out[20]=412.125000
numbers_out[21]=-431.750000
numbers_out[22]=451.375000
numbers_out[23]=-471.000000
numbers_out[24]=490.625000
numbers_out[25]=-510.250000
numbers_out[26]=529.875000
numbers_out[27]=-549.500000
numbers_out[28]=569.125000
numbers_out[29]=-588.750000
numbers_out[30]=608.375000
numbers_out[31]=-628.000000
numbers_out[32]=647.625000
numbers_out[33]=-667.250000
numbers_out[34]=686.875000
numbers_out[35]=-706.500000
numbers_out[36]=726.125000
numbers_out[37]=-745.750000
numbers_out[38]=765.375000
numbers_out[39]=-785.000000
接下来讲一下我们刚才禁用掉的scatter gather接口的用法。取消禁用后,之前的c代码就不能运行了。
之前没有启用scatter gather的时候,我们一次只能提交一个dma请求,等这个dma请求的数据传输完毕后,我们才能提交下一个dma传输请求。
有了scatter gather接口,我们就可以一次性提交很多很多dma请求,然后cpu去干其他的事情。这可以大大提高传输效率。
除此以外,scatter gather还可以将多个位于不同内存地址的缓冲区合并成一个axi4-stream数据包传输。
下面的示例演示了如何利用scatter gather功能批量收发3组数据包。
启用了scatter gather后,dma里面多出了一个m_axi_sg接口,点击run connection automation,连接到axi interconnect上:
vivado工程generate bitstream,然后导出xsa文件。回到vitis后,必须把platform工程删了重建,不然xpar_axi_dma_0_include_sg的值得不到更新。
原有的c程序不再可用,修改一下程序代码:
/******************************************************************************
*
* copyright (c) 2009 - 2014 xilinx, inc. all rights reserved.
*
* permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the software), to deal
* in the software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the software, and to permit persons to whom the software is
* furnished to do so, subject to the following conditions:
*
* the above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the software.
*
* use of the software is limited solely to applications:
* (a) running on a xilinx device, or
* (b) that interact with a xilinx device through a bus or interconnect.
*
* the software is provided as is, without warranty of any kind, express or
* implied, including but not limited to the warranties of merchantability,
* fitness for a particular purpose and noninfringement. in no event shall
* xilinx be liable for any claim, damages or other liability,
* whether in an action of contract, tort or otherwise, arising from, out of
* or in connection with the software or the use or other dealings in the
* software.
*
* except as contained in this notice, the name of the xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this software without prior written authorization from xilinx.
*
******************************************************************************/
/*
* helloworld.c: simple test application
*
* this application configures uart 16550 to baud rate 9600.
* ps7 uart (zynq) is not initialized by this application, since
* bootrom/bsp configures it to baud rate 115200
*
* ------------------------------------------------
* | uart type baud rate |
* ------------------------------------------------
* uartns550 9600
* uartlite configurable only in hw design
* ps7_uart 115200 (configured by bootrom/bsp)
*/
#include
#include
#include platform.h
/* xilinx的官方例程:c:\xilinx\vitis\2020.1\data\embeddedsw\xilinxprocessoriplib\drivers\axidma_v9_11\examples\xaxidma_example_sg_poll.c */
// dma无法通过axi interconnect访问microblaze本身的bram内存
// 只能访问挂接在axi interconnect上的内存
#define _countof(arr) (sizeof(arr) / sizeof(*(arr)))
typedef struct
{
int numbers_in[40];
float numbers_out[40];
} bram2_data;
typedef struct
{
uint8_t txbuf[640];
uint8_t rxbuf[640];
} bram2_bdringbuffer;
static bram2_data *bram2_data = (bram2_data *)0xc0000000;
static bram2_bdringbuffer *bram2_bdringbuf = (bram2_bdringbuffer *)0xc0008000;
static xaxidma xaxidma;
int main(void)
{
int i, n, ret = 0;
xaxidma_bd *bd, *p;
xaxidma_bdring *txring, *rxring;
xaxidma_config *cfg;
init_platform();
printf(hello world\n);
printf(successfully ran hello world application\n);
// 初始化dma
cfg = xaxidma_lookupconfig(xpar_axidma_0_device_id);
xaxidma_cfginitialize(&xaxidma, cfg);
ret = xaxidma_selftest(&xaxidma);
if (ret != xst_success)
{
printf(xaxidma_selftest() failed! ret=%d\n, ret);
goto err;
}
if (!xaxidma_hassg(&xaxidma))
{
printf(xpar_axi_dma_0_include_sg=%d\n, xpar_axi_dma_0_include_sg);
printf(please recreate and build vitis platform project!\n);
goto err;
}
// 初始化dma的输入数据
printf([0] numbers_in=%p, numbers_out=%p\n, bram2_data[0].numbers_in, bram2_data[0].numbers_out);
printf([1] numbers_in=%p, numbers_out=%p\n, bram2_data[1].numbers_in, bram2_data[1].numbers_out);
printf([2] numbers_in=%p, numbers_out=%p\n, bram2_data[2].numbers_in, bram2_data[2].numbers_out);
for (i = 0; i {
bram2_data[0].numbers_in[i] = 314 * (i + 1);
bram2_data[1].numbers_in[i] = -141 * (i + 1);
bram2_data[2].numbers_in[i] = -2718 * (i + 1);
if (i & 1)
{
bram2_data[0].numbers_in[i] = -bram2_data[0].numbers_in[i];
bram2_data[1].numbers_in[i] = -bram2_data[1].numbers_in[i];
bram2_data[2].numbers_in[i] = -bram2_data[2].numbers_in[i];
}
}
// 配置dma发送描述符
txring = xaxidma_gettxring(&xaxidma);
n = xaxidma_bdringcntcalc(xaxidma_bd_minimum_alignment, sizeof(bram2_bdringbuf->txbuf));
ret = xaxidma_bdringcreate(txring, (uintptr_t)bram2_bdringbuf->txbuf, (uintptr_t)bram2_bdringbuf->txbuf, xaxidma_bd_minimum_alignment, n);
if (ret != xst_success)
{
printf(xaxidma_bdringcreate(txring) failed! ret=%d\n, ret);
goto err;
}
printf(bdring tx count: %d\n, n);
ret = xaxidma_bdringalloc(txring, 3, &bd);
if (ret != xst_success)
{
printf(xaxidma_bdringalloc(txring) failed! ret=%d\n, ret);
goto err;
}
p = bd;
for (i = 0; i {
xaxidma_bdsetbufaddr(p, (uintptr_t)bram2_data[i].numbers_in);
xaxidma_bdsetlength(p, sizeof(bram2_data[i].numbers_in), txring->maxtransferlen);
xaxidma_bdsetctrl(p, xaxidma_bd_ctrl_txsof_mask | xaxidma_bd_ctrl_txeof_mask);
xaxidma_bdsetid(p, i);
p = (xaxidma_bd *)xaxidma_bdringnext(txring, p);
}
ret = xaxidma_bdringtohw(txring, 3, bd);
if (ret != xst_success)
{
printf(xaxidma_bdringtohw(txring) failed! ret=%d\n, ret);
goto err;
}
// 配置dma接收描述符
rxring = xaxidma_getrxring(&xaxidma);
n = xaxidma_bdringcntcalc(xaxidma_bd_minimum_alignment, sizeof(bram2_bdringbuf->rxbuf));
ret = xaxidma_bdringcreate(rxring, (uintptr_t)bram2_bdringbuf->rxbuf, (uintptr_t)bram2_bdringbuf->rxbuf, xaxidma_bd_minimum_alignment, n);
if (ret != xst_success)
{
printf(xaxidma_bdringcreate(rxring) failed! ret=%d\n, ret);
goto err;
}
printf(bdring rx count: %d\n, n);
ret = xaxidma_bdringalloc(rxring, 3, &bd);
if (ret != xst_success)
{
printf(xaxidma_bdringalloc(rxring) failed! ret=%d\n, ret);
goto err;
}
p = bd;
for (i = 0; i {
xaxidma_bdsetbufaddr(p, (uintptr_t)bram2_data[i].numbers_out);
xaxidma_bdsetlength(p, sizeof(bram2_data[i].numbers_out), rxring->maxtransferlen);
xaxidma_bdsetctrl(p, 0);
xaxidma_bdsetid(p, i);
p = (xaxidma_bd *)xaxidma_bdringnext(rxring, p);
}
ret = xaxidma_bdringtohw(rxring, 3, bd);
if (ret != xst_success)
{
printf(xaxidma_bdringtohw(rxring) failed! ret=%d\n, ret);
goto err;
}
// 开始发送数据
ret = xaxidma_bdringstart(txring);
if (ret != xst_success)
{
printf(xaxidma_bdringstart(txring) failed! ret=%d\n, ret);
goto err;
}
// 开始接收数据
ret = xaxidma_bdringstart(rxring);
if (ret != xst_success)
{
printf(xaxidma_bdringstart(rxring) failed! ret=%d\n, ret);
goto err;
}
// 等待收发结束
n = 0;
while (n {
// 检查发送是否结束
ret = xaxidma_bdringfromhw(txring, xaxidma_all_bds, &bd);
if (ret != 0)
{
n += ret;
p = bd;
for (i = 0; i {
printf(dma tx%lu complete!\n, xaxidma_bdgetid(p));
p = (xaxidma_bd *)xaxidma_bdringnext(txring, p);
}
ret = xaxidma_bdringfree(txring, ret, bd);
if (ret != xst_success)
printf(xaxidma_bdringfree(txring) failed! ret=%d\n, ret);
}
// 检查接收是否结束
ret = xaxidma_bdringfromhw(rxring, xaxidma_all_bds, &bd);
if (ret != 0)
{
n += ret;
p = bd;
for (i = 0; i {
printf(dma rx%lu complete!\n, xaxidma_bdgetid(p));
p = (xaxidma_bd *)xaxidma_bdringnext(rxring, p);
}
ret = xaxidma_bdringfree(rxring, ret, bd);
if (ret != xst_success)
printf(xaxidma_bdringfree(rxring) failed! ret=%d\n, ret);
}
}
err:
for (i = 0; i printf(numbers_out[%d]=%f,%f,%f\n, i, bram2_data[0].numbers_out[i], bram2_data[1].numbers_out[i], bram2_data[2].numbers_out[i]);
cleanup_platform();
return 0;
}
程序运行结果:
hello world
successfully ran hello world application
[0] numbers_in=0xc0000000, numbers_out=0xc00000a0
[1] numbers_in=0xc0000140, numbers_out=0xc00001e0
[2] numbers_in=0xc0000280, numbers_out=0xc0000320
bdring tx count: 10
bdring rx count: 10
dma tx0 complete!
dma tx1 complete!
dma tx2 complete!
dma rx0 complete!
dma rx1 complete!
dma rx2 complete!
numbers_out[0]=19.625000,-8.812500,-169.875000
numbers_out[1]=-39.250000,17.625000,339.750000
numbers_out[2]=58.875000,-26.437500,-509.625000
numbers_out[3]=-78.500000,35.250000,679.500000
numbers_out[4]=98.125000,-44.062500,-849.375000
numbers_out[5]=-117.750000,52.875000,1019.250000
numbers_out[6]=137.375000,-61.687500,-1189.125000
numbers_out[7]=-157.000000,70.500000,1359.000000
numbers_out[8]=176.625000,-79.312500,-1528.875000
numbers_out[9]=-196.250000,88.125000,1698.750000
numbers_out[10]=215.875000,-96.937500,-1868.625000
numbers_out[11]=-235.500000,105.750000,2038.500000
numbers_out[12]=255.125000,-114.562500,-2208.375000
numbers_out[13]=-274.750000,123.375000,2378.250000
numbers_out[14]=294.375000,-132.187500,-2548.125000
numbers_out[15]=-314.000000,141.000000,2718.000000
numbers_out[16]=333.625000,-149.812500,-2887.875000
numbers_out[17]=-353.250000,158.625000,3057.750000
numbers_out[18]=372.875000,-167.437500,-3227.625000
numbers_out[19]=-392.500000,176.250000,3397.500000
numbers_out[20]=412.125000,-185.062500,-3567.375000
numbers_out[21]=-431.750000,193.875000,3737.250000
numbers_out[22]=451.375000,-202.687500,-3907.125000
numbers_out[23]=-471.000000,211.500000,4077.000000
numbers_out[24]=490.625000,-220.312500,-4246.875000
numbers_out[25]=-510.250000,229.125000,4416.750000
numbers_out[26]=529.875000,-237.937500,-4586.625000
numbers_out[27]=-549.500000,246.750000,4756.500000
numbers_out[28]=569.125000,-255.562500,-4926.375000
numbers_out[29]=-588.750000,264.375000,5096.250000
numbers_out[30]=608.375000,-273.187500,-5266.125000
numbers_out[31]=-628.000000,282.000000,5436.000000
numbers_out[32]=647.625000,-290.812500,-5605.875000
numbers_out[33]=-667.250000,299.625000,5775.750000
numbers_out[34]=686.875000,-308.437500,-5945.625000
numbers_out[35]=-706.500000,317.250000,6115.500000
numbers_out[36]=726.125000,-326.062500,-6285.375000
numbers_out[37]=-745.750000,334.875000,6455.250000
numbers_out[38]=765.375000,-343.687500,-6625.125000
numbers_out[39]=-785.000000,352.500000,6795.000000


激光二极管的结构及其工作原理是什么?
光纤适配器的生产设备、流程和注意事项
新能源汽车突破2000万辆 中国跃居世界最大汽车出口国
日本开发出利用红外探头可准掌握血管的形状和位置的采血机器人
高速连接器设计的细节有哪些
Xilinx FPGA里面的AXI DMA IP核的简单用法
5G连接或成为物联网的未来
基于EOS平台的去中心化Oracle技术平台介绍
微软Surface Earbuds无线耳机支持任何语音助手
中国移动提前完成5G部署目标 所有地级市和部分重点县城提供5G SA服务
基因检测行业处于高速发展阶段,联合治疗将成为未来发展新趋势
采用Linux还是Windows Embedded,研华选择后者
手势识别技术广泛应用 但也存在很多不足
深度解析电动汽车痛点关键技术——超级充电
预防安全事件的发生,大华股份为暑期安全保驾护航
Agilent DSA91304A Infiniium 高性能示波器:13 GHz
结构光技术一个比较全面的简介
莱斯大学生物工程师开发3D打印血管网络,最终有望移植到人体器管中
宏电5G工业网关专在最大程度上发挥5G技术特性,为各行业的智能化赋能
M5Stack推出全新直流电机驱动模块