axi_datamover_0

何为 DataMover
 

DataMover 是 DMA 的一种形式。Direct Memory Access 对我们来说是一个更熟悉的名字。在不需要 CPU 干预的情况下,DMA 可以进行数据的搬运,包括但不仅限于将数据从外部存储,比如 DDR,搬运到内部寄存器,或者搬运到外部存储的另一个位置。这些都只需要 CPU 一句话的事:

DataMover 的接口
那么 DataMover 是如何进行他的工作呢,我们可以从他的端口来了解。这里以 DM 的读通道为例。DataMover 共有三路接口(status 一般在调试中用于观察状态),一路 AXI 总线,两路 AXIS 总线。 

读通道,将数据从如 DDR 这样的外部存储,搬运到 FPGA 的逻辑模块中。DDR 在 FPGA 上通过 MIG IP 访问,即 Memory 访问接口。MIG 提供了一个 AXI4 Slave 接口。DataMover 的 Master 接口连接到 MIG 的 Slave 接口,AXI4 协议提供了一种基于地址的访问 DDR 能力。关于 AXI4 对存储介质的地址访问,可以参考以下的文章,该文章中访问的是 BRAM ,但总线操作和访问 DDR 类似。

对zynq器件来讲,存在多种方式用于PS和PL数据交互。常见有:

1.利用AXI总线直接传输数据,直接读写DDR。

2.利用DMA进行传输,读写DDR,由PS侧进行控制。

3.利用BRAM进行数据交互。

4.利用Data Mover进行数据传输,完全由PL控制。

datamover能直接让PL侧对挂在到PS的DDR进行读写,PS侧不进行过多参与,十分方便。

目标:写一段逻辑,输入为想要发送到dram的数据,输出为想从dram读出的数据。数据均伴随有效信号,同时还需告知这个module读写数据的长度,地址,数据模式默认为递增模式。

先看看datamover都有哪些接口。拉出一个datamover,配置相关参数

datamover的引脚如下图所示,引出的都是与PL侧相关的,红框代表datamover和其他AXI设备交互数据的通路,不需要PL侧参与控制

PL侧操作的接口为绿色框部分:

CMD接口*2:写入命令,包括地址及相关控制信号

MM2S接口:读出数据,即memory map to stream

S2MM接口:写入数据,即stream to memory map

STS接口*2: 状态信号,用于调试

datamover读取写入数据都是通过valid-ready这样的握手协议,如果在数据搬运期间出现ready拉低的情况,会导致个别数据出错,要写逻辑解决这个问题又比较麻烦,同时也为了保证数据的连续性,干脆在读取和写入之前都加入一个stream的fifo进行缓冲。

写代码时仿真没问题,综合失败,出现[Synth 8-91] ambiguous clock in event control,这个在另外一个笔记细说。

关于数据会出错的问题,虽然已解决,但还在研究原因中。

代码如下:

