zynq PS侧DMA驱动

linux中,驱动必然会有驱动对应的设备类型。在linux4.4版本中,其设备是以设备树的形式展现的。

PS端设备树的devicetree表示如下

 

324         dmac_s: dmac@f8003000 {  
325             compatible = "arm,pl330", "arm,primecell";  
326             reg = <0xf8003000 0x1000>;  
327             interrupt-parent = <&intc>;  
328             interrupt-names = "abort", "dma0", "dma1", "dma2", "dma3",  
329                 "dma4", "dma5", "dma6", "dma7";  
330             interrupts = <0 13 4>,  
331                          <0 14 4>, <0 15 4>,  
332                          <0 16 4>, <0 17 4>,  
333                          <0 40 4>, <0 41 4>,  
334                          <0 42 4>, <0 43 4>;  
335             #dma-cells = <1>;  
336             #dma-channels = <8>;  
337             #dma-requests = <4>;  
338             clocks = <&clkc 27>;  
339             clock-names = "apb_pclk";  
340         };  


< drivers/of/platform.c>这个文件根据设备树信息创建设备信息,在驱动程序注册时就可以找到该设备信息,执行probe函数。

 

zynq下dma的设备channel如下:

 

 ​root@linaro-ubuntu-desktop:/sys/class/dma# ls
dma0chan0  dma0chan2  dma0chan4  dma0chan6  dma1chan0
dma0chan1  dma0chan3  dma0chan5  dma0chan7
root@linaro-ubuntu-desktop:/sys/class/dma# ll
total 0
drwxr-xr-x  2 root root 0 1970-01-01 00:00 ./
drwxr-xr-x 50 root root 0 1970-01-01 00:00 ../
lrwxrwxrwx  1 root root 0 1970-01-01 00:00 dma0chan0 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan0/
lrwxrwxrwx  1 root root 0 1970-01-01 00:00 dma0chan1 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan1/
lrwxrwxrwx  1 root root 0 1970-01-01 00:00 dma0chan2 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan2/
lrwxrwxrwx  1 root root 0 1970-01-01 00:00 dma0chan3 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan3/
lrwxrwxrwx  1 root root 0 1970-01-01 00:00 dma0chan4 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan4/
lrwxrwxrwx  1 root root 0 1970-01-01 00:00 dma0chan5 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan5/
lrwxrwxrwx  1 root root 0 1970-01-01 00:00 dma0chan6 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan6/
lrwxrwxrwx  1 root root 0 1970-01-01 00:00 dma0chan7 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan7/
lrwxrwxrwx  1 root root 0 1970-01-01 00:00 dma1chan0 -> ../../devices/soc0/fpga-axi@0/43000000.axivdma/dma/dma1chan0/


dma1chan0是xilinx AXI-VDMA IP生成的DMA控制器,其处于PL端,而dma0相关的控制器是ps端的pl330.本篇看pl330这个驱动程序的注册。

 

查看物理地址分布

 

 ​root@linaro-ubuntu-desktop:~# cat /proc/iomem 
00000000-1fffffff : System RAM
  00008000-006651a3 : Kernel code
  006a2000-00700867 : Kernel data
41600000-4160ffff : /fpga-axi@0/i2c@41600000
43000000-43000fff : /fpga-axi@0/axivdma@43000000
70e00000-70e0ffff : /fpga-axi@0/axi_hdmi@70e00000
75c00000-75c00fff : /fpga-axi@0/axi-spdif-tx@0x75c00000
77600000-77600fff : /fpga-axi@0/axi-i2s@0x77600000
79000000-7900ffff : /fpga-axi@0/axi-clkgen@79000000
e0001000-e0001fff : xuartps
e0002000-e0002fff : /amba/usb@e0002000
  e0002000-e0002fff : /amba/usb@e0002000
e000a000-e000afff : /amba/gpio@e000a000
e000b000-e000bfff : /amba/eth@e000b000
e000d000-e000dfff : /amba/spi@e000d000
e0100000-e0100fff : mmc0
f8003000-f8003fff : /amba/dmac@f8003000
  f8003000-f8003fff : /amba/dmac@f8003000
