// *************************************************************************** // *************************************************************************** // 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