zynq的stream数据mock和fifo缓冲和同步

zynq的stream数据mock和fifo缓冲和同步

readme.md

# 文件根目录setroot_dir"D:/workspace/gitee/999/ming_zynq_wave_prj/ming_zynq_wave/v2/vivado/src"# 源文件add_files$root_dir/stream_fifo/axis_generator.v add_files$root_dir/stream_fifo/axis_stream_fifo.v# tb文件add_files-filesetsim_1$root_dir/stream_fifo/tb_axis_generator.v add_files-filesetsim_1$root_dir/stream_fifo/tb_axis_stream_fifo.v add_files-filesetsim_1$root_dir/stream_fifo/tb_ip_axis_data_fifo.v

axis_generator.v

生成axi_stream数据流

`timescale 1ns / 1ps // // 示例: // P_AD_COUNT = 4 // seq ad0 ad1 ad2 ad3 // 0000 0001 0002 0003 0004 // 0001 0005 0006 0007 0008 // 0002 0009 000a 000b 000c // module axis_generator #( // 时钟频率 parameter P_CLK_FREQ = 100_000_000, // 采样周期(P_SAMPLE_PERIOD个时钟周期采集一次) parameter P_SAMPLE_PERIOD = 100, // AD数量 parameter P_AD_COUNT = 4 )( input wire i_clk, input wire i_rst_n, //(P_AD_COUNT+1)个u32 output reg [(P_AD_COUNT+1)*32-1:0] m_axis_tdata, output reg m_axis_tvalid, input wire m_axis_tready ); localparam P_AXIS_WIDTH = (P_AD_COUNT+1)*32; // 采样计时器 reg [31:0] r_timer; // 行号 reg [31:0] r_seq; // AD数据计数 reg [31:0] r_ad_counter; integer i; //////////////////////////////////////////////////////// // AXIS DATA GENERATOR //////////////////////////////////////////////////////// always @(posedge i_clk or negedge i_rst_n) begin if(!i_rst_n) begin r_timer <= 0; r_seq <= 0; r_ad_counter <= 0; m_axis_tdata <= 0; m_axis_tvalid <= 0; end else begin //-------------------------------------------------- // AXIS握手 //-------------------------------------------------- if(m_axis_tvalid) begin if(m_axis_tready) m_axis_tvalid <= 0; end //-------------------------------------------------- //旧数据收到后,才生成新数据 //-------------------------------------------------- else if(r_timer >= P_SAMPLE_PERIOD-1) begin r_timer <= 0; // 清空 m_axis_tdata <= 0; // seq m_axis_tdata[P_AXIS_WIDTH-1 -:32] <= r_seq; //AD for(i=0;i<P_AD_COUNT;i=i+1) begin m_axis_tdata[P_AXIS_WIDTH-33-i*32 -:32]<= r_ad_counter+i+1; end //数据有效 m_axis_tvalid <= 1'b1; // 行号+1 r_seq <= r_seq + 1; // AD计数增加AD数量 r_ad_counter <= r_ad_counter + P_AD_COUNT; end else begin r_timer <= r_timer + 1; end end end endmodule

tb_axis_generator.v

`timescale 1ns / 1ps module tb_axis_generator; //////////////////////////////////////////////////////// // Clock //////////////////////////////////////////////////////// reg i_clk; reg i_rst_n; initial begin i_clk = 0; forever #5 i_clk = ~i_clk; end //////////////////////////////////////////////////////// // Reset //////////////////////////////////////////////////////// initial begin i_rst_n = 0; #100; i_rst_n = 1; end //////////////////////////////////////////////////////// // AXIS //////////////////////////////////////////////////////// wire [159:0] m_axis_tdata; wire m_axis_tvalid; reg m_axis_tready; initial begin m_axis_tready = 1'b1; end //////////////////////////////////////////////////////// // DUT //////////////////////////////////////////////////////// axis_generator #( .P_SAMPLE_PERIOD(10), .P_AD_COUNT(4) )u_axis_generator ( .i_clk(i_clk), .i_rst_n(i_rst_n), .m_axis_tdata(m_axis_tdata), .m_axis_tvalid(m_axis_tvalid), .m_axis_tready(m_axis_tready) ); wire [31:0] seq; wire [31:0] ad0; wire [31:0] ad1; wire [31:0] ad2; wire [31:0] ad3; assign seq = m_axis_tdata[159:128]; assign ad0 = m_axis_tdata[127:96]; assign ad1 = m_axis_tdata[95:64]; assign ad2 = m_axis_tdata[63:32]; assign ad3 = m_axis_tdata[31:0]; //////////////////////////////////////////////////////// // Monitor //////////////////////////////////////////////////////// always @(posedge i_clk) begin if(m_axis_tvalid && m_axis_tready) begin $display("time=%0t seq=%d ad0=%d ad1=%d ad2=%d ad3=%d", $time,seq,ad0,ad1,ad2,ad3); if(seq==0) begin if(ad0!=1 ||ad1!=2 ||ad2!=3 ||ad3!=4) begin $display("ERROR first sample"); $stop; end end end end //////////////////////////////////////////////////////// // Simulation end //////////////////////////////////////////////////////// initial begin #2000; $display("Simulation finish"); $stop; end endmodule