f8005000-f8005fff : /amba/watchdog@f8005000
f8007000-f80070ff : /amba/devcfg@f8007000
f8007100-f800711f : /amba/adc@f8007100
f800c000-f800cfff : /amba/ocmc@f800c000
fffc0000-ffffffff : f800c000.ocmc
root@linaro-ubuntu-desktop:~# 

 

 

ARM采用统一编址,其访问内存和外设的指令是一样没有差异的,linux内核并不通过物理地址直接访问外设,而是通过虚拟地址,虚拟地址经过MMU转换成物理访问外设。所以外设需要使用ioremap()对将物理地址空间转换成虚拟地址。

I/O设备使用第三种地址,总线地址。如果一个设备在MMIO(memory mapped IO内存映射地址空间)有寄存器,或者其对系统存储系统执行DMA读写操作,设备使用的就是总线地址。

在系统枚举阶段,内核知道I/O设备以及他们的MMIO空间。如果一个设备支持DMA方式,驱动通过kmalloc()申请一段内存空间,返回申请空间的首地址,设为X,虚拟地址系统将虚拟地址X映射到物理地址Y。驱动程序可以使用虚拟地址X访问设备地址空间,但是设备本身却不行,这是因为DMA本身并不是通过虚拟地址方式来工作的。

在zynq7000设备里,DMA可以直接操作物理地址,但另一些处理器使用IOMMU将DMA地址转换物理地址,

 

linux建议使用DMA API而不是特定总线的DMA API,比如使用dma_map_*()接口而不是pci_map_*()接口,在zynq7000中,pl330 DMA已经实现了直接调用通用的DMA框架API即可以。

DMA开发相关API

 

首先得包含

 ​#include <linux/dma-mapping.h>

其提供了dma_addr_t定义,其可以作为设备使用DMA的源地址或者目的地址,并且可以使用DMA_XXX相关的API。

1.使用大DMA 一致性buffer

 ​void * dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag)

分配<size>大小的一致性区域。其返回值<dma_handle>能被强制类型转换成unsigned integer,这样就是总线的宽度

 ​void * dma_zalloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag)

是对dma_alloc_coherent的封装,并且将返回的地址空间内存清零。

 ​void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_handle)

释放一致性内存。

2.使用小DMA一致性buffer

需要包括如下代码

 ​#include <linux/dmapool.h>

其工作有点类似kmem_cache(),不过其使用的是DMA一致性分配器,而非__get_free_pages(),并且需要N-byte对齐。

 ​	struct dma_pool *
	dma_pool_create(const char *name, struct device *dev, size_t size, size_t align, size_t alloc);

“name”字段用于诊断,dev和size和传递给dma_alloc_coherent()类似,align以字节计,且需要是2的指数。如果设备没有跨界限制,传递0;传递4096则意味着分配的DMA空间不能跨越4KByte空间。

 ​void *dma_pool_zalloc(struct dma_pool *pool, gfp_t mem_flags, dma_addr_t *handle)
 ​	void *dma_pool_alloc(struct dma_pool *pool, gfp_t gfp_flags, dma_addr_t *dma_handle);

从pool分配内存,返回内存将会满足size和alignment要求。传递GFP_ATOMIC防止阻塞,或者如果允许阻塞,则可以传递GFP_KERNEL,和dma_alloc_coherent()类似,返回两个值,一个是CPU使用的地址,以及一个设备使用的DMA地址。

 ​void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t addr); 

释放由dma_pool_alloc()分配的内存空间到pool。

 ​void dma_pool_destroy(struct dma_pool *pool);

dma_pool_destroy()将内存池的资源释放。

part1c DMA寻址限制

 ​int dma_set_mask_and_coherent(struct device *dev, u64 mask)

检查mask是否合法,如果是跟新设备streaming以及DMA mask参数。返回0是正确。

 ​int dma_set_mask(struct device *dev, u64 mask)
