问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

ZYNQ中PS与PL交互操作总结:BRAM、DMA原理及仿真操作详解

创作时间:
作者:
@小白创作中心

ZYNQ中PS与PL交互操作总结:BRAM、DMA原理及仿真操作详解

引用
CSDN
1.
https://m.blog.csdn.net/weixin_45902229/article/details/143923460

ZYNQ是Xilinx公司推出的一款集成了ARM处理器(PS)和FPGA(PL)的SoC芯片,其PS与PL之间的数据交互方式多种多样,包括GPIO、DMA、HP、ACP等。本文将从全局角度介绍这些交互方式的特点和适用场景,并通过BRAM实验进行具体演示,帮助读者快速掌握ZYNQ中PS与PL的数据交互方法。

PL与PS交互综述

网络上关于PS PL交互的教程非常多,无论是做开发板的机构出教程还是个人博客。但是该部分内容看起来较为分散,大概一搜,有各种各样的情景:PS和PL谁做主机?需要交互的数据有什么特性?使用哪个总线?不同的场景选用的交互方式也是不尽相同。

那么在进行知识信息筛选、总结的时候,怎样才能尽快过滤冗余信息,提取自己所需的核心内容呢?本文旨在从全局认识PS与PL的数据交互方法,同时也进行一些具体的实操,既对全局有认识,又不钻牛角尖。

图1-1来自UG585,展示了ZYNQ的系统框架,清晰明了,信息密度大。

PS部分被单独框起来,和PL部分泾渭分明,从左往右看处于PS PL交界处的结构:

  • XADC:内置的ADC,和数据交互没关系。
  • GPIO:注意总线颜色是AXI 32bit总线,PS和PL之间,共4个GP接口,每个方向各有两个,能实现双向的数据传输,和数据交互有关。
  • DMA Sync:DMA同步,看到这是单向的自定义总线,从PL直指PS端的DMA 8 Channel,和数据传输有关。
  • IRQ:中断,PL端可以直指PS端的GIC,也就是PL端可以产生中断信号,和数据交互无关。
  • AES/SHA:可配置的加密有关的接口,和数据传输无关。
  • HP接口:4个AXI接口,PS只能作为从机,该部分直接连接到存储:DDR或者片上存储。
  • ACP:加速器一致性端口,PL端可以直接访问PS的缓存,提供缓存一致性访问,实现低延迟数据传输,和数据交互有关。

PS和PL的交互,无外乎就是用上面加粗的总线,网上一般总结的用DDR、BRAM、DMA、VDMA等,都是这些总线实现的具体手段。只要结合知识点、实操过程,反复的看图1-1,会有数据交互方面的收获的。

交互端口性能&特点(选择方案的凭据)

了解了总体框架后,可以对筛选出的,和数据交互有关的总线进行进一步学习,到这一步,就可以借鉴一些网络分享的琐碎知识点(当然也要结合UG585)

GPIO-AXI_GP

这里给出AXI_GP接口的一些特性,主要和AXI的参数有关,详见下图。

还有该接口的参数性能,详见下图。

该接口没有任何额外的FIFO缓冲,不像AXI_HP接口有精心设计的FIFO缓冲来提高性能和吞吐量。

这些接口仅用于一般用途,如寄存器的配置,指令等,不适用于高性能、大数据量传输。

注意!:在PL逻辑通信发生之前,必须通过LVL_SHFTR_EN使能PL电平移位器!

DMA-DMAC

The DMA controller (DMAC),直接内存访问控制器,是控制数据搬运的,其进行数据交互时,还需要和存储介质进行配合。

DMAC使用64bit-AXI主接口,以CPU_2x时钟速率运行,执行DMA数据传输到系统存储器和PL外设,有一个DMA指令集,控制DMA的传输。

这里搬运的内容比较多,详见下图9-3。

HP-AXI_HP

这四个AXI_HP接口为PL到DDR和OCM存储器提供了高带宽数据路径。

每个接口包括两个FIFO缓冲器,用于读写流量。

ACP-AXI_ACP

该接口和cache一致性有关,互联框图如下。

数据交互实验

GP通过BRAM

PS为主机,读写BRAM

先简单进行一个测试,实现PS往PL写10个数据,用Block Design简单搭建一个仿真环境。

这个结构暂时不需要写一行代码,验证起来十分方便。

自动分配了4K的地址,如下图。

到SDK中就可以开始最简单的操作了。

XBram_WriteReg函数是官方的API接口,可以往BRAM中写入数据。

