pluto_hdl_adi/library/util_fifo2axi_bridge/util_fifo2axi_bridge.v

614 lines
22 KiB
Verilog

// ***************************************************************************
// ***************************************************************************
// 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:
// <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
//
// 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