int dma_set_coherent_mask(struct device *dev, u64 mask

检查mask是否合法,如果是就跟新。

 ​u64 dma_get_required_mask(struct device *dev)

检查系统合法的DMA掩码。

part 1D Streaming(流式) DMA 映射

 ​dma_addr_t dma_map_single(struct device *dev, void *cpu_addr, size_t size, enum dma_data_direction direction)

将处理器的虚拟地址空间进行映射,这样让处理器可以访问外设,返回值是DMA地址。

其direction参数可选字段即意义如下:

 ​DMA_NONE		no direction (used for debugging) 
DMA_TO_DEVICE		data is going from the memory to the device 
DMA_FROM_DEVICE		data is coming from the device to the memory 
DMA_BIDIRECTIONAL	direction isn't known

并不是所有的存储空间都能够使用上面的函数进行映射。内核虚拟地址也许是连续的但是其映射的物理地址却可以不一样。由于这个API不提供scatter/gather功能,如果尝试映射非连续物理存储空间。所以由该API进行映射的地址需要确保物理地址连续,如kmalloc。

设备DMA的地址内存范围必须在dma_mask,需要确保由kmalloc分配的内存空间在dma_mask所能达到的范围之内。驱动程序也许会传递各种flags以限制DMA地址范围(比如X86能够表示的地址范围在前16MB内)。

对具有IOMMU的平台,物理地址连续性和dma_mask要求也许不再适用。然而,考虑到可移植性,通常会忽略IOMMU的存在。

 

内存操作粒度被成为cache line 宽度,为了让这里的API映射的DMA操作成功执行,映射的区域必须在cache line边界开始和结束(目的是不发生对一个cache line出现两个独立的映射区域)。

 

DMA_TO_DEVICE,在软件修改内存块后程序退出控制前,需要进行同步。一个这一原语被使用,由这一原语包括的这一区域被成为只读,如果同时设备想写,需要使用DMA_BIDIRECTIONAL标志。

DMA_FROM_DEVICE 在驱动程序修改数据前先同步。内存块需要被当成只读对待。如果驱动要写功能,则需要设置DMA_BIDIRECTIONAL。

DMA_BIDIRECTIONAL需要特殊对待。这意味这驱动程序不能确定内存的改变在是否发生在将存储空间交给设备前,同时不能确定设备是否需要改变这块内存。所以,需要双向同步内存块。一次是在内存控制权移交给设置前,一次是在获取数据前。

 ​void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, enum dma_data_direction direction) 

Unmap先前映射的区域,所有传递进来的参数必须和建立映射接口时的参数一致。

 ​dma_addr_t dma_map_page(struct device *dev, struct page *page, unsigned long offset, size_t size,
		    enum dma_data_direction direction)
void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, enum dma_data_direction direction)

对页进行映射和逆映射。轻易不要动<size>和<offset>这两个参数。

 ​int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)

在一些场景下,dma_map_single() and dma_map_page()在创建一个映射时也许会失败。调用上面的接口可以检测出错的原因。

 ​	int
	dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction)

返回segment 映射的DMA地址。

当使用scatterlist方式时,建立映射的过程如下:

 ​	int i, count = dma_map_sg(dev, sglist, nents, direction);
	struct scatterlist *sg;
	for_each_sg(sglist, sg, count, i) { hw_address[i] = sg_dma_address(sg);
		hw_len[i] = sg_dma_len(sg); }

zynq PS端pl330驱动注册

 

3002 static struct amba_driver pl330_driver = {
3003     .drv = {
3004         .owner = THIS_MODULE,
3005         .name = "dma-pl330",
3006         .pm = &pl330_pm,
3007     },
3008     .id_table = pl330_ids,
3009     .probe = pl330_probe,
3010     .remove = pl330_remove,
3011 };
3012 
3013 module_amba_driver(pl330_driver);

见到此则知道必然是调用probe函数。注册函数调用如下:

 

 

