ZYNQ MP AXI datamover IP使用流程說明


參考文檔
https://zhuanlan.zhihu.com/p/82509188
pg022_axi_datamover.pdf
 
前言
很久沒更新FPGA相關的東西,忙TI平台的東西去了。
本來打算使用vivado2020進行demo開發,但發現vitis編譯個helloworld工程都慢得要命(估計里面又加了許多沒啥用處的東西),還是vivado2018保平安。

 

ZYNQ PL跟PS之間的數據交互主要通過AXI內部總線進行交互。
分從機主機、高速低速等。根據不同的需要使用不同的交互接口。
這里主要講述AXI datamover IP的使用。
所以首先的問題就在於為啥使用這個IP。
IP功能:開發者通過操作AXI-stream接口操作PS端DDR。
IP交互邏輯:
                        對於寫DDR(數據由PL端產生,通過操作AXI-stream,AXI-stream協議轉換成AXI4,AXI4操作HP接口,從而寫入DDR),本文講述重點。
                        對於讀DDR(數據通過HP接口讀出到AXI4,AXI4轉協議AXI-stream,PL讀取AXI-stream的結果)
綜上述,AXI datamover IP主要用於PL端開發者不想用復雜的AXI-4協議,偷懶使用AXI-stream所使用。此IP相當於一個協議轉換的模塊。畢竟,寫個AXI-4協議比AXI-stream要復雜得多。
 
流程
這里主要以寫功能為例,讀出應該是類似的。項目中主要PL送數據給PS。
Example:單次寫入64(數據量)*32bit(總線位寬)數據,划分了4塊地址,寫完單次后寫入的基地址會遞增,到4回退。
(1)在block design中新建IP。這里只使能了寫數據通道,通道類型選擇full(還有種是basic,貌似簡單一點),位寬默認32bit,根據需要選擇不同位寬。
Maximum Burst Size:最大突發長度,指的是AXI-4協議中的單次突發的數據個數,BTT value 小於等於4*最大突傳。
Width of BTT field:指定BTT value的位寬,大於等於BTT value即可。

 

(2)這里取消了store forward。
The S2MM can include an optional Store and Forward block when the Enable Store 
Forward parameter is enabled. Enabling this parameter ensures that transfers are not 
posted to the AXI4 Write Address Channel until all of the data needed for the requested 
transfer is present in the Store and Forward FIFO. 根據需要選擇。

 

(3)連接對應信號,引出相應bus。
S_AXIS_S2MM_0:數據控制輸入口
S_AXIS_S2MM_CMD_0:命令控制輸入口
M_AXIS_S2MM_STS_0:狀態信息回傳,debug使用

 

 這里HP接口的地址如下圖所示:

 

