PYNQ FPGA 开发

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:
  1. Click File > Export > Block Design
  2. 或者,也可以在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端要做的工作

  1. Vivado板卡配置文件包含创建一个新的Vivado工程所需要的配置。以下为PYNQ-Z2配置文件的下载地址
    将配置文件安装到Vivado工具后,允许在创建Vivado工程的时候选择板卡以使工具对Zynq PS侧进行配置
  2. 解压缩板卡配置文件并拷贝到如下目录完成板卡配置文件的安装。
    \Vivado\data\boards

XDC约束文件

PYNQ-Z2的xdc约束文件可在如下地址进行下载

如何调用已有的ip

  • 教程中使用block design进行模块间的连线,按理说用.v文件例化,连接也是可以的。但是怎么连十个问题,还需要进一步看资料
    在这里插入图片描述
  • 如上图所示,为了例化matrixmul,使用了以下中间模块:
  1. processor system reset:与100M时钟和复位相关
  2. AXI interconnect:与PS/PL信息传递有关
  3. AXI DMA:与高速信息传递有关
  4. AXI smartconnect
  5. ZYNQ7 processing system
  • 连线结束后,创建wrapper
  • 生成bitstream .bit 文件
  • 导出block design .tcl文件
  • 最终这两个文件应当放在jupyter notebook的哪里?还不甚了解

python基础

  1. import+空格+tab 查看已安装的库
    e.g. import asyncio
  2. help()查看库的帮助文档(module reference)
    e.g. help(asyncio)
  3. dir()函数以列表的形式提供了所有该库下可调用函数或子库,此时我们可以更容易的找到我们需要的内容。
  • 假如我们对图中的Condition class感兴趣,我们只需键入help(asyncio.Condition)即可查看该模块的详细信息。
  1. help的功能更加广泛,我们可以对任意我们已经定义了的变量进行help来获得详细信息。
    e.g.
a=3
help(a) #出现int object的reference
  1. 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是可变对象,比如列表,则仍然有办法修改里面的值)
  1. list操作:
  • index从0开始
  • 增加:a.append(num) 增加num到list末尾
  • 修改:a[0]=1
  • 删除:a.pop(2)删除index 2元素,后面数据前移
  • 查找:a.index(num) 查找第一个num出现的index
  1. python调用对象方法的语法便是对象名object + . + function。
  • 上述的a list也是一个object
  1. 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对

测试板子上可用的引脚

  1. 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用
  1. arduino
    ar[0]-ar[13]/A
  2. 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


测试结果

  1. RAS中引脚1为3.3V,不是GND

S/N

B:181
W:305

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值