axis_stream_fifo.v

将 xpm_fifo_async 转换位 axi_stream接口

`timescale 1ns / 1ps //============================================================================== // 模块名称:axis_stream_fifo // // 功能说明: // 1. 将 AXI4-Stream 数据写入 Xilinx XPM 异步 FIFO,实现跨时钟域缓存。 // 2. s_axis_* 为写入侧接口,工作在 s_axis_clk 时钟域。 // 3. m_axis_* 为读出侧接口,工作在 m_axis_clk 时钟域。 // 4. FIFO 使用 READ_MODE="fwft"(First Word Fall Through)模式: // 当 FIFO 非空时,队首数据会直接出现在 dout 上,读侧用 tvalid/tready // 握手后,rd_en 再弹出当前数据并切到下一笔。 // // 复位注意事项: // XPM FIFO 复位释放后,内部仍会保持 wr_rst_busy / rd_rst_busy 一段时间。 // busy 期间不能拉高 wr_en 或 rd_en,否则仿真/硬件中可能出现丢数据或读序错乱。 // 所以本模块用 busy 信号约束 AXIS 的 tready/tvalid。 //============================================================================== module axis_stream_fifo #( // AXIS 数据位宽,只包含 tdata,不包含 tlast。 parameter P_DATA_WIDTH = 32, // FIFO 写深度,单位是 P_FIFO_WIDTH 宽度的数据字。 // XPM FIFO 对深度取值有约束,工程中建议使用 2 的幂。 parameter P_FIFO_DEPTH = 1024, // 是否携带 tlast: // 0:FIFO 中只保存 tdata。 // 1:FIFO 中保存 {tlast, tdata},读侧同步恢复 tlast。 parameter P_HAS_TLAST = 1 )( //-------------------------------------------------- // 写时钟域:AXIS Slave 输入侧 //-------------------------------------------------- input wire s_axis_clk, // 写侧低有效复位,同时作为 XPM FIFO 的统一复位来源。 input wire s_axis_rst_n, //-------------------------------------------------- // AXIS Slave 输入 //-------------------------------------------------- input wire [P_DATA_WIDTH-1:0] s_axis_tdata, // 写入 FIFO 的 AXIS 数据 input wire s_axis_tvalid, // 上游数据有效 output wire s_axis_tready, // FIFO 可接收数据 input wire s_axis_tlast, // 可选包结束标志,P_HAS_TLAST=1 时有效 //-------------------------------------------------- // 读时钟域:AXIS Master 输出侧 //-------------------------------------------------- input wire m_axis_clk, // 读侧低有效复位,只约束读侧 AXIS 输出握手。 input wire m_axis_rst_n, //-------------------------------------------------- // AXIS Master 输出 //-------------------------------------------------- output wire [P_DATA_WIDTH-1:0] m_axis_tdata, // 从 FIFO 读出的 AXIS 数据 output wire m_axis_tvalid, // FIFO 当前有有效输出数据 input wire m_axis_tready, // 下游准备接收数据 output wire m_axis_tlast // 可选包结束标志,P_HAS_TLAST=1 时恢复 ); // FIFO 内部存储宽度。 // 当 P_HAS_TLAST=1 时,最高位额外保存 tlast;否则宽度等于 P_DATA_WIDTH。 localparam P_FIFO_WIDTH = P_DATA_WIDTH +P_HAS_TLAST; //////////////////////////////////////////////////////// // FIFO 内部信号 //////////////////////////////////////////////////////// wire [P_FIFO_WIDTH-1:0] w_fifo_din; // 写入 XPM FIFO 的打包数据 wire [P_FIFO_WIDTH-1:0] w_fifo_dout; // 从 XPM FIFO 读出的打包数据 wire w_fifo_full; // FIFO 满标志,写时钟域有效 wire w_fifo_empty; // FIFO 空标志,读时钟域有效 wire w_fifo_wr_en; // XPM FIFO 写使能 wire w_fifo_rd_en; // XPM FIFO 读使能 wire w_wr_rst_busy; // XPM 写侧复位忙,busy 期间禁止写 wire w_rd_rst_busy; // XPM 读侧复位忙,busy 期间禁止读 //////////////////////////////////////////////////////// // AXIS 写端 //////////////////////////////////////////////////////// // 只有在写侧复位已释放、XPM 写侧不 busy、FIFO 未满时,才允许上游写入。 // 这样可避免 reset busy 阶段 s_axis_tready 提前拉高,导致上游误以为数据已写入。 assign s_axis_tready = s_axis_rst_n && ~w_wr_rst_busy && ~w_fifo_full; // AXIS 写握手成功时,向 XPM FIFO 写入一拍数据。 assign w_fifo_wr_en = s_axis_tvalid &&s_axis_tready; //////////////////////////////////////////////////////// // 打包数据 //////////////////////////////////////////////////////// generate if(P_HAS_TLAST) begin // 带 tlast 时,FIFO 最高位保存 tlast,低位保存 tdata。 assign w_fifo_din ={s_axis_tlast,s_axis_tdata}; end else begin // 不带 tlast 时,FIFO 只保存 tdata,节省 1 bit 存储宽度。 assign w_fifo_din = s_axis_tdata; end endgenerate //////////////////////////////////////////////////////// // AXIS 读端 //////////////////////////////////////////////////////// // FWFT 模式下,FIFO 非空时 dout 已经是当前队首数据。 // 读侧复位未释放或 XPM 读侧 busy 时,不对外声明 valid,也不产生 rd_en。 assign m_axis_tvalid = m_axis_rst_n && ~w_rd_rst_busy && ~w_fifo_empty; // AXIS 读握手成功时,弹出当前队首数据。 // 若 FIFO 中还有下一笔数据,FWFT 会自动把下一笔送到 dout。 assign w_fifo_rd_en = m_axis_tvalid && m_axis_tready; //////////////////////////////////////////////////////// // 解包 //////////////////////////////////////////////////////// generate if(P_HAS_TLAST) begin // 与写侧打包顺序对应:低 P_DATA_WIDTH 位是 tdata,最高位是 tlast。 assign m_axis_tdata =w_fifo_dout[P_DATA_WIDTH-1:0]; assign m_axis_tlast = w_fifo_dout[P_FIFO_WIDTH-1]; end else begin // 不带 tlast 时,读侧 tlast 固定为 0。 assign m_axis_tdata =w_fifo_dout; assign m_axis_tlast = 1'b0; end endgenerate //////////////////////////////////////////////////////// // Xilinx XPM Async FIFO //////////////////////////////////////////////////////// xpm_fifo_async #( // FIFO 深度与宽度由模块参数控制,写读宽度保持一致。 .FIFO_WRITE_DEPTH(P_FIFO_DEPTH), .WRITE_DATA_WIDTH(P_FIFO_WIDTH), .READ_DATA_WIDTH(P_FIFO_WIDTH), // FWFT 模式要求读延迟为 0:empty 拉低时,dout 已是有效队首数据。 .FIFO_READ_LATENCY(0), // 跨时钟域同步级数。级数越大,CDC 更稳健,但空/满标志延迟也更大。 .CDC_SYNC_STAGES(2), // First Word Fall Through:读侧无需先打一拍 rd_en 才看到第一笔数据。 .READ_MODE("fwft") ) u_xpm_fifo_async ( // 未使用休眠和错误注入功能,必须显式绑 0,避免仿真出现 X/Z。 .sleep(1'b0), // 写侧端口 .wr_clk(s_axis_clk), .wr_en(w_fifo_wr_en), .din(w_fifo_din), .full(w_fifo_full), // 读侧端口 .rd_clk(m_axis_clk), .rd_en( w_fifo_rd_en), .dout( w_fifo_dout), .empty(w_fifo_empty), // XPM FIFO 只有一个 rst 输入,这里使用写侧复位作为 FIFO 全局复位。 .rst( ~s_axis_rst_n), // ECC/错误注入未启用,相关输入固定为 0。 .injectsbiterr(1'b0), .injectdbiterr(1'b0), // 复位忙信号用于保护 AXIS 握手,busy 期间禁止读写 FIFO。 .wr_rst_busy(w_wr_rst_busy), .rd_rst_busy(w_rd_rst_busy) ); endmodule

tb_axis_stream_fifo.v

`timescale 1ns / 1ps module tb_axis_stream_fifo; //////////////////////////////////////////////////////// // Clock //////////////////////////////////////////////////////// reg i_clk; reg m_clk; initial begin i_clk = 0; forever #5 i_clk = ~i_clk; //100MHz end initial begin m_clk = 0; forever #7 m_clk = ~m_clk; //71MHz end //////////////////////////////////////////////////////// // Reset //////////////////////////////////////////////////////// reg i_rst_n; reg m_rst_n; initial begin i_rst_n = 0; m_rst_n = 0; #100; i_rst_n = 1; m_rst_n = 1; end //////////////////////////////////////////////////////// // AXIS generator //////////////////////////////////////////////////////// wire [159:0] s_axis_tdata; wire s_axis_tvalid; wire s_axis_tready; axis_generator #( // 每10个clk产生一次数据 .P_SAMPLE_PERIOD(10), // 4路AD .P_AD_COUNT(4) ) u_axis_generator ( .i_clk(i_clk), .i_rst_n(i_rst_n), .m_axis_tdata(s_axis_tdata), .m_axis_tvalid( s_axis_tvalid), .m_axis_tready( s_axis_tready) ); //////////////////////////////////////////////////////// // AXIS FIFO //////////////////////////////////////////////////////// wire [159:0] m_axis_tdata; wire m_axis_tvalid; reg m_axis_tready; axis_stream_fifo #( .P_DATA_WIDTH(160), .P_FIFO_DEPTH(16), .P_HAS_TLAST(0) ) u_axis_fifo ( //-------------------------------------------------- // 输入 //-------------------------------------------------- .s_axis_clk(i_clk), .s_axis_rst_n(i_rst_n), .s_axis_tdata(s_axis_tdata), .s_axis_tvalid(s_axis_tvalid), .s_axis_tready(s_axis_tready), .s_axis_tlast(1'b0), //-------------------------------------------------- // 输出 //-------------------------------------------------- .m_axis_clk(m_clk), .m_axis_rst_n(m_rst_n), .m_axis_tdata( m_axis_tdata), .m_axis_tvalid(m_axis_tvalid), .m_axis_tready(m_axis_tready), .m_axis_tlast() ); //////////////////////////////////////////////////////// // 下游 AXIS ready //////////////////////////////////////////////////////// // 模拟 DataMover // // 上电先不接收 // 测试FIFO缓存能力 //////////////////////////////////////////////////////// initial begin m_axis_tready = 1'b0; #400; m_axis_tready = 1'b1; end //////////////////////////////////////////////////////// // 数据解析 //////////////////////////////////////////////////////// wire [31:0] seq; wire [31:0] ad0; wire [31:0] ad1; wire [31:0] ad2; wire [31:0] ad3; assign seq = m_axis_tdata[159:128]; assign ad0 = m_axis_tdata[127:96]; assign ad1 = m_axis_tdata[95:64]; assign ad2 = m_axis_tdata[63:32]; assign ad3 = m_axis_tdata[31:0]; //////////////////////////////////////////////////////// // FIFO 写监控 //////////////////////////////////////////////////////// always @(posedge i_clk) begin if(s_axis_tvalid && s_axis_tready) begin $display( "FIFO WRITE time=%0t seq=%d ad0=%d ad1=%d ad2=%d ad3=%d", $time, s_axis_tdata[159:128], s_axis_tdata[127:96], s_axis_tdata[95:64], s_axis_tdata[63:32], s_axis_tdata[31:0] ); end end //////////////////////////////////////////////////////// // FIFO 读监控 //////////////////////////////////////////////////////// integer rx_count; initial begin rx_count = 0; end always @(posedge m_clk) begin if(m_axis_tvalid && m_axis_tready) begin $display("FIFO READ time=%0t seq=%d ad0=%d ad1=%d ad2=%d ad3=%d", $time,seq,ad0,ad1,ad2,ad3); //-------------------------------------------------- // 第一帧检查 //-------------------------------------------------- if(seq==0) begin if(ad0!=1 ||ad1!=2 ||ad2!=3 ||ad3!=4) begin $display("ERROR first sample"); $stop; end end //-------------------------------------------------- // 连续性检查 //-------------------------------------------------- if(seq != rx_count) begin $display("ERROR sequence expect=%d receive=%d",rx_count,seq); $stop; end rx_count = rx_count + 1; end end //////////////////////////////////////////////////////// // Simulation end //////////////////////////////////////////////////////// initial begin #20000; $display("=============================="); $display("Simulat finish"); $display("=============================="); $stop; end endmodule

