PYNQ框架HLS开发流程备忘

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 IPaxi_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

扫描二维码关注公众号,回复: 15344642 查看本文章

概述:
编写完成的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——江药 的相关视频

猜你喜欢

转载自blog.csdn.net/u014798590/article/details/121648687
HLS
今日推荐