Repository: louisliuwei/PynqDocs Branch: master Commit: 64eedf9f319f Files: 22 Total size: 95.1 KB Directory structure: gitextract_s6oqwpmp/ ├── README.md ├── SUMMARY.md └── pynq-zhong-wen-zi-liao/ ├── 01pynqz2-kai-fa-ban-shang-shou.md ├── 02pynq-chang-jian-wen-ti.md ├── 03jupyter-notebook-bi-zhi-bi-hui.md ├── 04pynq-overlay-jie-shao.md ├── 05baseoverlay-jie-shao.md ├── 06_logictools-overlay.md ├── 0701pynq-library-xiang-jie-ps-yu-pl-jie-kou.md ├── 0702pynq-library-xiang-jie-ip-fang-wen.md ├── 0703pynq-library-xiang-jie-ps-and-pl-control.md ├── 0704pynq-library-xiang-jie-iop.md ├── 0705pynq-library-xiang-jie-pynq-microblaze.md ├── 08pynq-kuai-su-shang-shou-shi-yan-jie-shao.md ├── 09overlay-she-ji-fang-fa-xue.md ├── 10-zi-ding-yi-overlay-she-ji-liu-cheng.md ├── 111-ji-yu-hls-de-jia-su-qi-overlay-she-ji-shi-li-kuai-su-sheng-cheng-ying-jian-ip.md ├── 112-ji-yu-hls-de-jia-su-qi-overlay-she-ji-shi-li-notebook-zhong-tiao-yong-ying-jian-ip.md ├── 12-di-san-fang-overlay-jie-shao-spyn.md ├── 13-yi-bnnpynq-wei-li-de-zi-ding-yi-overlay-fen-fa-fang-fa-jie-shao.md ├── 14python-ji-chu.md └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: README.md ================================================ # 介绍 ## PYNQ中文资料 Xilinx作为芯片生产商,正在不断地推出各种隐藏底层硬件细节的软件开发工具链,希望藉此提高软件开发者的生产效率,让硬件开发者做好硬件开发的工作,软件开发者只需在硬件开发者的基础上继续构建,而无需面面俱到的了解底层的实现原理,HLS和SDSoC工具就是其中的代表。HLS使得工程师可以快速将算法在C/C++级别进行硬件化加速,省去了HDL调试与优化的巨大精力。SDSoC使得工程师可以快速抉择将模块部署在可编程逻辑PL部分还是处理器PS部分,从而在最短的时间内调整到最优的系统性能。 尽管如此,HLS和SDSoC还是需要工程师对FPGA开发流程有较深的了解,开发应用的C/C++语言在易用性和可读性上依旧有所欠缺。正因如此,Xilinx推出的Pynq开发框架,结合了简单易学易上手的Python语言,上层应用开发者可以真正摆脱底层硬件细节的纠缠,将性能瓶颈交给专业的硬件工程师,专心开发纯软件层面的应用。 PYNQ作为一个全新的框架,很多同学和工程师都还比较陌生,中文资料相对也比较欠缺,基于此,我们计划逐渐推出更多的中文资料,希望能帮助大家熟悉PYNQ框架。 [Gitbook链接](https://pynqdocs.gitbook.io/pynq-tutorial/) # 内容列表 ======= ## 内容列表 * PYNQ-Z2开发板上手 * PYNQ常见问题 * Jupyter Notebook必知必会 * PYNQ Overlay介绍 * BaseOverlay介绍 * Logictools Overlay * PYNQ Library详解 - PS与PL接口 * PYNQ Library详解 - IP访问 * PYNQ Library详解 - PS and PL control * PYNQ Library详解 - IOP * PYNQ Library详解 - Pynq MicroBlaze * PYNQ快速上手实验介绍 * Overlay设计方法学 * 自定义Overlay设计流程 * 基于HLS的加速器Overlay设计实例 - 快速生成硬件IP * 基于HLS的加速器Overlay设计实例 - Notebook中调用硬件IP * 第三方Overlay介绍-SPYN * 以BNN-PYNQ为例的自定义Overlay分发方法介绍 * Python基础 >>>>>>> 5a4fd402eaeee8c6b9aec9a008d84595a4d65572 ================================================ FILE: SUMMARY.md ================================================ # Table of contents * [介绍](README.md) * [\[Pynq 中文资料\]](pynq-zhong-wen-zi-liao/README.md) * [01\_PYNQ-Z2开发板上手](pynq-zhong-wen-zi-liao/01pynqz2-kai-fa-ban-shang-shou.md) * [02\_PYNQ常见问题](pynq-zhong-wen-zi-liao/02pynq-chang-jian-wen-ti.md) * [03\_Jupyter Notebook必知必会](pynq-zhong-wen-zi-liao/03jupyter-notebook-bi-zhi-bi-hui.md) * [04\_PYNQ Overlay介绍](pynq-zhong-wen-zi-liao/04pynq-overlay-jie-shao.md) * [05\_BaseOverlay介绍](pynq-zhong-wen-zi-liao/05baseoverlay-jie-shao.md) * [06\_Logictools Overlay](pynq-zhong-wen-zi-liao/06_logictools-overlay.md) * [07-01\_PYNQ Library详解 - PS与PL接口](pynq-zhong-wen-zi-liao/0701pynq-library-xiang-jie-ps-yu-pl-jie-kou.md) * [07-02\_PYNQ Library详解 - IP访问](pynq-zhong-wen-zi-liao/0702pynq-library-xiang-jie-ip-fang-wen.md) * [07-03\_PYNQ Library详解 - PS and PL control](pynq-zhong-wen-zi-liao/0703pynq-library-xiang-jie-ps-and-pl-control.md) * [07-04\_PYNQ Library详解 - IOP](pynq-zhong-wen-zi-liao/0704pynq-library-xiang-jie-iop.md) * [07-05\_PYNQ Library详解 - Pynq MicroBlaze](pynq-zhong-wen-zi-liao/0705pynq-library-xiang-jie-pynq-microblaze.md) * [08\_PYNQ快速上手实验介绍](pynq-zhong-wen-zi-liao/08pynq-kuai-su-shang-shou-shi-yan-jie-shao.md) * [09\_Overlay设计方法学](pynq-zhong-wen-zi-liao/09overlay-she-ji-fang-fa-xue.md) * [10\_自定义Overlay设计流程](pynq-zhong-wen-zi-liao/10-zi-ding-yi-overlay-she-ji-liu-cheng.md) * [11-1\_基于HLS的加速器Overlay设计实例 - 快速生成硬件IP](pynq-zhong-wen-zi-liao/111-ji-yu-hls-de-jia-su-qi-overlay-she-ji-shi-li-kuai-su-sheng-cheng-ying-jian-ip.md) * [11-2\_基于HLS的加速器Overlay设计实例 - Notebook中调用硬件IP](pynq-zhong-wen-zi-liao/112-ji-yu-hls-de-jia-su-qi-overlay-she-ji-shi-li-notebook-zhong-tiao-yong-ying-jian-ip.md) * [12\_第三方Overlay介绍-SPYN](pynq-zhong-wen-zi-liao/12-di-san-fang-overlay-jie-shao-spyn.md) * [13\_以BNN-PYNQ为例的自定义Overlay分发方法介绍](pynq-zhong-wen-zi-liao/13-yi-bnnpynq-wei-li-de-zi-ding-yi-overlay-fen-fa-fang-fa-jie-shao.md) * [14\_Python基础](pynq-zhong-wen-zi-liao/14python-ji-chu.md) ================================================ FILE: pynq-zhong-wen-zi-liao/01pynqz2-kai-fa-ban-shang-shou.md ================================================ # 01\_PYNQ-Z2开发板上手 在开始之前,你需要准备好如下物品: * PYNQ-Z2开发板 * 装有最新版本Chrome浏览器的PC机 * 一根百兆/千兆网线 * 一根Micro USB线 * 至少8GB大小的Micro SD卡 ## 制作Micro SD卡 这一步的任务是制作一张可启动的Micro SD卡。在制作Micro SD卡之前,请确认你已经准备好下列物品: * 一台可以读写Micro SD卡的电脑,或者Micro SD卡读卡器 * PYNQ-Z2开发板的PYNQ镜像,可在[http://www.pynq.io/board.html下载](http://www.pynq.io/board.html下载) * 镜像烧录工具。根据不同的操作系统有不同的推荐: * Windows:可通过镜像烧录软件,如Win32DiskImager,官方下载链接为[https://sourceforge.net/projects/win32diskimager/](https://sourceforge.net/projects/win32diskimager/) * Mac OS X/Linux:dd命令行_sudo dd bs=4M if=pynq\_z1.img of=/dev/sdd_ 以在Windows下制作可启动的Micro SD卡为例。 打开Win32DiskImager,并根据开发板选择下载好的镜像文件,PYNQ-Z2.img。 选择MicroSD卡对应的盘符(请务必正确选择),点击写入然后等待完成。 ![](../.gitbook/assets/writeimage.png) 烧录完成之后可以看到对应的Micro SD盘符如下图所示。 ![](../.gitbook/assets/sdcard.png) Micros SD卡内被划分为两个分区,本例中99.7MB大小的FAT分区挂载的是Linux中的\*\*/boot\*\*分区,内部存放的是Boot.bin等启动所需文件。另一个ext4格式的分区在Windows中不可读,挂载的是根目录\*\*/\*\*分区。 \#\#\# 启动开发板 开发板启动步骤如下: - 配置成SD卡启动模式 - 配置为USB供电模式 - 把上一小节中制作好的Micro SD卡插入对应的卡槽 - 将Micro USB线连接到板板卡与PC(该线缆用于板卡供电/串口通讯/JTAG调试)。 - 将网线连接到板卡与PC机对应的接口,PC上配置本地网络静态IP为192.168.2.1,子网掩码为255.255.255.0。\(也可以通过路由器为PC机与板卡建立网络连接,PC机自动获取IP即可) - 打开电源拨码开关,等待板卡启动完毕。(可在串口观察系统启动打印信息,连接方式请参考下一小节) ![](../.gitbook/assets/startup.PNG) ## 与PYNQ-Z2建立串口连接 串口连接需要专门的串口连接软件来操作,这里我们推荐使用开源免费跨平台的PuTTY来进行连接。下面将只介绍Windows上的操作,mac和Linux上的操作类似。 首先我们需要知道串口端口号,打开**设备管理器**,展开端口列表即可看到,如COM5。 打开PuTTY软件,输入USB端口号和下列串口参数,点击连接即可。 * 波特率115200 Speed * 数据位 8 Data bits * 停止位1 Stop bits * 奇偶校验 None Parity * 流控制 No Flow Control ![](../.gitbook/assets/serialport.png) ## 连接Web UI: Jupyter Notebook 在这一小节中,我们将通过浏览器打开板载Linux上运行的嵌入式Web服务器Jupyter Notebook的页面,感受不一样的编程体验。 如果开发板是直接网线连到PC上的,默认的=192.168.2.99。如果是通过路由器建立的网络连接,则需要查询板卡的IP地址。通过putty软件在串口终端输入_ifconfig_命令即可查询到板卡的IP地址。 ![](../.gitbook/assets/ip.PNG) 打开Chrome浏览器输入 http://:9090 ,记住将替换为具体的IP,输入密码**xilinx**,即可进入到开发板上的Jupyter Notebook主页。推荐使用Chrome浏览器是因为Jupyter Notebook使用了许多最新需要浏览器支持的js框架。 ![](../.gitbook/assets/jupyternotebook.png) 主页中展示的例子可以让我们快速上手PYNQ相关的操作,base文件夹和logictools文件夹与具体的Overlay应用相关(Overlay的概念后面会介绍),common文件夹中的是与具体Overlay不绑定的较为通用的例子。对新手来说,getting\_started文件夹提供了了解PYNQ必知必会的几个例子,我们也建议读者朋友先从这里入手。 我们以PYNQ上自带的/base/board/board\_btns\_leds.ipynb为例,打开该notebook。点击Run按钮运行该notebook,根据notebook中的介绍操作按钮,观察板卡的运行状态。 ![](../.gitbook/assets/buttons_leds.PNG) 如果对Jupyter了解有限,可以先看本章后面的JupyterNotebook必知必会对它有一个初步的使用印象,再去进行下一步操作。 ## 通过samba传输文件 在开发过程中,如果需要在PC机与板卡之间传输一些较大的文件,可以通过PYNQ支持的samba协议将PYNQ的文件系统当作一个网络硬盘直接读取。在Windows中只需要打开资源管理器,输入\\xilinx即可成功连接。在Mac/Linux中同样可以打开文件管理器,输入smb:////xilinx进行挂载。注意,用户名和密码均为**xilinx**。 ![](../.gitbook/assets/samba.png) ================================================ FILE: pynq-zhong-wen-zi-liao/02pynq-chang-jian-wen-ti.md ================================================ # 02\_PYNQ常见问题 ## 什么是PYNQ? PYNQ是Python On Zynq的缩写,它是一个软件开发框架,指导硬件层、驱动层和应用层之间的接口设计,不是ISE、Vivado、SDSoC这样的IDE工具,更不是Zynq芯片的下一代芯片产品。 ![](../.gitbook/assets/framework.png) PYNQ框架的设计初衷是通过高层次的封装,将底层硬件FPGA实现细节与上层应用层的使用脱耦,对软件开发者来说,PYNQ框架已经提供了完整的访问FPGA资源的library,让上层应用开发者通过Python编程就可以调用FPGA模块,不需要懂Verilog/VHDL硬件编程就可以享受FPGA可并行计算、接口可方便扩展和可灵活配置带来的诸多好处。 在在PYNQ框架下,ARM A9 CPU上运行的软件包括: · 载有Jupyter Notebooks设计环境的网络服务器 · IPython内核和程序包 · Linux · FPGA的基本硬件库和API ## PYNQ就是通过Python语言直接对FPGA进行编程吗? 我相信这个问题应该是很多人第一次听说PYNQ框架的印象,事实上,在PYNQ框架下并不能通过Python对FPGA进行编程来取代传统的RTL编程方式。PYNQ框架是为软件开发者提供了访问FPGA资源的python接口,Python开发者可以忽略这些实现细节,通过python即可轻松访问FPGA,动态加载各种预编译好的各种FPGA应用,像调用函数一样去调用各种通过FPGA加速的应用或者访问连接到FPFA的外设。让软件工程师能轻松享受FPGA并行计算和可灵活配置的诸多好处。 ## PYNQ和PYNQ-Z2是一回事吗? PYNQ是一个软件框架,而PYNQ-Z2是支持PYNQ软件框架的硬件板卡,除了PYNQ-Z2,目前官方支持PYNQ框架的板卡还有ZCU104和PYNQ-Z1,只需要下载好已经编译好的PYNQ镜像,开机启动即可开始你的PYNQ之旅。目前其它的板卡也开始支持PYNQ框架了,比如Ultra96等。当然,用户也可以将PYNQ软件框架移植到自己的板卡上,不过对开发者会有更高的要求,需要了解更多的硬件知识。 ## PYNQ-Z2是否支持传统开发方式? 除了支持PYNQ框架,PYNQ-Z2也可以采用传统的ZYNQ开发方式,使用Vivado, SDK, SDSoC等工具进行开发。 ## 要到哪里学习PYNQ知识呢? Pynq.io是PYNQ官方网站,上面有英文资源汇总。 另外也欢迎关注PYNQ微信服务号,会不定期发布参考资料、PYNQ应用案例更新。 ![](../.gitbook/assets/pynq.png) ================================================ FILE: pynq-zhong-wen-zi-liao/03jupyter-notebook-bi-zhi-bi-hui.md ================================================ # 03\_Jupyter Notebook必知必会 本节将介绍在PYNQ开发中常见的Jupyter Notebook相关知识。如果想要获取更为完整的Jupyter Notebook的使用技巧可以访问Jupyter官网[http://jupyter.org/](http://jupyter.org/)。 ## Jupyter Notebook简介 Jupyter是基于网页的用于交互计算的应用程序,可被应用于全过程计算:开发、文档编写、运行代码和展示结果。Jupyter可以很方便地部署在嵌入式Linux中,作为嵌入式Linux的Web IDE。 Notebook主要由两部分组成,网页应用和文档部分。网页应用即基于网页形式的、结合了编写说明文档、数学公式、交互计算和其他富媒体形式的工具。简言之,网页应用是可以实现各种功能的工具。至于文档,Notebook中所有交互计算、编写说明文档、数学公式、图片以及其他富媒体形式的输入和输出,都是以文档的形式体现的。这些文档是保存为后缀名为.ipynb的JSON格式文件,不仅便于版本控制,也方便与他人共享。此外,文档还可以导出为:HTML、LaTeX、PDF等格式。 Notebook的下列特点使得它在可视化编程与展示、编写教程文档等方面应用极为广泛。 * 编程时具有语法高亮、缩进、tab补全的功能。 * 可直接通过浏览器运行代码,同时在代码块下方展示运行结果。 * 以富媒体格式展示计算结果。富媒体格式包括:HTML,LaTeX,PNG,SVG等。 * 对代码编写说明文档或语句时,支持Markdown语法。 * 支持使用LaTeX编写数学性说明。 ## 用户UI界面 这一节主要介绍的是用户UI界面中各个部分的功能组件。当我们输入密码xilinx之后看到的第一页就是Notebook的Dashboard,左侧上方Files代表着当前显示目录文件树,可以看到是否有正在运行的Notebook。在Running页面可以看到当前所有正在运行的Notebook的总览。在Nbextensions页面可以选择添加或删除不同的Notebook插件。Dashboard的右上方的两个按钮的功能分别是上传新文件和创建新文件/文件夹。 ![](../.gitbook/assets/jupyter_ui.png) 双击打开要编辑的Notebook之后,就进入了Notebook的编辑模式。页面最上方的是该Notebook的文件名,文件名右侧显示上一次保存的相关信息。第二行是Notebook的菜单栏,对Notebook的所有操作都可以在菜单栏的下拉菜单中找到。第二行右侧的是状态栏,常见状态如正在初始化、当前空闲、正在执行当前单元格的代码、运行死机都会显示在这里。第三行是快捷操作按钮,罗列出一些常用的快捷按钮(当然也可以用键盘快捷键代替)。中部是Notebook的正文,Notebook以单元格Cell为最小单位进行编排,每个单元格可以是代码单元格,也可以是Markdown单元格。点击上方的运行按钮即可运行该单元格的代码。Markdown单元格运行时将遵循Markdown语法对源文本进行渲染,常用来写备注和注释。代码单元格中存放的是可运行的代码,在本书中这里指的都是python代码,代码运行的输出直接附在该单元格的下方。 代码单元格左侧\[n\]内的数字代表着这是该Notebook第n次运行代码单元格,而在运行过程中会显示为\[\*\],代表该单元格正在运行。在Notebook中,一次只能运行一个单元格,运行序号靠后的单元格必须在序号考前的单元格运行完毕后才能轮到运行,在此之前只能显示\[\*\]或者上一次运行的编号。同一个Notebook中将共享一份全局变量表,运行序号靠前的代码单元格中的变量可以被靠后的代码单元格使用。 ![](../.gitbook/assets/juputermenu.png) 想要学习更多关于Jupyter的知识,可以通过chrome浏览器连接到板卡后打开getting\_started目录下的相应notebooks动手操作。 ![](../.gitbook/assets/jupyter_env.PNG) ================================================ FILE: pynq-zhong-wen-zi-liao/04pynq-overlay-jie-shao.md ================================================ # 04\_PYNQ Overlay介绍 ## OVERLAY介绍 Overlays,或者硬件库,都是可编程FPGA的设计理念。通过它们,用户可以把Zynq处理系统(Processing System of the Zynq)上的应用扩展到可编程逻辑层面上。Overlays可以用来加速软件应用或者为特定的应用自定义其硬件平台。 ![](../.gitbook/assets/01%20%286%29.png) 举例来说,FPGA可提供加速的一个经典案例便是图像处理。而一个软件工程师可以在FPGA结构上使用一个overlay进行相似的图像处理操作(比如边缘检测)。Overlays可以依据需求动态的加载到FPGA上,就像调用软件库一样。在这个例子里,不同overlay上的图像处理函数可以根据Python的指令分别进行加载调用。 PYNQ提供了一个Python交互界面,允许我们通过处理系统上的Python来控制可编程逻辑里的overlays。FPGA设计是一个非常专业化的任务,这需要专业的硬件工程知识。PYNQ的overlays就是由硬件设计师创建,并且包装成了PYNQ PYTHON API。软件开发者就可以无需重新自己设计overlay,而是直接使用这些写好的overlay来操作特定的硬件模块。这其实和专业软件工程师设计软件库并把它们包装成用户可用的API的道理一样。 ## 加载Overlay 一般来讲,我们把那些比较基层的、更像是用来做参考设计的overlay称为base overlay。它们是已经烧写在SD卡上的。当然了,我们也可以自己安装新的Overlay并在运行中使用他们。每一个Overlay都会有其对应的.bit文件,比如base\_overlay对应的就是base.bit。我们可以用下图的方法加载Overlay。 ![](../.gitbook/assets/02%20%285%29.png) 对于我们提到的已有的base overlay,PYNQ很贴心的为我们准备了专门的类,方便我们使用它们。之后,我们将通过学习base overlay来熟悉板的使用。调用代码如下图所示: ![](../.gitbook/assets/03.png) base\_overlay就是把bit文件转化后的python类。对于一个python类,我们自然可以使用前面介绍的辅助函数help来查看这个类到底是个什么东西,通过输入help\(base\_overlay\),我们可以查看这个类的具体介绍。下面只是一部分: ![](../.gitbook/assets/04%20%281%29.png) ================================================ FILE: pynq-zhong-wen-zi-liao/05baseoverlay-jie-shao.md ================================================ # 05\_BaseOverlay介绍 ## PYNQ-Z2 BASEOVERLAY PYNQ-Z2上的base overlay所涉及的硬件如下: * HDMI接口(输入输出) * Audio codec * 4个绿色LED,2个彩色LED,两个开关,四个按钮 * 两个Pmod PYNQ Microblaze * Arduino PYNQ Microblaze * RPI \(Raspberry Pi\) PYNQ MicroBlaze * 4个跟踪分析器(PMODA, PMODB, ARDUINO, RASPBERRYPI) ## HDMI PYNQ-Z2板上有HDMI输入与HDMI输出端口。HDMI接口是直接连接到可编程逻辑引脚上的。在板上没有外用的HDMI电路。HDMI接口是由可编程逻辑模块的HDMI IP控制的。 HDMI IP是链接到处理系统DRAM上的,视频可以以流的方式从HDMI输入端进入内存,然后再从HDMI输出端流出。这就允许我们通过python来处理视频数据,或者干脆通过python写一段视频流然后再由HDMI输出。 虽然Jupyter notebook支持内嵌的video形式,但是我们从HDMI捕捉到的视频数据会是原生的,如果不进行适当的编码处理,将无法在notebook上直接播放。 ### HDMI IN HDMI输入端IP可以捕捉标准的HDMI分辨率。在HDMI资源被连接后,HDMI控制器就会启动,并自动检测输入的数据。分辨率情况可以从Python 的HDMI类里读取,图像数据也可以流入处理系统DRAM。 ### HDMI OUT HDMI输出端IP支持一下分辨率: * 640x480 * 800x600 * 1280x720 \(720p\) * 1280x1024 * 1920x1080 \(1080p\) 数据可以从处理系统DRAM流到HDMI输出端。HDMI输出控制器能通过帧缓冲器来达到视频数据的流畅播放。 接下来,我们就来讲解如何使用HDMI,其源代码都可以在板上的getting\_started文件夹下base\_overlay\_video里找到。(我们使用的设备是1080p) 首先,我们得准备一个满足上述条件的输出显示屏,两根HDMI-HDMI线。将HDMI IN端与电脑相连,再将HDMI OUT端与准备的显示屏相连。打开我们的Jupyter Notebook。 ![](../.gitbook/assets/01%20%283%29.png) 第一步,如前面介绍,导入我们的base overlay,转化“base.bit”文件,这样我们就可以使用里面封装好的API了。我们使用base里的video模块来进行视频处理。Video模块下又分为hdmi\_in类与hdmi\_out类来分别处理HDMI IN流和HDMI OUT流。 ![](../.gitbook/assets/02.png) 第二步,我们得先对hdmi\_in和hdmi\_out进行相关配置,可以通过help来对configure进行一定了解。如下图所示: ![](../.gitbook/assets/03%20%281%29.png) 可以看到,hdmi\_in的configure函数只有一个参数pixelformat,并且有默认参数,通常情况下我们使用默认参数即可。并且从介绍中我们可以看出,在配置前我们必须先确保pipeline是处于非运行的状态。我们在之后会介绍其他的pixelformat状态。 ![](../.gitbook/assets/04%20%286%29.png) Hdmi\_out的configure函数与Hdmi\_in的有所不同,多了一个mode参数,这是因为我们需要选择输出的模式。Mode参数传递的是一个定义好的对象,不是我们自己随便写写就能出来的。我们需要使用pynq.lib.video下的VideoMode类来创建正确的参数信息。幸运的是,hdmi\_in对象为我们保存了输入的模式,我们可以先直接拿过来使用。至于pixelformat,我们选择默认的即可(即不用填写)。 ![](../.gitbook/assets/05%20%282%29.png) 配置好后,还得记得运行hdmi\_in.start\(\)和hdmi\_out.start\(\)来让输入输出端正确工作起来。此时,我们连接的显示屏应该会从蓝屏变为黑屏。 第三步,为了让屏幕有点什么,我们可以先尝试一下最简单的镜像,也就是把电脑屏幕拷贝到显示屏上。只需要输入hdmi\_in.tie\(hdmi\_out\)即可。我们也可以通过这个来检测屏幕是否有异常。 但是这样的话,我们无法对输出进行任何修改,只是单纯的拷贝了一个屏幕而已。为此,我们可以拆分输入输出的整合过程,这样就有了修改输出的余地。 ![](../.gitbook/assets/06.png) 我们运行上述代码,我们观察到屏幕在不断闪屏后最后不在变化。这是因为我们手写的输入输出没有任何缓冲,导致刷新不够快,被视觉捕捉到。由于上述程序for循环运行完后没有了后续输出,屏幕自然不会变化了。从输出结果我们可以看到刷新频率约为60fps。 但是,碍于处理器的性能最高只能做到60fps,如果在这之间加入一些帧处理的操作会使得实际帧数远远低于60fps,尽管如此,我们还是可以用其很好的进行图片处理。 ![](../.gitbook/assets/07%20%284%29.png) 比如我们假装使用了一个耗时0.1s的操作后再输出图像。运行程序后可以看出,虽然输出屏幕不再因为过快刷新而导致闪屏,但是屏幕流畅度大幅下降(约10fps)。 接下来,我们使用cv2库来尝试对得到的图像做一些简单的处理,下面的例子将只对屏幕做去色处理(即变成灰白界面)。 ![](../.gitbook/assets/08%20%286%29.png) 运行程序后可以看到,屏幕去掉了颜色且反转了颜色,并且在运行的时候更加卡顿,这是因为图像处理使得帧率掉到了1.4fps。 ![](../.gitbook/assets/09%20%281%29.png) 第四步,处理完所有的程序,我们还要调用close函数来释放所有资源。至此,一套完整的video处理基本完成了。 当然了,只是简单的去色这件事,我们可以通过其内置的模式简单的办到。通过在hdmi\_in的configure函数增添pixelformat参数,即可达成。如下图所示: ![](../.gitbook/assets/10%20%284%29.png) 由于去色是基于其更底层的代码,运行效率大大提高。现在,我们重新在尝试一下反色处理。注意!这里的PIXEL\_GRAY是由于我们之前运行过from pynq.lib.video import \*,这个语法允许我们直接使用pynq.lib.video里的变量名而无需加前缀。 ![](../.gitbook/assets/11%20%285%29.png) 可以看到,处理速度近乎翻了3倍。 当我们使用PIL库进行图像处理时,还有两个很好用的模式。第一个是常规的RGB模式。通过PIL库,我们可以很轻松的把hdmi\_in读入的图像打印到notebook上。代码如下所示: ![](../.gitbook/assets/12%20%284%29.png) ![](../.gitbook/assets/13.png) 另外一个模式是YCbCr,在一些图像处理算法与输出JPEG图像中很有用。我们采用如下方式进行更改: ![](../.gitbook/assets/14%20%281%29.png) 通过PIL库,我们还是可以很轻松的读取这种格式的图并将其转化成RBG: ![](../.gitbook/assets/15%20%282%29.png) 完成了操作后,不要忘记使用close函数来解放程序的占用。 事实上,PYNQ-Z2还为我们准备了人脸识别以及边缘检测这类高级应用,我们可以在base/video/文件夹下找到这些示例。 ## IOP-Pmod Pmod包是一个使用Pmod端的外部设备的驱动集合。一个Pmod端口是一个12引脚接口,很多来自Digilent和其他第三方合作生产商的Pmod设备都可使用。传统的Pmod外接设备包括了各式传感器(光、温度)、交互设备(网口、WIFI、蓝牙)以及输入输出设备(按钮、开关、LED)。 ![](../.gitbook/assets/16%20%284%29.png) 每一个Pmod连接器是由2排6引脚共计12引脚构成的。每一排由3.3V\(VCC\),接地\(GND\)以及4个数据引脚构成。如果使用两排的话那就是8个数据引脚。 ![](../.gitbook/assets/17.png) Pmod使用双排引脚时(2x4引脚或者2x6引脚),应该从左边开始插入(以连上VCC和GND) ![](../.gitbook/assets/18%20%283%29.png) 而只使用一拍的Pmod设备可以使用任意一排,但还是要从左边开始插入。如果你要使用一个别人写好了的驱动或者overlay的话,你需要检查到底那一排是支持这个设备的,并不一定是两排都能成功。比如说,Pmod ALS目前就只支持上面一排的端口。 所有的引脚都在3.3V时正常工作。根据不同设备的不同pull-up/pull-down I/O要求,Pmod数据引脚会有不同的IO标准。(例如IIC需要pull-up,SPI需要pull-down) 引脚0,1和4,5是连接到pull-down电阻器。这个可以支持SPI接口以及大部分外接设备。引脚2,3和6,7是连接到pull-up电阻器。这个可以支持IIC接口。 Pmod已经把这个pull-up/down传统考虑进去了,所以在使用Pmod的时候无需额外担心。接下来让我们看个Pmod的例子。同样的,代码我们都可以在base/Pmod下找到 ## OLED ![](../.gitbook/assets/19%20%282%29.png) 我们需要找一块OLED并将其连接到PMODA上。运行上述代码。 首先,同样的,我们需要导入base.bit文件。并在pynq.lib里导入响应的Pmod模块。在初始化响应模块的时候,我们需要告诉处理器我们使用了那个接口。因此在初始化pmod\_oled的时候,需要加入配置信息,也就是base.PMODA,如果用的是PmodB接口则只需用base.PMODB来初始化即可。 该模块的使用非常简单,通过help可以看到其只支持四个方法,分别是清屏、画线、画矩形、打印文字。使用help\(\)即可查看具体使用方法。 ## TC1传感器 接下来我们介绍一下热电偶(thermocouple)传感器的使用方法。我们需要相应的TC1传感器模块。 ![](../.gitbook/assets/20.png) 这次我们尝试一下PMODB接口,同样的,从pynq.lib里导入Pmod\_TC1类,并初始化该模块。上面的代码给出了基本的读取数据方法。 除了数据读取,传感器还为我们提供了数据记录的方法,方便我们根据时间轴来分析传感器数据。通过set\_log\_interval\_ms函数我们可以设定采集数据的时间间隔,默认是以1秒钟为间隔。 我们使用!\[\]\(images/Chapter\_05/21.png\)方法开始记录数据,并用!\[\]\(images/Chapter\_05/22.png\)结束数据记录。之后,我们只需要使用!\[\]\(images/Chapter\_05/23.png\)即可调出数据。但是注意,Pmod记录的数据只是传感器采集到的原始数据。我们需要特定的函数为我们转换成有用的数据。 ![](../.gitbook/assets/24%20%283%29.png) 在pynq.lib.pmod.pmod\_tcl里,为我们准备了相应的转换函数,其中reg\_to\_tc是转换成温度,reg\_to\_ref转换成相对指标。 其他的Pmod设备的使用方法均大同小异。通过dir\(pynq.lib\)我们可以看到,其自带的方法有如下几类: ![](../.gitbook/assets/25%20%282%29.png) 我们可以在base/pmod下找到大部分例子。 ## PMOD-Grove 除去Pmod外接设备,我们还有Pmod-Grove外接设备,它为我们提供了更多的零件。我们需要下面这个转换器来使用Grove套件。 ![](../.gitbook/assets/26.png) 在PYNQ Grove转换器上,一共有G1、G2、G3、G4四个端口,分别按照下图做了映射。如引脚0、4对应了G1端口。端口G1和G2支持SPI协议、GPIO以及timer Grove外接设备,但是不支持IIC设备。端口G3和G4对应另外一半引脚,支持IIC协议和GPIO外接设备。 ![](../.gitbook/assets/27.png) 接下来我们举个例子。 #### Grove LedBar 我们先将该设备与Grove Adapter连起来并插入PMODB上,由于它是由GPIO驱动,所以四个插口都可以达到目标,这里我们选取G1。 ![](../.gitbook/assets/28%20%282%29.png) 与以往一样,我们先从base.bit中得到各种配置参数。接着,从pynq.lib.pmod里导入Grove\_LEDbar类以及配置参数PMOD\_GROVE\_G1。 ![](../.gitbook/assets/29%20%283%29.png) 用上述代码初始化我们的LEDbar,可以看到,在使用Grove套件时候,我们需要一个额外的端口参数,也就是PMOD\_GROVE\_G1。 ![](../.gitbook/assets/30%20%283%29.png) Ledbar由8个绿灯,1个橙灯,一个红灯构成(我们一般称绿灯一头为底层),它为我们提供了简单的修改10个指示灯的函数write\_binary。我们可以通过10位的2进制来分别控制10个led,其中1代表开,0代表关。注意,传递的参数必须要加上0b以此彰显后面的10位数是个二进制数。 ![](../.gitbook/assets/31%20%282%29.png) 除此之外,每个LED还支持单独控制亮度,数值介于0-255之间,如果用16进制写法即介于0X00-0XFF之间。这里需要注意的是,虽然定义变量的时候使用了16进制定义,但是python会自动将其转换为10进制。上面的二进制也是如此,写的是二进制表示(方便看懂),但传递进去的实际为10进制。也就是说,我们不能使用hex\(255\)的方式来定义16进制。通过write\_brightness方法,我们不仅可以控制LED的开关,还可以同时控制其亮度。 Ledbar最大的用途其实还是通过bar的数目来显示进度。自然,该模块也为我们提供了相应的方法。我们可通过write\_level函数来直接显示一排LED。 write\_level\(level, bright\_level, green\_to\_red\)一共需要读入三个参数,分别是灯的个数,明亮程度,显示方向。其中明亮程度(bright\_level)按照上图的HIGH\MED\LOW\OFF划分,分别为3、2、1、0.显示方向(green\_to\_red)接收1/0,分别为从绿到红显示与从红到绿显示。 ![](../.gitbook/assets/32%20%283%29.png) 通过上述代码的例子,我们就可以很容易理解该函数的用法了。 ## Arduino 除去使用Grove Adapter,我们还可以使用Arduino PYNQ MicroBlaze来使用Grove套件。 ![](../.gitbook/assets/33%20%282%29.png) 通过使用PYNQ Shield接口,Grove设备就可连接上Arduino接口。下图即为PYNQ Sheild。 ![](../.gitbook/assets/34%20%281%29.png) 在PYNQ Shield上有4个IIC Grove连接器(板上的I2C),8个纵向Grove连接器(标记了G1-G7和UART),以及四个水平Grove连接器(标记了A1-A4)。 下面的表格标记了Grove端口的映射。 ![](../.gitbook/assets/35%20%283%29.png) 我们可以在pynq.lib.arduino包里找到相应的接口参数和Grove驱动。 ![](../.gitbook/assets/36.png) 上图代码即为通过Arduino来处理LEDbar。 ================================================ FILE: pynq-zhong-wen-zi-liao/06_logictools-overlay.md ================================================ # 06\_Logictools Overlay Logictools overlay 包含了可编程逻辑硬件区块来与外部数字逻辑电路连接。Python可以做出有限状态机(Finite State Machine)、布尔型逻辑函数和数字模式。一个可编程开关连接了硬件区和外部IO引脚之间的输入和输出。Logictools overlay也可以通过追踪分析器(trace analyzer)来捕捉IO接口传来的数据,方便我们分析调试。 ![](../.gitbook/assets/01%20%282%29.png) Logictools IP包含了4个主要硬件区块 * 模式生成器 * FSM生成器(有限状态机生成器) * 布尔型生成器 * 跟踪分析器 每一个区块不需要汇编配置文件,这意味着一个配置可以直接加载到生成器里并立即执行。 ## PYNQ-Z2 logic tools PYNQ-Z2 logictools overlay有两个logictools逻辑控制处理器(LCP),一个与Arduino header连接,一个与RPI header连接。 Arduino header有20个引脚,RPI有26个引脚,他们可以用作为LCP的GPIO。 板上的4个LED和4个按钮可以连接到任意一个LCP上,使得扩展输入成为了可能。注意!LED和按钮是共享的,在一个时刻只能被一个LCP使用。 ![](../.gitbook/assets/02%20%284%29.png) ## 布尔型生成器 ![](../.gitbook/assets/03%20%285%29.png) 与BaseOverlay不一样,我们要用LogicToolsOverlay来导入对应的logictools.bit。所谓布尔型生成器,就是用与、或、异或、非来构成最终的布尔型输出。在代码里,我们用“&”、“\|”、“^”、“~”来分别代表上面四个运算。接下来,我们先构建一个简单的表达式: ![](../.gitbook/assets/04%20%283%29.png)。LD2为班上的一个LED,PB3/0为板上的两个按钮。这里我们的运算就是PB3和PB0做异或运算后,把1/0赋值给LD2进行输出。 ![](../.gitbook/assets/05%20%284%29.png) ![](../.gitbook/assets/06%20%282%29.png) 从bit文件转换出的logictools\_olay类里,我们可以找到布尔型生成器,用其初始化一个布尔型生成器出来,并用上面的表达式配置该生成器,随后用run来运行。 这时候,我们可以按动板上的PB0/3并观察LD2,发现确实是按照异或法则进行的。 ![](../.gitbook/assets/07%20%285%29.png) 调用stop函数即可停止生成器运作。 刚刚,我们使用列表存储了一个表达式,事实上,我们可以使用可读性更高的字典来存储表达式,并且我们可以存储不止一个。 ![](../.gitbook/assets/08%20%283%29.png) 在上面的代码中,我们除了存储了异或门,还增加了一个与门。 \#\# 模式生成器 接下来我们展示一下如何操作模式生成器的单步模式。需要注意,并不是所有的logictool库中生成器都是单步的。 在这个例子里,我们只用python代码来模拟电路,并用追踪生成器捕捉到的波形来验证我们的结果。 ![](../.gitbook/assets/09%20%286%29.png) 首先,我们导入logictools overlay,并通过代码形式模拟波形。波形的构造满足一定格式。用{‘signal’:\[\]}来表明输入的信号波形。在\[\]内,我们逐一添加波形信息。格式为:{‘name’:’_’, ’pin’:’_’, ‘wave’:’lh.’}其中l代表low波,h表示high波,‘.’表示重复前面波形。 然后我们用logictools里的Waveform来将上述格式的信息转换为板能识别的波形。 ![](../.gitbook/assets/10%20%283%29.png) 接下来,我们按照上图代码为我们的模拟内容增加一点东西。外层的{‘signal’:\[\]}框架不变,在\[\]里,我们将之前的4个模拟波形信号用列表的方式打包,并命名为‘stimulus’(列表的第一个元素为名称),以同样的格式增加一栏‘analysis’。输出的效果如图所示。Analysis栏并没有任何输出,这是因为我们还未用模式生成器来追踪它。Waveform函数只是一个把代码转换成模拟波形并输出的函数而已,不具备追踪功能。 ![](../.gitbook/assets/11.png) 按照上图代码,我们生成一个模式生成器,其创建方式与布尔型生成器一模一样。在配置setup的时候,我们传入之前我们自己写的波形数据,并把模拟信号和分析内容指示给他。随后,我们调用模式生成器的step函数,即可跟踪模拟信号。重复运行step函数,我们可以看到,analysis栏波形按照上面stimulus栏的波形进行输出。 ![](../.gitbook/assets/12%20%285%29.png) 最后,在使用完后,使用reset进行重制。 ![](../.gitbook/assets/13%20%282%29.png) ## FSM生成器 最后,我们用FSM生成器来生成一个FSM(有限状态机)。这个例子中,我们做出来的FSM是一个格雷码计数器,它有三个状态位并可以通过8(即23)个状态来计数。计数器的输出是用格雷码编写的,这意味状态之间的转换只有一个2进制位会被改动(这是格雷码的特性)。 自然,我们先写入logictools.bit。 ![](../.gitbook/assets/14%20%282%29.png) 然后,我们编写相应的状态位。(下面的例子是Z1板上的,Z2板上并没有D0之类的接口) ![](../.gitbook/assets/15%20%284%29.png) 可以看出,要配置一个状态机,我们所需要描绘其输入、输出、状态、状态变换规则。这些均采用FSM规范格式。 ![](../.gitbook/assets/16%20%281%29.png) 随后,使用logictools里的FSM生成器创建一个,并用上面的对该状态机进行配置。 ![](../.gitbook/assets/17%20%281%29.png) 通过show\_state\_diagram\(\)函数,该状态机会返回一个我们所定义的状态机逻辑图,如下图所示。 ![](../.gitbook/assets/18%20%281%29.png) 为了能看到我们所写的结果,我们需要根据之前的input那一栏把D0与GND连接(逻辑归0),把D1连接到3.3V接口(逻辑归1),这些接口在Arduino区域可以找到。 * The reset input is connected to pin D0 of the Arduino connector * * Connect the reset input to GND for normal operation * When the reset input is set to logic 1 \(3.3V\), the counter resets to state 000 * The direction input is connected to pin D1 of the Arduino connector * * When the direction is set to logic 0, the counter counts down * Conversely, when the direction input is set to logic 1, the counter counts up ![](../.gitbook/assets/19%20%285%29.png) 使用run命令来运行我们生成的状态机。 最后用stop命令来完成善后工作。 ![](../.gitbook/assets/20%20%282%29.png) ================================================ FILE: pynq-zhong-wen-zi-liao/0701pynq-library-xiang-jie-ps-yu-pl-jie-kou.md ================================================ # 07-01\_PYNQ Library详解 - PS与PL接口 ## 前言 USB端口和其他的标准接口可以连接现成的USB和其他外部设备到Zynq PS上,并可以通过Python/Linux进行操控。PYNQ图像目前包含了一些最常用的驱动:USB webcams, WIFI接口,以及其他一些标准USB设备。 PYNQ提供overlay的一些底层控制,包括内存映射IO读写,内存分配等。 本章节将按照如下顺序逐一介绍: * Interrupt * MMIO * PS GPIO * Xlnk ## Interrupt(中断) Interrupt类代表了Vivado Block Design中的单一中断引脚。它通过使用一个wait函数去阻塞程序直到一个中断被抛出来模仿一个Python事件。这个事件会在中断被清除后自动清理。为了构造一个事件,把全限定(fully qualified)路径传递给Vivado Block Design中的引脚,比如说只传递’my\_ip/interrupt’。 中断只有在一个线程或者协程在等待对应事件的时候才能使用,推荐使用方法为在一个循环里进行等待(wait)操作,检查并清除IP里的中断注册器,然后再结束等待。比如说,AxiGPIO类就用这个方法等待所需要的值。 ```javascript class AxiGPIO(DefaultIP): # Rest of class definition def wait_for_level(self, value): while self.read() != value: self._interrupt.wait() # Clear interrupt self._mmio.write(IP_ISR, 0x1) ``` ## MMIO MMIO类允许Python对象获取映射的系统内存地址。特别的,在PL里的外部设备的注册与地址空间都是可以访问的。 在一个overlay里,连接到AXI通用端口的外设将会把他们的注册器或地址空间映射到系统内存里。通过PYNQ,一个IP的注册器或者地址空间就可以通过MMIO类从Python获取。 ![](../.gitbook/assets/53.png) MMIO提供了一个简单但方便的方法来访问控制外设。对于只有少量数据交互或者性能要求不高的外设,MMIO通常都是够用了的。如果对性能要求很高,或者大量数据要在PS和PL间传输,那么使用带有DMA IP和PYNQ DMA类的Zynq HP接口会更合适。 **实例:** 在这个例子里,数据会被写到一个IP并从相同的地址读取。 ```javascript IP_BASE_ADDRESS = 0x40000000 ADDRESS_RANGE = 0x1000 ADDRESS_OFFSET = 0x10 from pynq import MMIO mmio = MMIO(IP_BASE_ADDRESS, ADDRESS_RANGE) data = 0xdeadbeef mmio.write(ADDRESS_OFFSET, data) result = mmio.read(ADDRESS_OFFSET) ``` 这个例子假设内存映射区域是从0x40000000到0x40001000,且对PS来说是可访问的。更多的信息可以在pynq.mmio模块找到。 ## PS GPIO Zynq设备有最多从PS到PL的64个GPIO。它们可以用来做简单的操控。比如base overlay中通过PS GPIO来对IOP进行复位/重置。PS GPIO是一个非常简单的接口,并不需要PL中的IP就可以使用。 GPIO类就是用来控制PS GPIO的。\(注意: AXI GPIO是由AxiGPIO类控制的\) ![](../.gitbook/assets/54%20%281%29.png) PS GPIO使用Linux核来控制GPIO。这意味着操作系统会在运行时给GPIO分配一个标号。在使用PS GPIO之前,我们必须把Linux引脚标号映射到Python GPIO实例上。 Get\_gpio\_pin\(\)函数就是用来映射PS引脚标号到Linux引脚标号上的。 ```javascript from pynq import GPIO output = GPIO(GPIO.get_gpio_pin(0), 'out') input = GPIO(GPIO.get_gpio_pin(1), 'in') output.write(0) input.read() ``` 更多内容可以在pynq.gpio模块找到。 ## Xlnk Xlnk类用来分配连续内存。 连接到AXI Master(HP或ACP端口)的IP能访问PS DRAM。在PL里的IP访问DRAM前,必须给IP分配内存。Python里的一个数组将会被分配到虚拟内存的某个地方。被分配的物理内存地址必须提供给PL里的IP。 Xlnk可以分配内存,并提供物理指针。它可以分配连续地址,PL IP可以用的更有效。通过Numpy包,Xlnk可以分配数组,这允许专门制定数组的数据类型。Xlnk也被DMA隐式使用来分配内存。 **实例:** 我们创建一个Xlnk实例,并用cma\_array\(\)来分配unsigned 32-bit int的连续区块内存: ```javascript from pynq import Xlnk import numpy as np xlnk = Xlnk() input_buffer = xlnk.cma_array(shape=(5,), dtype=np.uint32) ``` physical\_address 是memory buffer的属性: ```javascript input_buffer.physical_address ``` 写数据到buffer: ```javascript for i in range(5): input_buffer[i] = i # Input buffer: [0 1 2 3 4] ``` 更多数据可在pynq.xlnk找到。 ================================================ FILE: pynq-zhong-wen-zi-liao/0702pynq-library-xiang-jie-ip-fang-wen.md ================================================ # 07-02\_PYNQ Library详解 - IP访问 ## 前言 Vivado工具为各种接口标准和协议的外设提供了IP,PYNQ给常用的外接设备- Video(HDMI IN/OUT)、 GPIO设备(Buttons, Switches, LEDs)、传感器和执行器等提供了Python API。这些PYNQ API也可以被扩展用以支持其他的IP。 本章节将按照如下顺序逐一介绍: * Audio * AxiGPIO * AxiIIC * DMA * Logictools * Video ## Audio(音频) Audio模块提供了从输入麦克风读取音频、从播放器播放音频以及读写音频文件的方法。Audio模块是连接到Audio IP子系统上来捕获播放内容。该模块可以支持不同的IP子系统。目前支持的有line-in, PYNQ-Z2上使用ADAU1761编码的HP/Mic,PYNQ-Z1上的脉冲宽度调制器(Pulse Width Modulation)的单输出与脉冲强度调制器(Pulse Density Modulated)的麦克风输入。 **实例:** Base Overlay有一个单独的Audio实例:audio。在overlay加载后,这个实例可以立即使用,如下所示: **PYNQ-Z1** > ```javascript > from pynq.overlays.base import BaseOverlay > base = BaseOverlay("base.bit") > pAudio = base.audio > > pAudio.load("/home/xilinx/pynq/lib/tests/pynq_welcome.pdm") > pAudio.play() > ``` **PYNQ-Z2** ```javascript from pynq.overlays.base import BaseOverlay base = BaseOverlay("base.bit") pAudio = base.audio pAudio.load("/home/xilinx/jupyter_notebooks/base/audio/data/recording_0.wav") pAudio.play() ``` 注:PYNQ-Z1支持直接的PDM播放,而PYNQ-Z2支持Wav) 我们可以在以下位置找到更多的例子。 ```text /base/audio/audio_playback.ipynb ``` ## AxiGPIO AxiGPIO类提供了读写通用设备如LED、按钮、开关等(需要通过AXI GPIO控制IP连接到PL上)并接受来自外部的中断。 每一个AXIGPIO能有至多两个通道,每一个通道至多有32个引脚。 ![](../.gitbook/assets/01%20%285%29.png) 通过read\(\)和write\(\)方法,我们可以在一个通道上对任意的GPIO进行读写数据。 setdirection\(\)和setlength\(\)可以从来配置IP。方向可以是“in”、“out”、“inout”。 通常方向是“inout”。只选择“in”或者“out”将会分别只允许读与写IP,如果要读“out”或者写“in”将会报错。 GPIO可以像一个数组一样来处理。这允许我们设定特定的‘位’并避免使用\*\*位屏蔽\*\*(bit mask)的需求。 中断信号\*ip2intc\_irpt\* 可以直接发送到AXI中断处理器上来引发PS的中断。我们可以在\*PYNQ and Asyncio\*部分中找到更多的有关异步IO和中断的信息。 ![](../.gitbook/assets/02%20%288%29.png) \#\#\# \*\*实例:\*\* 下面的例子只是展示了如何使用AxiGPIO类,实际使用的时候,可能会有LED, Button, Switches, 和RGBLED等扩展类可供使用。 在一个overlay加载了后,一个AxiGPIO实例可以立即通过AXI GPIO控制器的名字进行创建调用。 \`\`\`javascript from pynq import Overlay from pynq.lib import AxiGPIO ol = Overlay\("base.bit"\) led\_ip = ol.ip\_dict\['gpio\_leds'\] switches\_ip = ol.ip\_dict\['gpio\_switches'\] leds = AxiGPIO\(led\_ip\).channel1 switches = AxiGPIO\(switches\_ip\).channel1 \`\`\` 简单的读写: \`\`\`javascript mask = 0xffffffff leds.write\(0xf, mask\) switches.read\(\) \`\`\` 把AXI GPIO当做数组处理: \`\`\`javascript switches.setdirection\("in"\) switches.setlength\(3\) switches.read\(\) \`\`\` 我们可以在\[pynq.lib.axigpio Module\]\(https://pynq.readthedocs.io/en/latest/pynq\_package/pynq.lib/pynq.lib.axigpio.html\#pynq-lib-axigpio\)部分中知道更多的有关读写等操作的例子。在下面的文件中,我们可以找到一些有关按钮与LED的例子。/base/board/board\_btns\_leds.ipynb \# AxiIIC AxiIIC类提供了对AXI IIC控制器IP的读与写。send\(\)和receive\(\)方法可以用来读与写。 \`\`\`javascript send\(address, data, length, option=0\) \`\`\` - Address是外部设备IIC的地址 - Data是一个被发送到IP的字节数组 - length是发送的字节数 - option允许一个IIC重复运行 \`\`\`javascript receive\(address, data, length, option=0\) \`\`\` - Address是外部设备IIC的地址 - Data是一个被发送到IP的字节数组 - length是发送的字节数 - option允许一个IIC重复运行 更多有关AxiIIC模块和读写等操作可以在\[pynq.lib.axiiic Module\]\(https://pynq.readthedocs.io/en/latest/pynq\_package/pynq.lib/pynq.lib.axiiic.html\#pynq-lib-axiiic\) 中找到。 \#\# DMA(直接内存访问) PYNQ支持AX central DMA IP。DMA可以在PS DRAM与PL的快速交互中发挥出色的效果。DMA类只支持简单模式。 DMA有一个AXI轻型控制接口,一个读写通道以及连接到一个IP的流端口。 ![](../.gitbook/assets/03%20%284%29.png) 读通道会从PS DRAM中读取数据,并写入流中。写通道会从流中读取数据并写回PS DRAM里。 需要注意的是DMA在完成数据传输的时候,需要连接到DMA(写通道)的IP来设置AXI TLAST信号。若该信号未被设置,DMA将会永远无法完成该次传输。在使用HLS来产生IP的时候,这一点非常重要——TLAST信号必须在C代码中设置。 **实例:** 下面的例子假定overlay已经有一个AXI直接内存访问IP,一个来自DRAM的读通道,一个AXI Master流接口(用以操作输出流),一个写入DRAM的写通道,和一个AXI Slave流接口(用以操作输入流)。 在Python代码里,我们使用Xlnk创建了两个连续的内存缓冲。 DMA将会从input\_buffer里读取数据,并发送至AXI Master。然后DMA再从AXI Slave将数据写回output\_buffer。 AXI流之间是以本地环回接口的方式连接,以此在通过DMA发送和接收数据后,input\_buffer的内容可以转移到output\_buffer中。 需要注意的是在制作DMA的实例的时候,默认最大的交互量是14bits。若需要更大的DMA交互,在配置DMA的时候增大这个量。 下面的代码中没有写出下载bitstream的代码。 ```javascript import pynq.lib.dma from pynq import Xlnk import numpy as np xlnk = Xlnk() dma = ol.axi_dma input_buffer = xlnk.cma_array(shape=(5,), dtype=np.uint32) output_buffer = xlnk.cma_array(shape=(5,), dtype=np.uint32) ``` 写一些数据到数组里: ```javascript for i in range(5): input_buffer[i] = i Input buffer will contain: [0 1 2 3 4] ``` 把input\_buffer转移至DMA send通道,并从DMA recv通道读取数据到output\_buffer。wait\(\)方法确保DMA交互能够完成。 ```javascript dma.sendchannel.transfer(input_buffer) dma.recvchannel.transfer(output_buffer) dma.sendchannel.wait() dma.recvchannel.wait() Output buffer will contain: [0 1 2 3 4] ``` 更多有关DMA模块的信息可以在pynq.lib.dma模块中找到。 ## Logictools Logictools包含了Trace Analyzer以及三个PYNQ硬件生成器(布尔型生成器、FSM生成器、模式生成器)的驱动。 ![](../.gitbook/assets/04%20%282%29.png) Logictools overlay里的主要硬件模块的基础操作都是一致的,它们是setup\(\), run\(\), step\(\), stop\(\), reset\(\)。每一个模块可能有额外的独有方法来提供特定的功能。下图为运行的基本图解: ![](../.gitbook/assets/05%20%286%29.png) - 重置态 这个状态是一个模块在overlay加载后准备开始的状态。这个“重置”状态会一直维持直到使用setup\(\)方法进行配置。 使用reset\(\)来重新回到该状态。 在“重置”状态下,所有的逻辑工具overlay可用的IO都处于未连接状态。这可以预防一些由于粗心大意的驱动而造成的意外。 模式生成器有一个BRAM来存储将要生成的模式。在这个状态下,BRAM会被配置为0。类似的还有FSM生成器。 - 准备态 在这个状态下,生成器/分析器已经配置完毕。将要连接的输入输出引脚已经准备完毕,但是把这些引脚连接到内部硬件的接口开关还未配置。 - 运行态 一旦生成器在准备状态,调用run\(\)或step\(\)即可使它们进入运行态。在这个状态下,接口开关已经配置为连接外部IO。硬件模块在这个状态下开始运行。 运行时默认是以single-shot模式。这个模式下,生成器将会在追踪分析器采集到足够多的数据或者模式结束后才会停止,这之后生成器和分析器会一起回到准备态。 布尔型生成器是一个特例,它总是处于连续模式下运行。 在连续模式下,模式生成器会连续的生成它的模式,并在达到模式的末尾后循环运行。FSM生成器则会持续运行直到被停止。(使用stop\(\)函数) \*\*通用方法:\*\* - \*\*Setup\(\)\*\* 每一模块必须在使用前使用setup\(\)进行配置。 注意,当配置完成后,IO接口并未连接。 - \*\*Run\(\)\*\* 该方法启动模块,使之进入运行态。 - \*\*Step\(\)\*\* 与run\(\)方法类似,区别在于运行的时候是一步一步运行的。 在对模式生成器使用该函数时,碰到结尾即停止,不会循环。 FSM生成器只有在获得了足够多的样本后才能进行step操作。 - \*\*Stop\(\)\*\* 若一个模块正在运行,则在其重新运行前必须先停止。 一个模块的运行被停止后,它的输出就会与外部IO断开,只有当重新回到运行态后才会连接。 - \*\*Trace\(\)\*\* 追踪功能默认开启。当启用时,追踪分析器会捕获所有已连模块的信息。使用trace方法启用或者停用。 - \*\*Reset\(\)\*\* 重置生成器到初始状态,在更改硬件配置时我们需要调用该方法。 \# 布尔型生成器 ![](../.gitbook/assets/06%20%281%29.png) 这里,我们要用LogicToolsOverlay来导入对应的logictools.bit。所谓布尔型生成器,就是用与、或、异或、非来构成最终的布尔型输出。在代码里,我们用“&”、“\|”、“^”、“~”来分别代表上面四个运算。接下来,我们先构建一个简单的表达式: ![](../.gitbook/assets/07%20%283%29.png) LD2为板上的一个LED,PB3/0为板上的两个按钮。这里我们的运算就是PB3和PB0做异或运算后,把1/0赋值给LD2进行输出。 ![](../.gitbook/assets/08.png) ![](../.gitbook/assets/09%20%283%29.png) 从bit文件转换出的logictools\_olay类里,我们可以找到布尔型生成器,用其初始化一个布尔型生成器出来,并用上面的表达式配置该生成器,随后用run来运行。 这时候,我们可以按动板上的PB0/3并观察LD2,发现确实是按照异或法则进行的。 ![](../.gitbook/assets/10%20%282%29.png) 调用stop函数即可停止生成器运作。 刚刚,我们使用列表存储了一个表达式,事实上,我们可以使用可读性更高的字典来存储表达式,并且我们可以存储不止一个。 ![](../.gitbook/assets/11%20%283%29.png) 在上面的代码中,我们除了存储了异或门,还增加了一个与门。事实上,输入并不局限于按钮开关,板上的引脚都可以用作输入输出。如果需要更改硬件配置,记得使用reset函数进行重制。 ## 模式生成器 模式生成器支持至多64K的模式。根据sample clock的频率,会不断生产data word。 Sample clock也是可编程的。最慢的速度是252KHz,最快可以达到10MHz。 接下来我们展示一下如何操作模式生成器的单步模式。需要注意,并不是所有的logictool库中生成器都是单步的。 在这个例子里,我们只用python代码来模拟电路,并用追踪生成器捕捉到的波形来验证我们的结果。 ![](../.gitbook/assets/12%20%283%29.png) 首先,我们导入logictools overlay,并通过代码形式模拟波形。波形的构造满足一定格式。用{‘signal’:\[\]}来表明输入的信号波形。在\[\]内,我们逐一添加波形信息。格式为:{‘name’:’_’, ’pin’:’_’, ‘wave’:’lh.’}其中l代表low波,h表示high波,‘.’表示重复前面波形。 然后我们用logictools里的Waveform来将上述格式的信息转换为板能识别的波形。 ![](../.gitbook/assets/13%20%285%29.png) 接下来,我们按照上图代码为我们的模拟内容增加一点东西。外层的{‘signal’:\[\]}框架不变,在\[\]里,我们将之前的4个模拟波形信号用列表的方式打包,并命名为‘stimulus’(列表的第一个元素为名称),以同样的格式增加一栏‘analysis’。输出的效果如图所示。Analysis栏并没有任何输出,这是因为我们还未用模式生成器来追踪它。Waveform函数只是一个把代码转换成模拟波形并输出的函数而已,不具备追踪功能。 ![](../.gitbook/assets/14.png) 按照上图代码,我们生成一个模式生成器,其创建方式与布尔型生成器一模一样。在配置setup的时候,我们传入之前我们自己写的波形数据,并把模拟信号和分析内容指示给他。随后,我们调用模式生成器的step函数,即可跟踪模拟信号。重复运行step函数,我们可以看到,analysis栏波形按照上面stimulus栏的波形进行输出。 ![](../.gitbook/assets/15%20%285%29.png) 最后,在使用完后,使用reset进行重制。 ![](../.gitbook/assets/16.png) \# FSM生成器(有限状态机) FSM生成器有一个内置模块内存来执行有限状态机。在Arduino shield header上的20个引脚都是可用的。FSM必须有最少1个输入,最多可以有19个输出。最大的输入个数为8个。比如说,基于输入的个数,如下的配置都是可行的。 ![](../.gitbook/assets/17%20%282%29.png) 追踪分析器是由MicroBlaze子系统控制,它连在一个DMA上,DMA也受到加载配置信息的MicroBlaze子系统的控制,包括配置模块内存来执行FSM。 FSM的配置可以由特定的文本格式进行具化描写。 ![](../.gitbook/assets/18%20%282%29.png) \*\*实例:\*\* FSM的详细描述是由setup\(\)来传递的。Run\(\)用以启动FSM,就和其他生成器类似。 FSM有两个特有函数来显示FSM状态图与波形。 ![](../.gitbook/assets/19%20%281%29.png) \# Trace Analyzer 片上调试使得FPGA资源得以被用来监测调试时的内部外部信号。调试电路利用一些设计来保存系统运行时的信号数据。调试数据保存在芯片上的内存,并可以之后被读取分析。传统片上调试的常常会受限于本地内存的大小。这意味着只能得到有限的调试数据。 片上调试的概念已经被扩展到允许调试数据被保存在DDR内存。这使得我们可以获得更多的调试数据,并用python进行分析。 追踪分析器会监测PMOD和Arduino上的外部PL\*\*输入输出模块\*\*(IOB)。这些IOB是三态的。这意味着每一个引脚都会有三个内部信号:input(I),output(O),tri-state(T)。T信号被用来控制一个引脚是用作输入还是输出。Trace Analyzer会连接到IOP上的所有3个信号。 \# Video Video子包里有一整套驱动集合——从HDMI-IN端口读取数据、写数据到HDMI-OUT端口、传输数据、设定中断、操作视频帧。 视频硬件子系统含有HDMI-IN模块、HDMI-OUT模块和一个视频DMA。HDMI-IN和HDMI-OUT模块都支持彩色空间转换。例如从YCrCb到RGB或者反过来。 PYNQ-Z2板上有HDMI输入与HDMI输出端口。HDMI接口是直接连接到可编程逻辑引脚上的。在板上没有外用的HDMI电路。HDMI接口是由可编程逻辑模块的HDMI IP控制的。 HDMI IP是链接到处理系统DRAM上的,视频可以以流的方式从HDMI输入端进入内存,然后再从HDMI输出端流出。这就允许我们通过python来处理视频数据,或者干脆通过python写一段视频流然后再由HDMI输出。 虽然Jupyter notebook支持内嵌的video形式,但是我们从HDMI捕捉到的视频数据会是原生的,如果不进行适当的编码处理,将无法在notebook上直接播放。 - \*\*HDMI IN\*\* ![](../.gitbook/assets/20%20%283%29.png) HDMI输入端IP可以捕捉标准的HDMI分辨率。在HDMI资源被连接后,HDMI控制器就会启动,并自动检测输入的数据。分辨率情况可以从Python 的HDMI类里读取,图像数据也可以流入处理系统DRAM。 * **HDMI OUT** ![](../.gitbook/assets/21%20%283%29.png) HDMI输出端IP支持一下分辨率: 640x480 800x600 1280x720 \(720p\) 1280x1024 1920x1080 \(1080p\) 数据可以从处理系统DRAM流到HDMI输出端。HDMI输出控制器能通过帧缓冲器来达到视频数据的流畅播放。 接下来,我们就来讲解如何使用HDMI,其源代码都可以在板上的getting\_started文件夹下base\_overlay\_video里找到。(我们使用的设备是1080p) 首先,我们得准备一个满足上述条件的输出显示屏,两根HDMI-HDMI线。将HDMI IN端与电脑相连,再将HDMI OUT端与准备的显示屏相连。打开我们的Jupyter Notebook。 ![](../.gitbook/assets/22.png) 第一步,如前面介绍,导入我们的base overlay,转化“base.bit”文件,这样我们就可以使用里面封装好的API了。我们使用base里的video模块来进行视频处理。Video模块下又分为hdmi\_in类与hdmi\_out类来分别处理HDMI IN流和HDMI OUT流。 ![](../.gitbook/assets/23.png) 第二步,我们得先对hdmi\_in和hdmi\_out进行相关配置,可以通过help来对configure进行一定了解。如下图所示: ![](../.gitbook/assets/24%20%284%29.png) 可以看到,hdmi\_in的configure函数只有一个参数pixelformat,并且有默认参数,通常情况下我们使用默认参数即可。并且从介绍中我们可以看出,在配置前我们必须先确保pipeline是处于非运行的状态。我们在之后会介绍其他的pixelformat状态。 ![](../.gitbook/assets/25%20%283%29.png) Hdmi\_out的configure函数与Hdmi\_in的有所不同,多了一个mode参数,这是因为我们需要选择输出的模式。Mode参数传递的是一个定义好的对象,不是我们自己随便写写就能出来的。我们需要使用pynq.lib.video下的VideoMode类来创建正确的参数信息。幸运的是,hdmi\_in对象为我们保存了输入的模式,我们可以先直接拿过来使用。至于pixelformat,我们选择默认的即可(即不用填写)。 ![](../.gitbook/assets/26%20%282%29.png) 配置好后,还得记得运行hdmi\_in.start\(\)和hdmi\_out.start\(\)来让输入输出端正确工作起来。此时,我们连接的显示屏应该会从蓝屏变为黑屏。 第三步,为了让屏幕有点什么,我们可以先尝试一下最简单的镜像,也就是把电脑屏幕拷贝到显示屏上。只需要输入hdmi\_in.tie\(hdmi\_out\)即可。我们也可以通过这个来检测屏幕是否有异常。 但是这样的话,我们无法对输出进行任何修改,只是单纯的拷贝了一个屏幕而已。为此,我们可以拆分输入输出的整合过程,这样就有了修改输出的余地。 ![](../.gitbook/assets/27%20%281%29.png) 我们运行上述代码,我们观察到屏幕在不断闪屏后最后不在变化。这是因为我们手写的输入输出没有任何缓冲,导致刷新不够快,被视觉捕捉到。由于上述程序for循环运行完后没有了后续输出,屏幕自然不会变化了。从输出结果我们可以看到刷新频率约为60fps。 但是,碍于处理器的性能最高只能做到60fps,如果在这之间加入一些帧处理的操作会使得实际帧数远远低于60fps,尽管如此,我们还是可以用其很好的进行图片处理。 ![](../.gitbook/assets/28%20%283%29.png) 比如我们假装使用了一个耗时0.1s的操作后再输出图像。运行程序后可以看出,虽然输出屏幕不再因为过快刷新而导致闪屏,但是屏幕流畅度大幅下降(约10fps)。 接下来,我们使用cv2库来尝试对得到的图像做一些简单的处理,下面的例子将只对屏幕做去色处理(即变成灰白界面)。 ![](../.gitbook/assets/29.png) 运行程序后可以看到,屏幕去掉了颜色且反转了颜色,并且在运行的时候更加卡顿,这是因为图像处理使得帧率掉到了1.4fps。 ![](../.gitbook/assets/30%20%282%29.png) 第四步,处理完所有的程序,我们还要调用close函数来释放所有资源。至此,一套完整的video处理基本完成了。 当然了,只是简单的去色这件事,我们可以通过其内置的模式简单的办到。通过在hdmi\_in的configure函数增添pixelformat参数,即可达成。如下图所示: ![](../.gitbook/assets/31%20%281%29.png) 由于去色是基于其更底层的代码,运行效率大大提高。现在,我们重新在尝试一下反色处理。注意!这里的PIXEL\_GRAY是由于我们之前运行过from pynq.lib.video import \*,这个语法允许我们直接使用pynq.lib.video里的变量名而无需加前缀。 ![](../.gitbook/assets/32%20%281%29.png) 可以看到,处理速度近乎翻了3倍。 当我们使用PIL库进行图像处理时,还有两个很好用的模式。第一个是常规的RGB模式。通过PIL库,我们可以很轻松的把hdmi\_in读入的图像打印到notebook上。代码如下所示: ![](../.gitbook/assets/33%20%281%29.png) ![](../.gitbook/assets/34%20%282%29.png) 另外一个模式是YCbCr,在一些图像处理算法与输出JPEG图像中很有用。我们采用如下方式进行更改: ![](../.gitbook/assets/35%20%281%29.png) 通过PIL库,我们还是可以很轻松的读取这种格式的图并将其转化成RBG: ![](../.gitbook/assets/36%20%283%29.png) 完成了操作后,不要忘记使用close函数来解放程序的占用。 事实上,PYNQ-Z2还为我们准备了人脸识别以及边缘检测这类高级应用,我们可以在base/video/文件夹下找到这些示例。 ================================================ FILE: pynq-zhong-wen-zi-liao/0703pynq-library-xiang-jie-ps-and-pl-control.md ================================================ # 07-03\_PYNQ Library详解 - PS and PL control ## 前言 PYNQ也提供overlay的一些底层控制,包括overlay的控制和管理,以及PL的底层控制等,包括: ### **PS control** * PMBus ### **PL control** * Overlay * PL and Bitstream classes ## **PMBus** PYNQ 提供了电压和当前板上提供的许多使用PMBus(或其他LINUX核支持的协议)的传感器的访问权限。PYNQ使用libsensors API \([https://github.com/lm-sensors/lm-sensors](https://github.com/lm-sensors/lm-sensors)\)来监控传感器。 * **pynq.pmbus API** 所有的传感器可以通过使用pynq.get\_rails\(\)函数找到,该函数返回一个字典,标记了电压轨道名字到一个 Rail 类。每一个 Rail 类有成员voltage, current 和 power 传感器,当前状态读取可以从value 属性得到。 * **The DataRecorder** PMBus库的另一个方面是提供了能在一次测试里一个记录多个传感器值的DataRecorder 类。一个DataRecorder 是由被监控的传感器构造的,并最终会产生一个pandas DataFrame作为结果。record\(sample\_interval\) 函数开始以指定的采样速率进行记录。stop\(\) 函数会停止记录。如果 record 函数在with 块里使用,那么stop 会在块的最后自动调用,以确保监控线程是始终结束的。每一个样本由时间戳进行索引编号并包含一个会话识别器。识别器从0开始并且随着每一次 record 或者 mark\(\) 的调用而增长。这个识别器允许不同部分可以更进一步的分析。 **实例:** ```javascript from pynq import get_rails, DataRecorder rails = get_rails() recorder = DataRecorder(rails['12V'].power) with recorder.record(0.2): # Sample every 200 ms # Perform the first part of the test recorder.mark() # Perform the second part of the test results = recorder.frame ``` ## **Overlay** Overlay类是用来加载PYNQ overlays到PL上,并且管理控制已有的overlays。这个类是由overlay的.BIT文件进行实例化。默认的话,overlay的Tcl文件将会被解析,并且bitstream将会下载到PL上。这意味着要用overlay类,.BIT和.TCL必须得有。 若要不加载.BIT文件就实例化overlay,那就在实例化的时候传入参数download=False。 一开始下载bitstream后,overlay .TCL文件的时钟设定就会在bitstream下载完毕前被应用。 **实例:** .BIT文件路径可以是相对的也可以是绝对。 ```javascript from pynq import Overlay base = Overlay("base.bit") # bitstream implicitly downloaded to PL base = Overlay("base.bit", download=False) # Overlay is instantiated, but bitstream is not downloaded to PL base.download() # Explicitly download bitstream to PL base.is_loaded() # Checks if a bitstream is loaded base.reset() # Resets all the dictionaries kept int he overlay base.load_ip_data(myIP, data) # Provides a function to write data to the memory space of an IP # data is assumed to be in binary format ``` ip\_dict 包含了overlay的一列IP,并可以用来决定IP驱动、物理地址、版本、或者连接到IP上的中断。 ```javascript base.ip_dict ``` ## **PL and Bitstream classes** PL主要是一个Overlay类使用的内部类。PL类创立一个PL server来管理下载好的overlay。PL server能够防止不同应用的多个overlays覆写当前加载的overlay。 Overlay Tcl文件是由PL类解析并生成IP、clock、中断、gpio字典(IP、clocks和overlay信号的相关信息)。 Bitstream类可以在pl.py里找到,并可以用来下载一个bitstream文件到PL上而不需要使用overlay Tcl文件。这可以用来调试,但是这些属性也可以从Overlay类里获取到。使用Overlay类来访问这些属性是推荐的方法。 **实例:** * **PL** ```javascript PL.timestamp # Get the timestamp when the current overlay was loaded PL.ip_dict # List IP in the overlay PL.gpio_dict # List GPIO in the overlay PL.interrupt_controllers # List interrupt controllers in the overlay PL.interrupt_pins # List interrupt pins in the overlay PL.hierarchy_dict # List the hierarchies in the overlay ``` * **Bitstream** ```javascript from pynq import Bitstream bit = Bitstream("base.bit") # No overlay Tcl file required bit.download() bit.bitfile_name ``` ‘/opt/python3.6/lib/python3.6/site-packages/pynq/overlays/base/base.bit’ 更多有关PL模块信息可以在pynq.pl模块找到。 ================================================ FILE: pynq-zhong-wen-zi-liao/0704pynq-library-xiang-jie-iop.md ================================================ # 07-04\_PYNQ Library详解 - IOP ## 前言 Zynq平台通常有多个Headers和接口,它们用来连接外部设备或者直接连接Zynq PL引脚。许多现成的外部设备都可以连接到Pmod和Arduino接口上。其他的外部设备可以通过**转换器**(Adapter)或者**面包板**(Breadboard)连接到这些端口。需要注意的是当我们要使用一个外部设备的时候,我们必须先在overlay中构建一个控制器,并提供相应的软件驱动,然后我们才能使用这个设备。 接下来,我们按照如下目录逐一介绍: * Arduino * Grove * Pmod * RPi ## **Arduino** Arduino子包包含了所有用来控制连接到Arduino端口的外部设备的驱动装置。一个Arduino连接器可以把Arduino compatible shields连接到PL引脚上。不过要记得在一个overlay里必须有相应的控制器来执行对应的驱动,这之后shield才能被使用。Arduino引脚也可以用作通用引脚来连接传统的硬件设施。 ![](../.gitbook/assets/37.png) 如果有的话,一个Arduino PYNQ MicroBlaze可以控制Arduino接口。这个MicroBlaze就和Pmod的MicroBlaze一样,只是有更多的AXI控制器。 ![](../.gitbook/assets/38.png) 正如在图标里所示,Arduino PYNQ MicroBlaze有一个PYNQ MicroBlaze子系统,一个配置开关,以及以下AXI控制器: * AXI I2C * Frequency: 100KHz * Address mode: 7 bit * 2x AXI SPI * Master mode * Transaction Width: 8 * SCK Frequency: 6.25 MHz * FIFO Depth: 16 注意:一个SPI控制器是连接到专门的SPI引脚Arduino接口上的。 * 3x AXI GPIO * 16 Input/Output pins total * 6x AXI Timer * 32 bits * 1 Generate Output * 1 PWM Output * 1x AXI UART * 9600 Baud * 1x AXI XADC * 1V peak-to-peak \* 警告:模拟输入能通过内部的Xilinx XADC获得支持。这将支持1V peak-to-peak的输入。注意Arduino接口支持0-5V模拟输入,但没有外部电路的Zynq是不支持的。 * AXI Interrupt controller(AXI中断控制器) 管控MicroBlaze子系统上的外部设备的中断操作。 * Interrupt GPIO(中断GPIO) 有一个额外的AXI GPIO被用来标记发送到PS的中断请求。 * Configurable Switch(配置开关) 允许从特定设备发送信号至外部接口。 ![](../.gitbook/assets/39.png) \*\*实例:\*\* 在Base Overlay里,有一个Arduino PYNQ MicroBlaze实例。在overlay被下载后,这个实例可按如下方式调用。 \`\`\`javascript from pynq.overlays.base import BaseOverlay from pynq.lib.arduino import Arduino\_LCD18 lcd = Arduino\_LCD18\(base.ARDUINO\) lcd.clear\(\) \`\`\` 在pynq.lib.arduino包里可以找到更多有关Arduino的信息。 \# \*\*Grove\*\* Grove外部设备可以通过使用PYNQ Grove Adapter来连接到Pmod引脚上或者通过PYNQ Shield连接到Arduino引脚上。 - \#\#\# \*\*Pmod\*\* 连接Grove外部设备的第一个办法就是用Pmod PYNQ MicroBlaze。Grove设备可以依靠PYNQ Grove Adapter连接到Pmod端口。 ![](../.gitbook/assets/40%20%281%29.png) 上面这个板上,G1和G2是映射到Pmod引脚\[0,4\]和\[1,5\],也即连接到拥有**下拉电阻**(pull-down resistor)的引脚上。G1、G2端口支持SPI协议,GPIO,计时Grove设备,但是不支持IIC外部设备。G3、G4则映射到引脚\[2,6\]和\[3,7\],也即连接到**上拉电阻**(pull-up resistor)的引脚,并支持IIC协议和GPIO外部设备。 ![](../.gitbook/assets/41%20%281%29.png) ![](../.gitbook/assets/42%20%281%29.png) - \#\#\# Arduino 另一个连接Grove设备方法就是使用Arduino PYNQ MicroBlaze。 ![](../.gitbook/assets/43%20%281%29.png) ![](../.gitbook/assets/44%20%281%29.png) 在PYNQ Shield上有四个IIC Grove 连接器(IIC又可标记为I2C),8个垂直的Grove连接器(G1-G7、UART)和4个水平Grove连接器(A1-A4)。SCL和SDA引脚是连接在Arduino header上的SCL和SDA引脚。 下面的表格标记了映射交互协议。 ![](../.gitbook/assets/45.png) **实例:** 在Base Overlay里,有两个可用实例:PMODA和PMODB。在一个overlay下载后,可如下创建Grove外设实例: ```javascript from pynq.overlays.base import BaseOverlay from pynq.lib.pmod import Grove_Buzzer from pynq.lib.pmod import PMOD_GROVE_G1 base = BaseOverlay("base.bit") grove_buzzer = Grove_Buzzer(base.PMODB,PMOD_GROVE_G1) grove_buzzer.play_melody() ``` 我们可以在pynq.lib.pmod找到更多例子。板上下面的地址里可以找到更多有关PYNQ Grove Adapter的例子 ```javascript /base/pmod/ ``` Arduino接口的使用方法如下: ```javascript from pynq.overlays.base import BaseOverlay from pynq.lib.arduino import Grove_LEDbar from pynq.lib.arduino import ARDUINO_GROVE_G4 base = BaseOverlay("base.bit") ledbar = Grove_LEDbar(base.ARDUINO,ARDUINO_GROVE_G4) ledbar.reset() ``` ## **Pmod** Pmod包是一个使用Pmod端的外部设备的驱动集合。一个Pmod端口是一个12引脚接口,很多来自Digilent和其他第三方合作生产商的Pmod设备都可使用。传统的Pmod外接设备包括了各式传感器(光、温度)、交互设备(网口、WIFI、蓝牙)以及输入输出设备(按钮、开关、LED)。 ![](../.gitbook/assets/46.png) 每一个Pmod连接器是由2排6引脚共计12引脚构成的。每一排由3.3V\(VCC\),接地\(GND\)以及4个数据引脚构成。如果使用两排的话那就是8个数据引脚。 ![](../.gitbook/assets/47.png) Pmod使用双排引脚时(2x4引脚或者2x6引脚),应该从左边开始插入(以连上VCC和GND) ![](../.gitbook/assets/48.png) 而只使用一拍的Pmod设备可以使用任意一排,但还是要从左边开始插入。如果你要使用一个别人写好了的驱动或者overlay的话,你需要检查到底那一排是支持这个设备的,并不一定是两排都能成功。比如说,Pmod ALS目前就只支持上面一排的端口。 所有的引脚都在3.3V时正常工作。根据不同设备的不同上拉/下拉的I/O要求,Pmod数据引脚会有不同的IO标准。(例如IIC需要上拉,SPI需要下拉) 引脚0,1和4,5是连接到下拉电阻。这个可以支持SPI接口以及大部分外接设备。引脚2,3和6,7是连接到上拉电阻。这个可以支持IIC接口。 Pmod已经把这个上下拉的传统考虑进去了,所以在使用Pmod的时候无需额外担心。 ![](../.gitbook/assets/49%20%281%29.png) 正如上面所示,每一个Pmod PYNQ MicroBlaze有一个MicroBlaze子系统,配置开关,和如下AXI控制器: * AXI I2C * SCL Frequency 100 KHz * Address Mode: 7 bits * AXI SPI * Master mode * Transaction Width: 8 * SCK Frequency: 6.25 MHz * FIFO Depth: 16 * AXI GPIO * 8 Input/Output pins * AXI Timer * 32 bits * 1 Generate Output * 1 PWM Output * AXI Interrupt controller * 管理外设的中断。 * Interrupt GPIO * 有一个额外的AXI GPIO被用来标记发送到PS的中断请求。 * Configurable Switch * 允许从特定设备发送信号至外部接口。 **实例:** ```javascript from pynq.overlays.base import BaseOverlay from pynq.lib import Pmod_Timer base = BaseOverlay("base.bit") pt = Pmod_Timer(base.PMODA,0) pt.stop() ``` ## **RPi** Rpi子包是控制连接RPi(Raspberry Pi)接口外设的驱动集合。同样的,在使用具体设备之前,我们需要加载相应的overlay上的控制器。RPi引脚也可用作通用引脚来连接传统的硬件设备。 ![](../.gitbook/assets/50.png) ![](../.gitbook/assets/51%20%281%29.png) 如上图所示,RPi PYNQ MicroBlaze有一个PYNQ MicroBlaze子系统,一个配置开关和如下AXI控制器: * 2x AXI I2C * Frequency: 100KHz * Address mode: 7 bit * 2x AXI SPI * Master mode * Transaction Width: 8 * SCK Frequency: 6.25 MHz * FIFO Depth: 16 注意:一个SPI控制器是连接到专门的SPI引脚Arduino接口上的。 * 1x AXI GPIO * 28 Input/Output pins total * 2x AXI Timer * 32 bits * 1 Generate Output * 1 PWM Output * 1x AXI UART * 115200 Baud * AXI Interrupt controller * 管理外设的中断。 * Interrupt GPIO * 有一个额外的AXI GPIO被用来标记发送到PS的中断请求。 * Configurable Switch * 允许从特定设备发送信号至外部接口。 在pynq.lib.rpi包里可以找到更多信息。 ================================================ FILE: pynq-zhong-wen-zi-liao/0705pynq-library-xiang-jie-pynq-microblaze.md ================================================ # 07-05\_PYNQ Library详解 - Pynq MicroBlaze ## 前言 PYNQ库提供了对子系统Pynq MicroBlaze的支持。它允许我们加载预编译好的应用,并且可以在Jupyter中创建编译新的应用。 接下来,我们按照如下顺序逐一介绍: * MicroBlaze Subsystem * MicroBlaze RPC * MicroBlaze Library ## **MicroBlaze Subsystem** PYNQ MicroBlaze子系统可以由PynqMicroblaze类进行管控,这允许我们从Python下载程序,通过执行处理器的重置信号、共享数据内存读写和管理中断来进行操控。 每一个PYNQ MicroBlaze子系统都含有一个IOP(IO处理器),一个IOP定义了一些能被Python控制的交互与动作控制器。现在一共有三个IOP:Arduino,PMOD,Logictools。 该子系统含有一个MicroBlaze处理器,AXI交互、中断控制器,一个中断请求器和外部系统接口以及BRAM、内存控制器。 AXI交互控制器把MicroBlaze连接到中断控制器、中断请求器和外部接口上。 * 中断控制器是其他连接到MicroBlaze处理器上的交互/动作控制器的接口。 * 中断请求器发送中断请求至Zynq处理系统。 * 外部接口允许MIcroBlaze子系统与其他控制器或DDR内存进行交互。 * BRAM保存了MicroBlaze的指令和数据。 BRAM是双端口的:一个端口连接到MicroBlaze指令与数据端口,另一个连接到ARM® Cortex®-A9 通讯处理器。 如果外部接口连接到了DDR内存,则DDR可以用来在子系统和PS之间传输大量数据分段。 ![](../.gitbook/assets/52%20%281%29.png) **实例:** 在Base Overlay里,有三个IOP实例可用:PMODA, PMODB, Arduino。 ```javascript from pynq.overlays.base import BaseOverlay from pynq.lib import PynqMicroblaze base = BaseOverlay('base.bit') mb = PynqMicroblaze(base.iop1.mb_info, "/home/xilinx/pynq/lib/pmod/pmod_timer.bin") mb.reset() ``` 更多有关PynqMicroblaze类的信息和API可以在pynq.lib.pynqmicroblaze.pynqmicroblaze模块找到。 ## 创建一个新的PYNQ MicroBlaze 任何一个包含了MicroBlaze并且满足之前提及的AXI-代码内存、PS中断线、重置线的要求的**视图**(hierarchy)就可以在PYNQ里使用。然而,为了通过IPython magic来使用MicroBlaze,你必须提供一个**电板支持套件**(BSP)。BSPs是由Xilinx SDK里生成,并包含了所有的连接到MicroBlaze的外设的驱动与配置信息。 PYNQ提供了TCL脚本来从硬件描述文件中生成BSP,我们可以在boards/sw\_repo下找到,与之一起的还有Python或者C语言需要的驱动和pynqmb硬件概览库。创建并使用BSP需要以下几个步骤: * 从Vivado输出Hardware来生成HDF文件 * 在boards/sw\_repo文件夹下运行指令make HDF=$HDF\_FILE。如果没有提供HDF,那么Base Overlay的BSPs就会被生成。 * 复制生成了的BSP到板上并确保名字为bsp\_${hierarchy},这里${hierarchy}是MicroBlaze视图的名字。如果你有多个MicroBlaze,对于所有的视图我们可以采用相同的前缀。 * 在Python里调用add\_bsp\(BSP\_DIR\)。这是初始化你自己的overlay的一部分操作。 这些步骤会把BSP整合到PYNQ里并允许IPython在新的MicroBlaze子系统下使用_%%MicroBlaze_魔术。如果你希望重新使用一个已有的子系统,只要它的命名与Base Overlay一致——例如iop\_pmod _或者iop\_arduino_,那么正确的BSP就会被自动使用。 ## MicroBlaze RPC PYNQ MicroBlaze基础设备是构建在**远程调用**(RPC)层上的,该层是负责发送函数调用指令到MicroBlaze并处理所有的数据传送。 远程调用层支持用于接口函数的C语言。任何不满足以下要求的函数将会被略过: * 传递参数与返回的内容里没有struct类或者union类 * 返回内容不能是指针类型 * 不出现指针的指针。 所有的返回值都是通过复制的方式传回给Python。函数参数的传递依赖参数使用的类型。对于给定的非空原始数据按如下法则: * 非指针类型从PYNQ复制到MicroBlaze * 常指针类型从Python复制到MicroBlaze * 非常指针类型从Python复制到MicroBlaze,然后再在函数完毕后复制回去。 函数执行的顺序可以从下面看出: Python struct模块是用来把传递给函数的Python类型转换成MicroBlaze使用的合适的整数或者浮点值。在转换域以外的那些值会导致异常,使得MicroBlaze函数无法运行。数组类型与struct的处理方法类似,从Python的数组类转成C数组。对于非常数数组,数组会在适当的地方更新使得返回值能被调用者读取。转换规则的唯一异常就是char和const char指针,这俩会被优化成Python的bytearray和bytes类型。注意对一个bytes对象使用一个非常数char\*变量调用一个函数会导致错误因为bytes对象是只读的。 * **Long-running Functions** 对于返回为非空的函数,Python函数是同步的,它会等到前面的C函数结束后才返回到调用者。对于那些返回为空的函数,则函数是被异步调用的,Python函数会立即返回。这就牵扯到那些运行时间很长却又相互独立的函数如何在不堵塞Python线程的情况下载MicroBlaze上运行。当函数在运行的时候,其他函数无法调用除非这个运行时间很长的过程不停的调用yield来允许RPC处理请求。注意!MicroBlaze里是不支持多线程的,因此想要同时运行两个长时间过程只会导致最后只运行了其中一个,即便使用了yield功能。 * **Typedefs** RPC引擎完全支持typedefs并提供了额外的机制来使得C函数更像Python类。RPC层会识别那些typedef名作为前缀的函数名。举一个例子,i2c typedef有函数i2c\_read和i2c\_write,他们都以i2c类型作为第一个参数。于是RPC会创建一个类叫做i2c并有read和write方法。若想要达成这种转换,必须满足下述性质: * Typedef是一个原始type * 至少有一个函数会返回这个typedef * 至少一个函数命名遵循这个模式。 ## **MicroBlaze Library** PYNQ MicroBlaze库是与MicroBlaze子系统交互的主要方法。它含有一列包装好了的IO控制器的驱动,并对连接到PYNQ I/O开关的情况作了优化。 这篇文档描述了所有的C函数和类。 * **General Principles** 这个库提供了GPIO,I2C,SPI,PWM/Timer和UART函数。所有的这些库都遵循相同的设计。每一个都定义了一个类型,代表了设备的一个句柄。_\_open函数是用在设计了IO开关并用了引脚连接设备的情形下。引脚的数目取决于协议。_\_open\_device打开一个特定的设备并可以传递控制器的地址或者索引(由BSP定义)。\*\_close是用来释放一个句柄。 * **GPIO Devices** GPIO设备允许一个或多个引脚被直接读写。所有的函数都在gpio.h里。 #### gpio type 一个对单引脚或多引脚的句柄。 * gpio```gpio_open(int`` \`pin\) 根据特定的IO开关上的引脚返回给GPIO设备一个新的句柄。这个函数只能在设计中有一个IO开关的时候被调用。 * gpio```gpio_open_device(unsigned`` `int` \`device\) 返回给AXI GPIO处理器一个句柄,基于基地址或者设备索引。这个句柄允许通道1上的所有引脚被同时设定。 * gpio```gpio_configure(gpio`` `parent,` `int` `low,` `int` `hi,` `int` \`channel\) 返回一个绑定到控制器特定引脚的新句柄。这个函数不会改变父句柄的配置。 * void```gpio_set_direction(gpio`` `device,` `int` \`direction\) 设定特定句柄的所有引脚的方向。方向只能是GPIO\_IN 或 GPIO\_OUT. * void```gpio_write(gpio`` `device,` `unsigned` `int` \`value\) 设定句柄的输出引脚的值。如果该句柄有多引脚,那么最不重要的位会指代最低索引引脚。往已经配置好的引脚写东西没有任何作用。 * unsigned```int`` `gpio_read(gpio` \`device\) 读取句柄的输入引脚的值,若果有多引脚,则最不重要的位将会指代最低索引引脚。从配置好的引脚读取只会读取到0。 * void ``gpio\_close\(gpio\_device\) 将特定引脚重置到高阻抗输出并关闭设备。 * **I2C Devices** I2C驱动仅为master操作设计,并提供接口从slave device中进行读取。所有的这些函数都在i2c.h里。 * i2c type 代表了一个I2C master。多句柄都指代相同的master设备是允许的。 * i2c```i2c_open(int`` `sda,` `int` \`scl\) 打开一个连接上了IO开关I2C设备(IO开关已经配置为使用特定引脚)。调用这个函数会断开先前分配好的引脚并将它们重置到高阻抗状态。 * i2c```i2c_open_device(unsigned`` `int` \`device\) 通过基地址或者ID打开一个I2C master。 * void```i2c_read(i2c`` `dev_id,` `unsigned` `int` `slave_address,` `unsigned` `char*` `buffer,` `unsigned` `int` \`length\) 生成一个读指令到特定的slave。 buffer 是一个数组,由长度不小于 length的调用者分配。 * void```i2c_write(i2c`` `dev_id,` `unsigned` `int` `slave_address,` `unsigned` `char*` `buffer,` `unsigned` `int` \`length\) 生成一个写指令到特定的slave。 * void```i2c_close(i2c`` \`dev\_id\) 关闭I2C设备。 * **SPI Devices** SPI操作数据的同步传输,因此,没有读与写操作,只有传输函数。这些函数在spi.h里。 **spi type** 一个SPI master的句柄。 * spi```spi_open(unsigned`` `int` `spiclk,` `unsigned` `int` `miso,` `unsigned` `int` `mosi,` `unsigned` `int` \`ss\) 在特定引脚上打开一个SPI master。如果一个设备不需要引脚,那么传入“-1”即可。 * spi```spi_open_device(unsigned`` `int` \`device\) 根据基地址或者ID打开一个SPI master。 * spi```spi_configure(spi`` `dev_id,` `unsigned` `int` `clk_phase,` `unsigned` `int` \`clk\_polarity\) 使用特定的clock 相位与极性配置SPI master。这些设定对于一个SPI master的所有句柄都是通用的。 * void```spi_transfer(spi`` `dev_id,` `const` `char*` `write_data,` `char*` `read_data,` `unsigned` `int` \`length\); 从或者往SPI slave传输字节。write\_data 和 write\_data 应该是由调用者或者NULL分配的。缓冲的最小长度为length. * void```spi_close(spi`` \`dev\_id\) 关闭一个SPI master * **Timer Devices** 计时设备有两个作用。他们既可以被用来输出PWM信号也可以作为程序计时器来插入精确地延迟。同时使用上述两个功能是不可能的,如果在PWM正在运行时而又要去尝试延迟,则会导致undefined问题。所有的这些函数都在timer.h里。 #### timer type 一个AXI计时器句柄。 * timer```timer_open(unsigned`` `int` \`pin\) 打开一个连接到特定引脚的AXI计时器。 * timer```timer_open_device(unsigned`` `int` \`device\) 用地址或者设备ID打开计时器。 * void```timer_delay(timer`` `dev_id,` `unsigned` `int` \`cycles\) 用指定的一段时间来进行延迟。 * void```timer_pwm_generate(timer`` `dev_id,` `unsigned` `int` `period,` `unsigned` `int` \`pulse\) 用指定的计时器生成PWM信号。 * void ```timer_pwm_stop(timer`` \`dev\_id\) 停止PWM输出。 * void```timer_close(timer`` \`dev\_id\) 关闭指定的计时器。 * void```delay_us(unsigned`` `int` \`us\) 用微秒数来延迟程序,使用默认延迟计时器(编号0)。 * void```delay_ms(unsigned`` `int` \`ms\) 用毫秒数来延迟程序,使用默认延迟计时器(编号0)。 * **UART Devices** 这个设备驱动控制一个UART master。 * uart ``type 一个UART master设备的句柄。 * uart```uart_open(unsigned`` `int` `tx,` `unsigned` `int` \`rx\) 在特定引脚上打开UART设备。 * uart```uart_open_device(unsigned`` `int` \`device\) 用基地址或者索引打开UART设备。 * void```uart_read(uart`` `dev_id,` `char*` `read_data,` `unsigned` `int` \`length\) 从UART读取固定长度的数据。 * void uart\_write\(uart dev\_id, char\* write\_data, unsigned int length\) 写一段数据到UART。 * void uart\_close\(uart ``dev\_id\) 关闭UART。 ================================================ FILE: pynq-zhong-wen-zi-liao/08pynq-kuai-su-shang-shou-shi-yan-jie-shao.md ================================================ # 08\_PYNQ快速上手实验介绍 ## Pynq hands-on demos介绍 PYNQ-Z2快速上手demo集锦,清单如下所示 * ComputerVision * DeepLearning * InternetOfThings #### 安装 下载整个项目的压缩包\([链接](https://github.com/xupsh/pynq-hands-on-demos/archive/master.zip)\),并将它复制(可以通过网络、离线等多种方式)到你的PYNQ-Z2上。 每个文件夹中都有单独的使用指南(离线)。 ## cv2PYNQ 这是一个在PYNQ平台上加速OpenCV图像处理算法的Python扩展包。这个库目前实现了某几个特定的图像处理算法的硬件加速,可以在16ms内处理完1080p的灰度图的滤波算法。 目前已经实现的算法列表: * Sobel: 3x3; 5x5 * Scharr * Laplacian: ksize = 1; 3; 5 * blur: ksize = 3 * GaussinBlur: ksize = 3 * erode: ksize = 3 * dilate: ksize = 3 * Canny #### 安装 取决于Pynq软件版本的不同,安装方式各有不同。 首先需要打开PYNQ-Z1/Z2板卡上的Linux命令行界面,然后根据不同版本输入如下安装命令: > > = PYNQ v2.3 ```javascript sudo pip3 install -e . ``` > <= PYNQ v2.2 ```text sudo pip3.6 install -e . ``` 运行完安装脚本之后就可以在Jupyter界面看到`cv2PYNQ`的文件夹 #### 运行Sobel滤波算法的案例 在`cv2PYNQ`文件夹中有一个Sobel滤波算法的notebook,跟着其中的步骤做就可以了。 ## BNN-PYNQ 这个项目实现了在PYNQ上部署量化神经网络的任务,目前实现了多种不同精度的量化网络结构: * 1 bit weights and 1 bit activation \(W1A1\) for CNV and LFC * 1 bit weights and 2 bit activation \(W1A2\) for CNV and LFC * 2 bit weights and 2 bit activation \(W2A2\) for CNV #### 安装 取决于Pynq软件版本的不同,安装方式各有不同。 首先需要打开PYNQ-Z1/Z2板卡上的Linux命令行界面,然后根据不同版本输入如下安装命令: > > = PYNQ v2.3 ```javascript sudo pip3 install -e . ``` > <= PYNQ v2.2 ```javascript sudo pip3.6 install -e . ``` 运行完安装脚本之后就可以在Jupyter界面看到`bnn`的文件夹 #### 运行Road-Signs路标识别 在`bnn`文件夹中有一个路标识别的notebook,跟着其中的步骤做就可以了。 ## IoT 这个demo会教你如何在IoT场景中控制传感器和制动器 #### 安装 打开Jupyter首页,将如下两个notebook文件上传到Jupyter中即可。 * `arduino_grove_ledbar.ipynb` * `pmod_grove_usranger.ipynb` * `ledbar_and_ultrasonic_ranger.ipynb` #### 运行Demo 准备物件: * `Base Arduino shield` [http://wiki.seeedstudio.com/Base\_Shield\_V2/](http://wiki.seeedstudio.com/Base_Shield_V2/) * `Pmod grove adapter` [https://store.digilentinc.com/pynq-grove-system-add-on-board/](https://store.digilentinc.com/pynq-grove-system-add-on-board/) * grove led bar [http://wiki.seeedstudio.com/Grove-LED\_Bar/](http://wiki.seeedstudio.com/Grove-LED_Bar/) * grove ultrasonic ranger sensor [http://wiki.seeedstudio.com/Grove-Ultrasonic\_Ranger/](http://wiki.seeedstudio.com/Grove-Ultrasonic_Ranger/) 打开刚刚上传的notebook,根据其中的指令一步步照做即可。 以**超声波测距仪传感器**这个Demo为例。 ## 超声波测距仪传感器 通过Jupyter打开_InternetOfThings_目录下的pmod\_grove\_usranger.ipynb 这个例子展示了如何使用 [超声波测距仪传感器](https://www.seeedstudio.com/Grove---Ultrasonic-Ranger-p-960.html)。它的测量最大范围为400cm,测量最小范围是3cm,分辨率为1cm。 如果没有障碍物,则会默认返回500cm。 在这个notebook里,我们只展示如何控制grove ultrasonic ranger连接到Pmod接口上,因此需要一个pmod grove和转换器。当然读者也可以自己把控制移植到Arduino接口的版本上去。 [![usranger](https://github.com/xupsh/pynq-hands-on-demos/raw/master/InternetOfThings/2.png)](https://github.com/xupsh/pynq-hands-on-demos/blob/master/InternetOfThings/2.png) ```javascript from pynq.overlays.base import BaseOverlay base = BaseOverlay("base.bit") ``` * **使用 Microblaze 去控制超声波传感器** 下面的程序假设超声波传感器是连接在Pmod-Grove转接器的G1接口上的,以及该转接器连接在PMODA接口上。 时钟控制器的寄存器分布如下: | Register name | Register functionality | Register value | | :--- | :--- | :--- | | TCSR0 | Timer 0 Control and Status Register | 0x00 | | TLR0 | Timer 0 Load Register | 0x04 | | TCR0 | Timer 0 Counter Register | 0x08 | | TCSR1 | Timer 1 Control and Status Register | 0x10 | | TLR1 | Timer 1 Load Register | 0x14 | | TCR1 | Timer 1 Counter Register | 0x18 | ```javascript %%microblaze base.PMODA #include "xparameters.h" #include "xtmrctr.h" #include "gpio.h" #include "timer.h" #include #define TCSR0 0x00 #define TLR0 0x04 #define TCR0 0x08 #define TCSR1 0x10 #define TLR1 0x14 #define TCR1 0x18 #define MAX_COUNT 0xFFFFFFFF void create_10us_pulse(gpio usranger){ gpio_set_direction(usranger, GPIO_OUT); gpio_write(usranger, 0); delay_us(2); gpio_write(usranger, 1); delay_us(10); gpio_write(usranger, 0); } void configure_as_input(gpio usranger){ gpio_set_direction(usranger, GPIO_IN); } unsigned int capture_duration(gpio usranger){ unsigned int count1, count2; count1=0; count2=0; XTmrCtr_WriteReg(XPAR_TMRCTR_0_BASEADDR, 0, TLR0, 0x0); XTmrCtr_WriteReg(XPAR_TMRCTR_0_BASEADDR, 0, TCSR0, 0x190); while(!gpio_read(usranger)); count1=XTmrCtr_ReadReg(XPAR_TMRCTR_0_BASEADDR, 0, TCR0); while(gpio_read(usranger)); count2=XTmrCtr_ReadReg(XPAR_TMRCTR_0_BASEADDR, 0, TCR0); if(count2 > count1) { return (count2 - count1); } else { return((MAX_COUNT - count1) + count2); } } unsigned int read_raw(){ gpio usranger; usranger = gpio_open(PMOD_G1_A); create_10us_pulse(usranger); configure_as_input(usranger); return capture_duration(usranger); } ``` * **测量距离** 记住放一些障碍物在传感器面前,否则它将返回默认的500cm。 ```javascript from pynq import Clocks def read_distance_cm(): raw_value = read_raw() clk_period_ns = int(1000 / Clocks.fclk0_mhz) num_microseconds = raw_value * clk_period_ns * 0.001 if num_microseconds * 0.001 > 30: return 500 else: return num_microseconds/58 read_distance_cm() 11.873448275862069 ``` ================================================ FILE: pynq-zhong-wen-zi-liao/09overlay-she-ji-fang-fa-xue.md ================================================ # 09\_Overlay设计方法学 **前言:**如PYNQ介绍里描述的那样,overlays跟软件里的库类似。程序员可以将overlays实时的下载到Zynq® PL为软件应用提供所需的功能。 一个overlay是一个FPGA逻辑设计类。FPGA逻辑设计通常会为特定任务做相应的优化。Overlay是为广泛应用提供可配置和重用的功能而设计。一个PYNQ的overlay通常有Python的接口,让软件工程师像使用其它的Python包一样使用。 软件工程师会使用overlay,但通常不会创建overlay,因为创建overlay需要非常高的硬件设计技能。 创建overlay需要很多的组件: * Board Settings * PS-PL Interface * MicroBlaze Soft Processors * Python/C Integration * Python AsyncIO * Python Overlay API * Python Packaging 本章节将给出overlay的创建流程,以及如何将overlay集成到PYNQ中,但不会涉及详细的硬件设计细节,对硬件设计者来说必须掌握的技能。 ## Overlay设计 Overlay包含两个主要的组件:FPGA逻辑设计(bitstream)以及工程block diagram的tcl文件。Overlay设计是硬件工程师的任务,本章节将假设读者已经具备了使用Vivado工具进行ZYNQ系统构建和开发的能力。 ## PL设计 Xilinx® Vivado工具被用于创建Zynq系统,生成用于烧录Zynq PL侧的Bitstream文件。 我们可以到如下链接下载免费的WebPack版本。 [https://www.xilinx.com/products/design-tools/vivado/vivado-webpack.html](https://www.xilinx.com/products/design-tools/vivado/vivado-webpack.html) 鼓励硬件设计者为PYNQ overlay中需要使用的IP提供开发支持。一旦IP设计完成,PL侧的设计就和其它的Zynq系统开发流程一样。一个能被PYNQ使用的IP应该是可以内存映射和GPIO相连的。IP也可以作为AXI主设备。PYNQ提供了各种与PL接口的libraries,可以很方便的使用这些libraries来创建我们自己的驱动。下一节将会讲到PYNQ overlay的API。 ## Overlay Tcl文件 Tcl文件在PL设计中,由Vivado IPI模块设计(block design)产生,PYNQ使用Tcl文件来自动识别Zynq系统的配置、IP及版本、中断、复位以及其它的控制信号。基于这些信息,PYNQ可以自动修改某些系统配置、为设备自动分配驱动、使能或者禁用某些系统特性,信号被连接到相应的Python方法。 作为overlay的一部分,Tcl文件必须和bitstream文件一起提供。Tcl文件可在overlay硬件设计完成后,通过导出(exporting)IP Integrator block diagram来产生。Tcl文件应该在下载overlay的时候和bitstream一起被提供,PYNQ PL类会自动分析Tcl文件里的内容。 一个定制化(custom)或者手动创建的Tcl文件可被用于build一个Vivado工程,但是Vivado工具应该被用于根据block diagram产生和导出Tcl文件。这个自动生成的Tcl文件可确保PYNQ可以做正确的分析。 通过Vivado根据图形界面来为Block Design产生Tcl文件: _Click **File** > **Export** > **Block Design**_ 或者,也可以在Tcl console里: _write\_bd\_tcl_ Tcl文件的名字必须和bitstream的名字匹配,比如my\_overlay.bit和my\_overlay.tcl。 Tcl文件的内容会在overlay实例化和下载的时候被分析。 ![](../.gitbook/assets/01%20%281%29.png) 当找不到Tcl文件或者Tcl文件名字与Bitstream文件名字不匹配的时候,系统会报错。 ## 可编程性(Programmability) Overlay应该在bitstream生成完成之后依然具备可编程性,允许对系统进行客制化。大量的可重用的PYNQ IP模块对PYNQ的可编程性提供了支持。比如,Microblaze可被用于Pmod和Arduino接口。来自于不同overlay的可重用的IP,使得PYNQ在运行时具备可配置性。 ## Zynq PS配置 基于Zynq器件的Vivado工程具备两个部分:PL设计与PS配置设置。 PYNQ镜像被用于启动板卡及在启动阶段对PS侧进行配置,它覆盖了大多数PS侧需要的配置,包括DRAM配置和使能PS侧的外围设备,比如SD卡、以太网、USB和UART等。 配置包含了PS侧的时钟配置和PL侧使用的时钟配置。PL侧的时钟可根据Overlay的不同需求,在运行时进行配置。该配置由PYNQ Overlay类自动管理。 在下载新的overlay的过程中,会分析Tcl文件获取时钟配置。在下载bitstream之前会首先完成时钟配置。 ## 已有的Overlays 已有的overlays可以作为创建新的overlay的起点。Base overlay可以在PYNQ代码库的board目录找到,包括板卡外围设备的参考IP。 _/boards//base_ 目录下有现成的makefile文件,可用于重新build Vivado工程生成overlay需要的bitstream文件和Tcl文件。注:在Windows操作系统下,通过source Tcl文件来代替make方法build工程。 已经运行PYNQ框架的板卡上有可用的Base overlay的bitstream和Tcl文件,也可以在PYNQ的github工程目录下找到。 _/boards//base_ ## 板卡配置 ### Base overlay工程 对应板卡的Base overlay源代码可在PYNQ github上找到。工程可通过makefile或者Tcl文件重新build。 Base overlay可作为新设计一个overlay的起点。 ### Vivado板卡配置文件(board files) Vivado板卡配置文件包含创建一个新的Vivado工程所需要的配置。以下为PYNQ-Z2配置文件的下载地址。 [http://www.tul.com.tw/download/PYNQ-Z2\_board\_file\_v1.0.zip](http://www.tul.com.tw/download/PYNQ-Z2_board_file_v1.0.zip) 将配置文件安装到Vivado工具后,允许在创建Vivado工程的时候选择板卡以使工具对Zynq PS侧进行配置。 解压缩板卡配置文件并拷贝到如下目录完成板卡配置文件的安装。 _\Vivado\\data\boards_ 注:如果在拷贝板卡配置文件时Vivado是处于打开状态,则需要重新打开Vivado工具。 ### XDC约束文件 PYNQ-Z2的xdc约束文件可在如下地址进行下载: [http://www.tul.com.tw/download/PYNQ-Z2\_v1.0.xdc.zip](http://www.tul.com.tw/download/PYNQ-Z2_v1.0.xdc.zip) ## PS/PL接口 ![](../.gitbook/assets/02%20%287%29.png) Zynq器件的PS与PL之间有9个AXI总线接口。在PL侧有4个AXI master HP(High performance)接口,2个AXI master GP(General Purpose)接口,2个AXI slave GP接口和1个AXI master ACP接口。在PS侧还有连接到PL侧的GPIO控制器。 总共有4个用于管理PS和PL数据传输的pynq类。 * GPIO - General Purpose Input/Output * MMIO - Memory Mapped IO * Xlnk - Memory allocation * DMA - Direct Memory Access 具体使用哪个类,取决于IP所连接到的Zynq PS接口和IP的接口。 运行在PYNQ上的Python代码可以访问连接到AXI slave GP口的IP。_MMIO_被用于对该类接口进行操作。 连接到AXI master接口的IP不能由PS直接控制。AXI master接口允许IP直接访问DRAM。在这样做之前,必须对IP使用的内存进行分配(allocate)。Xlnx类被用于对该类接口进行操作。 DMA可被用于Zynq PS DRAM与PL之间高性能的数据传输。PYNQ的DMA类提供了对此类接口进行操作。 ### PS GPIO 总共由64个从PS到PL侧的GPIO。 从PS到PL侧的GPIO可被用于简单的通讯,比如这些GPIO可被用于复位(Resets)或者中断(Interrupts)。 GPIO并不需要映射到系统内存空间来访问。 更多关于PS GPIO的信息,请查看附件Pynq\_libraries-PS GPIO。 ### MMIO 任何连接到AXI slave接口的IP都会被映射到系统内存空间。_MMIO_可被用于读写内存映射的地址。一次MMIO操作会完成从内存空间读取或者存储32位数据。因为MMIO不支持突发方式进行数据的传输,所以MMIO比较适合通过AXI slave接口连接到PS侧的IP之间的小批量数据交互。 更多关于MMIO的信息,请查看附件Pynq\_libraries-PS MMIO。 ### Xlnk 在IP访问DRAM空间之前,内存必须被分配。Xlnk类允许对内存空间进行分配。Xlnk会分配一段连续的内存空间,以便于PS与PL之间进行高效的数据传输。Python或者其它运行在Linux上的代码可直接对这段内存进行访问。 因为PYNQ运行在Linux操作系统上,这段内存会存在与Linux的虚拟内存。Zynq AXI master接口的IP可对物理内存(Physical memory)进行访问。Xlnk提供了一个指向物理内存的指针,该指针可以被发送给overlay中的IP。物理地址被存储在了已分配内存空间实例的_physical\_address_属性中。Overlay中的IP可通过该物理地址访问同一段内存空间。 更多关于Xlnk的信息,请查看附件Pynq\_libraries-Xlnk。 ### DMA AXI stream接口通常被用于高性能的流应用(streaming applications)。AXI stream可通过连接到Zynq AXI HP口的DMA使用。 pynq.DMA类支持AXI DMA IP。允许从DRAM读取数据后发送到AXI stream,或者从AXI stream获取数据后写到DRAM。 更多关于DMA的信息,请查看附件Pynq\_libraries-DMA。 ### Interrupt 在python环境中,有特定与asyncio events相连的中断。为了集成到PYNQ框架,特定的中断必须被连接到AXI中断控制器,然后再连接到PS的第一个中断线(first interrupt line)。如果有超过32个中断,则可对AXI中断控制器进行级联。这是为了给其它非PYNQ直接控制的中断做预留,比如SDSoC加速器需要用到的中断。 中断由Interrupt类进行管理,基于PYNQ标准库中的asyncio。 更多关于中断类的信息,请查看附件Pynq\_libraries-Interrupt。 更多关于asyncio的信息,请查看Asyncio章节的内容。 ## PYNQ Microblaze子系统 ## Python-C 集成(Integration) 在有些情况下,通过Python类来管理与overlay的数据传输的效率并不高。通常情况下,性能由Python驱动和更高的性能库可以通过使用低级语言(比如C/C++)来开发,并可针对overlay进行相应的优化。库中的驱动函数可以从Python里通过CFFI(C foreign Function Interface)进行调用。 CFFI提供了一个C代码与Python之间的简单接口方式。CFFI包已经预安装到了PYNQ镜像中,它支持,API和ABI模式,这两种模式又分别包含“in-line”和“out-of-line”编译模式。Inline ABI\(Application Binary Interface\)兼容模式允许动态加载和运行来自与可执行模块的函数,API模式允许编译C扩展模块。 下面是一个在Python种调用 C函数strlen\(\)的例子。 C函数的原型如下: ```javascript size_t strlen(const char\*); ``` _C函数原型被传递给_cdef\(\),然后通过clib调用。 ```javascript from cffi import FFI ffi = FFI() ffi.cdef("size_t strlen(const char*);") clib = ffi.dlopen(None) length = clib.strlen(b"String to be evaluated.") print("{}".format(length)) ``` ​ 在公共库里的C函数可以在Python里通过CFFI调用。公共库可以使用CFFI在线编译,也可以离线编译。 ​ 关于更多关于CFFI和公共库的信息可以参考: [http://cffi.readthedocs.io/en/latest/overview.html](http://cffi.readthedocs.io/en/latest/overview.html) [http://www.tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html](http://www.tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html) #### PYNQ and Asyncio 与硬件交互经常会遇见等待加速器完结束或者等待数据。Polling不是一个有效的方式来等待数据,尤其是Python这种同时只能有一个线程在执行的语言来说。 Python asyncio库可以异步的管理多个IO-bound任务,因而可以避免来自等待响应或者低速IO子系统的阻塞。相反的,程序可以继续去执行其它准备好运行的任务。当之前繁忙的任务可以恢复后,它们会继续执行,循环会重复。 在PYNQ里,实时任务会经常使用PL侧的IP block来实现。同时,这些在PL侧执行的任务在任何时刻都有可能向PS侧发送中断。Python的asyncio库提供了一种管理这类来自于异步、IO-bound任务事件的有效方式。 PYNQ中的pynq.interrupt模块中的Interrupts类是asyncio的基础,它提供了一种可被用于等待中断产生的aysncio类事件。Video库,AXI GPIO和PynqMicroblaze驱动都是基于中断事件,提供了用于任何可能阻塞的函数的协程(coroutine)。 #### Asyncio基本原理 Asyncio 并发框架基于协程(coroutine),futures, 任务(tasks)和一个事件循环。在通过简单的例子阐明它们的使用方法之前,将会对他们做一下简要的介绍。 **协程(Coroutines)** 协程是一种新的Python语言结构(Construct)。协程引入了两个关键字,await和async。协程是可被暂停的状态函数(Stateful Functions)。这意味着当它们在等待任务或者事件完成的时候可退出执行。当被暂停,协程会保持它的状态。当等待的任务或者事件结束后协程会恢复运行。await关键字决定了协程中程序停止和恢复执行的位置。 #### Futures Futures是一个代理或者一个对象的包装,不是真实的目标对象。一旦异步计算完成,你就可以提取它。Futures是asyncio的重要组件:它可以将中止的操作封装后放入队列中,可查询它的完成状态,当操作完成后可获取返回结果,例化过程由并发框架例化,不需要用户直接干预。 #### 任务(Tasks) 协程并不直接执行,它们被封装成任务(tasks),并注册为事件循环(event loop)。Tasks是futures的子类。 #### 事件循环(Event loop) 事件循环负责执行所有准备好的任务,采用轮询的方式执行。 一个事件循环一次只执行一个任务,它依赖于协同调度,这意味着任务之间不会相互影响,当任务阻塞运行时会将控制权交给事件循环,这将会导致单线程并发代码中的所有的事件处理函数都按顺序执行后才会执行下一次循环。 下面是一个简单的例子,通过async def关键字定义了一个命名为wake\_up的协程。函数主要将wake\_up协程封装为wake\_up\_task任务并将其注册到事件循环中。在协程中,关键字await标注了程序被挂起和恢复的位置。事件循环执行了下面的任务。 1. 开始执行start\_up\_task任务; 2. 将start\_up\_task任务挂起并记录它的状态; 3. ​ 运行asyncio.sleep函数1到5秒钟; 4. 根据保存的状态恢复wake\_up\_task函数运行; 5. 运行任务直至结束,任务关闭。 最终事件循环关闭。 示例运行完毕后会有如下输出: ![](../.gitbook/assets/03%20%283%29.png) 在事件循环中的任何阻塞调用应该用协程来代替。如果不这么做,当阻塞调用发生时,它会阻塞循环的其它部分的运行。 如果你需要用到阻塞调用,应该将其放在单独的线程里。计算负载较大的任务也应该放在单独的线程/进程中。 ### Pynq Asyncio实例 Asyncio可被用于管理Overlay中各种阻塞操作。一个协程可以被运行在事件序列中来等待中断事件的发生。其它的用户程序也可以运行在事件循环中。如果中断发生,任何等待的协程将会被重新调度。中断协程的响应性取决于用户代码交出线程控制权的频率。 #### GPIO外围设备 用户I/O设备可能会产生中断,比如按钮被按下或者开关状态改变。Pynq的Button和Switch类中都有一个wait\_for\_level的成员函数和一个wait\_for\_level\_async的协程,它们都会被阻塞,直到按钮或者开关返回切换到特定的状态。Pynq中规定package中的所有协程都带有**\_async**后缀。 我们以Led灯控制为例,当按钮按下时对应的Led灯会亮起。首先需要定义一个含有该功能的协程。 ![](../.gitbook/assets/04%20%284%29.png) 然后将协程注册到默认的事件循环中。 ![](../.gitbook/assets/05%20%281%29.png) 最后,运行已经注册好协程的事件循环。 ![](../.gitbook/assets/06%20%285%29.png) #### PynqMicroblaze 在PynqMicroblaze中有一个Interrupt的成员变量,功能和asyncio类似。事件带有一个wait\(\)协程和一个clear\(\)方法。事件会被自动连接到正确的中断管脚,如果加载的Overlay中没有中断则会返回None。 例如: ![](../.gitbook/assets/07%20%286%29.png) 有两种方法来运行新的IOP封装类中的函数-从外部的asyncio事件循环中调用或者建立自己的事件循环然后从事件循环中来调用asyncio函数。 #### AsyncIO函数 对于中断驱动的功能,Pynq中同时提供了asyncio协程和阻塞函数的调用的方法。用户自定义的驱动也推荐提供这样的2种调用方法。阻塞函数可被用于不需要协程或者接受阻塞出现的事件循环中。 以下是一段定义协程的代码,可以注意到只需要添加async和await关键字就可以把一个函数定义为一个asyncio协程。 ![](../.gitbook/assets/08%20%281%29.png) #### 事件循环(Event Loops) 以下的代码实现了对asyncio协程封装,注册到默认的事件循环中,运行协程直到结束。 ![](../.gitbook/assets/09%20%284%29.png) #### 客制化的中断处理(Custom Interrupt Handling) Interrupt类允许客制化中断处理。 这个类对PL侧的AXI中断控制器管理进行了抽象化,我们并不需要详细的了解代码的细节。该中断类利用了中断连线的pin名字,为其提供了一个wait\_async协程和相应的wait函数。中断 以下是使用中断的通用模式: ![](../.gitbook/assets/10%20%285%29.png) 该模式避免了同一个中断被使用多次的竞争危害。 #### Examples 更多的例子请参考AsyncIO Buttons的notebook,可以在如下目录找到: ![](../.gitbook/assets/11%20%284%29.png) ================================================ FILE: pynq-zhong-wen-zi-liao/10-zi-ding-yi-overlay-she-ji-liu-cheng.md ================================================ # 10\_自定义Overlay设计流程 ### 简介: 本章节将介绍如何设计自定义Overlay。 重新设计Overlay类有三个主要的设计目标: a. 方便使用者采用前后一致的方式找到Overlay里包含的功能 b. 为新的硬件设计中新的IP提供简单的测试方法 c. 在不同的Overlay之间重复使用IP 本章节主要阐明了如何将新的IP在系统中互联,驱动的开发方法以及使用多个IP来创建一个复杂的系统。 ### 开发一个独立的IP 在这个简单的例子中我们将使用一个通过HLS工具生成的IP,一个32位加法器。HLS源代码 ![](../.gitbook/assets/01.png) 在Vivado block design中,除了该HLS IP之外,还需要一些胶连逻辑来将该IP连接到ZYNQ7 PS侧。在本章节假设读者已经具备了基本的硬件设计基础,所以将不再包含硬件部分的详细步骤,最终在block design上的连接如下图所示: ![](../.gitbook/assets/02%20%283%29.png) 为了和IP进行交互,我们需要加载包含该IP的Overlay。 ![](../.gitbook/assets/03%20%287%29.png) 新创建的overlay将会自动下载bitstream到FPGA,我们可以在overlay名称后面加“?”来获取overlay的内容。如下所示: ![](../.gitbook/assets/04%20%285%29.png) Overlay中所有的入口(设备)都可以通过overlay的**类属性**来访问,属性里包含了特定的驱动。 比如,通过访问Overlay中的scalar\_add属性,将会为该IP创建驱动。因为没有已知和scalar\_add对应的驱动,在我们和该IP交互的时候,DefaultIP将会作为该IP的驱动被使用。 ![](../.gitbook/assets/05.png) 通过读取该IP的hls源代码,我们可以知道该IP的具体使用方法,需要在在偏移地址0x10和0x18写入参数,然后从偏移地址0x20读取计算结果。 ![](../.gitbook/assets/06%20%283%29.png) ## 创建驱动 虽然**DefaultIP**被用来验证IP是否可以正常工作非常有用,但是对于Overlay的最终使用者来说,这样的方式并不友好。理想的情况是,我们为该IP提供一个特定的驱动,软件工程师像调用一个add函数一样来使用该硬件加速器。客制化的驱动需要继承**DefaultIP,**并根据IP类型添加一个类属性bindto。 类的构造方法使用了一个description参数,并将其传给super class的**init**构造方法。 Description是一个包含了地址分配、中断和连接到该IP的GPIO的字典。 ![](../.gitbook/assets/07%20%282%29.png) 在定义完新的驱动之后,如果我们重新加载overlay并查询帮助,就可以看到我们新的驱动已经和IP绑定了。 ![](../.gitbook/assets/08%20%282%29.png) 现在我们可以使用客制化的驱动,通过add方法取代DefaultIP来调用硬件IP。 ![](../.gitbook/assets/09.png) ## 重用IP 假设有其他人在重新设计Overlay的时候需要用到一个现成的IP。只要import包含了该IP驱动的python文件就会为该IP自动创建驱动。在本例中,我们假设在新的设计中使用了一个重新命名的Add,odd\_add。 ![](../.gitbook/assets/10%20%281%29.png) 通过查询可知驱动也被绑定到新的overlay了。 ![](../.gitbook/assets/11%20%281%29.png) ## IP层次结构(Hierarchies) 上图中的block design中的const\_multiply包含了如下的层级。 ![](../.gitbook/assets/12%20%282%29.png) 它包含了一个用于数据流与常量乘法的客制化IP,以及一个用于数据传输的DMA控制器(Engine)。因为流(stream)的使用,我们需要正确的控制DMA控制器中的TLAST信号,在HLS源代码中需要增加相应的progma和数据类型(types),虽然相对有一点复杂,但代码还是比较短。 ![](../.gitbook/assets/13%20%283%29.png) 通过查找HLS生成的文档,会发现需要通过配置偏移地址0x10寄存器的值来配置我们需要的常量。我们可以写一个简单的驱动来实现这个目的。 ![](../.gitbook/assets/14%20%285%29.png) 因为DMA的驱动已经存在于PYNQ的驱动中,所以只需要确保模块文件已经正确import即可。重新加载overlay可以确保我们新写的驱动生效。 ![](../.gitbook/assets/15%20%281%29.png) DMA驱动会传输numpy类型的数组到事先通过xlnk\(\)方法allocate DRAM的空间。让我们做一个测试,将5个数值与常数3相乘,获取计算结果。 ![](../.gitbook/assets/16%20%285%29.png) 我们会发现这种使用IP的方式对用户仍然不是很友好。更好的方式是将整个层级当作一个整体,写一个隐藏内部层级结构细节的驱动。Overlay类允许写类似于IP的隐藏层级结构细节的驱动,但细节稍有不同。 层级结构(Hierarchy)驱动需要继承pynq.DefaultHierarchy,类似于DefaultIP,它也有一个需要输入层级结构描述参数的构造函数。为了确定驱动是否应该绑定到(bind to)特定的层级结构(Hierarchy),类中包含了一个静态方法checkhierarchy,该方法会调用层级结构描述并判断驱动是否应该bind to该层次,如果是则返回True,否则会返回false。跟Default IP类似,任何满足需求的DefaultHierarchy的继承类都有一个checkhierarchy方法被自动注册。 对本设计中的常量乘法,驱动应该如下所示: ![](../.gitbook/assets/17%20%284%29.png) 我们可以重新加载overlay来确保更高级的驱动被加载。 ![](../.gitbook/assets/18.png) 调用新的驱动。 ![](../.gitbook/assets/19%20%283%29.png) ## Overlay客制化 在很多情况下默认的overlay已经足够使用,但有些overlay也需要客制化处理来提供更加友好的API。一个例子是默认的AXI GPIO驱动提供了channel 1和channel 2两个独立的属性,这也就意味着为了访问base overlay中的LEDs设备,需要如下的操作: ![](../.gitbook/assets/20%20%284%29.png) 为了移植该overlay,开发者可以提供一个客制化的类来提供一种更加友好的方式来呈现子系统。Base overlay已经包含了客制化的类来实现如下功能: a. 使AXI GPIO设备的命名更合理、限制操作的范围和方向; b. 让IOPs通过pmoda、pmodb、和arduino名字来访问; c. 创建一个和RGB LEDS交互的特殊类。 这样做的结果是,LEDS可以通过如下方式访问: ![](../.gitbook/assets/21%20%281%29.png) 很好定义的客制化类也可以为最终用户提供好的文档字符串。 ![](../.gitbook/assets/22%20%281%29.png) ## 创建一个客制化Overlay 客制化的Overlay应该继承自pynq.Overlay,获取bitstream文件的完整路径和额外的关键字参数。这些参数需要在**init**构造函数的开始即被传入到super\(\).**init**\(\)中,以初始化Overlay的属性。 本例将继续使用tutorial\_2的Overlay,增加一个更方便调用乘法的方法。 ![](../.gitbook/assets/23%20%283%29.png) 为了验证新的overlay类,我们可以采用之前的方法构造一个实例。 ![](../.gitbook/assets/24%20%281%29.png) ## 包含的驱动 Pynq的库包含了一系列的的在pynq.lib包中定义的驱动。包括: * AXI GPIO * AXI DMA \(仅simple模式\) * AXI VDMA * AXI Interrupt Controller \(内部使用\) * Pynq-Z1 Audio IP * Pynq-Z1 HDMI IP * Color convert IP * Pixel format conversion * HDMI input and output frontends * Pynq Microblaze program loading ================================================ FILE: pynq-zhong-wen-zi-liao/111-ji-yu-hls-de-jia-su-qi-overlay-she-ji-shi-li-kuai-su-sheng-cheng-ying-jian-ip.md ================================================ # 11-1\_基于HLS的加速器Overlay设计实例 - 快速生成硬件IP ## 介绍 本章节介绍了HLS的基本操作流程,你将熟悉HLS工程的创建、仿真、综合与实现流程。 ## 目标 * ​ 使用Vivado HLS工具创建工程 * ​ 对设计进行仿真 * ​ 对设计进行综合 * ​ 对设计进行实现 * ​ 使用Vivado HLS分析工具对工程进行分析 ## 步骤1:创建新的工程 ### 1. 创建一个适用于PYNQ-Z2的HLS工程 **1.1** 将lab1\_src目录下的3个源文件拷贝到**C:\xup\hls\_labs\lab1**目录下; ![](../.gitbook/assets/01%20%284%29.png) **1.2** 启动 Vivado HLS工具: **Start > Xilinx Design Tools > Vivado HLS 2018.2;** ![](../.gitbook/assets/02%20%282%29.png) **1.3** 点击 **Create New Project**创建新的HLS工程; **1.4** 点击**Browse…**选择工程目录到为 **:C:\xup\hls\_labs\lab1** ,项目名称填写为:**matrixmul** ,点击**Next**; **注:**工程目录与工程名称可自己定义 ![](../.gitbook/assets/03%20%286%29.png) **1.5** 点击 **Add Files…** 按钮,添加**C:\xup\hls\_labs\lab1**目录下的matrixmul.cpp 文件到工程中,**Top Function**栏内填写**matrixmul**; **注:**matrixmul为源文件matrixmul.cpp内的函数名,本lab将会把该函数综合为可在Vivado中调用的硬件IP。 ![](../.gitbook/assets/04.png) **1.6** 点击 **Next** 按钮; **1.7** 点击**Add Files…** 按钮, 添加**C:\xup\hls\_labs\lab1** 目录下的 **matrixmul\_test.cpp** 到工程中; ![](../.gitbook/assets/05%20%283%29.png) **1.8** 选中 matrixmul\_test.cpp文件,然后点击 **Edit CFLAG…** 按钮, 输入 **-DHW\_COSIM**, 点击**OK**; **注:**该flag会在后续的流程中使用到 ![](../.gitbook/assets/06%20%284%29.png) **1.9** 点击 **Next**; **1.10** 时钟周期设置为10ns,器件选择为**xc7z020clg400-1** ![](../.gitbook/assets/07.png) **1.11** 点击 **Finish**按钮,完成工程创建; ![](../.gitbook/assets/08%20%284%29.png) **1.12** 双击 **matrixmul.cpp**文件查看源代码内容; ![](../.gitbook/assets/09%20%282%29.png) ## 步骤 2:运行 C 代码仿真 (可跳过该步骤) ​ ### 2.运行C代码仿真,检查C代码是否正确 **2.1** 点击 **Project > Run C Simulation** 或者直接点击工具栏的![](c:/Users/ARTHURLI/Desktop/PYNQ%20DOCS/PynqDocs/images/Chapter_11/73.png) 按钮, 然后点击 **OK** 开始C代码仿真; _可以在_ _Console_ _窗口观察源代码编译过程与测试结果_ ![](c:/Users/ARTHURLI/Desktop/PYNQ%20DOCS/PynqDocs/images/Chapter_11/10.png) **2.2** 双击 **matrixmul\_test.cpp** **文件可以观察测试代码** **注:**因为我们在之前的步骤中对**HW\_COSIM**进行了定义,所以**matrixmul\_test** 的主函数会调用matrixmul 函数,对matrixmul函数的输出结果与软件计算结果进行比较并打印出来。 如果没有对**HW\_COSIM**进行定义,则只会打印出计算结果,不会判断自定义函数是否正确。 ## 步骤 3:C代码调试(可跳过该步骤) ### 3. 让代码在调试模式下运行 **3.1** 点击 **Project > Run C Simulation** 或者直接点击工具栏的 ![](c:/Users/ARTHURLI/Desktop/PYNQ%20DOCS/PynqDocs/images/Chapter_11/73.png) 按钮, 在弹出的窗口中勾选**Launch Debugger**,然后点击 **OK**; ![](c:/Users/ARTHURLI/Desktop/PYNQ%20DOCS/PynqDocs/images/Chapter_11/11.png) **注:**代码在编译过程中会增加-g选项,使得可执行代码中包含调试信息。 **3.2** Debug窗口将被自动打开,程序停留在main\(\) 函数入口; ![](../.gitbook/assets/12%20%281%29.png) **3.3** 在代码的第105行,输出“{“ 的位置,双击鼠标,在该位置添加断点; ![](../.gitbook/assets/13%20%281%29.png) **3.4** 在101行,调用matrixmul\(\) 函数的位置处添加一个断点; **3.5** 点击 **Step Over \(F6\)** 按钮 \( ![](c:/Users/ARTHURLI/Desktop/PYNQ%20DOCS/PynqDocs/images/Chapter_11/74.png) \), 观察代码执行过程中变量的变化; ![](../.gitbook/assets/14%20%283%29.png) ![](../.gitbook/assets/15.png) **3.6** 点击 **Resume** \( ![](c:/Users/ARTHURLI/Desktop/PYNQ%20DOCS/PynqDocs/images/Chapter_11/75.png) \) 按钮或者按**F8\*\***,\*\*代码将执行到 第101行; 可以观察到软件计算的结果,如下图所示; ![](../.gitbook/assets/16%20%283%29.png) **3.7** 点击 工具栏**Step Into \(F5\)** 按钮\(![](c:/Users/ARTHURLI/Desktop/PYNQ%20DOCS/PynqDocs/images/Chapter_11/76.png) \) 会跳转到 **matrixmul** 函数; **3.8** 点击 工具栏**Step Over \(F6\)** **几次,观察运算过程,**然后点击工具栏 **Step Return \(F7\)** 按钮可以返回到调用它的测试代码主程序内; **3.9** 代码将停留在 105 行的断点处. 观察软件计算与硬件计算结果。 ![](../.gitbook/assets/17%20%283%29.png) **3.10** 在第134行设置断点 \(**return err\_cnt;**位置\),点击**Resume** 按钮; 在Console窗口打印仿真结果。 **3.11** 点击工具栏 **Resume**按钮或者**Terminate**按钮结束调试会话。 ## 步骤4:设计综合 #### 4.切换到综合 \(Synthesis\) 界面,观察综合过程。 **4.1** 点击右上角的 ![](c:/Users/ARTHURLI/Desktop/PYNQ%20DOCS/PynqDocs/images/Chapter_11/77.png) ,切换到综合(Synthesis)界面; **4.2** 点击 **Solution > Run C Synthesis > Active Solution** 或者工具栏的 ![](c:/Users/ARTHURLI/Desktop/PYNQ%20DOCS/PynqDocs/images/Chapter_11/78.png) 按钮开始综合; **4.3** 综合完成后会显示综合报告,报告中包含了性能与资源预估,以及设计的延时信息; ![](../.gitbook/assets/19.png) **4.4** 综合后生成的文件如下所示; ![](../.gitbook/assets/20%20%285%29.png) **4.5** 报告中还包含了顶层接口信号 ![](../.gitbook/assets/21%20%282%29.png) ​ ## 步骤5: 运行C/RTL 协同仿真,会调用Verilog/VHDL仿真工具进行仿真。 **5.1** 点击 **Solution > Run C/RTL Cosimulation\*\***,打开软件协同仿真工具;\*\* ![](../.gitbook/assets/22%20%283%29.png) **5.2** 设置仿真工具为**Vivado Simulator**,语言为**Verilog**,Dunp Trace选择为**all**; ![](../.gitbook/assets/23%20%281%29.png) **5.3** 仿真报告如下所示; ![](../.gitbook/assets/24.png) **5.4** 点击工具栏的**Open Wave Viewer…**查看硬件仿真波形; ![](../.gitbook/assets/25.png) ## 步骤6:导出 RTL代码与实现结果 ### 在 Vivado HLS, 选择Verilog,导出硬件设计, and run the implementation by selecting Evaluate option. **6.1** 选择 **Solution > Export RTL** 或者点击工具栏 ![](c:/Users/ARTHURLI/Desktop/PYNQ%20DOCS/PynqDocs/images/Chapter_11/79.png) 按钮; **6.2** 选中 _Vivado synthesis, place and route_ 复选框来运行实现(implementation) 工具 ![](../.gitbook/assets/28%20%281%29.png) **6.3** 点击 **OK** ,设计实现将开始运行,完成后的设计实现报告如下; ![](../.gitbook/assets/29%20%282%29.png) **6.4** 生成的IP被以zip file格式存放到了impl\ip目录下. ![](../.gitbook/assets/30%20%281%29.png) **6.5** 点击**File > Exit**,关闭HLS工具 ================================================ FILE: pynq-zhong-wen-zi-liao/112-ji-yu-hls-de-jia-su-qi-overlay-she-ji-shi-li-notebook-zhong-tiao-yong-ying-jian-ip.md ================================================ # 11-2\_基于HLS的加速器Overlay设计实例 - Notebook中调用硬件IP ## 介绍 本章节介绍了IP的接口及for循环语句directive基本方法、在Vivado工程中实例化HLS IP的流程、以及在Jupyter Notebook上对IP的调用方法。 ## 目标 * ​ 使用基本的HLS directive * ​ 在Vivado中实例化HLS IP * ​ Jupyter notebook中调用HLS IP ## 步骤1:创建新的Solution ### 打开实验1中的HLS工程,创建新的Solution **1.1** 打开实验1中的HLS工程,点击 **Project > New Solution…**; ![](../.gitbook/assets/31.png) **1.2** 默认设置,点击 **Finish**按钮; ![](../.gitbook/assets/32%20%282%29.png) **1.3** 默认设置,点击 **Finish**按钮,工程中将会增加一个Solution2; ![](../.gitbook/assets/33.png) **1.4** 双击**matrixmul.cpp**,在**Directive**窗口,点击**matrixmul**后右键,将会出现对函数顶层模块插入Directive的选项; ![](../.gitbook/assets/34.png) **1.5** 点击Directive后,参照如下配置,点击**OK**; ![](../.gitbook/assets/35%20%282%29.png) **1.6** 使用同样的方法,对参数a、b、res和test添加directive,参照如下配置; ![](../.gitbook/assets/36%20%282%29.png) **1.7** 单击**Col**后,右键选择**Insert Dorective…**; ![](../.gitbook/assets/37%20%281%29.png) **1.8** 参照如下配置,点击OK; ![](../.gitbook/assets/38%20%281%29.png) **1.9** 最终的配置结果如下所示; ![](../.gitbook/assets/39%20%281%29.png) **1.10** 所有的directives都保存在了directives.tcl文件中; ![](../.gitbook/assets/40.png) **1.11** 点击 Solution > Run C Synthesis > Active Solution,对solution2进行综合; ![](../.gitbook/assets/41.png) **1.12** 点击工具栏 Export RTL按钮,导出IP; ![](../.gitbook/assets/42.png) **1.13** 弹出对话框作如下配置,点击OK; ![](../.gitbook/assets/43.png) **1.14** Export报告如下; ![](../.gitbook/assets/44.png) **1.15** 关闭HLS; ![](../.gitbook/assets/45%20%281%29.png) ## 步骤2:创建Vivado工程 ### 2.创建Vivado工程:例化HLS IP到工程中 **2.1** 启动 Vivado工具: Start > Xilinx Design Tools > Vivado 2018.2; **2.2** 新建Vivado工程; ![](../.gitbook/assets/46%20%281%29.png) **2.3** 一直点击**Next**按钮; **2.4** 选择PYNQ-Z2板; ![](../.gitbook/assets/48%20%281%29.png) **2.5** 将刚刚生成的zip包拷贝到Vivado工程的.ip.user\_files后解压缩; ![](../.gitbook/assets/49.png) ![](../.gitbook/assets/50%20%281%29.png) **2.6** 将.ip.user\_files文件夹添加到ip库; ![](../.gitbook/assets/51.png) **2.7** 创建 Block Design; ![](../.gitbook/assets/52.png) **2.8** 添加ZYNQ7 Processing System IP 到Block Design; ![](../.gitbook/assets/53%20%281%29.png) ![](../.gitbook/assets/54.png) **2.9** 点击Run Block Automation,保持默认配置,点击OK; ![](../.gitbook/assets/55.png) **2.10** 为Zynq Processing System增加HP0口; ![](../.gitbook/assets/56.png) **2.11** 例化Matrixmul到Block Design; ![](../.gitbook/assets/57.png) ![](../.gitbook/assets/58.png) **2.12** 通过AXI DMA将Matrixmul IP连接到PS侧,最终的Block Design如下图所示; ![](../.gitbook/assets/59.png) **2.13** 创建 HDL Wrapper文件; ![](../.gitbook/assets/60.png) ![](../.gitbook/assets/61.png) **2.14** 生成Bitstream文件; ![](../.gitbook/assets/62.png) **2.15** 生成Block Design的tcl文件; ![](../.gitbook/assets/63.png) ![](../.gitbook/assets/64.png) **2.16** 将tcl文件和bitstream文件拷贝到lab2\_src/matrixmul目录,并将tcl文件和bitstream文件重命名为matrixmul.tcl和matrixmulbit; ![](../.gitbook/assets/65.png) ## 步骤3:连接PYNQ-Z2测试 ### 3. 将PYNQ-Z2上电,通过Jupyter访问Matrixmul IP **3.1** 将笔记本或者PC机的IP地址设置为192.168.2.X ![](../.gitbook/assets/67.png) **3.2** 按如下方式设置好PYNQ-Z2,并将PYNQ-Z2通过网线连接到PC后,然后上电; ![](../.gitbook/assets/68.png) **3.3** 待PYNQ-Z2启动完成后,通过Winscp等sftp工具将包含tcl文件、bitstream文件和ipynb文件的matrixmul文件夹下载到板卡的jupyter\_notebooks目录中; **注**:用户名与密码均为: **xilinx** ![](../.gitbook/assets/69.png) ![](../.gitbook/assets/70.png) **3.4** 打开Chrome或者Firefox等IE浏览器,输入192.168.2.99,密码为:xilinx ![](../.gitbook/assets/71.png) **3.5** 打开matrixmul目录,运行matrixmul.ipynb开始测试, ![](../.gitbook/assets/72.png) ================================================ FILE: pynq-zhong-wen-zi-liao/12-di-san-fang-overlay-jie-shao-spyn.md ================================================ # 12\_第三方Overlay介绍-SPYN ## IIoT-SPYN IIoT-SPYN是一个开源项目,通过IIoT-EDDP和PYNQ器件,用户可以通过使用IIoT-SPYN控制、监视、捕获数据、可视化和分析汽车工业信息。 ## Overlay 简介 ![](../.gitbook/assets/01%20%287%29.png) 上图为工程项目示意图。通过软硬件划分,在PS端中负责Linux和通信,而PL端负责实时控制部分。在PYNQ中加载bit时,PL端的控制部分将以IP核形式呈现。通过Python,对IP核的参数配置来实现控制。 ![](../.gitbook/assets/02%20%281%29.png) 此项目中采用的FOC算法示意图,包括Clarke变换、Park变换、PWM编码器、PI控制器、电流采样等 ![](../.gitbook/assets/03%20%288%29.png) FOC算法模块的Vivado Block Design,可以观察到所有的算法模块都是通过Vivado HLS生成的。 ## 如何将IIoT-SPYN安装到PYNQ-Z2 image2.3上 * 方法1(在PYNQ可以连接网络的情况下): 在Jupyter界面中new选项里选择新建一个终端,在PYNQ板卡能连接网络的情况下,在终端输入: ```javascript sudo pip3 install --upgrade git+https://github.com/Xilinx/IIoT-SPYN.git ``` * 方法2(在PYNQ无法连接网络的情况下): 下载IIoT-SPYN的zip压缩包,上传压缩包到PYNQ板卡,在终端中unzip上传的压缩包,移动到解压的目录下然后采用pip安装 ```text sudo pip3 install -e . ``` ## Overlay API接口介绍 在此工程中,通过Motor\_Controller类来实例化一个对象进行对于电机的控制,Motor\_Controller类的主要属性包括: 1. ​ **init**\(\):init中主要是对MMIO的配置,初始化电机的模式。 2. ​ set\_mode\(mode\):set\_mode用于配置电机的模式,包括:“torque\_mode”(扭矩模式)、“rpm\_mode”(转速模式)、“reset\_mode”(默认模式)。 3. ​ capture\_mode\(mode\):capture\_mode用于配置捕获模式,包括:“ia\_ib\_angle\_rpm”、“id\_iq”、“vd\_vq” 4. ​ set\_torque\(value\):set\_torque用于配置电机的扭矩。 5. ​ stop\(\):stop用于配置停止电机。 6. ​ \_read\_controlreg\(value\):\_read\_controlreg用于读控制寄存器。 7. ​ read\_capturereg\(offset\):read\_capturereg用于读捕获寄存器,offset为偏移地址。 8. ​ \_write\_controlreg\(offset,value\):\_write\_controlreg用于写控制寄存器,offset为偏移地址。 9. ​ write\_capturereg\(offset, value\):write\_capturereg用于写捕获寄存器,offset为偏移地址。 10. ​ stream\_capture\(capture\_address\):stream\_capture用于捕获一系列的寄存器,capture\_address为捕获地址。 ## 应用案例介绍 在IIoT-SPYN的notebooks目录下包含了两个在此Overlay上的应用案例。在spyn.ipynb中介绍了通过IIoT-SPYN和EDPS板卡控制一个三相交流电动机,此案例中连接到PYNQ板卡上的是Trenz电机TEC0053。下面基于此应用案例的程序进行简要介绍: ​ 通过PYNQ的Overlay库加载bit文件,在正常运行的情况下,可以看到PYNQ板卡标记为“DONE”的LED闪烁\(为加载了bit文件的效果\) ```text from pynq import Overlay from pynq import MMIO import numpy as np overlay = Overlay("/usr/local/lib/python3.6/dist-packages/spyn/overlays/spyn.bit") overlay.download() ``` ​ 实例化一个电机控制,输出可用的电机控制模式 ```text from spyn.lib import * motor = Motor_Controller() print(f'Available motor modes : {motor.motor_modes}') ``` ​ 预设置电机控制模式 ```text motor.set_mode('reset_mode') ``` ​ 输出控制模块和捕获模块的信息 ```text print(f'Memory mapped IO blocks : {motor.mmio_blocks}') ``` 通过Jupyter交互小部件\(slider\)控制电机和设置电机运行模式 ```text from ipywidgets import interact, interactive, HBox, VBox import ipywidgets as widgets text = {'Motor': 'success', 'Forward': 'info', 'Reverse': 'warning'} buttons, colors = list(text.keys()), list(text.values()) toggle = [ widgets.ToggleButton(description=f'{buttons[i]}', button_style=f'{colors[i]}') for i in range(3)] mode = widgets.Dropdown(options=['Speed', 'Current']) def clicked(toggle_0=toggle[0], mode=mode, toggle_1=toggle[1], toggle_2=toggle[2], RPM=None, Torque=None): if toggle_0: if mode == 'Speed': motor.set_mode('rpm_mode') motor.set_rpm(RPM) elif mode == 'Current': motor.set_mode('torque_mode') motor.set_torque(Torque) else: motor.stop() w = interactive(clicked, RPM=widgets.IntSlider(min=-5000, max=5000, step=1, value=1000), Torque=widgets.IntSlider(min=-400, max=400, step=1, value=0)) VBox([HBox(w.children[:2]), w.children[2], w.children[3], w.children[4], w.children[5]]) ``` ​ 读取状态寄存器的相关信息 ```text motor_status = [(motor._read_controlreg(i + ANGLE.offset)) for i in range(0, 16, 4)] high_sp, low_sp = bytesplit(motor_status[1]) high_id, low_id = bytesplit(motor_status[2]) high_iq, low_iq = bytesplit(motor_status[3]) print(f'Angle in degrees : {motor_status[0] * 0.36}') print(f'Angle in steps per thousand: {(motor_status[0])}') print(f'Id : {np.int16(low_id) * 0.00039} Amp') print(f'Iq : {np.int16(low_iq) * 0.00039} Amp') print(f'Speed in RPM : {-(np.int16(low_sp))}') ``` ```text 通过一个循环测试电机的运转 ``` ```text import time motor.set_mode('rpm_mode') for i in range(2): motor.set_rpm(1000) time.sleep(1) motor.set_rpm(0) time.sleep(2) motor.set_rpm(-50) time.sleep(2) motor.set_rpm(0) time.sleep(2) motor.stop() ``` 捕获模式输出及选择 ```text print(f'Available stream capture modes : {motor.motor_capture_modes}') motor.capture_mode('ia_ib_angle_rpm') ``` 通过Xlnk类为捕获的流数据分配DMA ```text from pynq import Xlnk xlnk = Xlnk() input_buffer = xlnk.cma_array(shape=(256,), dtype=np.uint8) capture_address = input_buffer.physical_address print(f'Physical Address of data stream capture: {hex(capture_address)}') ``` ```text 按照控制方式来记录流数据 ``` ```text from pynq import MMIO # capture_count = int(input('Enter capture count: ')) capture_count = 1000 def continuous_capture(capture_count): mmio_stream = MMIO(capture_address, 256) cap_list = [([]) for i in range(4)] for _ in range(capture_count): motor.stream_capture(capture_address) for i in range(4, 260, 4): stream = mmio_stream.read(i - 4, 4) highbits, lowbits = bytesplit(stream) if (i % 8 != 0): cap_list[0].extend([(np.int16(lowbits))]) cap_list[1].extend([(np.int16(highbits))]) else: cap_list[2].extend([(np.int16(lowbits))]) cap_list[3].extend([(np.int16(highbits))]) return cap_list cap_list = continuous_capture(capture_count) Ia, Ib, angle, rpm = cap_list[0], cap_list[1], cap_list[3], cap_list[2] current_Ia = np.array(Ia) * 0.00039 current_Ib = np.array(Ib) * 0.00039 ``` 通过matplotlib将得到的数据通过图像表示出来 ```text %matplotlib inline import matplotlib.pyplot as plt fig = plt.figure(figsize=(20, 10)) ax = fig.add_subplot(111) ax.plot(current_Ia) plt.show() ``` ```text 停止电机并且重启xlnk以免发生内存泄漏 ``` ```text xlnk.xlnk_reset() motor.stop() ``` ================================================ FILE: pynq-zhong-wen-zi-liao/13-yi-bnnpynq-wei-li-de-zi-ding-yi-overlay-fen-fa-fang-fa-jie-shao.md ================================================ # 13\_以BNN-PYNQ为例的自定义Overlay分发方法介绍 Python 有非常丰富的第三方库可以使用,很多PYNQ开发者也会在 Github 上提交自己的适用于PYNQ的 Python 包。将一个完成的FPGA工程转换为PYNQ第三方包会方便我们进行PYNQ的开发。这个过程主要包括两个步骤:1.通过已经在PYNQ里的APIs对FPGA部分进行驱动的编写。2.将编写好的驱动做成Python库并且进行打包分发。 ![](https://github.com/louisliuwei/PynqDocs/tree/39175038b9c8bdc70026a790e4a774a2dc890f07/images/Chapter_13/01.png) 从PYNQ框架中可以看到,对于FPGA Bitstreams中的内容,PYNQ有overlays方法对于Bitstream进行类似Vivado中download Bitstream的行为。对于FPGA中其余部分,比如User designs里的内容,可以将它们作为PYNQ的IPs,通过上层的API(如GPIO、MMIO等)对它进行自定义的驱动编写。或者通过其他方法(如:通过python调用C来实现已经在HLS中实现的算法等)。 本节以BNN-PYNQ工程为例,探究在BNN-PYNQ中如何将一个完成的FPGA工程转换为PYNQ第三方包。 首先为第一个步骤,即对FPGA部分进行驱动的编写。在示例的notebook中,首先导入了bnn这个包,在bnn.py这个.py文件中有对bnn的详细描述,其中便包括了对FPGA部分的驱动编写。在bnn.py中,主要包含对三个类的定义:PynqBNN、CnvClassifier、LfcClassifier。其中,PynqBNN主要作为一个共享库加载指定网络并且将bitstream下载到FPGA(PL)中;CnvClassifier是CNV网络的分类器类,用于对cifar10格式的图像和需要预处理的图像进行推理;LfcClassifier是LFC网络的分类器类,用于对mnist格式的图像进行推理。两个类在构造时,都会加载共享库,并且将bitstream下载到FPGA(PL)中。如在CnvClassifier中构造函数的定义: ```text def __init__(self, network, params, runtime=RUNTIME_HW): if params in available_params(network): self.net = network self.params = params self.runtime = runtime self.usecPerImage = 0.0 self.bnn = PynqBNN(runtime, network) self.bnn.load_parameters(os.path.join(params, network)) self.classes = self.bnn.classes else: print("ERROR: parameters are not availlable for {0}".format(network)) ``` ​ 在CnvClassifier的构造函数中,实例化了一个PynqBNN对象。而在PynqBNN中,构造函数的描述如下: ```text def __init__(self, runtime, network, load_overlay=True): self.bitstream_name = None if runtime == RUNTIME_HW: self.bitstream_name="{0}-{1}.bit".format(network,PLATFORM) self.bitstream_path=os.path.join(BNN_BIT_DIR, self.bitstream_name) if PL.bitfile_name != self.bitstream_path: if load_overlay: Overlay(self.bitstream_path).download() else: raise RuntimeError("Incorrect Overlay loaded") dllname = "{0}-{1}-{2}.so".format(runtime, network,PLATFORM) if dllname not in _libraries: _libraries[dllname] = _ffi.dlopen(os.path.join(BNN_LIB_DIR, dllname)) self.interface = _libraries[dllname] self.num_classes = 0 ``` ​ 在PynqBNN的构造函数中,主要部分即为通过os进行文件检索等操作找到bitstream,并且使用Overlay加载它。因此在示例中实例化一个CnvClassifier对象时,PYNQ板卡上的Done信号灯会闪烁(即代表有bitstream加载到PL部分)。 ​ 在BNN-PYNQ中,因为网络的相关定义主要通过Vivado HLS进行编写,主要语言对象为C++。因此,项目的核心用法为将C++代码编译为shared library后,python主要作为一个接口对象通过C++共享库与主机库进行通信,具体方法为借助CFFI调用C动态库。如在bnn.py中对CFFI的使用: ```text import cffi _ffi = cffi.FFI() _ffi.cdef(""" void load_parameters(const char* path); int inference(const char* path, int results[64], int number_class, float *usecPerImage); int* inference_multiple(const char* path, int number_class, int *image_number, float *usecPerImage, int enable_detail); void free_results(int * result); void deinit(); ``` 在通过源文件编译及在python声明之后,就可以通过python调用c语言定义的函数了,如在PynqBNN中对网络的参数进行读取的函数的定义: ```text def load_parameters(self, params): if not os.path.isabs(params): params = os.path.join(BNN_PARAM_DIR, params) if os.path.isdir(params): self.interface.load_parameters(params.encode()) self.classes = [] with open (os.path.join(params, "classes.txt")) as f: self.classes = [c.strip() for c in f.readlines()] filter(None, self.classes) else: print("\nERROR: No such parameter directory \"" + params + "\"") ``` 在确认参数文件路径正确之后,会调用之前的声明的C函数load\_parameters,用于加载和读取参数。对工程的更多实现方法及内部原理,可以参考项目主页: [https://github.com/Xilinx/BNN-PYNQ](https://github.com/Xilinx/BNN-PYNQ) 至此,已经简略了解了BNN-PYNQ的基本工作原理和对FPGA进行驱动编写驱动的过程。 在对驱动编写好后,如果要想向 Github仓库提交自己开发的包,首先要将自己的代码打包,才能上传分发。Python 库打包分发的关键在于编写 setup.py 。setup.py是setuptools的构建脚本。它告诉setuptools你的包(例如名称和版本)以及要包含的代码文件。一个简单的setup.py可以编写如下: ```text import setuptools with open("README.md", "r") as fh: long_description = fh.read() setuptools.setup( name="example-pkg-your-username", version="0.0.1", author="Example Author", author_email="author@example.com", description="A small example package", long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/pypa/sampleproject", packages=setuptools.find_packages(), classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], ) ``` 可以从实例中看到,setup.py 文件编写的规则是从 setuptools导入 setup 函数,并传入各类参数进行调用。setup函数常用的参数如下: | 参数 | 说明 | | :--- | :--- | | name | 包名称 | | version | 包版本 | | author | 程序的作者 | | author\_email | 作者邮箱 | | url | 程序的官网地址 ,对于许多项目这是一个指向GitHub,GitLab,Bitbucket或类似代码托管服务的链接 | | description | 对于程序的简单描述 | | requires | 指定依赖的其他包 | | packages | 需要处理的包目录\(通常为包含 **init**.py 的文件夹\),对于复杂的工程,可以使用find\_packages\(\)去自动发现所有的包 | | classifiers | 程序的所属分类列表 | | include\_package\_data | 自动包含包内所有受版本控制\(cvs/svn/git\)的数据文件 | | package\_data | 指定包内需要包含的数据文件 | | data\_files | 打包时需要打包的数据文件,如图片,配置文件等 | ​ 关于setup.py的更多信息,可以参考: [https://packaging.python.org/guides/distributing-packages-using-setuptools/](https://packaging.python.org/guides/distributing-packages-using-setuptools/) packages是setup.py中一个重要的参数,它声明了需要处理的包目录。因此在对setup.py进行编写时,需要注意文件的目录结构,BNN的文件目录结构如下图所示: ![](https://github.com/louisliuwei/PynqDocs/tree/39175038b9c8bdc70026a790e4a774a2dc890f07/images/Chapter_13/02.png) 图5-2 bnn的文件结构 其中,bnn包含了对LfcClassifier和CnvClassifier两个Pyhton类的描述、notebooks中包含了示例notebook,在安装的过程中移到“/home/xilinx/jupyter\_notebooks/bnn/”目录下、tests中包含了测试脚本和测试的图片。 BNN的setup.py描述如下(部分核心代码): ```text from setuptools import setup, find_packages import subprocess import sys import shutil import bnn import os from glob import glob import site if os.environ['BOARD'] != 'Ultra96' and os.environ['BOARD'] != 'Pynq-Z1' and os.environ['BOARD'] != 'Pynq-Z2': print("Only supported on a Ultra96, Pynq-Z1 or Pynq-Z2 Board") exit(1) setup( name = "bnn-pynq", version = bnn.__version__, url = 'kwa/pynq', license = 'Apache Software License', author = "Nicholas Fraser, Giulio Gambardella, Peter Ogden, Yaman Umuroglu, Christoph Doehring", author_email = "pynq_support@xilinx.com", include_package_data = True, packages = ['bnn'], package_data = { '' : ['*.bit','*.tcl','*.so','*.bin','*.txt', '*.cpp', '*.h', '*.sh'], }, data_files = [(os.path.join('/home/xilinx/jupyter_notebooks/bnn',root.replace('notebooks/','')), [os.path.join(root, f) for f in files]) for root, dirs, files in os.walk('notebooks/')], description = "Classification using a hardware accelerated neural network with different precision for weights and activation" ) ``` 在此**setup.py**中,主要包括了以下参数:name、version、url、license、author、author\_email、include\_package\_data、packages、package\_data、data\_files、description。 packages中直接指定了包目录,即bnn。 package\_data 中指定了包内需要包含的数据文件,包括:'_.bit'(bit文件,用于比特流的下载)、'_.tcl'(脚本文件,用于解析硬件设计中的ip核信息)、'_.bin'(二进制文件,包含网络训练的参数信息)、'_.cpp', '_.h'(在Vivado HLS中对网络定义时的c++文件和头文件)、'_.so'(c进行编译之后的文件,可以借助cffi调用这个动态链接库)。 data\_files中指定了包含的数据路径。 在对PYNQ的setup.py编写时,经常会进行板卡环境的检查,即: ```text if os.environ['BOARD'] != 'Ultra96' and os.environ['BOARD'] != 'Pynq-Z1' and os.environ['BOARD'] != 'Pynq-Z2': print("Only supported on a Ultra96, Pynq-Z1 or Pynq-Z2 Board") ``` 通过导入os,可以检查板载的详细信息,当板卡不适配时,可以输出错误信息。 在编写好后setup.py后,就完成了对库的打包过程,这时可以选择上传到代码管理网站如Github后,通过pip和git指令安装: ```text sudo pip3 install git+https://github.com/Xilinx/BNN-PYNQ.git (on PYNQ v2.3) ``` 也可以将BNN包源文件下载到PYNQ后,在根目录中,通过pip指令安装: ```text sudo pip3 install -e . (on PYNQ v2.3) ``` ================================================ FILE: pynq-zhong-wen-zi-liao/14python-ji-chu.md ================================================ # 14\_Python基础 ## 前言 Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可读性,相比其他语言经常使用英文关键字,其他语言的一些标点符号,它具有比其他语言更有特色语法结构。 * Python 是一种解释型语言: 这意味着开发过程中没有了编译这个环节。类似于PHP和Perl语言。 * Python 是交互式语言: 这意味着,您可以在一个Python提示符,直接互动执行写你的程序。 * Python 是面向对象语言: 这意味着Python支持面向对象的风格或代码封装在对象的编程技术。 Python的另一大优势与便利性在于其开源性。网络上有相当多且高效有用的开源库可供我们进一步开发使用,比如著名的矩阵计算库numpy,科学计算库scipy等等。PYNQ已经为我们预装了这些有用的库,我们先来介绍一下python的基础用法,并在最后一小节讲讲如何使用pip来添加查询新的库。 通过键入关键词import,空格后按下tab即可查看所有已经安装的库。如图所示: ![](../.gitbook/assets/01%20%288%29.png) ## Python常用辅助函数 当我们使用(import)一个新的库的时候,我们常常不知道这个库有什么,该如何使用这个库。这个时候我们就可以借助两个关键的函数:help\(\), dir\(\) 来帮助我们了解库的使用。我们以asyncio库为例。 ![](../.gitbook/assets/02%20%286%29.png) 导入库后,我们首先调用help函数,help函数会给我们提供asyncio库的制作者所写的帮助文档。通过这个文档,我们可以非常轻松的了解库里有哪些函数以及这些函数有什么作用。但是有的时候,我们希望可以更快的寻找我们需要的函数而非阅读全部的帮助文档。此时我们即可借助dir\(\)函数。 ![](../.gitbook/assets/03%20%282%29.png) dir\(\)函数以列表的形式提供了所有该库下可调用函数或子库,此时我们可以更容易的找到我们需要的内容。假如我们对图中的Condition模块感兴趣,我们只需键入help\(asyncio.Condition\)即可查看该模块的详细信息。 ![](../.gitbook/assets/04%20%287%29.png) 依赖help与dir,我们通常就可以快速达成我们对所需要函数的快速检索以及使用,这对我们日后开发程序是非常有帮助的。事实上,help的功能更加广泛,我们可以对任意我们已经定义了的变量进行help来获得详细信息。 ![](../.gitbook/assets/05%20%285%29.png) ## Python基础知识概览 与其他语言不同,python的变量定义无需申明类型,直接拿来定义即可。这是因为python的所有变量类型都是以object(对象)定义的。在上一小结末尾,我们定义了a = 3,通过help可以看到,a是一个int object(整数对象),这也是python的一大特色。Python通过名字空间与对象空间的链接达成了对变量的管理。具体来说,当python执行a = 3的时候,它首先在对象空间里创建一个int object,其值为3,接着在名字空间里创建(或寻找)变量名a,将其指向对象“3”。贴切的说,a里存储的并不是3,而是“3”这个对象的地址。此外,对象名只能由英文数字下划线组成且数字不能作为开头,变量名区分大小写。 Python基础的对象主要有int(整数),float(浮点数),str(字符串),list(列表),tuple(元组),dict(字典),set(集合)。集合相对来说不怎么常用,这里不予讨论。Int与float同其他语言一致,包含基本的加减乘除(+-_/)以及整除(//)取余(%)幂次(\*_),如下所示: ![](../.gitbook/assets/06%20%286%29.png) 字符串对象可用单引号‘string’、双引号“string”、三单引号‘‘‘string’’’或者三双引号“““string”””表示,但是通常三引号用作注释说明,一般只使用单引号与双引号。 与其他语言不同,python并没有数组对象array,相对的,其有元组\(value1, value2, …\)与列表\[value1, value2, …\],两者唯一的区别在于元组是一个不可变对象——无法添加删除新的元素、无法变更元组内不可变对象的值(这列可能有点拗口,但是如果某个value是可变对象比如列表,则仍然有办法修改里面的值),而列表为可变对象(增改删查皆可)。基本操作如下所示: ![](../.gitbook/assets/07%20%281%29.png) 在上面的示范里,我们可以发现a + . + function的用法。正如前面介绍,这正是因为列表a也是一个对象,而python调用对象方法的语法便是对象名object + . + function。 最后是字典对象{key1: value1,…}其采用键值对组合。其中键(key)必须为不可变对象,值(value)可为任意对象。字典本身为可变对象,即可增改删查,这里只介绍最基本的增与改。 ![](../.gitbook/assets/08%20%285%29.png) ## Python简单实例——分解因数 这一节,我们将通过一个简单实例来了解python的一些基本操作:函数定义、条件判断、for循环迭代。代码如下: ![](../.gitbook/assets/09%20%285%29.png) 为了实现代码的整洁与重复使用,函数的定义是必须要掌握的。Python中使用关键词def + function\_name\(arg1, arg2, …\)来定义函数。上面代码就定义了一个名为factorize的函数,其参数为n。我们注意到,与其他的语言不同,python并没有各类括号来划分代码块,因为其采用的是冒号“:”加缩进(tab)的形式来划分。冒号为缩进提示符,因此必不可少,下面处于同一级别缩进的都属于def的函数范围。当然,缩进也可以嵌套,子缩进属于父缩进。若要结束函数定义,只需退回到和def的缩进级别(0缩进)即可。下图展示了def的具体形式。 ![](../.gitbook/assets/10.png) 函数定义的时候可以指定任意多个参数,同时也允许赋予默认值。但是拥有默认值的形参必须排在最后。即上面的图示中,若写成arg1,arg2 = 3, arg3,便是非法的。函数最后可以使用关键词return来返回函数体中的数据,且一旦碰到return,函数调用就结束了。上面的示例中我们返回了arg1,因此q得到的值就是func\(4,6\)里的4。有的时候,为了能让别人读懂函数的作用,我们通常会在def的下一行增添注释,也就是因素分解实例中的三引号部分。 回到实例,在函数内部,我们首先定义了空列表factor,随后使用了选择分支结构if….else,其具体结构如下所示: ![](../.gitbook/assets/11%20%282%29.png) 其中只有if是必要的,并且采用冒号+缩进的方式提示程序代码块到何处结束。Elif和else是分支选项,可有可无,elif可以写多个,而else只能有一个,他们都与if同级缩进。 最后是for循环结构,语法如下: ![](../.gitbook/assets/12.png) 注意到与别的语言不同,for循环并不是只能迭代次数循环,iterate\_unit可以是任何一个可迭代对象,常见的有元组、列表、字符串。每一次迭代的内容会赋给item,通过对item进行操作来完成循环目的。在实例中,我们的循环部分首先使用内置函数range\(start, stop, step\)生成了一个可迭代对象,并通过循环该对象完成因数分解操作。其中range函数返回的是一个从start开始(包含start)至stop结束(不包含stop)且间隔为step的整数列列表(比如range\(0,6,2\)返回的的是\[0,2,4\])。 了解了if和for后,我们再纵观整个程序一遍。程序首先判断输入的n是否是一个可以被因数分解的对象,随后从1至 根号n遍历,找到所有的因数(即可以被整除),并将因数添加至列表factor,最后返回列表。 有关更多的python教程,还请找专门的书籍自行学习。 ## Python库的使用 我们之前说过,python的强大之处在于其丰富的开源库,我们接下来以Numpy与pynq的结合为例稍微的体验一下库的运用。 ![](../.gitbook/assets/13%20%284%29.png) ![](../.gitbook/assets/14%20%284%29.png) Numpy包为我们提供了传统的数组形式,而pynq包则为我们在numpy的基础上进一步封装,使得其成为了可被PYNQ板的ARM处理器可处理的形式。换而言之,我们可以用numpy包来处理从硬件上搜集的数据,也可以用pynq包把数据包装后传回硬件。我们要写的只不过是一行代码,其内部具体的实现原理无需我们推敲,这正是python的开源性为我们带来的便利性。如果想知道函数的具体用法,我们只需使用help即可。如图所示: ![](../.gitbook/assets/15%20%283%29.png) ## Python高级实例——异步IO操作 在与可编程逻辑设备通过事件交互的时候,python3.5以后的版本的asyncio模块可以发挥巨大的优势(PYNQ已经搭载了3.6版本)。我们可以使用这个模块来异步处理PYNQ板上的多个IO任务,这样就可以避免有的IO占线太长而导致其他IO操作堵塞。也就是说,程序可以优先处理准备好运行的了IO任务,最后再回过头去把占线的任务解决掉。 当然了,这里我们并不知道我们的可编程逻辑设备上运行的是什么,所以我们这里仅仅在程序上模拟一下异步处理(即使用下面的asyncio.sleep方法达成占线操作)。代码如下: ![](../.gitbook/assets/16%20%282%29.png) 我们在定义函数关键词def前增加关键词async,即可定义“异步函数”,其余语法不变。我们称一个异步函数为协程(Coroutine),其特点是只允许在其内部的出现的await操作(await出现在async def以外的地方会提示语法错误)。关键词await 允许该协程在此处中断,先去执行其他协程,并在await后的内容结束后立即回到原先协程继续执行未完成的代码。在上面定义的函数中,我们用asyncio.sleep来模拟IO的占线操作(这里我们不可以使用time.sleep,因为await后必须是一个Awaitable对象,asyncio.sleep相当于协程版的time.sleep),在实际操作中,我们await后加的可能是另外一个IO操作协程。接下来,我们就用这个函数来模拟异步IO操作。代码如下: ![](../.gitbook/assets/17%20%285%29.png) 为了体现出异步处理的强大,我们特地让第二个IO任务的执行时间高于第一个IO任务。从输出可以看到,两个协程的开始时间是几乎一致的。也就是说,当第一个任务运行到await后,并未等待占线结束,而是直接运行第二个协程。当第一个协程的5s结束后,程序并没有等待运行中的第二个协程的8s操作,而是中断回到第一个协程继续后面的操作,当8s过后再回过头去执行第二个协程。 ## Pip的使用 简介提到,Python为我们提供了相当多的优质的库,同时,python也为我们提供了专门的库管理工具——pip。通过pip,我们可以非常简单的下载安装新的库。本小节就来介绍如何给PYNQ板装新的库。 ![](../.gitbook/assets/18%20%284%29.png) 首先,我们打开PYNQ的Jupyter,并在New下找到Terminal,点击后进入终端操作界面。界面如下: ![](../.gitbook/assets/19%20%284%29.png) 接着我们输入sudo pip3或者sudo pip3.6测试一下自己的pip命令是哪一个(对于sd卡里烧写了不同版本的PYNQ来说,命令可能不一样),之后附加的命令内容均一致。 ![](../.gitbook/assets/20%20%281%29.png) 这里使用的sudo pip3.6命令, 首先我们键入sudo pip3.6 list,即可列出所有已安装的库。 ![](../.gitbook/assets/21.png) 这里的列表因人而异,因为自己电脑上本来就装过的库也是可以调用的。 至于安装新的库,有两种方法。第一种通过电脑联网下载库的文件夹(下图为整个文件夹的内容,该库为cv2pynq),该文件夹里有着setup.py用以帮助安装。 ![](../.gitbook/assets/22%20%282%29.png) 首先,我们把整个文件夹通过samber传输文件的方法拷贝至PYNQ的pynq文件夹内。这里我将文件夹取名为pynqhodm。 ![](../.gitbook/assets/23%20%282%29.png) 打开jupyter notebook的终端,转移到当前文件夹,语法是 cd pynq/YOUR\_FILENAME,成功后,命令行的前缀会变为如下所示: ![](../.gitbook/assets/24%20%282%29.png) 接着我们运行安装命令“sudo pip3.6 install –e .”,注意,这个末尾的“.”不可漏! ![](../.gitbook/assets/25%20%281%29.png) 提示了Successfully之后即表明安装成功。 第二种方法,需要pynq连接上电脑以及外网,经过测试,我们需要使用v2.3版本的镜像。也即pip命令为sudo pip3的版本。 第一步,我们需要确保我们的板子连上外网,为此,你需要你的电脑连上外网。打开网络和Internet设置界面,找到更改适配器选项。 ![](../.gitbook/assets/26%20%281%29.png) ![](../.gitbook/assets/27%20%282%29.png) 可以看到我们连接到外网的WLAN以及连接到板子的以太网。我们右键WLAN找到属性界面。 ![](../.gitbook/assets/28.png) 在共享界面,选择板子连上的以太网进行共享。点击确定,若跳出提出弹窗,点击确定即可。 此时,由于共享了网络,我们板子的IP地址不再是默认的192.168.2.99,而是改成了动态配置IP地址。因此第二步是找到板子串口的新IP地址。我们需要上网下载一个工具来连接串口。 登录网址[https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html获得putty) 获得putty工具。下载适合自己系统的版本。 ![](../.gitbook/assets/29%20%281%29.png) 安装完成后,打开putty。 ![](../.gitbook/assets/30.png) 接下来我们需要找到自己的Serial port。打开控制面板,在硬件和声音里找到设备管理器。 ![](../.gitbook/assets/31%20%283%29.png) 在设备管理器的端口下找到自己的端口。 ![](../.gitbook/assets/32.png) 图中为COM5。 ![](../.gitbook/assets/33%20%283%29.png) 填写完上述三个配置后,点击open。(speed栏固定115200) 打入回车后,出现如下界面。 ![](../.gitbook/assets/34%20%283%29.png) 输入命令ifconfig后,即可查看串口IP地址。 ![](../.gitbook/assets/35.png) 打开浏览器,即可用该ip地址登录jupyter notebook。 最后一步,打开terminal终端,输入sudo pip3 install package\_name 即可直接安装新的包。以Luminoth为例。 ![](../.gitbook/assets/36%20%281%29.png) 倘若包确实存在却碰到失败,多半是因为网络延迟造成,尝试多次执行安装命令即可。 ================================================ FILE: pynq-zhong-wen-zi-liao/README.md ================================================ # \[Pynq 中文资料\]