util_axis_fifo_asym: Initial commit

main
Istvan Csomortani 2021-02-09 09:08:42 +00:00 committed by Adrian Costina
parent 1543eb8881
commit 0959c2bcad
3 changed files with 366 additions and 0 deletions

View File

@ -0,0 +1,15 @@
####################################################################################
## Copyright 2018(c) Analog Devices, Inc.
## Auto-generated, do not modify!
####################################################################################
LIBRARY_NAME := util_axis_fifo_asym
GENERIC_DEPS += util_axis_fifo_asym.v
XILINX_DEPS += util_axis_fifo_asym_ip.tcl
XILINX_LIB_DEPS += util_cdc
XILINX_LIB_DEPS += util_axis_fifo
include ../scripts/library.mk

View File

@ -0,0 +1,309 @@
// ***************************************************************************
// ***************************************************************************
// 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_axis_fifo_asym #(
parameter ASYNC_CLK = 1,
parameter S_DATA_WIDTH = 64,
parameter S_ADDRESS_WIDTH = 5,
parameter M_DATA_WIDTH = 128,
parameter M_AXIS_REGISTERED = 1,
parameter ALMOST_EMPTY_THRESHOLD = 4,
parameter ALMOST_FULL_THRESHOLD = 4,
parameter TLAST_EN = 0,
parameter TKEEP_EN = 0
) (
input m_axis_aclk,
input m_axis_aresetn,
input m_axis_ready,
output m_axis_valid,
output [M_DATA_WIDTH-1:0] m_axis_data,
output [M_DATA_WIDTH/8-1:0] m_axis_tkeep,
output m_axis_tlast,
output m_axis_empty,
output m_axis_almost_empty,
output [31:0] m_axis_level,
input s_axis_aclk,
input s_axis_aresetn,
output s_axis_ready,
input s_axis_valid,
input [S_DATA_WIDTH-1:0] s_axis_data,
input [S_DATA_WIDTH/8-1:0] s_axis_tkeep,
input s_axis_tlast,
output s_axis_full,
output s_axis_almost_full,
output [S_ADDRESS_WIDTH-1:0] s_axis_room
);
// define which interface has a wider bus
localparam RATIO_TYPE = (S_DATA_WIDTH >= M_DATA_WIDTH) ? 1 : 0;
// bus width ratio
localparam RATIO = (RATIO_TYPE) ? S_DATA_WIDTH/M_DATA_WIDTH : M_DATA_WIDTH/S_DATA_WIDTH;
// atomic parameters - NOTE: depth is always defined by the slave attributes
localparam A_WIDTH = (RATIO_TYPE) ? M_DATA_WIDTH : S_DATA_WIDTH;
localparam A_ADDRESS = (RATIO_TYPE) ? S_ADDRESS_WIDTH : (S_ADDRESS_WIDTH-$clog2(RATIO));
localparam A_ALMOST_FULL_THRESHOLD = (RATIO_TYPE) ? ALMOST_FULL_THRESHOLD : (ALMOST_FULL_THRESHOLD/RATIO);
localparam A_ALMOST_EMPTY_THRESHOLD = (RATIO_TYPE) ? (ALMOST_EMPTY_THRESHOLD/RATIO) : ALMOST_EMPTY_THRESHOLD;
// slave and master sequencers
reg [$clog2(RATIO)-1:0] s_axis_counter;
reg [$clog2(RATIO)-1:0] m_axis_counter;
wire [RATIO-1:0] m_axis_ready_int_s;
wire [RATIO-1:0] m_axis_valid_int_s;
wire [RATIO*A_WIDTH-1:0] m_axis_data_int_s;
wire [RATIO*A_WIDTH/8-1:0] m_axis_tkeep_int_s;
wire [RATIO-1:0] m_axis_tlast_int_s;
wire [RATIO-1:0] m_axis_empty_int_s;
wire [RATIO-1:0] m_axis_almost_empty_int_s;
wire [RATIO*A_ADDRESS-1:0] m_axis_level_int_s;
wire [RATIO-1:0] s_axis_ready_int_s;
wire [RATIO-1:0] s_axis_valid_int_s;
wire [RATIO*A_WIDTH-1:0] s_axis_data_int_s;
wire [RATIO*A_WIDTH/8-1:0] s_axis_tkeep_int_s;
wire [RATIO-1:0] s_axis_tlast_int_s;
wire [RATIO-1:0] s_axis_full_int_s;
wire [RATIO-1:0] s_axis_almost_full_int_s;
wire [RATIO*A_ADDRESS-1:0] s_axis_room_int_s;
// instantiate the FIFOs
genvar i;
generate
for (i=0; i<RATIO; i=i+1) begin
util_axis_fifo #(
.DATA_WIDTH (A_WIDTH),
.ADDRESS_WIDTH (A_ADDRESS),
.ASYNC_CLK (ASYNC_CLK),
.M_AXIS_REGISTERED (M_AXIS_REGISTERED),
.ALMOST_EMPTY_THRESHOLD (A_ALMOST_EMPTY_THRESHOLD),
.ALMOST_FULL_THRESHOLD (A_ALMOST_FULL_THRESHOLD),
.TKEEP_EN (TKEEP_EN),
.TLAST_EN (TLAST_EN))
i_fifo (
.m_axis_aclk (m_axis_aclk),
.m_axis_aresetn (m_axis_aresetn),
.m_axis_ready (m_axis_ready_int_s[i]),
.m_axis_valid (m_axis_valid_int_s[i]),
.m_axis_data (m_axis_data_int_s[A_WIDTH*i+:A_WIDTH]),
.m_axis_tlast (m_axis_tlast_int_s[i]),
.m_axis_tkeep (m_axis_tkeep_int_s[A_WIDTH/8*i+:A_WIDTH/8]),
.m_axis_level (m_axis_level_int_s[A_ADDRESS*i+:A_ADDRESS]),
.m_axis_empty (m_axis_empty_int_s[i]),
.m_axis_almost_empty (m_axis_almost_empty_int_s[i]),
.s_axis_aclk (s_axis_aclk),
.s_axis_aresetn (s_axis_aresetn),
.s_axis_ready (s_axis_ready_int_s[i]),
.s_axis_valid (s_axis_valid_int_s[i]),
.s_axis_data (s_axis_data_int_s[A_WIDTH*i+:A_WIDTH]),
.s_axis_tkeep (s_axis_tkeep_int_s[A_WIDTH/8*i+:A_WIDTH/8]),
.s_axis_tlast (s_axis_tlast_int_s[i]),
.s_axis_room (s_axis_room_int_s[A_ADDRESS*i+:A_ADDRESS]),
.s_axis_full (s_axis_full_int_s[i]),
.s_axis_almost_full (s_axis_almost_full_int_s[i])
);
end
endgenerate
// write or slave logic
generate
if (RATIO_TYPE) begin : big_slave
for (i=0; i<RATIO; i=i+1) begin
assign s_axis_valid_int_s[i] = s_axis_valid & s_axis_ready;
assign s_axis_tlast_int_s[i] = s_axis_tlast;
end
assign s_axis_tkeep_int_s = s_axis_tkeep;
assign s_axis_data_int_s = s_axis_data;
// if every instance is ready, the interface is ready
assign s_axis_ready = &(s_axis_ready_int_s);
// if one of the atomic instance is full, s_axis_full is asserted
assign s_axis_full = |s_axis_full_int_s;
assign s_axis_almost_full = |s_axis_almost_full_int_s;
// the FIFO has the same room as the atomic FIFO
assign s_axis_room = s_axis_room_int_s[A_ADDRESS-1:0];
end else begin : small_slave
reg [RATIO-1:0] s_axis_valid_int_d = {RATIO{1'b0}};
for (i=0; i<RATIO; i=i+1) begin
assign s_axis_data_int_s[A_WIDTH*i+:A_WIDTH] = s_axis_data;
assign s_axis_tkeep_int_s[A_WIDTH/8*i+:A_WIDTH/8] = (s_axis_counter == i) ? s_axis_tkeep : {A_WIDTH/8{1'b0}};
assign s_axis_tlast_int_s[i] = (s_axis_counter == i) ? s_axis_tlast : 1'b0;
end
// insert invalid writes (TKEEP==0) in case if the TLAST arrives before the RATIO
// boundary
always @(*) begin
case ({s_axis_valid, s_axis_tlast})
2'b00 : s_axis_valid_int_d = {RATIO{1'b0}};
2'b01 : s_axis_valid_int_d = {RATIO{1'b0}};
2'b10 : s_axis_valid_int_d = {{RATIO-1{1'b0}}, 1'b1} << s_axis_counter;
2'b11 : s_axis_valid_int_d = {RATIO{1'b1}} << s_axis_counter;
endcase
end
assign s_axis_valid_int_s = s_axis_valid_int_d;
// READY/FULL/ALMOST_FULL is driven by the current atomic instance
assign s_axis_ready = s_axis_ready_int_s >> s_axis_counter;
assign s_axis_almost_full = s_axis_almost_full_int_s >> s_axis_counter;
// the FIFO has the same room as the last atomic instance
// (NOTE: this is not the real room value, rather the value will be updated
// after every RATIO number of writes)
assign s_axis_full = s_axis_full_int_s[RATIO-1];
assign s_axis_room = {s_axis_room_int_s[A_ADDRESS*(RATIO-1)+:A_ADDRESS], {$clog2(RATIO){1'b1}}};
end
endgenerate
// read or slave logic
generate
if (RATIO_TYPE) begin : small_master
for (i=0; i<RATIO; i=i+1) begin
assign m_axis_ready_int_s[i] = (m_axis_counter == i) ? m_axis_ready : 1'b0;
end
assign m_axis_data = m_axis_data_int_s >> (m_axis_counter*A_WIDTH) ;
assign m_axis_tkeep = m_axis_tkeep_int_s >> (m_axis_counter*A_WIDTH/8) ;
// VALID/EMPTY/ALMOST_EMPTY is driven by the current atomic instance
assign m_axis_valid = m_axis_valid_int_s >> m_axis_counter;
// the FIFO has the same level as the last atomic instance
// (NOTE: this is not the real level value, rather the value will be updated
// after every RATIO number of reads)
assign m_axis_level = {m_axis_level_int_s[A_ADDRESS-1:0], {$clog2(RATIO){1'b0}}};
assign m_axis_almost_empty = m_axis_almost_empty_int_s[RATIO-1];
assign m_axis_empty = m_axis_empty_int_s[RATIO-1];
end else begin : big_master
for (i=0; i<RATIO; i=i+1) begin
assign m_axis_ready_int_s[i] = m_axis_ready;
end
for (i=0; i<RATIO; i=i+1) begin
assign m_axis_tkeep[i*A_WIDTH/8+:A_WIDTH/8] = (m_axis_tlast_int_s[i:0] == 0) ||
(m_axis_tlast_int_s[i]) ?
m_axis_tkeep_int_s[i*A_WIDTH/8+:A_WIDTH/8] :
{(A_WIDTH/8){1'b0}};
end
assign m_axis_data = m_axis_data_int_s;
// if every instance has a valid data, the interface has valid data,
// otherwise valid is asserted only if TLAST is asserted
assign m_axis_valid = (|m_axis_tlast_int_s) ? |m_axis_valid_int_s : &m_axis_valid_int_s;
// if one of the atomic instance is empty, m_axis_empty should be asserted
assign m_axis_empty = |m_axis_empty_int_s;
assign m_axis_almost_empty = |m_axis_almost_empty_int_s;
// the FIFO has the same room as the atomic FIFO
assign m_axis_level = m_axis_level_int_s[A_ADDRESS-1:0];
end
assign m_axis_tlast = (m_axis_valid) ? |m_axis_tlast_int_s : 1'b0;
endgenerate
// slave handshake counter
reg s_axis_tlast_d = 0;
always @(posedge s_axis_aclk) begin
s_axis_tlast_d <= s_axis_tlast;
end
generate
if (RATIO == 1) begin
always @(*) begin
s_axis_counter <= 1'b1;
end
end else if (RATIO > 1) begin
if (RATIO_TYPE) begin
always @(posedge s_axis_aclk) begin
if (!s_axis_aresetn) begin
s_axis_counter <= 0;
end else begin
if (s_axis_ready && s_axis_valid) begin
s_axis_counter <= s_axis_counter + 1'b1;
end
end
end
end else begin
// in case of a small slave, after an active TLAST reset the counter
always @(posedge s_axis_aclk) begin
if (!s_axis_aresetn || s_axis_tlast_d) begin
s_axis_counter <= 0;
end else begin
if (s_axis_ready && s_axis_valid) begin
s_axis_counter <= s_axis_counter + 1'b1;
end
end
end
end
end
endgenerate
// master handshake sequencer
generate
if (RATIO == 1) begin
always @(*) begin
m_axis_counter <= 1'b0;
end
end else if (RATIO > 1) begin
always @(posedge m_axis_aclk) begin
if (!m_axis_aresetn) begin
m_axis_counter <= 0;
end else begin
if (m_axis_ready && m_axis_valid) begin
m_axis_counter <= m_axis_counter + 1'b1;
end
end
end
end
endgenerate
endmodule

View File

@ -0,0 +1,42 @@
source ../scripts/adi_env.tcl
source $ad_hdl_dir/library/scripts/adi_ip_xilinx.tcl
adi_ip_create util_axis_fifo_asym
adi_ip_files util_axis_fifo_asym [list \
"util_axis_fifo_asym.v" \
]
adi_ip_properties_lite util_axis_fifo_asym
adi_ip_add_core_dependencies { \
analog.com:user:util_cdc:1.0 \
analog.com:user:util_axis_fifo:1.0 \
}
adi_add_bus "s_axis" "slave" \
"xilinx.com:interface:axis_rtl:1.0" \
"xilinx.com:interface:axis:1.0" \
{
{"s_axis_valid" "TVALID"} \
{"s_axis_ready" "TREADY"} \
{"s_axis_data" "TDATA"} \
{"s_axis_tlast" "TLAST"} \
}
adi_add_bus "m_axis" "master" \
"xilinx.com:interface:axis_rtl:1.0" \
"xilinx.com:interface:axis:1.0" \
{
{"m_axis_valid" "TVALID"} \
{"m_axis_ready" "TREADY"} \
{"m_axis_data" "TDATA"} \
{"m_axis_tlast" "TLAST"} \
}
adi_add_bus_clock "m_axis_aclk" "m_axis" "m_axis_aresetn"
adi_add_bus_clock "s_axis_aclk" "s_axis" "s_axis_aresetn"
## TODO: Validate RD_ADDRESS_WIDTH
ipx::save_core [ipx::current_core]