From 157a8dee1709c68ac97b377a64612f9f795672b2 Mon Sep 17 00:00:00 2001 From: Istvan Csomortani Date: Mon, 15 Mar 2021 08:49:56 +0000 Subject: [PATCH] util_fifo2axi_bridge: Initial commit --- library/Makefile | 2 + library/util_fifo2axi_bridge/Makefile | 16 + .../util_fifo2axi_bridge.v | 613 ++++++++++++++++++ .../util_fifo2axi_bridge_constr.xdc | 17 + .../util_fifo2axi_bridge_ip.tcl | 72 ++ 5 files changed, 720 insertions(+) create mode 100644 library/util_fifo2axi_bridge/Makefile create mode 100644 library/util_fifo2axi_bridge/util_fifo2axi_bridge.v create mode 100644 library/util_fifo2axi_bridge/util_fifo2axi_bridge_constr.xdc create mode 100644 library/util_fifo2axi_bridge/util_fifo2axi_bridge_ip.tcl diff --git a/library/Makefile b/library/Makefile index 86d6bbc69..4b02d13c1 100644 --- a/library/Makefile +++ b/library/Makefile @@ -100,6 +100,7 @@ clean: $(MAKE) -C util_dec256sinc24b clean $(MAKE) -C util_delay clean $(MAKE) -C util_extract clean + $(MAKE) -C util_fifo2axi_bridge clean $(MAKE) -C util_fir_dec clean $(MAKE) -C util_fir_int clean $(MAKE) -C util_gmii_to_rgmii clean @@ -215,6 +216,7 @@ lib: $(MAKE) -C util_dec256sinc24b $(MAKE) -C util_delay $(MAKE) -C util_extract + $(MAKE) -C util_fifo2axi_bridge $(MAKE) -C util_fir_dec $(MAKE) -C util_fir_int $(MAKE) -C util_gmii_to_rgmii diff --git a/library/util_fifo2axi_bridge/Makefile b/library/util_fifo2axi_bridge/Makefile new file mode 100644 index 000000000..211bf20ac --- /dev/null +++ b/library/util_fifo2axi_bridge/Makefile @@ -0,0 +1,16 @@ +#################################################################################### +## Copyright 2018(c) Analog Devices, Inc. +## Auto-generated, do not modify! +#################################################################################### + +LIBRARY_NAME := util_fifo2axi_bridge + +GENERIC_DEPS += util_fifo2axi_bridge.v + +XILINX_DEPS += util_fifo2axi_bridge_constr.xdc +XILINX_DEPS += util_fifo2axi_bridge_ip.tcl + +XILINX_LIB_DEPS += util_axis_fifo_asym +XILINX_LIB_DEPS += util_cdc + +include ../scripts/library.mk diff --git a/library/util_fifo2axi_bridge/util_fifo2axi_bridge.v b/library/util_fifo2axi_bridge/util_fifo2axi_bridge.v new file mode 100644 index 000000000..0d1a81788 --- /dev/null +++ b/library/util_fifo2axi_bridge/util_fifo2axi_bridge.v @@ -0,0 +1,613 @@ +// *************************************************************************** +// *************************************************************************** +// Copyright 2014 - 2017 (c) Analog Devices, Inc. All rights reserved. +// +// In this HDL repository, there are many different and unique modules, consisting +// of various HDL (Verilog or VHDL) components. The individual modules are +// developed independently, and may be accompanied by separate and unique license +// terms. +// +// The user should read each of these license terms, and understand the +// freedoms and responsibilities that he or she has by using this source/core. +// +// This core is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +// A PARTICULAR PURPOSE. +// +// Redistribution and use of source or resulting binaries, with or without modification +// of this file, are permitted under one of the following two license terms: +// +// 1. The GNU General Public License version 2 as published by the +// Free Software Foundation, which can be found in the top level directory +// of this repository (LICENSE_GPL2), and also online at: +// +// +// OR +// +// 2. An ADI specific BSD license, which can be found in the top level directory +// of this repository (LICENSE_ADIBSD), and also on-line at: +// https://github.com/analogdevicesinc/hdl/blob/master/LICENSE_ADIBSD +// This will allow to generate bit files and not release the source code, +// as long as it attaches to an ADI device. +// +// *************************************************************************** +// *************************************************************************** +`timescale 1ns/1ps + +module util_fifo2axi_bridge #( + + parameter SRC_DATA_WIDTH = 64, + parameter SRC_ADDR_WIDTH = 8, + parameter DST_DATA_WIDTH = 128, + parameter DST_ADDR_WIDTH = 7, + parameter AXI_DATA_WIDTH = 512, + parameter AXI_ADDR_WIDTH = 32, + parameter AXI_ADDRESS = 32'h00000000, + parameter AXI_ADDRESS_LIMIT = 32'hffffffff, + parameter REMOVE_NULL_BEAT_EN = 0 + +) ( + + input fifo_src_clk, + input fifo_src_resetn, + input fifo_src_wen, + input [SRC_ADDR_WIDTH-1:0] fifo_src_waddr, + input [SRC_DATA_WIDTH-1:0] fifo_src_wdata, + input fifo_src_wlast, + + input fifo_dst_clk, + input fifo_dst_resetn, + input fifo_dst_ren, + input [DST_ADDR_WIDTH-1:0] fifo_dst_raddr, + output [DST_DATA_WIDTH-1:0] fifo_dst_rdata, + + // Signal to the controller, that the bridge is ready to push data to the + // destination + output fifo_dst_ready, + + // Status signals + output fifo_src_full, // FULL asserts when SOURCE data rate (e.g. ADC) is higher than DDR data rate + output fifo_dst_empty, // EMPTY asserts when DESTINATION data rate (e.g. DAC) is higher than DDR data rate + + // AXI4 interface + + input axi_clk, + input axi_resetn, + output reg axi_awvalid = 1'b0, + output [ 3:0] axi_awid, + output [ 1:0] axi_awburst, + output axi_awlock, + output [ 3:0] axi_awcache, + output [ 2:0] axi_awprot, + output [ 3:0] axi_awqos, + output [ 7:0] axi_awlen, + output [ 2:0] axi_awsize, + output reg [(AXI_ADDR_WIDTH-1):0] axi_awaddr = {AXI_ADDR_WIDTH{1'b0}}, + input axi_awready, + output reg axi_wvalid = 1'b0, + output [(AXI_DATA_WIDTH-1):0] axi_wdata, + output [(AXI_DATA_WIDTH/8-1):0] axi_wstrb, + output axi_wlast, + input axi_wready, + input axi_bvalid, + input [ 3:0] axi_bid, + input [ 1:0] axi_bresp, + output reg axi_bready = 1'b1, // always ready for write response + output reg axi_arvalid = 1'b0, + output [ 3:0] axi_arid, + output [ 1:0] axi_arburst, + output axi_arlock, + output [ 3:0] axi_arcache, + output [ 2:0] axi_arprot, + output [ 3:0] axi_arqos, + output [ 7:0] axi_arlen, + output [ 2:0] axi_arsize, + output reg [(AXI_ADDR_WIDTH-1):0] axi_araddr = {AXI_ADDR_WIDTH{1'b0}}, + input axi_arready, + input axi_rvalid, + input [ 3:0] axi_rid, + input [ 1:0] axi_rresp, + input axi_rlast, + input [(AXI_DATA_WIDTH-1):0] axi_rdata, + output axi_rready + +); + +// AXI Memory Mapped related parameters +localparam AXI_BYTE_WIDTH = AXI_DATA_WIDTH/8; +localparam AXI_SIZE = (AXI_BYTE_WIDTH > 64) ? 3'b111 : + (AXI_BYTE_WIDTH > 32) ? 3'b110 : + (AXI_BYTE_WIDTH > 16) ? 3'b101 : + (AXI_BYTE_WIDTH > 8) ? 3'b100 : + (AXI_BYTE_WIDTH > 4) ? 3'b011 : + (AXI_BYTE_WIDTH > 2) ? 3'b010 : + (AXI_BYTE_WIDTH > 1) ? 3'b001 : 3'b000; + +// we are using the max burst length by default, respecting the 4kbyte +// boundary defined by the AXI4 standard +localparam AXI_LENGTH = 4096/AXI_BYTE_WIDTH - 1; +localparam AXI_LENGTH_INDEX = $clog2(AXI_LENGTH); +localparam AXI_ADDR_INCR = (AXI_LENGTH + 1) * AXI_BYTE_WIDTH; + +// CDC FIFO's must be capable to store a 2 x 4Kbytes +// WARNING: Changing this depth will brake other part of the bridge +localparam FOUR_KB = 4096 * 8; +localparam [31:0] AXI_CDC_ADDR_WIDTH = $clog2(((2 * FOUR_KB) / AXI_DATA_WIDTH) - 1); +localparam [31:0] SRC_CDC_ADDR_WIDTH = $clog2(((2 * FOUR_KB) / SRC_DATA_WIDTH) - 1); +localparam [31:0] DST_CDC_ADDR_WIDTH = $clog2(((2 * FOUR_KB) / DST_DATA_WIDTH) - 1); + +localparam SRC_4KB_BYTE = FOUR_KB/SRC_DATA_WIDTH; +localparam DST_4KB_BYTE = FOUR_KB/DST_DATA_WIDTH; +localparam DST_4KB_BYTE_INDEX = $clog2(FOUR_KB/DST_DATA_WIDTH); +localparam AXI_4KB_BYTE = FOUR_KB/AXI_DATA_WIDTH; + +// almost full and almost empty thresholds for the CDC FIFO's +localparam WR_CDC_ALMOST_EMPTY_THRESHOLD = (SRC_DATA_WIDTH < AXI_DATA_WIDTH) ? + AXI_4KB_BYTE : + AXI_4KB_BYTE/(SRC_DATA_WIDTH/AXI_DATA_WIDTH); +localparam RD_CDC_ALMOST_EMPTY_THRESHOLD = (DST_DATA_WIDTH > AXI_DATA_WIDTH) ? + DST_4KB_BYTE : + DST_4KB_BYTE/(DST_DATA_WIDTH/AXI_DATA_WIDTH); + +//internal register and signals + +wire [AXI_DATA_WIDTH-1:0] axi_wdata_int_s; +wire [AXI_DATA_WIDTH/8-1:0] axi_wkeep_int_s; +wire axi_wlast_int_s; +wire axi_fifo_src_last_received_s; +wire axi_wcdc_almost_empty; +wire axi_wcdc_empty; +wire [AXI_CDC_ADDR_WIDTH-1:0] axi_wcdc_level_s; +wire axi_wdata_en_s; +wire axi_fifo_src_resetn; +wire axi_fifo_dst_resetn; +wire axi_rxfer_start_s; +wire axi_src_resetn; +wire axi_dst_resetn; + +reg fifo_src_last_received = 1'b0; + +reg axi_wxfer_active = 1'b0; +reg axi_rxfer_active = 1'b0; +reg axi_rdata_en = 1'b0; +reg [ 7:0] axi_awlen_d = 1'b0; +reg [ 7:0] axi_wbeat_counter = 8'b0; +reg [31:0] axi_wburst_counter = 32'b0; +reg [31:0] axi_rburst_counter = 32'b0; +reg [AXI_CDC_ADDR_WIDTH-1:0] axi_last_burst_length = {AXI_CDC_ADDR_WIDTH{1'b0}}; +reg [ 1:0] axi_wxfer_active_d = 1'b0; +reg axi_write_done = 1'b0; +reg axi_rxfer_active_d = 1'b0; + + +//----------------------------------------------------------------------------- +// Source side logic - CDC FIFO instance +//----------------------------------------------------------------------------- + +// Source CDC FIFO - source clock domain to storage unit's clock domain +util_axis_fifo_asym #( + .S_DATA_WIDTH (SRC_DATA_WIDTH), + .S_ADDRESS_WIDTH (SRC_CDC_ADDR_WIDTH), + .M_DATA_WIDTH (AXI_DATA_WIDTH), + .ASYNC_CLK (1), + .ALMOST_FULL_THRESHOLD (1), + .ALMOST_EMPTY_THRESHOLD (WR_CDC_ALMOST_EMPTY_THRESHOLD), + .TLAST_EN (1), + .TKEEP_EN (1)) +i_source_cdc_fifo ( + .s_axis_aclk (fifo_src_clk), + .s_axis_aresetn (fifo_src_resetn), + .s_axis_ready (), // TODO: check for overflow + .s_axis_valid (fifo_src_wen), // first positive edge generate address + .s_axis_data (fifo_src_wdata), + .s_axis_tkeep ({SRC_DATA_WIDTH{1'b1}}), + .s_axis_tlast (fifo_src_wlast), + .s_axis_room (), + .s_axis_full (fifo_src_full), // if asserted the memory can not keep up with the ADC + .s_axis_almost_full (), + .m_axis_aclk (axi_clk), + .m_axis_aresetn (axi_resetn), + .m_axis_ready (axi_wready_int_s), + .m_axis_valid (axi_wvalid_int_s), + .m_axis_data (axi_wdata_int_s), + .m_axis_tkeep (axi_wkeep_int_s), + .m_axis_tlast (axi_wlast_int_s), + .m_axis_level (axi_wcdc_level_s), + .m_axis_empty (axi_wcdc_empty), + .m_axis_almost_empty (axi_wcdc_almost_empty) // almost empty should be set to a full burst +); + +// save the last TKEEP for read +// NOTE: we are writing invalid data to the memory in case of a fractional last beat +reg [AXI_DATA_WIDTH/8-1:0] axi_wlast_tkeep; +always @(posedge axi_clk) begin + if (axi_src_resetn == 1'b0) begin + axi_wlast_tkeep <= 0; + end else begin + if (axi_wvalid_int_s & axi_wlast_int_s) + axi_wlast_tkeep <= axi_wkeep_int_s; + end +end + +// it helps to flush the source CDC FIFO +always @(posedge fifo_src_clk) begin + if (fifo_src_resetn == 1'b0) begin + fifo_src_last_received <= 1'b0; + end else begin + if (fifo_src_wen && fifo_src_wlast) begin + fifo_src_last_received <= 1'b1; + end + end +end + +sync_bits #( + .NUM_OF_BITS (1), + .ASYNC_CLK (1)) +i_axi_fifo_src_last_received_sync ( + .in_bits (fifo_src_last_received), + .out_clk (axi_clk), + .out_resetn (axi_resetn), + .out_bits (axi_fifo_src_last_received_s)); + +// transfer FIFO resets to AXI clock domain +sync_bits #( + .NUM_OF_BITS (2), + .ASYNC_CLK (1)) +i_axi_fifo_resetn_sync ( + .in_bits ({fifo_src_resetn, fifo_dst_resetn}), + .out_clk (axi_clk), + .out_resetn (axi_resetn), + .out_bits ({axi_fifo_src_resetn, axi_fifo_dst_resetn})); + +//----------------------------------------------------------------------------- +// Storage side logic - AXI WRITE control and data path +//----------------------------------------------------------------------------- + +// AXI write reset +assign axi_src_resetn = axi_resetn & axi_fifo_src_resetn; + +// Make sure that the CDC FIFO contains enough data for a full burst, +// otherwise wait until the last data beat is received +assign axi_wdata_en_s = ~axi_wcdc_almost_empty | (axi_fifo_src_last_received_s & axi_wvalid_int_s); + +always @(posedge axi_clk) begin + if (axi_src_resetn == 1'b0) begin + axi_write_done <= 1'b0; + end else begin + if (axi_wlast && axi_wlast_int_s) begin + axi_write_done <= 1'b1; + end + end +end + +always @(posedge axi_clk) begin + if (axi_src_resetn == 1'b0) begin + axi_wxfer_active <= 1'b0; + end else begin + if (~axi_wxfer_active) begin + axi_wxfer_active <= axi_wdata_en_s; + end else if (axi_wready & axi_wvalid & axi_wlast) begin + axi_wxfer_active <= 1'b0; + end + end +end + +always @(posedge axi_clk) begin + axi_wxfer_active_d <= {axi_wxfer_active_d, axi_wxfer_active}; +end + +// WRITE address and data channel control +always @(posedge axi_clk) begin : awvalid_control + if (axi_resetn == 1'b0) begin + axi_awvalid <= 1'b0; + end else begin + if (~axi_wxfer_active_d[0] & axi_wxfer_active) begin + axi_awvalid <= 1'b1; + end else if (axi_awready) begin + axi_awvalid <= 1'b0; + end + end +end /* awvalid_control */ + +always @(posedge axi_clk) begin : wvalid_control + if (axi_resetn == 1'b0) begin + axi_wvalid <= 1'b0; + end else begin + if (~axi_wxfer_active_d[1] & axi_wxfer_active_d[0]) begin + axi_wvalid <= axi_wvalid_int_s; + end else if (axi_wready && axi_wlast) begin + axi_wvalid <= 1'b0; + end + end +end /* wvalid_control */ + +always @(posedge axi_clk) begin : awaddr_control + if (axi_resetn == 1'b0) begin + axi_awaddr <= AXI_ADDRESS; + end else begin + if (axi_awvalid && axi_awready) begin + axi_awaddr <= axi_awaddr + AXI_ADDR_INCR; + end + end +end + +assign axi_awid = 4'b0000; +assign axi_awburst = 2'b01; // INCR (Incrementing address burst) +assign axi_awlock = 1'b0; // Normal access +assign axi_awcache = 4'b0010; // Cacheable, but not allocate +assign axi_awprot = 3'b000; // Normal, secure, data access +assign axi_awqos = 4'b0000; // Not used + +// 4KB by default, adjust at the last burst +assign axi_awlen = (axi_fifo_src_last_received_s & ~axi_wcdc_level_s[AXI_CDC_ADDR_WIDTH-1] & + axi_awvalid & axi_awready) ? + axi_wcdc_level_s : AXI_LENGTH; +assign axi_awsize = AXI_SIZE; + +// assert write_ready when the MIG/EMIF is ready and write FSM is in one of the DATA state +assign axi_wready_int_s = axi_wvalid & axi_wready & axi_wxfer_active_d[1]; +assign axi_wstrb = {AXI_BYTE_WIDTH{1'b1}}; +assign axi_wlast = (axi_wbeat_counter == axi_awlen_d) ? axi_wvalid : 1'b0; +assign axi_wdata = (axi_wxfer_active) ? axi_wdata_int_s : {AXI_DATA_WIDTH{1'b0}}; + +// latch AWLEN for WLAST generation + +always @(posedge axi_clk) begin + if (axi_awvalid & axi_awready) + axi_awlen_d <= axi_awlen; +end + +// WRITE beat counter to assert WLAST at the right time +always @(posedge axi_clk) begin + if ((axi_resetn == 1'b0) || (axi_wxfer_active == 1'b0)) begin + axi_wbeat_counter <= 0; + end else begin + if (axi_wvalid && axi_wready) begin + axi_wbeat_counter <= axi_wbeat_counter + 1'b1; + end + end +end + +// NOTE: the write CDC FIFO has a 2x4KB depth, so we need to save the last +// burst length when the level is less than 4KB +// WARNING: if the CDC depth changes for some reason this will brake +always @(posedge axi_clk) begin + if (axi_fifo_src_last_received_s & ~axi_wcdc_level_s[SRC_CDC_ADDR_WIDTH-1] & + axi_awvalid & axi_awready) begin + axi_last_burst_length <= axi_wcdc_level_s; + end +end + +//----------------------------------------------------------------------------- +// Storage side logic - READ control and data path +//----------------------------------------------------------------------------- + +// AXI read reset +assign axi_dst_resetn = axi_resetn & axi_fifo_dst_resetn; + +// the bridge is in a constant read state after the write was finished, until +// resets (e.g. before the next initialization/write) +always @(posedge axi_clk) begin + if (axi_dst_resetn == 1'b0) begin + axi_rdata_en <= 1'b0; + end if (axi_write_done) begin + axi_rdata_en <= 1'b1; + end +end + +// status flag to indicate an active read transfer +always @(posedge axi_clk) begin + if (axi_dst_resetn == 1'b0) begin + axi_rxfer_active <= 1'b0; + axi_rxfer_active_d <= 1'b0; + end else begin + if (axi_rdata_en & ~axi_rxfer_active) begin + axi_rxfer_active <= 1'b1; + end if (axi_rvalid & axi_rready & axi_rlast) begin + axi_rxfer_active <= 1'b0; + end + axi_rxfer_active_d <= axi_rxfer_active; + end +end + +// Read address and data channel control +assign axi_rxfer_start_s = axi_rxfer_active & ~axi_rxfer_active_d; +always @(posedge axi_clk) begin + if (axi_resetn == 1'b0) begin + axi_arvalid <= 1'b0; + end else begin + if (axi_rxfer_start_s) begin + axi_arvalid <= 1'b1; + end else if (axi_arready) begin + axi_arvalid <= 1'b0; + end + end +end + +// the bridge needs to predict the last read burst to specify its length +// this is done by counting the write and read burst, and presuming that after +// a X number of write burst will follow n*X number of read burst, where n is +// an integer number + +always @(posedge axi_clk) begin + if (axi_resetn == 1'b0) begin + axi_wburst_counter <= 0; + end else begin + if (axi_awvalid && axi_awready) begin + axi_wburst_counter <= axi_wburst_counter + 1'b1; + end + end +end + +always @(posedge axi_clk) begin + if (axi_resetn == 1'b0) begin + axi_rburst_counter <= 0; + end else begin + if (axi_arvalid && axi_arready) begin + axi_rburst_counter <= (axi_rburst_counter == axi_wburst_counter) ? 32'b0 : axi_rburst_counter + 1'b1; + end + end +end + +// READ address generation +always @(posedge axi_clk) begin + if (axi_resetn == 1'b0) begin + axi_araddr <= {AXI_ADDR_WIDTH{1'b0}}; + end else begin + if (axi_arvalid && axi_arready) begin + axi_araddr <= (axi_rburst_counter == axi_wburst_counter) ? AXI_ADDRESS : axi_araddr + AXI_ADDR_INCR; + end + end +end + +// READ burst length generation +assign axi_arlen = (axi_arvalid & axi_arready & + (axi_rburst_counter == axi_wburst_counter - 1)) ? + axi_last_burst_length : AXI_LENGTH; + +assign axi_arid = 4'b0000; +assign axi_arburst = 2'b01; +assign axi_arlock = 1'b0; +assign axi_arcache = 4'b0010; +assign axi_arprot = 3'b000; +assign axi_arqos = 4'b0000; +assign axi_arsize = AXI_SIZE; + +wire [AXI_DATA_WIDTH/8:0] axi_rlast_keep_s = (axi_rlast) ? axi_wlast_tkeep : {AXI_DATA_WIDTH/8{1'b1}}; + +wire [DST_DATA_WIDTH/8-1:0] fifo_dst_tkeep_int_s; +wire [DST_DATA_WIDTH-1:0] fifo_dst_tdata_int_s; +wire fifo_dst_tready_int_s; +wire fifo_dst_tvalid_int_s; +wire fifo_dst_almost_empty_s; +wire fifo_dst_empty_s; + +// Destination CDC FIFO - storage unit's clock domain to destination clock +// domain +util_axis_fifo_asym #( + .S_DATA_WIDTH (AXI_DATA_WIDTH), + .S_ADDRESS_WIDTH (AXI_CDC_ADDR_WIDTH), + .M_DATA_WIDTH (DST_DATA_WIDTH), + .ASYNC_CLK (1), + .ALMOST_FULL_THRESHOLD (AXI_4KB_BYTE), + .ALMOST_EMPTY_THRESHOLD (DST_4KB_BYTE), + .TKEEP_EN (1), + .TLAST_EN (1)) +i_destination_cdc_fifo ( + .s_axis_aclk (axi_clk), + .s_axis_aresetn (axi_resetn), + .s_axis_ready (axi_rready), + .s_axis_valid (axi_rvalid), + .s_axis_data (axi_rdata), + .s_axis_tkeep (axi_rlast_keep_s), + .s_axis_tlast (axi_rlast), + .s_axis_full (), + .s_axis_almost_full (), + .m_axis_aclk (fifo_dst_clk), + .m_axis_aresetn (fifo_dst_resetn), + .m_axis_ready (fifo_dst_tready_int_s & fifo_dst_cdc_ready), + .m_axis_valid (fifo_dst_tvalid_int_s), + .m_axis_data (fifo_dst_tdata_int_s), + .m_axis_tkeep (fifo_dst_tkeep_int_s), + .m_axis_tlast (), + .m_axis_almost_empty (fifo_dst_almost_empty_s), + .m_axis_empty (fifo_dst_empty_s) // if asserted the memory can not keep up with the DAC rate +); + +// Assuming that the EMIF has a higher bandwidth than the destination +// interface, load at least one burst into CDC FIFO +reg fifo_dst_cdc_ready = 0; +always @(posedge fifo_dst_clk) begin + if (fifo_dst_resetn == 1'b0) begin + fifo_dst_cdc_ready <= 1'b0; + end else begin + if (~fifo_dst_almost_empty_s) begin + fifo_dst_cdc_ready <= 1'b1; + end else if (fifo_dst_empty_s) begin + fifo_dst_cdc_ready <= 1'b0; + end + end +end + +generate + if (REMOVE_NULL_BEAT_EN) begin : null_beat_filter + + wire fifo_nf_ready; + wire fifo_nf_almost_empty; + + reg fifo_dst_almost_empty_d = 0; + reg fifo_nf_resetn = 0; + + // NULL filter FIFO will come out of reset after the CDC FIFO has at least + // one full AXIS4 burst (4KByte) + always @(posedge fifo_dst_clk) begin + fifo_dst_almost_empty_d <= fifo_dst_almost_empty_s; + end + + always @(posedge fifo_dst_clk) begin + if (~fifo_dst_resetn) begin + fifo_nf_resetn <= 0; + end else begin + if (fifo_dst_almost_empty_d & ~fifo_dst_almost_empty_s) begin + fifo_nf_resetn <= 1; + end + end + end + + util_axis_fifo #( + .DATA_WIDTH (DST_DATA_WIDTH), + .ADDRESS_WIDTH (4), + .ASYNC_CLK (0), + .ALMOST_EMPTY_THRESHOLD (12), + .ALMOST_FULL_THRESHOLD (8), + .REMOVE_NULL_BEAT_EN (1), + .TKEEP_EN (1), + .TLAST_EN (0)) + i_null_filter_fifo ( + .s_axis_aclk (fifo_dst_clk), + .s_axis_aresetn (fifo_nf_resetn), + .s_axis_ready (fifo_dst_tready_int_s), + .s_axis_valid (fifo_dst_tvalid_int_s), + .s_axis_data (fifo_dst_tdata_int_s), + .s_axis_tkeep (fifo_dst_tkeep_int_s), + .s_axis_tlast (), + .s_axis_full (), + .s_axis_almost_full (), + .m_axis_aclk (fifo_dst_clk), + .m_axis_aresetn (fifo_nf_resetn), + .m_axis_ready (fifo_dst_ren), // FIFO is ready after CDC FIFO is ready and it's almost empty + .m_axis_valid (), + .m_axis_data (fifo_dst_rdata), + .m_axis_tkeep (), + .m_axis_tlast (), + .m_axis_almost_empty (fifo_nf_almost_empty), + .m_axis_empty () // if asserted the memory can not keep up with the DAC rate + ); + + reg fifo_dst_ready_int = 1'b0; + always @(posedge fifo_dst_clk) begin + if (fifo_dst_resetn == 1'b0) begin + fifo_dst_ready_int <= 1'b0; + end else begin + // bridge is ready when null filter FIFO is ready + if (fifo_dst_cdc_ready & ~fifo_nf_almost_empty) begin + fifo_dst_ready_int <= 1'b1; + end + end + end + + assign fifo_dst_ready = fifo_dst_ready_int; + assign fifo_dst_empty = fifo_dst_empty_s | fifo_nf_almost_empty; + + end else begin + + assign fifo_dst_tready_int_s = fifo_dst_ren; + assign fifo_dst_ready = fifo_dst_cdc_ready; + assign fifo_dst_rdata = fifo_dst_tdata_int_s; + assign fifo_dst_empty = fifo_dst_empty_s; + + end + endgenerate + +endmodule + diff --git a/library/util_fifo2axi_bridge/util_fifo2axi_bridge_constr.xdc b/library/util_fifo2axi_bridge/util_fifo2axi_bridge_constr.xdc new file mode 100644 index 000000000..e89818e70 --- /dev/null +++ b/library/util_fifo2axi_bridge/util_fifo2axi_bridge_constr.xdc @@ -0,0 +1,17 @@ + +set_property ASYNC_REG TRUE \ + [get_cells -quiet -hier *cdc_sync_stage1_reg*] \ + [get_cells -quiet -hier *cdc_sync_stage2_reg*] + +set_false_path \ + -to [get_pins -hierarchical * -filter {NAME=~*i_waddr_sync_gray/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -to [get_pins -hierarchical * -filter {NAME=~*i_raddr_sync_gray/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -to [get_pins -hierarchical * -filter {NAME=~*i_axi_fifo_src_last_received_sync/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -to [get_pins -hierarchical * -filter {NAME=~*i_axi_fifo_resetn_sync/cdc_sync_stage1_reg[*]/D}] + diff --git a/library/util_fifo2axi_bridge/util_fifo2axi_bridge_ip.tcl b/library/util_fifo2axi_bridge/util_fifo2axi_bridge_ip.tcl new file mode 100644 index 000000000..57b1720e6 --- /dev/null +++ b/library/util_fifo2axi_bridge/util_fifo2axi_bridge_ip.tcl @@ -0,0 +1,72 @@ +source ../scripts/adi_env.tcl +source $ad_hdl_dir/library/scripts/adi_ip_xilinx.tcl + +adi_ip_create util_fifo2axi_bridge +adi_ip_files util_fifo2axi_bridge [list \ + "util_fifo2axi_bridge_constr.xdc" \ + "util_fifo2axi_bridge.v" \ +] + +adi_ip_properties_lite util_fifo2axi_bridge + +adi_ip_add_core_dependencies { \ + analog.com:user:util_cdc:1.0 \ + analog.com:user:util_axis_fifo_asym:1.0 \ +} + +set_property display_name "ADI FIFO to AXI4 bridge" [ipx::current_core] +set_property description "Bridge between a FIFO READ/WRITE interface and an AXI4 Memory Mapped interface" [ipx::current_core] + +## Infer the AXI4 Memory Map interface +adi_add_bus "ddr_axi" "master" \ + "xilinx.com:interface:aximm_rtl:1.0" \ + "xilinx.com:interface:aximm:1.0" \ + [ list \ + {"axi_awvalid" "AWVALID"} \ + {"axi_awid" "AWID"} \ + {"axi_awburst" "AWBURST"} \ + {"axi_awlock" "AWLOCK"} \ + {"axi_awcache" "AWCACHE"} \ + {"axi_awprot" "AWPROT"} \ + {"axi_awqos" "AWQOS"} \ + {"axi_awlen" "AWLEN"} \ + {"axi_awsize" "AWSIZE"} \ + {"axi_awaddr" "AWADDR"} \ + {"axi_awready" "AWREADY"} \ + {"axi_wvalid" "WVALID"} \ + {"axi_wdata" "WDATA"} \ + {"axi_wstrb" "WSTRB"} \ + {"axi_wlast" "WLAST"} \ + {"axi_wready" "WREADY"} \ + {"axi_bvalid" "BVALID"} \ + {"axi_bid" "BID"} \ + {"axi_bresp" "BRESP"} \ + {"axi_bready" "BREADY"} \ + {"axi_arvalid" "ARVALID"} \ + {"axi_arid" "ARID"} \ + {"axi_arburst" "ARBURST"} \ + {"axi_arlock" "ARLOCK"} \ + {"axi_arcache" "ARCACHE"} \ + {"axi_arprot" "ARPROT"} \ + {"axi_arqos" "ARQOS"} \ + {"axi_arlen" "ARLEN"} \ + {"axi_arsize" "ARSIZE"} \ + {"axi_araddr" "ARADDR"} \ + {"axi_arready" "ARREADY"} \ + {"axi_rvalid" "RVALID"} \ + {"axi_rready" "RREADY"} \ + {"axi_rid" "RID"} \ + {"axi_rresp" "RRESP"} \ + {"axi_rlast" "RLAST"} \ + {"axi_rdata" "RDATA"} ] +adi_add_bus_clock "axi_clk" "ddr_axi" "axi_resetn" + +ipx::add_address_space ddr_axi [ipx::current_core] +set_property master_address_space_ref ddr_axi [ipx::get_bus_interfaces ddr_axi \ + -of_objects [ipx::current_core]] +set_property range 4294967296 [ipx::get_address_spaces ddr_axi \ + -of_objects [ipx::current_core]] +set_property width 512 [ipx::get_address_spaces ddr_axi \ + -of_objects [ipx::current_core]] + +ipx::save_core [ipx::current_core]