`timescale 1ns / 1ps
/*//
描述:将64bit数据通过datamover转发到zynq 的DDR,或将数据读回
接口说明:
时钟——clk
复位——rst

64bit待发送数据—— fpga2dram_data
数据有效信号——fpga2dram_valid
启动信号——fpga2dram_start,该信号配置CMD,最好比数据信号提前一段时间
写地址——write_address
写数据长度——write_length,字节数 Bytes


64bit待接收数据—— dram2fpga_data
数据有效信号——dram2fpga_valid
启动信号——dram2fpga_start
读地址——read_address
读数据长度——read_length,字节数 Byte

其余信号直接引出到TOP层,打包成IP时会自己生成接口

//*/


module datamover_device
#(
    parameter DATA_WIDTH = 64,
    parameter DATA_BYTES = 8
)
(


    input clk,
    input rst_n,

///fpga 2 dram 
    input wire  [DATA_WIDTH-1:0]   fpga2dram_data,//数据发送到dram
    input wire           fpga2dram_valid,//有效信号
    input wire           fpga2dram_start, //开始信号,比valid信号提前几个周期,用于配置写到dram的地址等   

/// dram 2 fpga 
    output wire  [DATA_WIDTH-1:0]  dram2fpga_data,//数据从dram来
    output wire          dram2fpga_valid,//有效信号
    input wire           dram2fpga_start, //开始信号,配置读dram的地址等   

///地址和长度
    input wire [31:0]   write_address,//写dram的地址
    input wire [31:0]   read_address,//读dram的地址

    input wire [22:0]   write_length,//写的的总长度
    input wire [22:0]   read_length,//读的总长度


//data mover接收数据信号    
//CMD信号
    output wire [71:0]  M_AXIS_MM2S_CMD_TDATA,
    input  wire         M_AXIS_MM2S_CMD_TREADY,
    output reg          M_AXIS_MM2S_CMD_TVALID,
//数据信号
    input  wire [DATA_WIDTH-1:0]  S_AXIS_MM2S_TDATA, 
    input  wire [7:0]   S_AXIS_MM2S_TKEEP, 
    input  wire         S_AXIS_MM2S_TLAST, 
    output wire         S_AXIS_MM2S_TREADY, 
    input  wire         S_AXIS_MM2S_TVALID,
//sts状态信号 
    input  wire [7:0]   S_AXIS_MM2S_STS_TDATA, 
    input  wire         S_AXIS_MM2S_STS_TKEEP, 
    input  wire         S_AXIS_MM2S_STS_TLAST, 
    output wire         S_AXIS_MM2S_STS_TREADY, 
    input  wire         S_AXIS_MM2S_STS_TVALID,

//data mover发送数据信号  
//CMD信号
    output  wire [71:0] M_AXIS_S2MM_CMD_TDATA,
    input   wire        M_AXIS_S2MM_CMD_TREADY,
    output  reg         M_AXIS_S2MM_CMD_TVALID,
//数据信号  
    output  wire  [DATA_WIDTH-1:0] M_AXIS_S2MM_TDATA,
    output  wire [7:0]  M_AXIS_S2MM_TKEEP,
    output  reg         M_AXIS_S2MM_TLAST,
    input   wire        M_AXIS_S2MM_TREADY,
    output  wire         M_AXIS_S2MM_TVALID,
//sts状态信号  
    input   wire [7:0]  S_AXIS_S2MM_STS_TDATA,
    input   wire [0:0]  S_AXIS_S2MM_STS_TKEEP,
    input   wire        S_AXIS_S2MM_STS_TLAST,
    output  wire        S_AXIS_S2MM_STS_TREADY,
    input   wire        S_AXIS_S2MM_STS_TVALID

    );

//检测fpga2dram的起始信号上升沿
wire fpga2dram_start_edge;
reg fpga2dram_start_buff;
always @(posedge clk) begin
    fpga2dram_start_buff <= fpga2dram_start;
end
assign fpga2dram_start_edge = ~fpga2dram_start_buff & fpga2dram_start; ///检测上升沿


//检测dram2fpga的起始信号上升沿
wire dram2fpga_start_edge;
reg dram2fpga_start_buff;
always @(posedge clk) begin
    dram2fpga_start_buff <= dram2fpga_start;
end
assign dram2fpga_start_edge = ~dram2fpga_start_buff & dram2fpga_start; ///检测上升沿



/fpga 传输数据到dram
assign M_AXIS_S2MM_TKEEP = 8'hff;
wire fpga2dram_fifo_wr_valid = fpga2dram_valid;
wire fpga2dram_fifo_wr_ready; //应该是一直ready的


axis_data_fifo axis_data_fifo_fpag2dram (
  .s_axis_aresetn(rst_n),  // input wire s_axis_aresetn
  .s_axis_aclk(clk),        // input wire s_axis_aclk

  .s_axis_tvalid(fpga2dram_fifo_wr_valid),    // input wire s_axis_tvalid
  .s_axis_tready(fpga2dram_fifo_wr_ready),    // output wire s_axis_tready
  .s_axis_tdata (fpga2dram_data),      // input wire [63 : 0] s_axis_tdata

  .m_axis_tvalid(M_AXIS_S2MM_TVALID),    // output wire m_axis_tvalid
  .m_axis_tready(M_AXIS_S2MM_TREADY),    // input wire m_axis_tready
  .m_axis_tdata (M_AXIS_S2MM_TDATA)      // output wire [63 : 0] m_axis_tdata
); 
///产生last信号
reg [22:0] fpga2dram_count;
reg puul_down_last;//拉低last信号
always @(posedge clk or negedge rst_n)begin
    if(~rst_n)begin
        fpga2dram_count<=0;
        M_AXIS_S2MM_TLAST<=0;
        puul_down_last<=0;
    end
    else if(puul_down_last)begin
        M_AXIS_S2MM_TLAST<=0;
        fpga2dram_count<=0;
        puul_down_last<=0;
    end

    else if(M_AXIS_S2MM_TVALID & M_AXIS_S2MM_TREADY)begin

        fpga2dram_count<=fpga2dram_count+1'b1;

        if(fpga2dram_count==write_length/DATA_BYTES-2)begin
            M_AXIS_S2MM_TLAST<=1;
            puul_down_last<=1;
        end
    end

end


///写CMD命令
wire [71:0] fpga2dram_cmd = {
                    4'b0000,
                    4'b0000,
                    write_address,
                    1'b0,//DRR
                    1'b1,//EOF
                    6'b000000,//DSA
                    1'b1,//type 1:incr 0:fixed
                    write_length
                    };
assign M_AXIS_S2MM_CMD_TDATA = fpga2dram_cmd;
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
        M_AXIS_S2MM_CMD_TVALID <= 0;
    end
    else if(fpga2dram_start_edge&M_AXIS_S2MM_CMD_TREADY)begin ///外部配置信号拉高 同时ready
        M_AXIS_S2MM_CMD_TVALID <= 1;
    end
    else begin
        M_AXIS_S2MM_CMD_TVALID <= 0;//拉低cmd 这之后的握手就是fifo自己操作的部分了 不需要用户参与
    end
end


///ila调试sts信号
assign S_AXIS_S2MM_STS_TREADY =1'b1 ;
 ila_fpga2dram ila_fpga2dram_inst (
    .clk(clk), // input wire clk


    .probe0(S_AXIS_S2MM_STS_TDATA), // input wire [7:0]  probe0  
    .probe1(S_AXIS_S2MM_STS_TKEEP), // input wire [0:0]  probe1 
    .probe2(S_AXIS_S2MM_STS_TLAST), // input wire [0:0]  probe2 
    .probe3(S_AXIS_S2MM_STS_TREADY), // input wire [0:0]  probe3 
    .probe4(S_AXIS_S2MM_STS_TVALID) // input wire [0:0]  probe4
);

///监控fifo到datamover的信号
ila_fpga2dram_data3 ila_fpga2dram_data3_inst (
    .clk(clk), // input wire clk

    .probe0(M_AXIS_S2MM_TVALID), // input wire [0:0]  probe0  
    .probe1(M_AXIS_S2MM_TREADY), // input wire [0:0]  probe1 
    .probe2(M_AXIS_S2MM_TDATA), // input wire [63:0]  probe2 
    .probe3(M_AXIS_S2MM_TLAST) // input wire [0:0]  probe3

);


/dram 传输数据到fpga


axis_data_fifo axis_data_fifo_dram2fpga (
  .s_axis_aresetn(rst_n),  // input wire s_axis_aresetn
  .s_axis_aclk(clk),        // input wire s_axis_aclk

  .s_axis_tvalid(S_AXIS_MM2S_TVALID),    // input wire s_axis_tvalid
  .s_axis_tready(S_AXIS_MM2S_TREADY),    // output wire s_axis_tready
  .s_axis_tdata(S_AXIS_MM2S_TDATA),      // input wire [63 : 0] s_axis_tdata

  .m_axis_tvalid(dram2fpga_valid),    // output wire m_axis_tvalid
  .m_axis_tready(1'b1),    // input wire m_axis_tready
  .m_axis_tdata(dram2fpga_data)      // output wire [63 : 0] m_axis_tdata
); 
wire [71:0] dram2fpga_cmd = {
                    4'b0000,
                    4'b0000,
                    read_address,
                    1'b0,//DRR
                    1'b1,//EOF
                    6'b000000,//DSA
                    1'b1,//type 1:incr 0:fixed
                    read_length
                    };
assign M_AXIS_MM2S_CMD_TDATA = dram2fpga_cmd;

reg [2:0] dram2fpga_count;
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
        M_AXIS_MM2S_CMD_TVALID <= 0;
        dram2fpga_count <=0;
    end
    else if(dram2fpga_start_edge)begin
        dram2fpga_count <= dram2fpga_count+1'b1;
    end
    else if(dram2fpga_count==1 && M_AXIS_MM2S_CMD_TREADY)begin ///外部配置信号拉高 同时ready
        M_AXIS_MM2S_CMD_TVALID <= 1;
        dram2fpga_count <= dram2fpga_count+1'b1;
    end
    else if(dram2fpga_count==2)begin
        M_AXIS_MM2S_CMD_TVALID <= 0;//拉低cmd 这之后的握手就是fifo自己操作的部分了 用户就用有效信号和数据信号
        dram2fpga_count <=0;
    end
end


assign S_AXIS_MM2S_STS_TREADY = 1'b1;
ila_dram2fpga ila_dram2fpga_inst (
    .clk(clk), // input wire clk

    .probe0(S_AXIS_MM2S_STS_TDATA), // input wire [7:0]  probe0  
    .probe1(S_AXIS_MM2S_STS_TKEEP), // input wire [0:0]  probe1 
    .probe2(S_AXIS_MM2S_STS_TLAST), // input wire [0:0]  probe2 
    .probe3(S_AXIS_MM2S_STS_TREADY), // input wire [0:0]  probe3 
    .probe4(S_AXIS_MM2S_STS_TVALID) // input wire [0:0]  probe4
);

endmodule
  • 4
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值