使用AXI DataMover进行PS DDR读写

/ 0评 / 1

偶然发现了DataMover这个IP,确实解决了很多问题,一般我们在PL端要读写AXI Slave Port的PS时候,总是很不喜欢写复杂的AXI Full协议.就算实现AXI Lite协议也觉得很累啊.

DataMover就是支持AXIS转AXI接口,配合其他IP可以解决非常多问题,常见情况下PL遇到什么问题.

搜索PG022可以找到AXI DataMover的IP文档.这里暂时单纯讨论到PS的写方向,反过来原理也是一样的,总共分为.对于PS侧,PS永远是从机.

我们传输数据进去时候,同时要声明我们传输的一些细节,即命令接口.

数据映射规则是这样的,假设我们传输的地址是32位.

传输结果.

刚才设置的TAG会传递过来,可以用来区分不同的传输.如果最高位是1,那么传输就OK了.

那么整体设计就明朗了.

pl_ddr是按8位数据写的,所以使用位宽转换成64位,由于PL DDR一次提供1字节,而DataMover一次要8字节,明显带宽上有很大差距,所以又连接了一个Data FIFO,要注意STS的READY要拉高,不然第二次传输他就以为是错误了.

PL代码参考.

module pl_ddr(
    input wire clk,

    (* X_INTERFACE_PARAMETER = "POLARITY ACTIVE_HIGH" *)
    input wire rst,

    input wire [7:0] i_data,
    input wire       i_trigger,

    (* X_INTERFACE_INFO = "xilinx.com:interface:axis:1.0 M_AXIS TLAST" *)
    output reg        m_axis_tlast,
    (* X_INTERFACE_INFO = "xilinx.com:interface:axis:1.0 M_AXIS TKEEP" *)
    output reg        m_axis_tkeep,
    (* X_INTERFACE_INFO = "xilinx.com:interface:axis:1.0 M_AXIS TDATA" *)
    output reg [7:0] m_axis_tdata,
    (* X_INTERFACE_INFO = "xilinx.com:interface:axis:1.0 M_AXIS TREADY" *)
    input  wire       m_axis_tready,
    (* X_INTERFACE_INFO = "xilinx.com:interface:axis:1.0 M_AXIS TVALID" *)
    output reg        m_axis_tvalid,

    (* X_INTERFACE_INFO = "xilinx.com:interface:axis:1.0 M_AXIS_S2MM_CMD TVALID" *)
    output reg        m_axis_s2mm_cmd_tvalid,
    (* X_INTERFACE_INFO = "xilinx.com:interface:axis:1.0 M_AXIS_S2MM_CMD TREADY" *)
    input  wire       m_axis_s2mm_cmd_tready,
    (* X_INTERFACE_INFO = "xilinx.com:interface:axis:1.0 M_AXIS_S2MM_CMD TDATA" *)
    output reg [71:0] m_axis_s2mm_cmd_tdata,

    (* X_INTERFACE_INFO = "xilinx.com:interface:axis:1.0 S_AXIS_S2MM_STS TVALID" *) 
    input  wire        s_axis_s2mm_sts_tvalid,
    (* X_INTERFACE_INFO = "xilinx.com:interface:axis:1.0 S_AXIS_S2MM_STS TREADY" *) 
    output wire         s_axis_s2mm_sts_tready,
    (* X_INTERFACE_INFO = "xilinx.com:interface:axis:1.0 S_AXIS_S2MM_STS TLAST" *) 
    input  wire        s_axis_s2mm_sts_tlast,
    (* X_INTERFACE_INFO = "xilinx.com:interface:axis:1.0 S_AXIS_S2MM_STS TKEEP" *) 
    input  wire        s_axis_s2mm_sts_tkeep,
    (* X_INTERFACE_INFO = "xilinx.com:interface:axis:1.0 S_AXIS_S2MM_STS TDATA" *) 
    input  wire [7:0]  s_axis_s2mm_sts_tdata
);

reg [2:0] sync_trigger_ff;

// 信号为高时发生上升沿
wire sync_trigger = !sync_trigger_ff[2] && sync_trigger_ff[1];

reg [15:0] counter;
reg [1:0]  state;

assign s_axis_s2mm_sts_tready = 1'b1;

always @ (posedge clk) begin
    if(rst) begin
        sync_trigger_ff <= 0;
    end else begin
        sync_trigger_ff <= {sync_trigger_ff[1:0],i_trigger};
    end
end

always @ (posedge clk) begin
    if(rst) begin
        m_axis_tdata <= i_data;
        counter <= 0;
        state <= 0;
    end else begin
        case (state)
            2'b00: begin
                counter <= 0;
                m_axis_tdata <= i_data;
                if(sync_trigger) begin
                    state <= 2'b01;
                end else begin
                    state <= 2'b00;
                end
            end
            2'b01: begin
                if (counter < 8192) begin
                    if(m_axis_tready) begin
                        m_axis_tdata <= m_axis_tdata + 1'b1;
                        counter <= counter + 1'b1;
                    end
                end else begin  
                    state <= 2'b00;
                end
            end
        endcase
    end
end

// TVALID
always @ (posedge clk) begin
    if(rst) begin
        m_axis_tvalid <= 0;
    end else begin
        if(state == 2'b01 && counter < 8192) begin
            m_axis_tvalid <= 1'b1;
        end else begin
            m_axis_tvalid <= 1'b0;
        end
    end
end

// TLAST
always @ (posedge clk) begin
    if(rst) begin
        m_axis_tkeep <= 0;
    end else begin
        if(state == 2'b01) begin
            m_axis_tkeep <= 4'b1111;
        end else begin
            m_axis_tkeep <= 4'b0000;
        end
    end
end

// TKEEP
always @ (posedge clk) begin
    if(rst) begin
        m_axis_tkeep <= 0;
    end else begin
        if(state == 2'b01) begin
            m_axis_tkeep <= 4'b1111;
        end else begin
            m_axis_tkeep <= 4'b0000;
        end
    end
end

// 锁存器可以暂时锁存CMD直到TREADY
always @ (posedge clk) begin
    if(rst) begin
        m_axis_s2mm_cmd_tvalid <= 0;
        m_axis_s2mm_cmd_tdata <= 0;
    end
    else if(sync_trigger) begin
        m_axis_s2mm_cmd_tvalid <= 1'b1;
        m_axis_s2mm_cmd_tdata <= {9'd0,32'h1000_0000,1'b0,1'b1,6'd0,1'b1,23'd8192};
    end else if(m_axis_s2mm_cmd_tready) begin
        m_axis_s2mm_cmd_tvalid <= 0;
        m_axis_s2mm_cmd_tdata <= 0;       
    end
end

endmodule

在ILA中验证,AXI总线已经反馈OKAY,并且STS也是回报OK的.

我的VIO配置.

PS读取偷懒的话,就失能全部Cache验证.

另外也可以使用Store and Forward功能,这样就算输入输出不同宽度,也是可以暂存,但是他只在Full模式有效,不如多使用几个额外IP.

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注