BaseAddress是BRAM的基地址,这个是Block Design地址分配页面决定的,本文的实例中,为0x40000000

RegOffset是地址偏移量,也就是在基地址的肩膀上,偏移多少个寄存器,注意,AXI总线是字节寻址的,映射地址时,按照4字节寻址。

Data:往里写的数据,32bit无符号整型

掌握这一个函数,就可以进行写数据操作了,这里SDK开Debug,ILA那面也抓信号。

下图为代码抓取结果,可以看到,PS端向PL端的写操作成功,地址间隔为4,数据为0~9共10个数据。

下面使用类似的方法,把写进去的数据再一个个读出来。

使用的函数为XBram_ReadReg,在内存中看读取出的数值。

PL作为主机,读写BRAM

这里需要对BD进行修改,之前是通过真双端口RAM的A口进行读写,实际上一直是PS在通过bram_ctrl进行控制,现在要写一点逻辑,让PL也参与其中。

先给出更改后的BD框图如下,bram_0是要自己写逻辑的模块,封装成了IP核,这里关于模块封装IP的步骤,放在另一个文章中:

【FPGA开发】Vivado自定义封装IP核,绑定总线

最终的bd文件如下所示。

下面编辑 bram_0中的逻辑,想要实现的效果是,通过B端口往里写10个数,让PS通过A端口去读。

自编的小PL逻辑

module bram(
    input          i_clk            ,
    input          i_rst            ,
    input          i_trig     ,
    output [31:0]  addr_b           ,
    output         clk_b            ,
    output [31:0]  din_b            ,
    input  [31:0]  dout_b           ,
    output         en_b             ,
    output         rst_b            ,
    output [3:0]   we_b  
);
//*****REG*****//
reg [31:0]  r_addr_b  ;            
reg [31:0]  r_din_b   ;            
reg         r_en_b    ;            
reg [3:0]   r_we_b    ;
//*****ASSIGN*****//
assign addr_b = r_addr_b ;
assign din_b  = r_din_b  ;
assign en_b   = r_en_b   ;
assign we_b   = r_we_b   ;
assign rst_b = i_rst ;
assign clk_b = i_clk ;
reg [7:0] r_cnt;
localparam P_DATA_NUM = 10;
//*****ALWAYS*****//
always @(posedge i_clk , posedge i_rst) begin
    if(i_rst)
        r_cnt <= 'd0;
    else if(r_cnt == P_DATA_NUM)
        r_cnt <= r_cnt;
    else if(i_trig || r_cnt)
        r_cnt <= r_cnt + 1;
end
always @(posedge i_clk , posedge i_rst) begin
    if(i_rst)
        r_addr_b <= 'd0;
    else if (r_cnt == P_DATA_NUM)
        r_addr_b <= r_addr_b;
    else if(r_cnt >= 1)
        r_addr_b <= r_addr_b + 4;
end
always @(posedge i_clk , posedge i_rst) begin
    if(i_rst)
        r_din_b <= 'd0;
       else if(r_cnt == P_DATA_NUM)
        r_din_b <= r_din_b; 
    else if(r_cnt >= 1)
        r_din_b <= r_din_b+1;
end
always @(posedge i_clk , posedge i_rst) begin
    if(i_rst)
        r_en_b <= 'd0;
    else if(r_cnt == P_DATA_NUM)
        r_en_b <= 'd0;
    else if(i_trig)
        r_en_b <= 'd1;
end
always @(posedge i_clk , posedge i_rst) begin
    if(i_rst)
        r_we_b <= 'd0;
    else if(r_cnt == P_DATA_NUM )
        r_we_b <= 'd0;
    else if(i_trig)
        r_we_b <= 4'hf;
end
endmodule  

为了方便观察,加Vio和ILA

Vio触发PL端通过B口想BRAM中写入数据,PS端单步Debug,挨个读数据。

这里联调时,需要让PS的Debug先跑起来,再把PL部分跑起来,要不然不对

第一个状态:PS端已经跑起来了,PL端等待Vio触发

第二个状态:Vio已经触发PL的写数据操作,写了10个数据,地址增加间隔为4,数据0~9连续

第三个状态:在SDK中单步走,把PL的十个数据读出来

可以看到0~9成功的读出来了。

PS端核心代码:

for(i=0;i<=9;i+=1) {
    recv_data[i] = XBram_ReadReg(0x40000000, 4*i);
}

DMA方式交互

BRAM的方式适合数据量较少,地址不连续、长度规则的传输,而高速率、数据量大、数据连续的情景适合DMA传输。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号