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
<think>好的,我现在要处理用户关于AXI Datamover IP核中S2MM功能的测试代码请求。首先,我需要理解用户的需求。用户提到的是FPGA开发,特别是使用Xilinx的Vivado工具,所以需要确保提供的代码与Vivado兼容,并且符合AXI协议。 用户引用了三篇资料,其中两篇提到AXI-Stream总线和AXI4协议的区别,第三篇涉及AXI Datamover IP核的使用,特别是xUser和xCache字段的设置。这说明用户可能已经了解了一些基础,但需要具体的测试代码示例来验证S2MM功能。 接下来,我需要考虑AXI Datamover的S2MM模式。S2MM负责将数据从流接口传输到内存映射的位置,比如DDR。测试代码需要配置Datamover IP,生成测试数据流,并验证数据是否正确写入内存。 首先,测试代码的结构可能包括以下几个部分:IP核的配置、AXI Stream数据的生成、控制命令的发送,以及状态监测和结果验证。需要确保在Vivado中正确设置IP核参数,比如使能S2MM通道,配置数据宽度,以及是否需要xUser和xCache字段。 然后,测试平台部分需要生成模拟的AXI Stream数据,这可能涉及到使用Verilog或VHDL的测试台架,或者利用Vivado的仿真工具。同时,控制命令的格式需要正确,包括目标地址、数据长度等,并且通过AXI-Lite或AXI-MM接口发送这些命令到Datamover。 用户的问题还可能涉及到如何触发数据传输,如何处理完成中断或状态信号,以及如何检查内存中的数据是否正确。因此,在示例代码中需要包含状态机的控制逻辑,以及可能的错误处理机制。 另外,需要参考引用中的内容,特别是关于AXI-Stream的信号简化和Datamover IP核的配置注意事项。例如,如果用户没有使能xUser和xCache,命令的位宽会不同,这点在代码中必须正确体现,否则会导致位宽不匹配的错误。 最后,考虑到用户可能需要实际的仿真示例,提供Verilog或VHDL的测试台代码片段会有帮助,同时解释关键部分的作用,比如如何连接AXI Stream接口,如何设置命令寄存器,以及如何监测传输状态。 总结步骤:配置IP核,生成测试数据流,发送控制命令,监测传输状态,验证数据正确性。同时需要确保代码符合AXI协议,特别是S2MM的相关信号,如TREADY、TVALID、TLAST等。</think>### AXI Datamover S2MM 测试代码实现步骤 #### 1. **IP核配置(Vivado环境)** - 在Block Design中添加AXI Datamover IP核 - 配置模式为S2MM Only - 设置数据位宽(例如64位) - 若需要xUser/xCache字段,需勾选对应选项 - 连接时钟、复位、AXI Stream接口和DDR内存映射接口[^3] #### 2. **测试代码框架(Verilog示例)** ```verilog module s2mm_test( input wire clk, input wire resetn ); // AXI Stream接口信号 wire [63:0] s_axis_s2mm_tdata; wire [7:0] s_axis_s2mm_tkeep; wire s_axis_s2mm_tlast; wire s_axis_s2mm_tvalid; wire s_axis_s2mm_tready; // 控制接口信号 wire [71:0] s_axis_cmd_tdata; // 命令通道位宽根据配置变化 wire s_axis_cmd_tvalid; wire s_axis_cmd_tready; // 状态接口信号 wire [7:0] s_axis_sts_tdata; wire s_axis_sts_tvalid; wire s_axis_sts_tready; // 实例化Datamover IP axi_datamover_0 your_datamover_inst ( // 时钟与复位 .m_axi_s2mm_aclk(clk), .m_axi_s2mm_aresetn(resetn), // S2MM Stream接口 .s_axis_s2mm_tdata(s_axis_s2mm_tdata), .s_axis_s2mm_tkeep(s_axis_s2mm_tkeep), .s_axis_s2mm_tlast(s_axis_s2mm_tlast), .s_axis_s2mm_tvalid(s_axis_s2mm_tvalid), .s_axis_s2mm_tready(s_axis_s2mm_tready), // 命令接口 .s_axis_cmd_tdata(s_axis_cmd_tdata), .s_axis_cmd_tvalid(s_axis_cmd_tvalid), .s_axis_cmd_tready(s_axis_cmd_tready), // 状态接口 .m_axis_sts_tdata(s_axis_sts_tdata), .m_axis_sts_tvalid(s_axis_sts_tvalid), .m_axis_sts_tready(s_axis_sts_tready) ); // 测试数据生成模块 test_pattern_generator u_gen( .clk(clk), .resetn(resetn), .tdata(s_axis_s2mm_tdata), .tvalid(s_axis_s2mm_tvalid), .tready(s_axis_s2mm_tready), .tlast(s_axis_s2mm_tlast) ); // 控制模块 control_fsm u_ctl( .clk(clk), .resetn(resetn), .cmd_tdata(s_axis_cmd_tdata), .cmd_tvalid(s_axis_cmd_tvalid), .cmd_tready(s_axis_cmd_tready), .sts_tdata(s_axis_sts_tdata), .sts_tvalid(s_axis_sts_tvalid) ); endmodule ``` #### 3. **测试数据生成器** ```verilog module test_pattern_generator( input wire clk, input wire resetn, output reg [63:0] tdata, output reg tvalid, input wire tready, output reg tlast ); reg [31:0] counter; always @(posedge clk) begin if(!resetn) begin counter <= 0; tvalid <= 0; tlast <= 0; end else begin if(tready) begin tdata <= {counter[15:0], 48'h0}; // 生成递增测试数据 tvalid <= (counter < 1024); // 发送1KB数据 tlast <= (counter == 1023); counter <= counter + 1; end end end endmodule ``` #### 4. **控制状态机核心逻辑** ```verilog // 命令包格式: // [23:0] - 目标地址低24位 // [31:24] - 目标地址高8位 // [63:32] - 数据长度(字节数) // [71:64] - 控制字段(可选) parameter CMD_ADDR = 32'h8000_0000; // DDR起始地址 parameter DATA_LEN = 1024; // 传输数据长度 always @(posedge clk) begin if(!resetn) begin state <= IDLE; end else case(state) IDLE: if(cmd_tready) begin cmd_tdata <= {8'h0, DATA_LEN, CMD_ADDR}; cmd_tvalid <= 1'b1; state <= SEND_CMD; end SEND_CMD: if(cmd_tvalid && cmd_tready) begin cmd_tvalid <= 1'b0; state <= WAIT_COMPLETE; end WAIT_COMPLETE: if(sts_tvalid) begin // 检查状态包[7:0] if(sts_tdata[7:0] == 8'h01) $display("Transfer completed"); else $display("Error detected: %h", sts_tdata); state <= IDLE; end endcase end ``` ### 关键验证点 1. **握手时序验证**:确保TVALID/TREADY信号符合AXI Stream协议要求 2. **数据完整性检查**:通过AXI Master接口读取DDR数据验证写入正确性 3. **异常处理测试**:模拟错误传输(如突发中断)检测状态机恢复能力 ### 仿真建议 ```tcl # Vivado仿真脚本示例 launch_simulation log_wave -r * run 10us ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值