2775 static int
2776 pl330_probe(struct amba_device *adev, const struct amba_id *id)
2777 {
2778     struct dma_pl330_platdata *pdat;
2779     struct pl330_config *pcfg;
2780     struct pl330_dmac *pl330;
2781     struct dma_pl330_chan *pch, *_p;
2782     struct dma_device *pd;
2783     struct resource *res;
2784     int i, ret, irq;
2785     int num_chan;
2786 
2787     pdat = dev_get_platdata(&adev->dev);

上面定义了三个重要的数据结构体,它们的关系如下图:

 

 

2806     pl330->base = devm_ioremap_resource(&adev->dev, res);
2807     if (IS_ERR(pl330->base))
2808         return PTR_ERR(pl330->base);

对地址空间进行映射,并且存在了struct pl330_dmac结构体里。

 

 

2812     for (i = 0; i < AMBA_NR_IRQS; i++) {
2813         irq = adev->irq[i];
2814         if (irq) {
2815             ret = devm_request_irq(&adev->dev, irq,
2816                            pl330_irq_handler, 0,
2817                            dev_name(&adev->dev), pl330);
2818             if (ret)
2819                 return ret;
2820         } else {
2821             break;
2822         }
2823     }

每一个channel对应于一个中断,但是它们的中断服务函数是同一个。

2825     pcfg = &pl330->pcfg;
2826
2827     pcfg->periph_id = adev->periphid;
2828     ret = pl330_add(pl330);

设置pl330的configure字段。

 

 

2832     INIT_LIST_HEAD(&pl330->desc_pool);
2833     spin_lock_init(&pl330->pool_lock);
2834 
2835     /* Create a descriptor pool of default size */
2836     if (!add_desc(pl330, GFP_KERNEL, NR_DEFAULT_DESC))
2837         dev_warn(&adev->dev, "unable to allocate desc\n");

设置pl330的描述符池,并且初始化16个描述符。

2842     if (pdat)
2843         num_chan = max_t(int, pdat->nr_valid_peri, pcfg->num_chan);
2844     else
2845         num_chan = max_t(int, pcfg->num_peri, pcfg->num_chan);
2846 
2847     pl330->num_peripherals = num_chan;
2848 
2849     pl330->peripherals = kzalloc(num_chan * sizeof(*pch), GFP_KERNEL);
2850     if (!pl330->peripherals) {

初始化channel数,并且为dma channel分配内存空间。

2856     for (i = 0; i < num_chan; i++) {
2857         pch = &pl330->peripherals[i];
2858         if (!adev->dev.of_node)
2859             pch->chan.private = pdat ? &pdat->peri_id[i] : NULL;
2860         else
2861             pch->chan.private = adev->dev.of_node;
2862 
2863         INIT_LIST_HEAD(&pch->submitted_list);
2864         INIT_LIST_HEAD(&pch->work_list);
2865         INIT_LIST_HEAD(&pch->completed_list);
2866         spin_lock_init(&pch->lock);
2867         pch->thread = NULL;
2868         pch->chan.device = pd;
2869         pch->dmac = pl330;
2870 
2871         /* Add the channel to the DMAC list */
2872         list_add_tail(&pch->chan.device_node, &pd->channels);
2873     }

初始化dma_pl330_chan相关字段。

 

 

2886     pd->device_alloc_chan_resources = pl330_alloc_chan_resources;
2887     pd->device_free_chan_resources = pl330_free_chan_resources;
2888     pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy;
2889     pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic;
2890     pd->device_tx_status = pl330_tx_status;
2891     pd->device_prep_slave_sg = pl330_prep_slave_sg;
2892     pd->device_config = pl330_config;
2893     pd->device_pause = pl330_pause;
2894     pd->device_terminate_all = pl330_terminate_all;
2895     pd->device_issue_pending = pl330_issue_pending;
2896     pd->src_addr_widths = PL330_DMA_BUSWIDTHS;
2897     pd->dst_addr_widths = PL330_DMA_BUSWIDTHS;
2898     pd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
2899     pd->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;

初始化上图中struct dma_device的若干成员和函数。

 

 

2901     ret = dma_async_device_register(pd);
2902     if (ret) {
2903         dev_err(&adev->dev, "unable to register DMAC\n");
2904         goto probe_err3;
2905     }

注册dma设备并创建sys/class接口,会有如下显示:

 

 

 ​root@linaro-ubuntu-desktop:/sys/class/dma# ls
dma0chan0  dma0chan2  dma0chan4  dma0chan6  dma1chan0
dma0chan1  dma0chan3  dma0chan5  dma0chan7

将DMA控制器注册到DT DMA helpers。通过of_dma_list就可以找到该DMA控制器。

 

 

2907     if (adev->dev.of_node) {
2908         ret = of_dma_controller_register(adev->dev.of_node,
2909                      of_dma_pl330_xlate, pl330);

至此,DMA注册函数基本流程完毕。但是还留下了一个irq函数。

 

DMA中断服务函数

 

2720 static irqreturn_t pl330_irq_handler(int irq, void *data)
2721 {
2722     if (pl330_update(data))
2723         return IRQ_HANDLED;
2724     else
2725         return IRQ_NONE;
2726 }

该函数实际上调用了pl330_update去完成中断服务函数的请求。由于其是在中断函数中调用的,则中断函数的那些注意事项还是要遵从的。

 

 

1533 static int pl330_update(struct pl330_dmac *pl330)
1534 {
1535     struct dma_pl330_desc *descdone, *tmp;
1536     unsigned long flags;
1537     void __iomem *regs;
1538     u32 val;
1539     int id, ev, ret = 0;
1540 
1541     regs = pl330->base;

该函数的base地址是经过ioremap得到的。

1545     val = readl(regs + FSM) & 0x1;
1546     if (val)
1547         pl330->dmac_tbd.reset_mngr = true;
1548     else
1549         pl330->dmac_tbd.reset_mngr = false;

首先都FSM寄存器,然后看bit0是否需要复位mngr。

 

 

1551     val = readl(regs + FSC) & ((1 << pl330->pcfg.num_chan) - 1);
1552     pl330->dmac_tbd.reset_chan |= val;
1553     if (val) {
1554         int i = 0;
1555         while (i < pl330->pcfg.num_chan) {
1556             if (val & (1 << i)) {
1557                 dev_info(pl330->ddma.dev,
1558                     "Reset Channel-%d\t CS-%x FTC-%x\n",
1559                         i, readl(regs + CS(i)),
1560                         readl(regs + FTC(i)));
1561                 _stop(&pl330->channels[i]);
1562             }
1563             i++;
1564         }
1565     }

各个通复位。

 

 

1568     val = readl(regs + ES);
1569     if (pl330->pcfg.num_events < 32
1570             && val & ~((1 << pl330->pcfg.num_events) - 1)) {
1571         pl330->dmac_tbd.reset_dmac = true;
1572         dev_err(pl330->ddma.dev, "%s:%d Unexpected!\n", __func__,
1573             __LINE__);
1574         ret = 1;
1575         goto updt_exit;
1576     }

读取事件寄存器,并判断是否出错了。出错则置reset标志。

 

 

1578     for (ev = 0; ev < pl330->pcfg.num_events; ev++) {
1579         if (val & (1 << ev)) { /* Event occurred */
1580             struct pl330_thread *thrd;
1581             u32 inten = readl(regs + INTEN);
1582             int active;
1583 
1584             /* Clear the event */
1585             if (inten & (1 << ev))
1586                 writel(1 << ev, regs + INTCLR);
1587 
1588             ret = 1;
1589 
1590             id = pl330->events[ev];
1591 
1592             thrd = &pl330->channels[id];
1593 
1594             active = thrd->req_running;
1595             if (active == -1) /* Aborted */
1596                 continue;
1597 
1598             /* Detach the req */
1599             descdone = thrd->req[active].desc;
1600             thrd->req[active].desc = NULL;
1601 
1602             thrd->req_running = -1;
1603 
1604             /* Get going again ASAP */
1605             _start(thrd);
1606 
1607             /* For now, just make a list of callbacks to be done */
1608             list_add_tail(&descdone->rqd, &pl330->req_done);
1609         }
1610     }

处理相应事件。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Zynq PL-PS DMA是指Xilinx开发的Zynq系列可编程逻辑和处理系统之间的数据传输机制。Zynq是一种集成了ARM处理系统和可编程逻辑(PL)的SoC(系统级片上系统)芯片。 在Zynq芯片中,处理系统(PS)是由ARM Cortex-A9处理器组成的,用于运行操作系统和处理高级任务,而可编程逻辑(PL)是由可配置的逻辑单元组成的,用于实现硬件加速和高性能计算。而PL-PS DMA是用于在两者之间进行高速数据传输的重要组件。 DMA(Direct Memory Access)是一种数据传输技术,允许数据在内存和外设之间直接进行传输,而不需要CPU的干预。在Zynq PL-PS DMA中,DMA控制器位于PS中,用于管理数据在PL和PS之间的传输。 Zynq PL-PS DMA具有以下特点:高性能、低延迟、高带宽。通过使用DMA,PL可以直接从内存中读取数据,不需要CPU的介入,从而提高了数据传输效率和系统性能。 在使用Zynq PL-PS DMA时,首先需要在PL和PS之间建立合适的数据通道。然后,通过编程配置DMA控制器的寄存器,设置数据传输的起始地址、传输长度、传输方向等参数。最后,启动DMA传输过程,并通过中断或轮询方式检查传输状态,以确保数据正确传输。 总之,Zynq PL-PS DMA是一种用于实现Zynq系列芯片中可编程逻辑和处理系统之间高速数据传输的技术,通过提供高性能、低延迟和高带宽的数据传输通道,提高了系统的数据传输效率和整体性能。 ### 回答2: Zynq PL PS DMA是指Zynq系列中的可编程逻辑(PL),处理系统(PS)和直接内存访问(DMA)的一种配置。 Zynq系列芯片是赛灵思公司针对嵌入式系统设计开发的产品。它将传统的处理器系统和可编程逻辑(FPGA)集成到一个芯片中,为嵌入式系统设计带来更高的灵活性和性能。 PL是可编程的逻辑部分,其中包含了可编程逻辑电路,如FPGA。PL可以用于实现各种不同的功能,例如数字信号处理、图像处理、乘法器等。PL可以被重新配置和优化,以满足不同应用的需求。 PS是指处理系统,它是Zynq芯片中的ARM Cortex-A9核心。PS负责运行嵌入式操作系统(如Linux),管理系统的整体运行和资源分配。PS还可以执行一些实时任务,如控制外部设备等。 DMA是指直接内存访问。DMA可以在处理器不参与的情况下,通过直接从外部设备读取或写入数据到内存中,实现高速的数据传输。DMA可以显著提高数据传输的效率,减少了处理器对数据传输的负担。 Zynq PL PS DMA的配置可以充分发挥Zynq芯片的优势。PL的灵活性可以满足各种不同的应用需求,而PS的处理能力可以支持操作系统的运行和系统管理。DMA的使用可以加快数据传输速度,提高系统的响应速度。 总之,Zynq PL PS DMA的配置充分利用了可编程逻辑、处理系统和直接内存访问的优势,为嵌入式系统设计提供了更高的灵活性、性能和效率。 ### 回答3: Zynq是赛灵思公司(Xilinx)推出的一种嵌入式平台,它将硬件逻辑(PL)与处理系统(PS)集成在一个芯片上。PL是FPGA实现的可编程逻辑部分,而PS则是ARM处理器系统。 DMA(Direct Memory Access)是一种数据传输方式,允许外部设备直接访问系统内存,无需CPU的干预。在Zynq平台中,PL和PS都可以使用DMA进行数据传输。 在PL中使用DMA,我们可以通过定制的硬件逻辑实现高速、低延迟的数据传输。DMA可以从外部设备接收数据,将其存储在PL的内存中,并通过总线接口将数据传输到PS的内存中。这种方式可以减少数据传输的CPU开销,提高系统的性能。 在PS中使用DMA,可以实现高效的数据传输与处理。例如,通过使用DMA,可以将大数据块从外部设备传输到PS的内存中,然后再由CPU进行处理。这样可以提高数据处理的效率,减少CPU的负担。 总之,Zynq平台中的PL和PS均可使用DMA进行数据传输,通过利用DMA可以提高数据传输的效率,降低系统的延迟,从而提升整体性能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值