(4)編寫PL端控制模塊。采用先發命令再發數據的模式。先后關系貌似可以交換,具體參考IP文檔。
NOTE:BTT的值跟單次tvalid的數據量要一致,BTT value = data beat*data bus width/byte = 64*32/8 = 256,否則會出錯。 
`timescale 1ns/1ps
module Control_AXI_stream (
    input           i_clk                    ,
    input           i_rst_n                  ,

    output  [31:0]  S_AXIS_S2MM_0_tdata      ,
    output  [3:0]   S_AXIS_S2MM_0_tkeep      ,
    output          S_AXIS_S2MM_0_tlast      ,
    input           S_AXIS_S2MM_0_tready     ,
    output          S_AXIS_S2MM_0_tvalid     ,

    output  [71:0]  S_AXIS_S2MM_CMD_0_tdata  ,
    input           S_AXIS_S2MM_CMD_0_tready ,
    output          S_AXIS_S2MM_CMD_0_tvalid  
);
////傳輸起始控制
wire w_tri_en;
vio_0  inst_vio_0 (
    .clk               (i_clk),       
    .probe_out0        (w_tri_en),
    .probe_out1        () 
);

reg [1:0] r_tri_en_edge = 'd0;
always @(posedge i_clk)
begin
    r_tri_en_edge <= {r_tri_en_edge[0],w_tri_en};
end
////命令注入
reg [71:0] r_S_AXIS_S2MM_CMD_0_tdata = 'd0;
reg r_S_AXIS_S2MM_CMD_0_tvalid = 'd0;
always @(posedge i_clk)
begin
    if (r_tri_en_edge == 2'b01)
        r_S_AXIS_S2MM_CMD_0_tvalid <= 1'b1;
    else if (S_AXIS_S2MM_CMD_0_tready)
        r_S_AXIS_S2MM_CMD_0_tvalid <= 1'b0;
end
reg [31:0] r_addr_axi = 'd0;
reg r_S_AXIS_S2MM_0_tlast = 1'b0;
always @(posedge i_clk)
begin
    if (r_S_AXIS_S2MM_0_tlast & S_AXIS_S2MM_0_tready)
    begin
        if (r_addr_axi == 'd192)
            r_addr_axi <= 'd0;
        else 
            r_addr_axi <= r_addr_axi + 'd64;
    end
end
wire [71:0] w_S_AXIS_S2MM_CMD_0_tdata;


wire [63:32]    w_SADDR;
wire [31:31]    w_DRR;
wire [30:30]    w_EOF;
wire [29:24]    w_DSA;
wire [23:23]    w_Type;  
wire [22:0]     w_BTT;
assign w_SADDR = r_addr_axi;
assign w_DRR   = 'd0;
assign w_EOF   = 'd1;
assign w_DSA   = 'd0;
assign w_Type  = 'd1;
assign w_BTT   = 'd256; //256bytes
assign w_S_AXIS_S2MM_CMD_0_tdata = {
    8'd0,
    w_SADDR,
    w_DRR,
    w_EOF,
    w_DSA,
    w_Type,
    w_BTT
};
reg [31:0] r_data = 'h1234;
reg r_S_AXIS_S2MM_0_tvalid = 1'b0;
always @(posedge i_clk)
begin
    if (r_S_AXIS_S2MM_0_tvalid & S_AXIS_S2MM_0_tready)
        r_data <= r_data + 'd1;
end

reg [5:0] r_cnt_num = 'd0;
always @(posedge i_clk)
begin
    if (r_S_AXIS_S2MM_0_tvalid)
    begin
        if (S_AXIS_S2MM_0_tready)
            r_cnt_num <= r_cnt_num + 'd1;
    end
    else 
        r_cnt_num <= 'd0;

end

always @(posedge i_clk)
begin
    if ((r_cnt_num == 'd62) && S_AXIS_S2MM_0_tready)
        r_S_AXIS_S2MM_0_tlast <= 1'b1;
    else if (r_S_AXIS_S2MM_0_tlast & S_AXIS_S2MM_0_tready)
        r_S_AXIS_S2MM_0_tlast <= 1'b0;
end 

always @(posedge i_clk)
begin
    if (S_AXIS_S2MM_CMD_0_tready & r_S_AXIS_S2MM_CMD_0_tvalid)
        r_S_AXIS_S2MM_0_tvalid <= 1'b1;
    else if (r_S_AXIS_S2MM_0_tlast & S_AXIS_S2MM_0_tready)
        r_S_AXIS_S2MM_0_tvalid <= 1'b0;
end 

assign S_AXIS_S2MM_0_tdata  =  r_data;
assign S_AXIS_S2MM_0_tkeep  =  4'b1111;
assign S_AXIS_S2MM_0_tlast  =  r_S_AXIS_S2MM_0_tlast;
assign S_AXIS_S2MM_0_tvalid =  r_S_AXIS_S2MM_0_tvalid; 

assign S_AXIS_S2MM_CMD_0_tdata  = w_S_AXIS_S2MM_CMD_0_tdata;
assign S_AXIS_S2MM_CMD_0_tvalid = r_S_AXIS_S2MM_CMD_0_tvalid;

////debug 
ila_0  inst_ila_0 (
    .clk               (i_clk),   
    .probe0            (S_AXIS_S2MM_0_tdata),
    .probe1            (S_AXIS_S2MM_0_tkeep),
    .probe2            (S_AXIS_S2MM_0_tlast),
    .probe3            (S_AXIS_S2MM_0_tready),
    .probe4            (S_AXIS_S2MM_0_tvalid),
    .probe5            (S_AXIS_S2MM_CMD_0_tdata),
    .probe6            (S_AXIS_S2MM_CMD_0_tready),
    .probe7            (S_AXIS_S2MM_CMD_0_tvalid),
    .probe8            (r_addr_axi),
    .probe9            (r_cnt_num)
);
////debug end 

endmodule // end the Control_AXI_stream model

 

(5)編譯工程,導出到SDK。SDK源代碼使用下述代碼做簡單測試,同時可以使用SDK中的memory調試界面實時顯示DDR指定地址的數據值,查看是否寫入。
#include <stdio.h>
#include "xil_cache.h"
#include "xparameters.h"
#include "xparameters_ps.h"
#include "xil_printf.h"
#include "xil_io.h"

#define DDR_BASEARDDR  0x00000000 //從設置基地址開始讀取

int main()
{
    int i=0;
    char A;
    int rev;

    Xil_DCacheDisable();
    print("PL RW DDR TEST!\n\r");
    print("Please input A to get data\n\r");
    while(1){
    scanf("%c",&A);
    if(A=='A'){
        printf("start\n\r");
        while(i*4<128){
                    rev = Xil_In32(DDR_BASEARDDR+i*4);
                    xil_printf("the address at  %x data is : %x \r\n" ,DDR_BASEARDDR+i*4, rev);
                    ++i;
            }
            i=0;
        }
    }
    return 0;

}

 

測試方法:
(1)通過VIO設置上升沿啟動一次傳輸。PS通過串口打印相關地址對應的寫入值查看是否寫入。同時ILA在線debug總線信號,確認數據的一致性是否吻合。
   通過SDK的memory調試界面,顯示對應地址數據,查看是否寫入。

 

 

綜上,可以看到數據是正確的。
 
深度思考:
當然上述demo只是這個IP的一個簡單使用,更高階的用法根據應用場景的不同而不同。
注意DDR讀寫同一時刻只能一個主機,所以實際使用中需要PL跟CPU設置握手信號,避免讀寫錯誤的數據。
此IP的讀寫PS端DDR方式跟AXI DMA的異同?都是對PS端對應位置進行讀寫,但datamover的方式是CPU無需初始化DMA,PL為絕對主控。
 
以上。
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM