0、设计思路
本文目的是为在PYNQ调用HLS所生成的bit文件所写,旨在明晰整个流程和细节,属于个人学习笔记,将随着工作的开展而逐步更新完善,该文章可能不会对您的工作有所帮助,但也希望您能指出文中的错误,谢谢。
当前将从矩阵乘法示例,将整个流程进行细化,以供日后自己的IP使用。
以下以矩阵乘法举例,HLS源文件详见https://download.csdn.net/download/u014798590/64515794
里面包含矩阵乘法的源文件与测试文件。
本文如有不详细的地方可参考该大佬的文章https://blog.csdn.net/qq_42334072/article/details/106769534
Tips:
01、PS与PL通讯
根据《The Zynq Book》所提到,PL(可编程逻辑)与PS(处理系统)中采用AXI(Advanced eXtensible Interface)进行通讯。
有关AXI的介绍与使用,详见《The Zynq Book》中的第19章以及《UG871 HLS Tutoial》ch.4的 lab4章节
关于图片的设计思路,送进去之前由PS端的Python已经转成了rgb位图,然后调用AXI接口写进去一帧数据,进行处理后读出来,本例中,图像采用AXI_Stream实现
02、使用自己的ip思路
如下图可以看到,例程所设计的IP(image_process),具有输入输出两个接口,我们在Diagram中,将image IP与axi_dam进行连接,图像处理教程,是非常好的学习资料,在深度学习中,image IP的本质是一个卷积层,输入原始图像,获得卷积后的图像,后续将在这个例子中进行学习。
03、在Vivado中添加第三方板卡
访问该网站 https://github.com/Avnet/bdf;
将对应板卡的文件夹,复制到 E:\Xilinx\Vivado\2018.3\data\boards\board_files文件夹中;
需要注意的是如果板卡具有多个version,只需要拷贝一个文件夹;
在工程中切换Parts/Boards,而后搜索,选择。
1、HLS设计
请注意,HLS选择的芯片型号,一定要和Vivado设计中的芯片or板卡型号一致,否则在BD中 ,或无法找到你所打包的IP
概述:
编写完成的HLS文件,测试、综合完成后生成IP核。
1.1、流程备忘
1.1.5、添加头文件
在综合前,需要先进行以下流程的设置
project——》project setting——》synthesis——》Top Function
选择对应的函数
C synthesis
1.2、Pragma设计与优化
详见HLS相关学习博客,《Xilinx HLS 学习笔记3(for循环优化)》
下例为对for循环的优化,增加流水线
在对程序进行优化的过程中,可以使用Pragma代码或者HLS Directive方法进行配置
以下分开进行展示
1.2.1、Directive 方式
首先对需要展开或流水的for循环命名
然后可在右侧的Directive中看到其
右键命名,可进行insert directive设置,在弹出的对话框中,根据实际情况,选择命令
最后可看到相关命令出现,表明配置生效
1.2.2、Pragma方式
直接使用pragma HLS PIPELINE进行配置
最终结果
1.2.3、对接口配置(留坑)
同理也可使用两种方法实现一样的效果,(关于接口的作用与定义,待更新)
1.3、综合、打包IP
最后在该路径下找到IP文件夹(下一步中会用到)
D:\ZYNQ_PACK\Zynq7020\HLS_Project\AXI_Test1\IP_Matrix\Matrix\solution1\impl
2、Vivado设计
概述:
导入上述生成的IP核,加入ZYNQ、AXI等IP,进行一定配置后,绘制Diagram进行连线、连线后导出bit、Hwh、tcl文件。同时记录所使用地址。
2.1、流程备忘
1、创建工程输入文件路径和文件名后,全部Next
2、选择对应的芯片/板卡如7020、ultra96(第三方板卡见上方)等
3、创建Block Design(BD)
4、添加ZYNQ 的IP(不同板卡,出现的核心可能不同,本例使用的是Ultra96 故核心是UltraScale+)
5、运行Block Automation(块自动化)
6、添加自己的IP
选择上述生成的IP核文件夹路径
选择上述的所生成的IP文件夹
添加完成后,就可以看到IP的路径了
而后在DB中,查找对应的IP名称,进行添加IP
我们自己编写的IP就显示出来了
7、配置PS核
8、在自动布线中,配置HP0-HP2
使其与我们所写的IP端口相匹配
10、最后再连接中断信号线(请注意,此处直接连线,会导致PYNQ 2.6版本加载bit文件时报错interrupts,建议连线中间增加concat块,下图2
最后生成的BD如下图所示(各部分作用留坑),可以点击BD图上的check(框勾),以此检查连线是否有错误。
请注意,有些方法表示在第11步中,不一定需要使用generate output products步骤,而是直接生成顶层文件(creat HDL wrapper),然后执行第12步的生成bitstream
11、导出结果,在右侧的Design Sources右键项目,在完成Generate Output Products后,
选择Create HDL Wrapper生成顶层文件
弹出如下对话框
顶层文件生成完成后,出现下图
12、导出文件
在以下地址找到相应的bit文件
HLS_Project\AXI_Test1\project_1\project_1.runs\impl_1
在生成完成bit流后,将出现资源开销等关键信息,
然后我们切换回BD页面,导出Block Design——》tcl文件
2.1.1、文件路径
tcl文件导出可以自行设置导出目录(默认在工程的根目录)
最后在该路径下找到hwh文件
2018版vivado路径为**AXI_Test1\project_1\project_1.srcs\sources_1\bd\Ultra96_Design\hw_handoff***
2020版vivado路径为AXI_Test1\project_1\project_1.gen\sources_1\bd\Ultra96_Design\hw_handoff***
bit文件在目录工程文件夹\project_1.runs\impl_1下,的wrapper.bit文件
最终获得这三个文件
在完成这部分内容后,我们就可以打包导出bit、Hwh、tcl等文件到jupyter项目文件夹中。
2.2、查看地址
PS端在调用我们的上述文件进行计算时,需要通过如下地址来进行操作,在BD中打开如下路径,就可以进行查看
3、PYNQ调用
概述:
加载所述的bit、hwh、tcl文件;加载模型推理所需要的偏置、权重等dat文件;第三步中还需要设置一些寄存器和偏移量,用于操作和控制IP
需要注意的是,上述三个文件,文件名前缀需要改为一致(如1.tcl;1.bit;1.hwh),才能在PYNQ中正确调用。
3.1、上传文件与调用
上传文件可以采用jupyter网页上传方式,或者直接通过FTP上传到工程的根目录下。
from pynq import Overlay
from pynq import Xlnk
from pynq.lib.video import *
from PIL import ImageDraw as PIL_ImageDraw
from PIL import ImageFont
from pynq import MMIO
from PIL import Image as PIL_Image
import numpy as np
import math
import os
import inspect
import matplotlib.pyplot as plt
import time
import ctypes
overlay=Overlay("design_1.bit")
xlnk=Xlnk()
xlnk.xlnk_reset()
print('ok')
3.2、设置地址
根据2.2章节的截图所示,进行地址设置
img_in_b=xlnk.cma_array(shape=(4,), dtype=np.int32)
in_buffer_address_b=img_in_b.physical_address
img_in_a=xlnk.cma_array(shape=(4,), dtype=np.int32)
in_buffer_address_a=img_in_a.physical_address
img_in_c=xlnk.cma_array(shape=(4,), dtype=np.int32)
in_buffer_address_c=img_in_c.physical_address
print("in buffer_address_b:",in_buffer_address_b)
print("in buffer_address_a:",in_buffer_address_a)
a=[2,1,4,3]
b=[1,2,1,0]
np.copyto(img_in_b,a)
np.copyto(img_in_a,b)
IP_BASE_ADDRESS =0X0080000000
ADDRESS_RANGE =0x30
FPGA_img_addr_AP_CTRL=0x00
FPGA_img_addr_GIE =0x04
FPGA_img_addr_IER =0x08
FPGA_img_addr_ISR =0x0c
FPGA_img_addr_b=0x10
FPGA_img_addr_a=0x18
FPGA_img_addr_c=0x20
3.3、运行程序
程序运行中需要与HLS所开发的程序对应,调用方式举例如下图所示
def Matrix():
mmio=MMIO(IP_BASE_ADDRESS, ADDRESS_RANGE)
while True:
ap_idle=(mmio.read(FPGA_img_addr_AP_CTRL)>>2)&0x01
if(ap_idle):
break
mmio.write(FPGA_img_addr_b,in_buffer_address_b)
mmio.write(FPGA_img_addr_a,in_buffer_address_a)
mmio.write(FPGA_img_addr_c,in_buffer_address_c)
mmio.write(FPGA_img_addr_GIE,0)
mmio.write(FPGA_img_addr_AP_CTRL,1)
while True:
ap_done=(mmio.read(FPGA_img_addr_AP_CTRL)>>1)&0x01
if(ap_done):
break;
print("b_address:",mmio.read(FPGA_img_addr_b))
print("a_address:",mmio.read(FPGA_img_addr_a))
time.sleep(3)
start=time.time()
Matrix()
stop=time.time()
time_cifar_fpga=stop-start
print("Matrix time:",time_cifar_fpga)
time.sleep(3)
print("The out is",img_in_c)
4、参考资料
《HLS教程 UG817》
《AXI参考指南 UG1037》
《HLS用户指南 UG902》
本文还参考了B站Up——江药 的相关视频