教程: PYNQ的AXI DMA的硬件设计和使用PYNQ的DMA

原文链接:

DMA读写数据

DMA读数据即PS的数据经过DMA流向自己的IP

DMA写数据即自己的IP的数据经过DMA流向PS

DMA使用了两种AXI接口

  • AXI Master
    • M_AXI_MM2S 读通道 连自己的IP
    • M_AXI_S2MM 写通道 连PS
  • AXI Stream
    • M_AXIS_MM2S 读通道 连PS
    • S_AXIS_S2MM 写通道 连自己的IP

数据流向

DMA读数据

DMA读数据时的数据流向

  • PS —> M_AXI_MM2S —> M_AXIS_MM2S —> 自己的IP

如果IP没有准备好从M_AXIS端口接收数据,那么这个端口将停滞。你也可以使用AXI流FIFO。

DMA写数据

DMA写数据时的数据流向

  • 自己的IP —> S_AXIS_S2MM —> M_AXI_S2MM —> PS

如果IP试图写回数据,但DMA写还没有开始,那么S_AXIS通道将使IP停滞。同样,如果需要,可以使用FIFO。

其他

DMA有一些内置的缓冲,所以如果你试图调试你的设计,你可能会看到一些(或全部)数据从内存中读取,但它不一定被发送到你的IP,可能在内部或HP端口FIFO中排队。

PYNQ只支持从连续的内存缓冲区的DMA。PYNQ不支持DMA的散点收集功能,即:数据可以从零散的或不连续的内存位置传输。

可以在DMA上启用Scatter-Gather,以允许多达8,388,608字节的多次传输(来自连续的内存缓冲区)。如果你这样做,你需要使用M_AXI_SG端口而不是M_AXI端口。这在本教程中没有涉及。

SG的一个替代方案是在软件中把内存传输分割成67,108,863或更小的块,并运行多个DMA传输。

AXI DMA IP设置

Width of Buffer Length Register

这个值决定了单个DMA传输的最大数据包大小。width=26允许传输67,108,863(226-1)字节——DMA支持的最大尺寸。

可以将其设置为较小的值并节省少量的PL资源。

当使用DMA时,如果你试图进行传输,但只看到缓冲区的第一部分被传输,请在你的硬件设计中检查Width of Buffer Length Register的值,并检查你正在传输多少数据。将默认值设置为14位是一个常见的错误,它将限制DMA的传输量为16,384(214)字节。如果你试图发送超过这个数量的数据,一旦支持的最大字节数传输完毕,传输就会终止。记住要检查传输的字节数大小。

Address width

检查Address width是否设置为32。在这个例子中,我将把DMA连接到Zynq的32位PS存储器上。如果你将其连接到更大的存储器上,例如你使用Zynq Ultrascale+或者你的DMA连接到PL连接的存储器上,你可以将其设置为64位。

DMA read and write channels

这个例子将同时使用DMA的读和写通道,但你可能只需要启用其中一个通道。你的设计中也可以有多个DMA。

  • 在这个设计中,让读和写两个通道都处于启用状态

  • 将内存映射的数据宽度设置为64,与HP端口相匹配(在PYNQ图像中定义并在启动时应用)。

  • 你可以增加burst width以提高数据传输的效率。通常,当你增加最大突发的大小时,硬件资源的利用率会增加,但这不应该对总体利用率产生明显的影响。

  • 确保Allow unaligned transfers没有被启用。PYNQ不支持这一点。

DMA AXI主端口需要连接到PS DRAM。这将通过Zynq HP(AXI Slave)端口完成。这些端口在默认情况下是不启用的。在内部,有两个连接到PS存储器,四个HP端口被连接到。HP0和HP1共用一个开关到一个端口,HP2和HP3共用一个开关到另一个。对于这个例子和一些设计来说,这种差别可能并不明显,但是当只需要两个HP端口时,将它们连接到不共享一个开关的HP端口上会更有效率,即HP0和HP2或HP1和HP3一起。

硬件连接

在这里插入图片描述

软件代码

from pynq import Overlay

# .bit和.hwh文件需要命名为相同的名字
# .bit文件所在路径: {project_name}/{project_name}.runs/impl_1/{top verilog file}.bit
# .hwh文件所在路径: {project_name}/{project_name}.srcs/sources_1/bd/{bd name}/hw_handoff/{bd name}.hwh
ol = Overlay("../../hardware/HW_DMA_Tutorial/design_1_wrapper.bit")
print(ol.ip_dict)	# 找到dma的对象名
dma = ol.dma
dma_send = ol.dma.sendchannel
dma_recv = ol.dma.recvchannel

DMA读数据:数据从PS流向FIFO

from pynq import allocate
import numpy as np

# input_buffer初始化
data_size = 1000
input_buffer = allocate(shape=(data_size,), dtype=np.uint32)
for i in range(data_size):
    input_buffer[i] = i + 0xcafe0000
    print(hex(input_buffer[i]))

# DMA读
dma_send.transfer(input_buffer)

DMA写数据:数据从FIFO流向PS

# output_buffer初始化
output_buffer = allocate(shape=(data_size,), dtype=np.uint32)
# 查看output_buffer的初值
for i in range(10):
    print('0x' + format(output_buffer[i], '02x'))

# DMA写
dma_recv.transfer(output_buffer)

dma_recv.idle可以取True和False

  • True: 可以执行dma_recv.transfer
  • False: 不能执行dma_recv.transfer,若执行会报错
print(dma_recv.error)
print(dma_recv.idle)
print(dma_recv.running)

其他

print(dma.register_map)
print("Input buffer address   :", hex(input_buffer.physical_address))
print("Output buffer address  :", hex(output_buffer.physical_address))
print("---")
print("DMA Source address     :", hex(dma.register_map.MM2S_SA.Source_Address))
print("DMA Destination address:", hex(dma.register_map.S2MM_DA.Destination_Address))

# 释放内存缓冲区,以避免内存泄漏
del input_buffer, output_buffer

猜你喜欢

转载自blog.csdn.net/inv1796915552/article/details/129721355