赞
踩
上一篇博文为读者介绍了 HLS 中基于 数组 和 float型 的 四则运算IP核 的制作,然后在 Vivado 中把 IP核 加入到工程中与 ZYNQ Processing System 相连,然后导出硬件信息供给 SDK 用,至于 SDK 怎么用这个自定义的 四则运算IP核 ?笔者在这一篇博文继续给读者讲解。
从 Vivado 软件中运行 SDK 后会出现下面的界面(读者会发现这款 SDK 是用 Eclipse 开发的)。上面两个框是我们的硬件信息,下面的框框内的 add_0 就是 加法器IP核 导出的硬件信息了。
在菜单栏点击 File —— New —— Application Project ,弹出创建工程界面,输入项目名为 add (英文的无空格就可以),如下图所示,然后点击 Next。
然后选择工程模板,选择 Hello World 工程模板,如下图。
工程创建成功后如下图所示。
点开导航栏左侧的 Project Explorer 会发现里面有很多的文件,其中最最重要的就三个:第一个是 arith/src/Helloworld.c ,这个 c文件 里面有ZYNQ的CPU程序入口 main函数 ;第二个是 arith_bsp/ps7_cortexa9_0/include/xparameters.h ,这个头文件里面有我们所用到的 四则运算IP核 映射到FPGA的DDR中的内存地址和相关驱动参数(这些是 自动生成 的);第三个是add_bsp/ps7_cortexa9_0/include/xarith.h ,其命名方式是与读者设计的 IP核命名 相关,在其前面加上一个 ‘x’ 而已,比如我们的加法器是 arith ,因此它的命名方式就是 xarith.h ,这里面存放着我们调用自定义 四则运算IP核 的库函数(或者叫做 驱动)。
有了以上三个文件,就可以开始 搞事情 了。
第一步,点开 helloworld.c ,输入以下代码。
#include <stdio.h> #include "platform.h" #include "xil_printf.h" #include "xarith.h" int main() { int status; int length; init_platform(); float data[4]; float res[4]; float ref[4]; XArith My_IP; status = XArith_Initialize(&My_IP, XPAR_ARITH_0_DEVICE_ID); if(status == XST_SUCCESS) print("IP Initialize Successfully!\n\r"); data[0] = 10.5; data[1] = 5.5; data[2] = 8.0; data[3] = 0.5; ref[0] = data[0] + data[1]; ref[1] = data[0] - data[1]; ref[2] = data[2] * data[3]; ref[3] = data[2] / data[3]; length = XArith_Write_data_Words(&My_IP, 0, data, 4); if(length == 4) printf("Write OK!\n\r"); XArith_Start(&My_IP); while(!XArith_IsDone(&My_IP)); length = XArith_Read_res_Words(&My_IP, 0, res, 4); if (length == 4) print("Hello World\n\r"); printf("PS(ARM) : ref[0] = %f, ref[1] = %f, ref[2] = %f, ref[3] = %f \n\r", ref[0], ref[1], ref[2], ref[3]); printf("PL(FPGA) : res[0] = %f, res[1] = %f, res[2] = %f, res[3] = %f \n\r", res[0], res[1], res[2], res[3]); cleanup_platform(); return 0; }
首先需要先定义 XArith 类型的结构体 My_IP ,再调用 XArith_Initialize() 初始化 IP核 ,如果返回值为 XST_SUCCESS 即表示初始化成功。然后定义输入到 IP核 的数组 data 和存储返回结果的数组 res ,之后利用库函数 XArith_Write_data_Words() 把变量送到PL端,接着调用 XArith_Start() 启动PL端的 IP核 开始计算,然后利用 while(!XArith_IsDone(&My_IP)); 阻塞方式等待 IP核 运算完毕,最后用 XArith_Read_res_Words() 取得 IP核 的返回结果。最终用ARM和FPGA做计算,对比结果,验证是否有误。
上述的 XArith 结构体类型,以及对 IP核 的调用的库函数(驱动)全部来自于头文件 xarith.h 中。其中 XArith_Initialize() 的第二个参数是 IP核 的 ID ,这个 ID 号到 xparameters.h 头文件中找。所有用到的东西都在下面的截图中。
这里需要补充三点知识:
第一点是数组维度,我们在 HLS 中设置的存储输入数据和输出数据的是2*2的二维数组,而到了 SDK 中,从输入数据到IP核的函数 XArith_Write_data_Words(XArith *InstancePtr, int offset, int *data, int length) 来看,输入时候用到的指针其实是一维的,然后根据输入的第四个形参 length ,把length长的数组一行一行地填充慢 HLS 中的二维数组,也就是说 SDK 中长度为4的 data 数组的第0个到第3个(总共4个)分别放到了 HLS(实际上是IP核) 上对应的第0行第0列、第0行第1列、第1行第0列、第1行第1列这四个位置。
第二点是数据类型,从 XArith_Write_data_Words(XArith *InstancePtr, int offset, int *data, int length) 这一个函数定义来看,我们需要输入的数据其实是 float 型的,但是这里生成的驱动函数的 data 指针是 int 型,经过笔者实验发现,不论在 HLS 上定义的是 int 型还是 float 型,最终生成的驱动函数的数据指针都是 int 型,这个函数送到IP核应该是按照二进制送进去的,float 型变量所占的内存空间是4个字节, int 型也是4个字节,这样直接强制转换完送进去,输出时候用的 XArith_Read_res_Words(XArith InstancePtr, int offset, intdata, int length) 函数再次强制转换之后,结果会跟我们预期的一样。
第三点是赋值,我们定义的 data 变量是 float 型数组,在给这样的 float 型变量赋值的时候,建议是用带小数赋值,或者加强制类型转换,比如我要给一个 float 类型的变量 a 赋0的话,最好用 a = 0.0 或者 a = (float)0 ,笔者认为这样更安全,因为此前自己开发的时候遇到很多次这样的 BUG ,都是处在这个问题上,所以这里提醒笔者以防不小心出了这个 BUG 不好找。
另外,有详细看 xarith.h 文件的读者会发现,例如写数据的函数,会有两个,一个是 XArith_Write_data_Words(XArith *InstancePtr, int offset, int *data, int length),另一个是 XArith_Write_data_Bytes(XArith *InstancePtr, int offset, char *data, int length) ,这里区别在于写数据时候用的是 Words(字节) 还是 char(字符) ,一个是4字节一个是单字节,因为我们用到的是 float 变量,所以用 字节 类型的函数即可,同理读取输出结果的函数也是一样。读者有兴趣可以自行尝试用 字符 类型的函数做实验。
部分截图如下。
程序写好之后就可以烧录到板子上了。
第一步先把程序烧录到 ZYNQ 上,如下图所示操作。
用串口连接PC和开发板,(这里假设读者都已经安装好串口驱动之类的,如果不知道怎么做的,提供开发板的产商应该会给相应的资料,照做即可),打开串口调试助手,看运行结果,如下图所示。可以看到这里程序只运行到初始化这一步,没有之后我们所预想的有打印数据出来,这主要是因为硬件信息未烧录到FPGA中,因此在运行 while(!XAdd_IsDone(&MyAdd)); 会阻塞住不正常运行,到下一步。
第二步,把程序同时烧录到 ZYNQ 和 FPGA 上,操作如下图所示。
如果出现下面图示的警告,点 OK ,等待串口助手显示我们预想的结果就可以了。
如果读者都按照以上操作做完的话,会在串口助手上看到这样的结果,跟我们的预期一样,大功告成。
这里补充两点:
第一点是为什么要先烧录到ARM再烧录到FPGA,因为一个新的工程如果没有进行烧录到ARM的操作的话,之后烧录到FPGA的操作界面会缺少一个生成的System Debugger,有兴趣的读者可以试一下。
第二点是串口正常连接时的设备显示和串口调试助手的配置,如下图所示。
正常安装完USB驱动之后,用USB线把开发板上的UART口和JTAG口分别连到PC上,就可以正常使用了。
至此,所有的操作完成。
这篇博文笔者详细的介绍了从HLS上 二维数组 和 float 型数据的 四则运算IP核 的制作,到Vivado上顶层电路的连接,最后到SDK上应用程序的开发与验证,完成了用ARM核控制FPGA完成 四则运算 的任务,并教会读者怎么在HLS中用 数组 和 float 型变量。
我们把流程重新理一遍。
以上便是全部操作流程。
当我们需要用到含有大量数据的数组的时候,可以在 HLS 上把形参设置成数组,如果需要用到的数据含有小数的时候,可以 把数组定义为 float 型,之后再 SDK 上把一维的数组按照需要的顺序放到 IP核 ,float 型数据也是直接用驱动函数放进去,在调用 写函数 的时候会有强制类型转换,调用 读函数 的时候也会有强制类型转换,最终会得到我们所希望的结果,所以读者不必担心驱动函数上面 int 型的变量和 float 型变量不匹配的问题。另外,如果在 HLS 上面定义的数组是二维的,那么这个二维数组是按一行一行展开成一个行向量,去和 SDK 上的一维数组对应的,这个地方读者在自己设计的时候需要非常注意。
非常感谢读者读到了这里,这一博文会有很多地方与上一篇博文《Vivado开发套件设计笔记(2)——加法器设计》相似,笔者也是怕读者只读这一篇博文,而没有时间去读上一篇,所以才出此下策,希望读者可以理解。如果不幸让读者感到不悦,笔者将在下一篇博文中介绍 DDR3 的使用,以此谢罪。
[1]: Xilinx Zynq-7000 嵌入式系统设计与实现(何宾著)
[2]: Vivado开发套件设计笔记(1)——入门简介
[3]: Vivado开发套件设计笔记(2)——加法器设计——变量(上)
[4]: Vivado开发套件设计笔记(2)——加法器设计——变量(下)
[5]: Vivado开发套件设计笔记(3)——加法器设计——指针(上)
原创性声明:本文属于作者原创性文章,小弟码字辛苦,转载还请注明出处。谢谢~
如果有哪些地方表述的不够得体和清晰,有存在的任何问题,亦或者程序存在任何考虑不周和漏洞,欢迎评论和指正,谢谢各路大佬。
需要代码和有需要相关技术支持的可咨询QQ:297461921
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。