axis_data_fifo_0 .v

ip catalog生成的axis_data_fifo_0

//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG axis_data_fifo_0 your_instance_name ( .s_axis_aresetn(s_axis_aresetn), // input wire s_axis_aresetn .s_axis_aclk(s_axis_aclk), // input wire s_axis_aclk .s_axis_tvalid(s_axis_tvalid), // input wire s_axis_tvalid .s_axis_tready(s_axis_tready), // output wire s_axis_tready .s_axis_tdata(s_axis_tdata), // input wire [159 : 0] s_axis_tdata .m_axis_tvalid(m_axis_tvalid), // output wire m_axis_tvalid .m_axis_tready(m_axis_tready), // input wire m_axis_tready .m_axis_tdata(m_axis_tdata) // output wire [159 : 0] m_axis_tdata );

axis_data_fifo_0 配置

参数配置值
Component Nameaxis_data_fifo_0
FIFO depth512
Memory typeAuto
Independent clocksNo
CDC sync stages3
Enable packet modeNo
ACLKEN conversion modeNone
Enable ECCNo
Include ECC error injectionNo

参数配置值
TDATA width (bytes)20
Enable TSTRBNo
Enable TKEEPNo
Enable TLASTNo
TID width (bits)0
TDEST width (bits)0
TUSER width (bits)0

