PYNQ与FPGA关系
在PYNQ框架下,ARM A9 CPU上运行的软件包括:
· 载有Jupyter Notebooks设计环境的网络服务器
· IPython内核和程序包
· Linux
· FPGA的基本硬件库和API
PYNQ框架是为软件开发者提供了访问FPGA资源的python接口,Python开发者可以忽略这些实现细节,通过python即可轻松访问FPGA,动态加载各种预编译好的各种FPGA应用(overlay),像调用函数一样去调用各种通过FPGA加速的应用或者访问连接到FPFA的外设
待解决问题
- 如何调用overlay?如何封装可用的自定义overlay?
PYNQ架构
例化(调用)-instantiation
上图表示:每个overlay有对应的.bit文件,通过Overlay()方法来加载.bit
上图表示:baseoverlay类的加载。base_overlay就是把bit文件转化后的python类。对于一个python类,我们自然可以使用辅助函数help来查看这个类到底是个什么东西,通过输入help(base_overlay),我们可以查看这个类的具体介绍
base_overlay.download() #重新download overlay
PL.bitfile_name #
检查PL的状态
from pynq import PL
PL.bitfile_name #返回.bit文件的绝对路径
# e.g.'/usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/overlays/base/base.bit'
PL.timestamp #返回时间戳
#e.g. '2022/10/6 14:16:52 +710996'
ol.is_loaded() #返回是否加载成功
#e.g. True
import 库文件环境路径
/usr/local/share/pynq-venv/lib/python3.10/site-packages/
新概念
PMOD
可以理解为一种12引脚的接口标准,用于挂载外设
microblaze
xilinx中的软核CPU,可用于控制和交互代码的编写
Logictools overlay
用于分析调试
FSM generator
3bit格雷码计数器
上图为状态示意图
如图为FSM规范格式(specificaion format)
- 使用跟踪分析仪(trace analyzer)
fsm_generator.trace()
如图,D0表示引脚AR0,D1表示引脚AR1
D0:0工作,1复位
D1:0向下计数,1向上级数
- help函数
help(fsm_generator.setup)
注意,内部不要加()否则会报错缺少参数
Interrupt
用self.read()来读值,套在while里面判断
用self.interrupt.wait()来等待
用self.mmio.write(IP_ISR,0x1)来清除结束中断
def wait_for_level(self, value):
while self.read() != value:
self._interrupt.wait()#用while中的wait()来等待某个值
#此处加中断的执行任务
# Clear interrupt,向IP_ISR中写1(通过MMIO)
self._mmio.write(IP_ISR, 0x1)
MMIO-Memory Mapped IO
如上图,表示了PS和PL的交互关系,启下——
AXI_GPx和AXI_HPx/PS PL接口
- GP是general purpose,32bit,(不是GPIO),HP是high performance(高速),64bit,带有DMA IP
- Zynq器件的PS与PL之间有9个AXI总线接口,如上图所示
- PS为M:运行在PYNQ上的Python代码可以访问IP。MMIO被用于对该类接口进行操作。
- PL为M:允许IP直接访问PS的DRAM。在这样做之前,必须对IP使用的内存进行分配(allocate)。Xlnx类被用于对该类接口进行操作。
- DMA:DMA可被用于Zynq PS DRAM与PL之间高性能的数据传输。PYNQ的DMA类提供了对此类接口进行操作。
- PS GPIO: 总共由64个从PS到PL侧的GPIO。可被用于简单的通讯,比如这些GPIO可被用于复位(Resets)或者中断(Interrupts)。
- GPIO并不需要映射到系统内存空间来访问。
- ACP Port: Accelerator Coherency Port,PL直接访问CPU的内部缓存,而不经过DRAM,速度更快
MMIO
- PS为M,IP都会被映射到系统内存空间。MMIO可被用于读写内存映射的地址
- 一次MMIO操作会完成从内存空间读取或者存储32位数据
- MMIO不支持突发方式进行数据的传输,适用于小批量数据交互。
Xlink
- PL为M,分配一段PS中的内存供PL访问,提供一个物理指针
中断
- 中断由Interrupt类进行管理,基于PYNQ标准库中的asyncio。
——承上:python PS的GPx端口是M,FPGA overlay是S
python通过MMIO来访问挂载在PS上的外设注册和地址
DMA
- 可通过连接到Zynq AXI HP口的DMA使用
- 允许从DRAM读取数据后发送到AXI stream,或者从AXI stream获取数据后写到DRAM。
IP_BASE_ADDRESS = 0x40000000
ADDRESS_RANGE = 0x1000
ADDRESS_OFFSET = 0x10
from pynq import MMIO
mmio = MMIO(IP_BASE_ADDRESS, ADDRESS_RANGE)#定义mmio地址段,从BASE到BASE+RANGE
data = 0xdeadbeef
mmio.write(ADDRESS_OFFSET, data)#用OFFSET来写入数据到指定位置
result = mmio.read(ADDRESS_OFFSET)#同样用OFFSET来读取
问题:
- 如何知道地址指代的到底是什么区域?具体有什么作用?
答:关键是要找到内存是如何映射的
GPIO类
GPIO类就是用来控制PS GPIO的。直接用ARM处理器的寄存器控制,无需使用IP核。适用于简单,低速控制
注意区别: AXI GPIO是由AxiGPIO类控制的,需使用PL部分的IP,通过AXI与PS通信
PS GPIO使用Linux核来控制GPIO。这意味着操作系统会在运行时给GPIO分配一个标号。在使用PS GPIO之前,我们必须把Linux引脚标号映射到Python GPIO实例上。
映射标号:Get_gpio_pin()
映射目的:让linux系统认识GPIO,当作系统上的一个端口进行控制
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()
Xlnk(X-link)共享内存
Xlnk类用来分配连续内存(CMA-continuous mem allocator)。用于让PL来访问PS中的数据,比如python中的数组。在创建时需要在PS中指定一块内存,并提供物理指针。实际上DMA隐式使用了XInk
from pynq import Xlnk
import numpy as np
xlnk = Xlnk()#创建对象
input_buffer = xlnk.cma_array(shape=(5,), dtype=np.uint32)#分配内存,大小/类型
可通过:input_buffer.physical_address访问分配的物理地址(如有需要)
#写入数据
for i in range(5):
input_buffer[i] = i
# Input buffer: [0 1 2 3 4]
AXI GPIO
AxiGPIO类提供了读写通用设备如LED、按钮、开关等,并接受来自外部的中断。
每一个AXIGPIO能有至多两个通道,每一个通道至多有32个引脚。
上图为axi_gpio IP的框图
- 使用:
- read()和write()读写
- setdirection()和setlength()可以用来配置IP。方向可以是“in”、“out”、“inout”。 通常方向是“inout”
- GPIO可以像一个数组一样来处理
如何设计overlay
TCL
作为overlay的一部分,Tcl文件必须和bitstream文件一起提供.Tcl文件可在overlay硬件设计完成后,通过导出(exporting)IP Integrator block diagram来产生。Tcl文件应该在下载overlay的时候和bitstream一起被提供,PYNQ PL类会自动分析Tcl文件里的内容。
- 产生TCL:
- Click File > Export > Block Design
- 或者,也可以在Tcl console里:write_bd_tcl
- 命名:
Tcl文件的名字必须和bitstream的名字匹配,比如my_overlay.bit和my_overlay.tcl。
Tcl文件的内容会在overlay实例化和下载的时候被分析。
PL侧时钟
PL侧的时钟可根据Overlay的不同需求,在运行时进行配置。该配置由PYNQ Overlay类自动管理。在下载新的overlay的过程中,会分析Tcl文件获取时钟配置。在下载bitstream之前会首先完成时钟配置。
创建overlay在vivado端要做的工作
- Vivado板卡配置文件包含创建一个新的Vivado工程所需要的配置。以下为PYNQ-Z2配置文件的下载地址。
将配置文件安装到Vivado工具后,允许在创建Vivado工程的时候选择板卡以使工具对Zynq PS侧进行配置 - 解压缩板卡配置文件并拷贝到如下目录完成板卡配置文件的安装。
\Vivado\data\boards
XDC约束文件
PYNQ-Z2的xdc约束文件可在如下地址进行下载
如何调用已有的ip
- 教程中使用block design进行模块间的连线,按理说用.v文件例化,连接也是可以的。但是怎么连十个问题,还需要进一步看资料
- 如上图所示,为了例化matrixmul,使用了以下中间模块:
- processor system reset:与100M时钟和复位相关
- AXI interconnect:与PS/PL信息传递有关
- AXI DMA:与高速信息传递有关
- AXI smartconnect
- ZYNQ7 processing system
- 连线结束后,创建wrapper
- 生成bitstream .bit 文件
- 导出block design .tcl文件
- 最终这两个文件应当放在jupyter notebook的哪里?还不甚了解
python基础
- import+空格+tab 查看已安装的库
e.g. import asyncio - help()查看库的帮助文档(module reference)
e.g. help(asyncio) - dir()函数以列表的形式提供了所有该库下可调用函数或子库,此时我们可以更容易的找到我们需要的内容。
- 假如我们对图中的Condition class感兴趣,我们只需键入help(asyncio.Condition)即可查看该模块的详细信息。
- help的功能更加广泛,我们可以对任意我们已经定义了的变量进行help来获得详细信息。
e.g.
a=3
help(a) #出现int object的reference
- Python通过名字空间与对象空间的链接(名字指向对象空间的地址)达成了对变量的管理
- 例如:int object
- 当python执行a = 3的时候,它首先在对象空间里创建一个int object,其值为3,接着在名字空间里创建变量名a,将其指向对象“3”。贴切的说,a里存储的并不是3,而是“3”这个对象的地址
- Python基础的对象主要有int(整数),float(浮点数),str(字符串),list(列表),tuple(元组),dict(字典),set(集合)不常用
- int/float: 整除:// 取余:%幂:**
- str: 字符串对象可用单引号‘string’、双引号“string”、三单引号‘‘‘string’’’或者三双引号“““string”””表示
- tuple(value1, value2, …)与list[value1, value2, …]: 两者唯一的区别在于tuple是一个不可变对象——无法添加删除新的元素、无法变更元组内不可变对象的值(这可能有点拗口,但是如果某个value是可变对象,比如列表,则仍然有办法修改里面的值)
- list操作:
- index从0开始
- 增加:a.append(num) 增加num到list末尾
- 修改:a[0]=1
- 删除:a.pop(2)删除index 2元素,后面数据前移
- 查找:a.index(num) 查找第一个num出现的index
- python调用对象方法的语法便是对象名object + . + function。
- 上述的a list也是一个object
- dict对象:
- {key1:value1,…}其采用键值对组合。其中键(key)必须为不可变对象,值(value)可为任意对象。字典本身为可变对象,即可增改删查
a = {'apple':1,2:'pear'}
print(a)
a['apple']=3 #改value
a[2]='carrot' #改value
a['int']=6 #增加key-value对
测试板子上可用的引脚
- A0-A5
- 作用1:作为0-3.3V的analog input,自带电路会降到0-1V之后输入XADC
- 作用2:digital IO,0-3.3V
- 如何选择?
1.直接输入后就送到XADC,之后从XADC的digital输出口读取电压值(ar_an0_p、ar_an0_n…)
2.在xdc中配置a[0]-a[5],即可作为正常IO用
- arduino
ar[0]-ar[13]/A - SPI-6Pins
引脚排列:
12
34
56
测试代码:
source:
module pin_test(
output wire [7:0] ja , //PmodeA
output wire [7:0] jb , //PmodeB
output wire [14:0] ar , //J4
output wire [5:0] a , //J1
output wire [26:0] ra //RAS PI
);
assign ja = 8'b11111111;
assign jb = 8'b11111111;
assign ar = 14'b11111111111111;
assign a = 6'b111111;
assign ra = 26'b11111111111111111111111111;
endmodule
xdc:
注意:
- 复制过来的代码一定要解除注释
- PmodA全部引脚和RAS中的共用,不可同时用
- 注意,官网给的RAS中少一个V7引脚,RAS共有28个IO
##PmodB
set_property -dict { PACKAGE_PIN W14 IOSTANDARD LVCMOS33 } [get_ports { jb[0] }]; #IO_L8P_T1_34 Sch=jb_p[1]
set_property -dict { PACKAGE_PIN Y14 IOSTANDARD LVCMOS33 } [get_ports { jb[1] }]; #IO_L8N_T1_34 Sch=jb_n[1]
set_property -dict { PACKAGE_PIN T11 IOSTANDARD LVCMOS33 } [get_ports { jb[2] }]; #IO_L1P_T0_34 Sch=jb_p[2]
set_property -dict { PACKAGE_PIN T10 IOSTANDARD LVCMOS33 } [get_ports { jb[3] }]; #IO_L1N_T0_34 Sch=jb_n[2]
set_property -dict { PACKAGE_PIN V16 IOSTANDARD LVCMOS33 } [get_ports { jb[4] }]; #IO_L18P_T2_34 Sch=jb_p[3]
set_property -dict { PACKAGE_PIN W16 IOSTANDARD LVCMOS33 } [get_ports { jb[5] }]; #IO_L18N_T2_34 Sch=jb_n[3]
set_property -dict { PACKAGE_PIN V12 IOSTANDARD LVCMOS33 } [get_ports { jb[6] }]; #IO_L4P_T0_34 Sch=jb_p[4]
set_property -dict { PACKAGE_PIN W13 IOSTANDARD LVCMOS33 } [get_ports { jb[7] }]; #IO_L4N_T0_34 Sch=jb_n[4]
##Arduino Digital I/O
set_property -dict { PACKAGE_PIN T14 IOSTANDARD LVCMOS33 } [get_ports { ar[0] }]; #IO_L5P_T0_34 Sch=ar[0]
set_property -dict { PACKAGE_PIN U12 IOSTANDARD LVCMOS33 } [get_ports { ar[1] }]; #IO_L2N_T0_34 Sch=ar[1]
set_property -dict { PACKAGE_PIN U13 IOSTANDARD LVCMOS33 } [get_ports { ar[2] }]; #IO_L3P_T0_DQS_PUDC_B_34 Sch=ar[2]
set_property -dict { PACKAGE_PIN V13 IOSTANDARD LVCMOS33 } [get_ports { ar[3] }]; #IO_L3N_T0_DQS_34 Sch=ar[3]
set_property -dict { PACKAGE_PIN V15 IOSTANDARD LVCMOS33 } [get_ports { ar[4] }]; #IO_L10P_T1_34 Sch=ar[4]
set_property -dict { PACKAGE_PIN T15 IOSTANDARD LVCMOS33 } [get_ports { ar[5] }]; #IO_L5N_T0_34 Sch=ar[5]
set_property -dict { PACKAGE_PIN R16 IOSTANDARD LVCMOS33 } [get_ports { ar[6] }]; #IO_L19P_T3_34 Sch=ar[6]
set_property -dict { PACKAGE_PIN U17 IOSTANDARD LVCMOS33 } [get_ports { ar[7] }]; #IO_L9N_T1_DQS_34 Sch=ar[7]
set_property -dict { PACKAGE_PIN V17 IOSTANDARD LVCMOS33 } [get_ports { ar[8] }]; #IO_L21P_T3_DQS_34 Sch=ar[8]
set_property -dict { PACKAGE_PIN V18 IOSTANDARD LVCMOS33 } [get_ports { ar[9] }]; #IO_L21N_T3_DQS_34 Sch=ar[9]
set_property -dict { PACKAGE_PIN T16 IOSTANDARD LVCMOS33 } [get_ports { ar[10] }]; #IO_L9P_T1_DQS_34 Sch=ar[10]
set_property -dict { PACKAGE_PIN R17 IOSTANDARD LVCMOS33 } [get_ports { ar[11] }]; #IO_L19N_T3_VREF_34 Sch=ar[11]
set_property -dict { PACKAGE_PIN P18 IOSTANDARD LVCMOS33 } [get_ports { ar[12] }]; #IO_L23N_T3_34 Sch=ar[12]
set_property -dict { PACKAGE_PIN N17 IOSTANDARD LVCMOS33 } [get_ports { ar[13] }]; #IO_L23P_T3_34 Sch=ar[13]
set_property -dict { PACKAGE_PIN Y13 IOSTANDARD LVCMOS33 } [get_ports { ar[14] }]; #IO_L20N_T3_13 Sch=a
##Arduino Digital I/O On Outer Analog Header
##NOTE: These pins should be used when using the analog header signals A0-A5 as digital I/O
set_property -dict { PACKAGE_PIN Y11 IOSTANDARD LVCMOS33 } [get_ports { a[0] }]; #IO_L18N_T2_13 Sch=a[0]
set_property -dict { PACKAGE_PIN Y12 IOSTANDARD LVCMOS33 } [get_ports { a[1] }]; #IO_L20P_T3_13 Sch=a[1]
set_property -dict { PACKAGE_PIN W11 IOSTANDARD LVCMOS33 } [get_ports { a[2] }]; #IO_L18P_T2_13 Sch=a[2]
set_property -dict { PACKAGE_PIN V11 IOSTANDARD LVCMOS33 } [get_ports { a[3] }]; #IO_L21P_T3_DQS_13 Sch=a[3]
set_property -dict { PACKAGE_PIN T5 IOSTANDARD LVCMOS33 } [get_ports { a[4] }]; #IO_L19P_T3_13 Sch=a[4]
set_property -dict { PACKAGE_PIN U10 IOSTANDARD LVCMOS33 } [get_ports { a[5] }]; #IO_L12N_T1_MRCC_13 Sch=a[5]
##Raspberry Digital I/O
set_property -dict { PACKAGE_PIN W18 IOSTANDARD LVCMOS33 } [get_ports { ra[0] }]; #IO_L22P_T3_34 Sch=rpio_02_r
set_property -dict { PACKAGE_PIN W19 IOSTANDARD LVCMOS33 } [get_ports { ra[1] }]; #IO_L22N_T3_34 Sch=rpio_03_r
set_property -dict { PACKAGE_PIN Y18 IOSTANDARD LVCMOS33 } [get_ports { ra[2] }]; #IO_L17P_T2_34 Sch=rpio_04_r
set_property -dict { PACKAGE_PIN Y19 IOSTANDARD LVCMOS33 } [get_ports { ra[3] }]; #IO_L17N_T2_34 Sch=rpio_05_r
set_property -dict { PACKAGE_PIN U18 IOSTANDARD LVCMOS33 } [get_ports { ra[4] }]; #IO_L22P_T3_13 Sch=rpio_06_r
set_property -dict { PACKAGE_PIN U19 IOSTANDARD LVCMOS33 } [get_ports { ra[5] }]; #IO_L12P_T1_MRCC_34 Sch=rpio_07_r
set_property -dict { PACKAGE_PIN F19 IOSTANDARD LVCMOS33 } [get_ports { ra[6] }]; #IO_L12N_T1_MRCC_34 Sch=rpio_08_r
set_property -dict { PACKAGE_PIN V10 IOSTANDARD LVCMOS33 } [get_ports { ra[7] }]; #IO_L21N_T3_DQS_13 Sch=rpio_09_r
set_property -dict { PACKAGE_PIN V8 IOSTANDARD LVCMOS33 } [get_ports { ra[8] }]; #IO_L15P_T2_DQS_13 Sch=rpio_10_r
set_property -dict { PACKAGE_PIN W10 IOSTANDARD LVCMOS33 } [get_ports { ra[9] }]; #IO_L16P_T2_13 Sch=rpio_11_r
set_property -dict { PACKAGE_PIN B20 IOSTANDARD LVCMOS33 } [get_ports { ra[10] }]; #IO_L1N_T0_AD0N_35 Sch=rpio_12_r
set_property -dict { PACKAGE_PIN W8 IOSTANDARD LVCMOS33 } [get_ports { ra[11] }]; #IO_L15N_T2_DQS_13 Sch=rpio_13_r
set_property -dict { PACKAGE_PIN V6 IOSTANDARD LVCMOS33 } [get_ports { ra[12] }]; #IO_L22P_T3_13 Sch=rpio_14_r
set_property -dict { PACKAGE_PIN Y6 IOSTANDARD LVCMOS33 } [get_ports { ra[13] }]; #IO_L13N_T2_MRCC_13 Sch=rpio_15_r
set_property -dict { PACKAGE_PIN B19 IOSTANDARD LVCMOS33 } [get_ports { ra[14] }]; #IO_L2P_T0_AD8P_35 Sch=rpio_16_r
set_property -dict { PACKAGE_PIN U7 IOSTANDARD LVCMOS33 } [get_ports { ra[15] }]; #IO_L11P_T1_SRCC_13 Sch=rpio_17_r
set_property -dict { PACKAGE_PIN C20 IOSTANDARD LVCMOS33 } [get_ports { ra[16] }]; #IO_L1P_T0_AD0P_35 Sch=rpio_18_r
set_property -dict { PACKAGE_PIN Y8 IOSTANDARD LVCMOS33 } [get_ports { ra[17] }]; #IO_L14N_T2_SRCC_13 Sch=rpio_19_r
set_property -dict { PACKAGE_PIN A20 IOSTANDARD LVCMOS33 } [get_ports { ra[18] }]; #IO_L2N_T0_AD8N_35 Sch=rpio_20_r
set_property -dict { PACKAGE_PIN Y9 IOSTANDARD LVCMOS33 } [get_ports { ra[19] }]; #IO_L14P_T2_SRCC_13 Sch=rpio_21_r
set_property -dict { PACKAGE_PIN U8 IOSTANDARD LVCMOS33 } [get_ports { ra[20] }]; #IO_L17N_T2_13 Sch=rpio_22_r
set_property -dict { PACKAGE_PIN W6 IOSTANDARD LVCMOS33 } [get_ports { ra[21] }]; #IO_IO_L22N_T3_13 Sch=rpio_23_r
set_property -dict { PACKAGE_PIN Y7 IOSTANDARD LVCMOS33 } [get_ports { ra[22] }]; #IO_L13P_T2_MRCC_13 Sch=rpio_24_r
set_property -dict { PACKAGE_PIN F20 IOSTANDARD LVCMOS33 } [get_ports { ra[23] }]; #IO_L15N_T2_DQS_AD12N_35 Sch=rpio_25_r
set_property -dict { PACKAGE_PIN W9 IOSTANDARD LVCMOS33 } [get_ports { ra[24] }]; #IO_L16N_T2_13 Sch=rpio_26_r
set_property -dict { PACKAGE_PIN Y16 IOSTANDARD LVCMOS33 } [get_ports { ra[25] }]; #IO_L7P_T1_34 Sch=rpio_sd_r
set_property -dict { PACKAGE_PIN Y17 IOSTANDARD LVCMOS33 } [get_ports { ra[26] }]; #IO_L7N_T1_34 Sch=rpio_sc_r
set_property -dict { PACKAGE_PIN V7 IOSTANDARD LVCMOS33 } [get_ports { ra[27] }]; #IO_L7N_T1_34 Sch=rpio_sc_r
测试结果
- RAS中引脚1为3.3V,不是GND
S/N
B:181
W:305