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.vaxis_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 endmoduletb_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 endmoduleaxis_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) ); endmoduletb_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 endmoduleaxis_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 Name | axis_data_fifo_0 |
| FIFO depth | 512 |
| Memory type | Auto |
| Independent clocks | No |
| CDC sync stages | 3 |
| Enable packet mode | No |
| ACLKEN conversion mode | None |
| Enable ECC | No |
| Include ECC error injection | No |
| 参数 | 配置值 |
|---|---|
| TDATA width (bytes) | 20 |
| Enable TSTRB | No |
| Enable TKEEP | No |
| Enable TLAST | No |
| 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 finishtb_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