tb_ip_axis_data_fifo.v

`timescale 1ns / 1ps module tb_ip_axis_data_fifo; //////////////////////////////////////////////////////// // Clock //////////////////////////////////////////////////////// reg s_axis_aclk; initial begin s_axis_aclk = 0; forever #5 s_axis_aclk = ~s_axis_aclk; //100MHz end //////////////////////////////////////////////////////// // Reset //////////////////////////////////////////////////////// reg s_axis_aresetn; initial begin s_axis_aresetn = 0; #100; s_axis_aresetn = 1; end //////////////////////////////////////////////////////// // AXIS input from generator //////////////////////////////////////////////////////// wire [159:0] s_axis_tdata; wire s_axis_tvalid; wire s_axis_tready; //////////////////////////////////////////////////////// // AXIS output //////////////////////////////////////////////////////// wire [159:0] m_axis_tdata; wire m_axis_tvalid; reg m_axis_tready; //////////////////////////////////////////////////////// // AXIS generator //////////////////////////////////////////////////////// axis_generator #( .P_SAMPLE_PERIOD(10), .P_AD_COUNT(4) ) u_axis_generator ( .i_clk(s_axis_aclk), .i_rst_n(s_axis_aresetn), .m_axis_tdata(s_axis_tdata), .m_axis_tvalid(s_axis_tvalid), .m_axis_tready(s_axis_tready) ); //////////////////////////////////////////////////////// // DUT //////////////////////////////////////////////////////// axis_data_fifo_0 u_axis_data_fifo_0 ( .s_axis_aresetn(s_axis_aresetn), .s_axis_aclk(s_axis_aclk), .s_axis_tvalid(s_axis_tvalid), .s_axis_tready(s_axis_tready), .s_axis_tdata(s_axis_tdata), .m_axis_tvalid(m_axis_tvalid), .m_axis_tready(m_axis_tready), .m_axis_tdata(m_axis_tdata) ); //延时读 initial begin m_axis_tready = 1'b0; #500; m_axis_tready = 1'b1; end //////////////////////////////////////////////////////// // FIFO WRITE monitor //////////////////////////////////////////////////////// always @(posedge s_axis_aclk) begin if(s_axis_tvalid && s_axis_tready) begin $display( "FIFO WRITE time=%0t seq=%d ad0=%d ad1=%d ad2=%d ad3=%d", $time, s_axis_tdata[159:128], s_axis_tdata[127:96], s_axis_tdata[95:64], s_axis_tdata[63:32], s_axis_tdata[31:0] ); end end //////////////////////////////////////////////////////// // FIFO READ monitor //////////////////////////////////////////////////////// integer rx_count; initial begin rx_count = 0; end always @(posedge s_axis_aclk) begin if(m_axis_tvalid && m_axis_tready) begin $display( "FIFO READ time=%0t seq=%d ad0=%d ad1=%d ad2=%d ad3=%d", $time, m_axis_tdata[159:128], m_axis_tdata[127:96], m_axis_tdata[95:64], m_axis_tdata[63:32], m_axis_tdata[31:0]); //----------------------------------------- // seq连续检查 //----------------------------------------- if(m_axis_tdata[159:128] != rx_count) begin $display( "ERROR seq expect=%d receive=%d", rx_count, m_axis_tdata[159:128] ); $stop; end //----------------------------------------- // AD检查 //----------------------------------------- if(m_axis_tdata[127:96] != rx_count*4+1 || m_axis_tdata[95:64] != rx_count*4+2 || m_axis_tdata[63:32] != rx_count*4+3 || m_axis_tdata[31:0] != rx_count*4+4) begin $display("ERROR AD DATA"); $stop; end rx_count = rx_count + 1; end end //////////////////////////////////////////////////////// // Finish //////////////////////////////////////////////////////// initial begin #20000; $display("=============================="); $display("Simulation finish"); $display("RX COUNT=%d",rx_count); $display("=============================="); $stop; end endmodule

测试

tb_axis_generator 输出

Vivado Simulator2020.2Time resolution is1pstime=205000seq=0ad0=1ad1=2ad2=3ad3=4time=315000seq=1ad0=5ad1=6ad2=7ad3=8...time=1855000seq=15ad0=61ad1=62ad2=63ad3=64time=1965000seq=16ad0=65ad1=66ad2=67ad3=68Simulation finish

tb_axis_generator 输出

FIFO WRITEtime=455000seq=0ad0=1ad1=2ad2=3ad3=4FIFO READtime=553000seq=0ad0=1ad1=2ad2=3ad3=4... FIFO READtime=19915000seq=176ad0=705ad1=706ad2=707ad3=708==============================Simulat finish==============================

tb_ip_axis_data_fifo.v 输出

FIFO WRITEtime=205000seq=0ad0=1ad1=2ad2=3ad3=4FIFO WRITEtime=315000seq=1ad0=5ad1=6ad2=7ad3=8... FIFO READtime=19815000seq=178ad0=713ad1=714ad2=715ad3=716FIFO WRITEtime=19895000seq=179ad0=717ad1=718ad2=719ad3=720FIFO READtime=19925000seq=179ad0=717ad1=718ad2=719ad3=720==============================Simulation finish RXCOUNT=180